nginx-push-stream模块源码学习(三)——发布
一、概述
发布:发布者将MSG post到某一特定通道上,channel将信息缓存
在说明发布流程之前有必要说明下channel和msg的数据结构。
二、数据结构
2.1 MSG
发布时,模块先将消息转化为ngx_http_push_stream_msg_t的数据结构进行存储。
- // message queue
- typedef struct {
- ngx_queue_t queue; // this MUST be first
- time_t expires;//消息过期时间
- time_t time;//消息创建时间
- ngx_flag_t deleted;//是否已删除
- ngx_int_t id;
- ngx_str_t *raw;//纯文本
- ngx_int_t tag;
- ngx_str_t *event_id;//支持event source
- ngx_str_t *event_id_message;
- ngx_str_t *formatted_messages;//格式化后消息
- ngx_int_t workers_ref_count;//待发送该消息ngx worker计数
- } ngx_http_push_stream_msg_t;
@queue:每个channel会维护一个消息链表,每向channel发布一条消息,channel将其添加到自身的消息链表中。
@expires,@deleted:消息的过期时间,待介绍过订阅流程后,我会整理出一条消息的生产周期,到时会详细阐述该字段的意义。
@raw,@formatted_messages:该模块允许自定义三种消息模板——header模板:当收到订阅请求后发送模板消息;message模板:对消息体格式化;footer模板:断开连接时发送该模板。raw为消息原始内容,后者为应用message模板格式化后的信息
2.2 channel
channel作为发布订阅的中间载体,理解理解它的存储至关重要。
- typedef struct {
- ngx_rbtree_node_t node; // this MUST be first
- ngx_str_t id;
- ngx_uint_t last_message_id;
- time_t last_message_time;
- ngx_int_t last_message_tag;
- ngx_uint_t stored_messages;//# of messages
- ngx_uint_t subscribers;//# of subscribers
- ngx_http_push_stream_pid_queue_t workers_with_subscribers;//处理该channel上订阅者的ngx worker进程链表
- ngx_http_push_stream_msg_t message_queue;//消息链表
- time_t expires;//过期时间
- ngx_flag_t deleted;//是否已删除
- ngx_flag_t broadcast;//是否为广播通道
- ngx_http_push_stream_msg_t *channel_deleted_message;//已删除消息链表
- } ngx_http_push_stream_channel_t;
2.3 worker msg
- // messages to worker processes
- typedef struct {
- ngx_queue_t queue;
- ngx_http_push_stream_msg_t *msg; // ->shared memory
- ngx_pid_t pid;
- ngx_http_push_stream_channel_t *channel; // ->shared memory
- ngx_http_push_stream_queue_elem_t *subscribers_sentinel; // ->a worker's local pool
- } ngx_http_push_stream_worker_msg_t;
模块初始化时为每个ngx worker分配一片独立的工作区,工作区中维护一份消息链表。
三、流程
发布流程总的流程图如图所示:
对于删除channel和获取channel info的流程比较简单,不做阐述,具体说明下发布消息流程,流程图如图所示:
需要说明的是“向所有订阅者发送MSG”的过程:
- 向每个有该channel订阅者的worker(workers_with_subscriber)的消息链表中插入一条消息
- 向上述worker发送CHECK_MESSAGES指令,触发msg发送流程(ngx_http_push_stream_process_worker_message)
MSG发送(ngx_http_push_stream_process_worker_message):
- // now let's respond to some requests!
- //对于该channel上的所有订阅者
- while ((cur = (ngx_http_push_stream_queue_elem_t *) ngx_queue_next(&cur->queue)) != subscribers_sentinel) {
- ngx_http_push_stream_subscriber_t *subscriber = (ngx_http_push_stream_subscriber_t *) cur->value;
- //如果订阅者为longpolling模式
- if (subscriber->longpolling) {
- ngx_http_push_stream_queue_elem_t *prev = (ngx_http_push_stream_queue_elem_t *) ngx_queue_prev(&cur-
- >queue);
- //发送longpolling头(last Modified/Etag)
- ngx_http_push_stream_add_polling_headers(subscriber->request, msg->time, msg->tag, subscriber->reque
- st->pool);
- ngx_http_send_header(subscriber->request);
- //发送模块配置header模板
- ngx_http_push_stream_send_response_content_header(subscriber->request, ngx_http_get_module_loc_conf(
- subscriber->request, ngx_http_push_stream_module));
- //发送响应MSG
- ngx_http_push_stream_send_response_message(subscriber->request, channel, msg);
- //发送footer模板,last chunck("\0"CRLF CRLF)
- ngx_http_push_stream_send_response_finalize(subscriber->request);
- cur = prev;
- } else {//stream或polling模式
- if (ngx_http_push_stream_send_response_message(subscriber->request, channel, msg) == NGX_ERROR) {
- ngx_http_push_stream_queue_elem_t *prev = (ngx_http_push_stream_queue_elem_t *) ngx_queue_prev(&
- cur->queue);
- ngx_http_push_stream_send_response_finalize(subscriber->request);
- cur = prev;
- }
- }
说明:
可以看出push stream模块在发布过程中针对longpolling和stream两种模式的不同:
- Longpolling模式下,每次发布消息时会发送longpolling头:last modified和etag,使得客户端下次请求时可据此判断服务端是否有更新的消息待发布。
- longpolling模式下,订阅者每次请求都会在获得数据后断开重连,所以每次发布时都会发送header模板
- ngx_http_push_stream_send_response_finalize同时会清理订阅者
nginx-push-stream模块源码学习(三)——发布的更多相关文章
- ngx-push-stream模块源码学习(四)——订阅
一.概述 push stream模块允许三种模式的订阅者: longpolling:每收到服务端响应数据即断开连接然后迅速重连,连接耗时可以忽略 stream:与服务端保持长连接,持续不断的请求-&g ...
- nginx-push-stream模块源码学习(二)——模块初始化
本文重点介绍push stream模块的构成,至于nginx如何启动.维护该模块不会详细阐述,以后有时间会做详细阐述. 一.模块定义 1.1. 模块配置 通用nginx模块的配置struct有三种, ...
- nginx健康检查模块源码分析
nginx健康检查模块 本文所说的nginx健康检查模块是指nginx_upstream_check_module模块.nginx_upstream_check_module模块是Taobao定制的用 ...
- mybatis源码学习(三)-一级缓存二级缓存
本文主要是个人学习mybatis缓存的学习笔记,主要有以下几个知识点 1.一级缓存配置信息 2.一级缓存源码学习笔记 3.二级缓存配置信息 4.二级缓存源码 5.一级缓存.二级缓存总结 1.一级缓存配 ...
- Vue源码学习三 ———— Vue构造函数包装
Vue源码学习二 是对Vue的原型对象的包装,最后从Vue的出生文件导出了 Vue这个构造函数 来到 src/core/index.js 代码是: import Vue from './instanc ...
- 05.ElementUI源码学习:项目发布配置(github pages&npm package)
0x00.前言 书接上文.项目第一个组件已经封装好,说明文档也已编写好.下面需要将说明文档发布到外网上,以此来展示和推广项目,使用 Github Pages功能实现.同时将组件发布之 npm 上,方便 ...
- ngx-push-stream模块源码学习(五)——内存清理
1.定时器 采用nginx自身的定时器管理机制,具体细节待学习过nginx源码后加以补充 2.channel的生成周期 (0).初始(诞生) 发布.订阅均有可能产生ch ...
- spring源码学习(三)--spring循环引用源码学习
在spring中,是支持单实例bean的循环引用(循环依赖)的,循环依赖,简单而言,就是A类中注入了B类,B类中注入了A类,首先贴出我的代码示例 @Component public class Add ...
- [spring源码学习]三、IOC源码——自定义配置文件读取
一.环境准备 在文件读取的时候,第9步我们发现spring会根据标签的namespace来选择读取方式,联想spring里提供的各种标签,比如<aop:xxx>等应该会有不同的读取和解析方 ...
随机推荐
- 虚拟WiFi
转载用WinForm写一个虚拟WiFi助手玩玩(附源码) 这早不是什么新鲜的东西了,同类软件已经有很多,但不是收费就是有广告,在学校的时候就想自已写一个了,但那时候啥也没学,对C的掌握程度也就是定 ...
- LeetCode——Spiral Matrix
Given a matrix of m x n elements (m rows, n columns), return all elements of the matrix in spiral or ...
- asp.net mvc3 的数据验证(一)
原文:asp.net mvc3 的数据验证(一) 对于web开发人员来说,对用户输入的信息进行验证是一个重要但是繁琐的工作,而且很多开发者都会忽略.asp.net mvc3框架使用的是叫做“ ...
- PHP 调用微信JS-SDK 开发详解 [网摘]
一:准备文件,并将文件置于网站根目录下 access_token.json {"access_token":"","expire_time" ...
- java设计模式之十桥接模式(Bridge)
桥接模式就是把事物和其具体实现分开,使他们可以各自独立的变化.桥接的用意是:将抽象化与实现化解耦,使得二者可以独立变化,像我们常用的JDBC桥DriverManager一样,JDBC进行连接数据库的时 ...
- JAVA 代码生成。SimpleCaptcha
去官方网站下载Jar包: http://simplecaptcha.sourceforge.net/ Javadocs: http://simplecaptcha.sourceforge.net/ja ...
- 来选择一款适合你网站的CMS建站程序吧
1:首页我们要搞清楚什么叫cms? ContentManagementSystem就是cms的全名,意思就是内容管理系统.cms整站系统是以文章系统为核心,增加用户需要的模块,如文章.图片.下载等,提 ...
- iOS_23_undress Girl
最后效果图: watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvcHJlX2VtaW5lbnQ=/font/5a6L5L2T/fontsize/400/fill ...
- PHP+Mysql+jQuery实现中国地图区域数据统计(raphael.js)
使用过百度统计或者cnzz统计的童鞋应该知道,后台有一个地图统计,不同访问量的省份显示的颜色也不一样,今天我将带领大家开发一个这样的案例.上一篇<使用raphael.js绘制中国地图>文章 ...
- Java 多线程之内存一致性错误
当不同的线程针对相同的数据却读到了不同的值时就发生了内存一致性错误.内存一致性错误的原因是非常复杂的.幸运的是我们程序员不需要详细的理解这些原因,我们需要做的事情就是使用策略来规避这些. 避免内存一致 ...