nginx-push-stream模块源码学习(三)——发布

[size=medium][b]一、概述[/b][/size]
发布:发布者将MSG post到某一特定通道上,channel将信息缓存
在说明发布流程之前有必要说明下channel和msg的数据结构。
[size=medium][b]二、数据结构[/b][/size]
2.1 MSG
发布时,模块先将消息转化为ngx_http_push_stream_msg_t的数据结构进行存储。
[code]
// 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;
[/code]
@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
[code]
// 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;
[/code]
模块初始化时为每个ngx worker分配一片独立的工作区,工作区中维护一份消息链表。
[size=medium][b]三、流程[/b][/size]
发布流程总的流程图如图所示:
[img]http://dl.iteye.com/upload/attachment/611443/7382500f-431a-3040-8e01-51963fc34e3a.png[/img]
对于删除channel和获取channel info的流程比较简单,不做阐述,具体说明下发布消息流程,流程图如图所示:

[img]http://dl.iteye.com/upload/attachment/616217/431113eb-860d-3d99-8508-3e4609345bf7.png[/img]


需要说明的是“向所有订阅者发送MSG”的过程:
[list]
[*]向每个有该channel订阅者的worker(workers_with_subscriber)的消息链表中插入一条消息
[*]向上述worker发送CHECK_MESSAGES指令,触发msg发送流程(ngx_http_push_stream_process_worker_message)
[/list]
MSG发送(ngx_http_push_stream_process_worker_message):
[code]
// 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;
}
}
[/code]
说明:
可以看出push stream模块在发布过程中针对longpolling和stream两种模式的不同:
[list]
[*]Longpolling模式下,每次发布消息时会发送longpolling头:last modified和etag,使得客户端下次请求时可据此判断服务端是否有更新的消息待发布。
[*]longpolling模式下,订阅者每次请求都会在获得数据后断开重连,所以每次发布时都会发送header模板
[*]ngx_http_push_stream_send_response_finalize同时会[color=red]清理订阅者[/color]
[/list]
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值