<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Big toy for boy! &#187; comet</title>
	<atom:link href="http://bigtoy4boy.com/blog/tag/comet/feed/" rel="self" type="application/rss+xml" />
	<link>http://bigtoy4boy.com/blog</link>
	<description>一个有关一些个人兴趣爱好的博客</description>
	<lastBuildDate>Thu, 10 Jun 2010 02:33:44 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.9.2</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>翻译：Nginx与长连接服务—低延迟信息推送</title>
		<link>http://bigtoy4boy.com/blog/2009/10/nginx-comet-low-latency-server-push/</link>
		<comments>http://bigtoy4boy.com/blog/2009/10/nginx-comet-low-latency-server-push/#comments</comments>
		<pubDate>Tue, 27 Oct 2009 03:13:22 +0000</pubDate>
		<dc:creator>羽高</dc:creator>
				<category><![CDATA[互联网技术]]></category>
		<category><![CDATA[comet]]></category>
		<category><![CDATA[nginx]]></category>

		<guid isPermaLink="false">http://bigtoy4boy.com/blog/?p=313</guid>
		<description><![CDATA[原文链接：Nginx &#38; Comet: Low Latency Server Push
作者：Ilya Grigorik
翻译：孙绍轩
服务器推送（Server Push）是高效的、延迟低的数据交换方式。如果数据发送端与接收端都在互联网中公开可见，可以使用PubSubHubbub或simpler Webhook等方法完成任务。但是如果数据接收方在防火墙内、在内网或它只是一个浏览器（只可以向外发送数据请求，无法处理传入的数据），则实现服务器推送就更难了。如果你有冒险精神，你可以建立一个反向HTTP服务器。如果你寻求可靠的解决方案，也许你要等待HTML5的WebSocket&#8217;s API特性了。但如果你需要即刻可以实现的解决方案，你可以妥协一下，使用异步推送模式来代替，你可以使用Comet，也被称为反向Ajax、HTTP服务器推送或HTTP流。
早在2006年Alex Russel提出了一个不坏的技术思路，那就是长连接（Comet）概念：从客户端发起并保持一个连接直到数据出现并传送（long polling），或者永远保持一个连接，通过它推送数据到客户端（streaming）。这两种方法的好处是数据传送非常及时。因此长连接技术广泛用于聊天应用（Facebook, Google, Meebo等）以及实现即时触发的机制。
将Nginx变成一个长连接服务器
实现长连接服务比较大的问题是特殊的隐形需求以及事件驱动web服务器能否高效处理众多的长连接。Friendfeed的Tornado服务器是一个标准应用级服务器的好例子。另外，感谢Leo Ponomarev的努力，你现在可以用nginx_http_push_module插件使你的Nginx服务器变身成为一台完全功能的长连接服务器。

使用自定义的一套框架结构，Leo的插件只提供两个对外的接口：一个是订阅者，一个是发布者。客户端连接Nginx服务器，创建针对一个频道的long-polling长连接并等待数据。同时，发布者只是简单的将数据使用POST方法提交给Nginx，插件收到数据后将它一个个发给等待的客户端。这表明发布者不需要直接传递数据，它只是一个简单的事件产生器！没有比这个更简单的方法了。
还有更高的功能是，客户端和发布端可以建立任意的频道，并且插件也提供消息队列功能，这表明Nginx服务器会在客户端断线的情况下临时保存消息。队列消息可以按照时间、等待列表长度或内存限制大小来失效释放。
Nginx和Ruby配置例子
一开始，你需要从源代码编译一个Nginx。解压源代码包，从GitHub获取插件的源码并放入Nginx的源码目录，然后使用下面的参数编译（./configure &#8211;add-module=/path/to/plugin &#38;&#38; make &#38;&#38; make install）。下一步，参考readme文件和协议文件，了解所有的参数选项。一个多客户端接收信息的配置例子如下：
&#62; nginx-push.conf
# 内部发布点（保证私有或不对外公开）
location /publish {
set $push_channel_id $arg_id;      #/?id=239aff3 或类似的文本标示
push_sender;

push_store_messages on;            # 打开消息队列
push_message_timeout 2h;           # 2小时后消息失效
push_max_message_buffer_length 10; # 保存10条消息
push_min_message_recipients 0;     # 清除前最小接收人数目
}

# 公开的长连接接收点
location /activity {
push_listener;

 # 一个频道编号能有多少客户端同时连接
 # &#8211; last: 只有最频繁请求的客户端能保持，其它连接返回409
 # &#8211; first: 只有最早连接的那个客户端可以保持，其它连接返回409
 [...]]]></description>
			<content:encoded><![CDATA[<p>原文链接：<a href="http://feeds.igvita.com/~r/igvita/~3/21hcnE4IGjM/" target="_blank">Nginx &amp; Comet: Low Latency Server Push</a></p>
<p>作者：Ilya Grigorik</p>
<p>翻译：孙绍轩</p>
<p><img class="alignleft" src="http://www.igvita.com/blog/posts/09/comet.png" alt="" width="159" height="159" />服务器推送（Server Push）是高效的、延迟低的数据交换方式。如果数据发送端与接收端都在互联网中公开可见，可以使用<span style="text-decoration: underline;"><a href="http://www.igvita.com/2009/06/29/http-pubsub-webhooks-pubsubhubbub/">PubSubHubbub</a></span>或<a href="http://www.webhooks.org/">simpler Webhook</a>等方法完成任务。但是如果数据接收方在防火墙内、在内网或它只是一个浏览器（只可以向外发送数据请求，无法处理传入的数据），则实现服务器推送就更难了。如果你有冒险精神，你可以建立一个<a href="http://www.igvita.com/2009/08/18/smart-clients-reversehttp-websockets/">反向HTTP服务器</a>。如果你寻求可靠的解决方案，也许你要等待HTML5的<a href="http://www.igvita.com/2009/08/18/smart-clients-reversehttp-websockets/" target="_blank">WebSocket&#8217;s API</a>特性了。但如果你需要即刻可以实现的解决方案，你可以妥协一下，使用异步推送模式来代替，你可以使用<a href="http://en.wikipedia.org/wiki/Comet_%28programming%29" target="_blank">Comet</a>，也被称为反向Ajax、HTTP服务器推送或HTTP流。</p>
<p>早在<a href="http://alex.dojotoolkit.org/2006/03/comet-low-latency-data-for-the-browser/">2006年Alex Russel</a>提出了一个不坏的技术思路，那就是长连接（Comet）概念：从客户端发起并保持一个连接直到数据出现并传送（<a href="http://en.wikipedia.org/wiki/Comet_%28programming%29#Ajax_with_long_polling">long polling</a>），或者永远保持一个连接，通过它推送数据到客户端（<a href="http://en.wikipedia.org/wiki/Comet_%28programming%29#Streaming">streaming</a>）。这两种方法的好处是数据传送非常及时。因此长连接技术广泛用于聊天应用（Facebook, Google, Meebo等）以及实现即时触发的机制。</p>
<p align="left"><strong>将</strong><strong>Nginx</strong><strong>变成一个长连接服务器</strong></p>
<p align="left">实现长连接服务比较大的问题是特殊的隐形需求以及事件驱动web服务器能否高效处理众多的长连接。<a href="http://www.tornadoweb.org/">Friendfeed的Tornado</a>服务器是一个标准应用级服务器的好例子。另外，感谢Leo Ponomarev的努力，你现在可以用<a href="http://github.com/slact/nginx_http_push_module" target="_blank">nginx_http_push_module</a>插件使你的Nginx服务器变身成为一台完全功能的长连接服务器。</p>
<p align="left"><img class="alignnone" src="http://www.igvita.com/posts/09/nginx-push.png" alt="" width="622" height="88" /></p>
<p align="left">使用自定义的一套框架结构，Leo的插件只提供两个对外的接口：一个是订阅者，一个是发布者。客户端连接Nginx服务器，创建针对一个频道的long-polling长连接并等待数据。同时，发布者只是简单的将数据使用POST方法提交给Nginx，插件收到数据后将它一个个发给等待的客户端。这表明发布者不需要直接传递数据，它只是一个简单的事件产生器！没有比这个更简单的方法了。</p>
<p align="left">还有更高的功能是，客户端和发布端可以建立任意的频道，并且插件也提供消息队列功能，这表明Nginx服务器会在客户端断线的情况下临时保存消息。队列消息可以按照时间、等待列表长度或内存限制大小来失效释放。</p>
<p align="left"><strong>Nginx</strong><strong>和</strong><strong>Ruby</strong><strong>配置例子</strong></p>
<p align="left">一开始，你需要从源代码编译一个Nginx。解压源代码包，从GitHub获取插件的源码并放入Nginx的源码目录，然后使用下面的参数编译（<strong>./configure &#8211;add-module=/path/to/plugin &amp;&amp; make &amp;&amp; make install</strong>）。下一步，参考<a href="http://github.com/slact/nginx_http_push_module/blob/master/README">readme</a>文件和<a href="http://github.com/slact/nginx_http_push_module/blob/master/protocol.txt">协议</a>文件，了解所有的参数选项。一个多客户端接收信息的配置例子如下：</p>
<p align="left"><a href="javascript:void(0);" target="_blank"><strong>&gt; nginx-push.conf</strong></a></p>
<p align="left"><em># </em><em>内部发布点（保证私有或不对外公开）</em></p>
<p align="left">location /publish <strong>{</strong></p>
<p align="left">set <strong>$push_channel_id</strong> <strong>$arg_id</strong>;      <em>#/?id=239aff3 </em><em>或类似的文本标示</em></p>
<p align="left">push_sender;</p>
<p align="left">
<p align="left">push_store_messages on;            <em># </em><em>打开消息队列</em></p>
<p align="left">push_message_timeout 2h;           <em># 2</em><em>小时后消息失效</em></p>
<p align="left">push_max_message_buffer_length 10; <em># </em><em>保存10条消息</em></p>
<p align="left">push_min_message_recipients 0;     <em># </em><em>清除前最小接收人数目</em></p>
<p align="left"><strong>}</strong></p>
<p align="left">
<p align="left"><em># </em><em>公开的长连接接收点</em></p>
<p align="left">location /activity <strong>{</strong></p>
<p align="left">push_listener;</p>
<p align="left">
<p align="left"><em> # </em><em>一个频道编号能有多少客户端同时连接</em></p>
<p align="left"><em> # &#8211; last: </em><em>只有最频繁请求的客户端能保持，其它连接返回409</em></p>
<p align="left"><em> # &#8211; first: </em><em>只有最早连接的那个客户端可以保持，其它连接返回409</em></p>
<p align="left"><em> # &#8211; broadcast: </em><em>任何数量的客户端连接都会是长连接</em></p>
<p align="left">push_listener_concurrency broadcast;</p>
<p align="left">set <strong>$push_channel_id</strong> <strong>$arg_id</strong>;</p>
<p align="left">default_type  text/plain;</p>
<p align="left"><strong>}</strong></p>
<p align="left">
<p align="left">当你编译配置好你的Nginx服务器，并且启动它，我们可以建立一个简单的广播场景，一个数据发送广播方，几个订阅信息接收方来测试我们的长连接服务器。</p>
<p align="left"><a href="javascript:void(0);" target="_blank"><strong>&gt; comet-push-consume.rb</strong></a></p>
<p align="left"><strong>require</strong> &#8216;rubygems&#8217;</p>
<p align="left"><strong>require</strong> &#8216;em-http&#8217;</p>
<p align="left">
<p align="left"><strong>def</strong> subscribe<strong>(</strong>opts<strong>)</strong></p>
<p align="left">listener = <strong>EventMachine::HttpRequest</strong>.new<strong>(</strong>&#8216;http://127.0.0.1/activity?id=&#8217;+ opts<strong>[</strong><strong>:channel</strong><strong>])</strong>.get <strong>:head</strong> =&gt; opts<strong>[</strong><strong>:head</strong><strong>]</strong></p>
<p align="left">listener.callback <strong>{</strong></p>
<p align="left"><em># </em><em>打印所获取的内容，并重新订阅这个频道</em></p>
<p align="left"><em># </em><em>使用last-modified头去忽略之前已经获取的数据。</em></p>
<p align="left"><strong>puts</strong> &#8220;Listener recieved: &#8221; + listener.response + &#8220;\\n&#8221;</p>
<p align="left">
<p align="left">modified = listener.response_header<strong>[</strong>'LAST_MODIFIED'<strong>]</strong></p>
<p align="left">subscribe<strong>({</strong>:channel =&gt; opts<strong>[</strong><strong>:channel</strong><strong>]</strong>, <strong>:head</strong> =&gt; <strong>{</strong>&#8216;If-Modified-Since&#8217; =&gt; modified<strong>}})</strong></p>
<p align="left"><strong>}</strong></p>
<p align="left"><strong>end</strong></p>
<p align="left">
<p align="left">EventMachine.run <strong>{</strong></p>
<p align="left">channel = &#8220;pub&#8221;</p>
<p align="left">
<p align="left"><em> # </em><em>每5秒钟发布一个新的消息</em></p>
<p align="left">EM.add_periodic_timer<strong>(</strong>5<strong>)</strong> <strong>do</strong></p>
<p align="left">time = <strong>Time</strong>.now</p>
<p align="left">publisher = <strong>EventMachine::HttpRequest</strong>.new<strong>(</strong>&#8216;http://127.0.0.1/publish?id=&#8217;+channel<strong>)</strong>.post <strong>:body</strong> =&gt; &#8220;Hello @ #{time}&#8221;</p>
<p align="left">publisher.callback <strong>{</strong></p>
<p align="left"><strong>puts</strong> &#8220;Published message @ #{time}&#8221;</p>
<p align="left"><strong>puts</strong> &#8220;Response code: &#8221; + publisher.response_header.status.to_s</p>
<p align="left"><strong>puts</strong> &#8220;Headers: &#8221; + publisher.response_header.inspect</p>
<p align="left"><strong>puts</strong> &#8220;Body: \\n&#8221; + publisher.response</p>
<p align="left"><strong>puts</strong> &#8220;\\n&#8221;</p>
<p align="left"><strong>}</strong></p>
<p align="left"><strong>end</strong></p>
<p align="left">
<p align="left"><em> # </em><em>打开两个客户端</em></p>
<p align="left">subscribe<strong>(</strong><strong>:channel</strong> =&gt; channel<strong>)</strong></p>
<p align="left">subscribe<strong>(</strong><strong>:channel</strong> =&gt; channel<strong>)</strong></p>
<p align="left"><strong>}</strong></p>
<h4 style="padding-top: 10px; padding-right: 0px; padding-bottom: 0px; padding-left: 5px; color: #333333; font-size: 1em; margin: 0px;"><a style="color: #444444; padding: 0px; margin: 0px;" href="http://www.igvita.com/download.php?file=http://www.igvita.com/downloads/nginx-push.zip">nginx-push.zip (完整的Nginx配置和Ruby代码)</a></h4>
<p align="left">在上面的代码中，每5秒钟数据发布端向Nginx服务器发出新的事件，服务器将数据通过长连接转发给两个订阅的客户端。当消息发送到客户端，服务器会断开他们的连接，客户端会立即重连并等待下一次数据的到来。结果就是使用Nginx实现了一个数据发布端到客户端的实时消息推送机制！</p>
<p align="left"><strong>期待生产环境下的长连接服务</strong></p>
<p align="left">Leo的模块还在开发期，还需要时间来稳定下来，但它是一个需要关注的项目。最近的更新计划都着重于bug修复，未来的计划里有描述要加入流的模式：代替现在每次数据获取后都要重连的情况（long polling），Nginx会保持连接，将数据一段段的实时传送给客户端。拥有这个功能后你能很方便的部署你自己的信息触发式API（例如：<a href="http://apiwiki.twitter.com/Streaming-API-Documentation">Twitter流</a>）。</p>
<p>最后，不要忘记数字不断增长的<a href="http://wiki.nginx.org/Nginx3rdPartyModules">其它</a><a href="http://wiki.nginx.org/NginxModules">Nginx模块</a>，或者如果你感兴趣的话，可以参考Evan Miller编写的<a href="http://www.evanmiller.org/nginx-modules-guide.html">指引</a>来开发自己的Nginx模块。</p>
]]></content:encoded>
			<wfw:commentRss>http://bigtoy4boy.com/blog/2009/10/nginx-comet-low-latency-server-push/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>长连接应用中一些琐碎的注意事项</title>
		<link>http://bigtoy4boy.com/blog/2009/06/some-little-things-in-comet-app/</link>
		<comments>http://bigtoy4boy.com/blog/2009/06/some-little-things-in-comet-app/#comments</comments>
		<pubDate>Fri, 12 Jun 2009 02:36:26 +0000</pubDate>
		<dc:creator>羽高</dc:creator>
				<category><![CDATA[互联网技术]]></category>
		<category><![CDATA[chrome]]></category>
		<category><![CDATA[comet]]></category>
		<category><![CDATA[nginx]]></category>

		<guid isPermaLink="false">http://bigtoy4boy.com/blog/?p=266</guid>
		<description><![CDATA[通常长连接应用的结构思路网上都有很多介绍，但是很多细节并没有透露。最近实现具体的应用时探索了几点细节，记录下来。主要的问题是客户端浏览器要有实时的消息返回，但通常整个链路上都会出现一些设置问题导致的无法实时返回的情况。
1、nginx服务器代理模式不会实时返回数据
因为现在主流的web服务器还没有特别适合事件驱动的，所以长连接部分需要单独写server程序，但我们又希望用统一的80端口来做服务端口，所以我用nignx做了前面的代理，根据规则转发长连接请求到后台的独立服务器上。
于是问题来了，通过nginx转发并不直接回复给客户端，需要数据传输后断开连接，客户端才能收到返回的所有内容。这是后需要将默认的proxy_pass的buffer关掉，有一个属性是proxy_buffering off;关掉以后nignx会直接返回结果而没有缓存等待所有内容都从后台服务器传送完成才返回给客户端。
2、Chrome浏览器的实时处理数据机制
Chrome和其他浏览器不一样，如果你一开始返回一个http头，里面表明有很长的数据需要下载，Chrome没有收到一定量数据前，是不会在浏览器中渲染或者执行这些已经下载的数据的，所以导致你无法通过长连接触发你想触发的javascript或一些你想做的操作。
解决方案是在后台的服务器上判断如果是Chrome来访则要在内容body里面先输出标准的html头部head和body标签，然后后面跟随至少2048个字节的字符，可以随意，我用点符号来填充，然后才是正式的我要输出的内容。这样Chrome收够了2048字节的内容后就会触发对代码的处理工作，后续的代码也就变成实时触发的效果了。这算是一个针对Chrome的hack吧。
]]></description>
			<content:encoded><![CDATA[<p>通常长连接应用的结构思路网上都有很多介绍，但是很多细节并没有透露。最近实现具体的应用时探索了几点细节，记录下来。主要的问题是客户端浏览器要有实时的消息返回，但通常整个链路上都会出现一些设置问题导致的无法实时返回的情况。</p>
<p>1、nginx服务器代理模式不会实时返回数据</p>
<p>因为现在主流的web服务器还没有特别适合事件驱动的，所以长连接部分需要单独写server程序，但我们又希望用统一的80端口来做服务端口，所以我用nignx做了前面的代理，根据规则转发长连接请求到后台的独立服务器上。</p>
<p>于是问题来了，通过nginx转发并不直接回复给客户端，需要数据传输后断开连接，客户端才能收到返回的所有内容。这是后需要将默认的proxy_pass的buffer关掉，有一个属性是proxy_buffering off;关掉以后nignx会直接返回结果而没有缓存等待所有内容都从后台服务器传送完成才返回给客户端。</p>
<p>2、Chrome浏览器的实时处理数据机制</p>
<p>Chrome和其他浏览器不一样，如果你一开始返回一个http头，里面表明有很长的数据需要下载，Chrome没有收到一定量数据前，是不会在浏览器中渲染或者执行这些已经下载的数据的，所以导致你无法通过长连接触发你想触发的javascript或一些你想做的操作。</p>
<p>解决方案是在后台的服务器上判断如果是Chrome来访则要在内容body里面先输出标准的html头部head和body标签，然后后面跟随至少2048个字节的字符，可以随意，我用点符号来填充，然后才是正式的我要输出的内容。这样Chrome收够了2048字节的内容后就会触发对代码的处理工作，后续的代码也就变成实时触发的效果了。这算是一个针对Chrome的hack吧。</p>
]]></content:encoded>
			<wfw:commentRss>http://bigtoy4boy.com/blog/2009/06/some-little-things-in-comet-app/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
