<?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"
	>

<channel>
	<title>IDISC的生活</title>
	<atom:link href="http://idisc.blog.techweb.com.cn/feed" rel="self" type="application/rss+xml" />
	<link>http://idisc.blog.techweb.com.cn</link>
	<description>什么生活？小游戏人生，呵呵</description>
	<pubDate>Thu, 22 Jan 2009 06:48:16 +0000</pubDate>
	<generator>http://wordpress.org/?v=2.6.3</generator>
	<language>en</language>
			<item>
		<title>用Mochiweb打造百万级Comet应用，第二部分</title>
		<link>http://idisc.blog.techweb.com.cn/archives/8.html</link>
		<comments>http://idisc.blog.techweb.com.cn/archives/8.html#comments</comments>
		<pubDate>Thu, 22 Jan 2009 06:44:20 +0000</pubDate>
		<dc:creator>idisc</dc:creator>
		
		<category><![CDATA[erlang]]></category>

		<guid isPermaLink="false">http://idisc.blog.techweb.com.cn/?p=8</guid>
		<description><![CDATA[提示：如有转载请注明作者 小游戏 及出处 
原文：A Million-user Comet Application with Mochiweb, Part 2
参考资料：Comet&#8211;基于 HTTP 长连接、无须在浏览器端安装插件的“服务器推”技术为“Comet”
             MochiWeb&#8211;建立轻量级HTTP服务器的Erlang库 
在第一部分 , 我们构建了一个每10秒向客户端发送一条消息的mochiweb comet应用（没什么用处）。我们微调了一下linux内核，做了一个能够建立大量网络连接以测试应用性能和所耗内存的工具 。我们发现每个连接花费大约45K内存。
本系列的第二部分讲的主要是把应用变得更加有用，更加节省内存：

%E
]]></description>
			<content:encoded><![CDATA[<p>提示：如有转载请注明作者 <a href="http://www.xyx172.com/" target="_blank">小游戏</a> 及出处 </p>
<p>原文：<a title="A Million-user Comet Application with Mochiweb, Part 2" rel="bookmark" href="http://www.metabrew.com/article/a-million-user-comet-application-with-mochiweb-part-2/">A Million-user Comet Application with Mochiweb, Part 2</a></p>
<p>参考资料：Comet&#8211;基于 HTTP 长连接、无须在浏览器端安装插件的“服务器推”技术为“Comet”<br />
             MochiWeb&#8211;建立轻量级HTTP服务器的Erlang库 </p>
<p>在<a href="http://idisc.javaeye.com/blog/267028">第一部分</a> , 我们构建了一个每10秒向客户端发送一条消息的mochiweb comet应用（没什么用处）。我们微调了一下linux内核，做了一个能够建立大量网络连接以测试应用性能和所耗内存的工具 。我们发现每个连接花费大约45K内存。</p>
<p>本系列的第二部分讲的主要是把应用变得更加有用，更加节省内存：</p>
<ul>
<li>%E</p>
]]></content:encoded>
			<wfw:commentRss>http://idisc.blog.techweb.com.cn/archives/8.html/feed</wfw:commentRss>
		</item>
		<item>
		<title>用Mochiweb打造百万级Comet应用，第三部分</title>
		<link>http://idisc.blog.techweb.com.cn/archives/10.html</link>
		<comments>http://idisc.blog.techweb.com.cn/archives/10.html#comments</comments>
		<pubDate>Thu, 22 Jan 2009 06:43:48 +0000</pubDate>
		<dc:creator>idisc</dc:creator>
		
		<category><![CDATA[erlang]]></category>

		<guid isPermaLink="false">http://idisc.blog.techweb.com.cn/?p=10</guid>
		<description><![CDATA[提示：如有转载请注明作者 小游戏 及出处
原文：A Million-user Comet Application with Mochiweb, Part 3
参考资料：Comet&#8211;基于 HTTP 长连接、无须在浏览器端安装插件的“服务器推”技术为“Comet”
             MochiWeb&#8211;建立轻量级HTTP服务器的Erlang库
在这个系列的第一部分 和第二部分 展示了怎样用mochiweb构建一个comet应用，怎样把消息路由到连接的客户端。 我们完成了把应用内存压缩到每个连接8KB的程度。我们也做了老式的c10k测试, 注意到10,000个连接用户时到底发生了什么。 我们也做了几个图。很有乐趣，但是现在是时候把我们标题所宣称的做好了，把它调优到一百万个连接。
有以下内容：

添加一个发布订阅式的基于Mnesia的订阅数据库
为一百万用户生成一个真实的朋友数据集
调整mnesia载入朋友数据
从一个机子打开一百万连接
有一百万连接用户的基准测试
用Libevent + C进行连接处理
最后的思考

这个测试的挑战之一是实际上一个测试用机实际上只能打开1M个连接。写一个能接收1M个连接的服务器比创建1M个连接用来测试更容易些，所以这篇文章的相当一部分是关于在一台机器上打开1M个连接的技术 。
赶快进行我们的发布订阅
在第二部分 我们用路由器给特定用户发送消息。对于聊天/及时通讯系统这是很好的，但是我们有更加有吸引力的事情要做。在我们进行大规模伸缩测试前，让我们再添加一个订阅数据库。我们让应用存储你的朋友是谁，这样， 当你的朋友有些什么事情消息时都会推送给你.
我的意图是把这个用于Last.fm，我能够得到实时的我朋友正在听的歌曲的反馈。他也同样的适合由社会化网络产生的其他信息  Flickr图片上传，Facebook的newsfeed, Twitter的消息，总总。 FriendFeed甚至有一个beta版的实时API，所以这种事确定很热门. (即使我还没有听说过除了Facebook用Erlang做这种事）。
实现订阅管理器
我们正实现一个通用订阅管理器，但是我们将把一个人自动签名给其朋友列表中的人 - 这样你可以认为这就是一个朋友数据库。
订阅管理器API：

add_subscriptions([{Subscriber, Subscribee},...])
remove_subscriptions([{Subscriber, Subscribee},...])
get_subscribers(User)

subsmanager.erl



-module ( subsmanager) .


-behaviour ( gen_server) .


-include( &#8220;/usr/local/lib/erlang/lib/stdlib-1.15.4/include/qlc.hrl&#8221; ) .


-export ( [ init/1 , handle_call/3 , handle_cast/2 , handle_info/2 , terminate/2 , code_change/3 ] ) .


-export ( [ add_subscriptions/1 ,


         remove_subscriptions/1 ,


         get_subscribers/1 ,


         first_run/0 ,


         stop/0 ,


         start_link /0 ] ) .


-record( subscription, { subscriber, subscribee} ) .


-record( state, { } ) . % state is all [...]]]></description>
			<content:encoded><![CDATA[<p>提示：如有转载请注明作者 <a href="http://www.xyx172.com/" target="_blank">小游戏</a> 及出处</p>
<p>原文：<a title="A Million-user Comet Application with Mochiweb, Part 3" rel="bookmark" href="http://www.metabrew.com/article/a-million-user-comet-application-with-mochiweb-part-3/">A Million-user Comet Application with Mochiweb, Part 3</a></p>
<p>参考资料：Comet&#8211;基于 HTTP 长连接、无须在浏览器端安装插件的“服务器推”技术为“Comet”<br />
             MochiWeb&#8211;建立轻量级HTTP服务器的Erlang库</p>
<p>在这个系列的<a href="http://idisc.javaeye.com/blog/267028" target="_blank">第一部分</a> 和<a href="http://idisc.javaeye.com/blog/270076" target="_blank">第二部分</a> 展示了怎样用mochiweb构建一个comet应用，怎样把消息路由到连接的客户端。 我们完成了把应用内存压缩到每个连接8KB的程度。我们也做了老式的c10k测试, 注意到10,000个连接用户时到底发生了什么。 我们也做了几个图。很有乐趣，但是现在是时候把我们标题所宣称的做好了，把它调优到一百万个连接。</p>
<p>有以下内容：</p>
<ul>
<li>添加一个发布订阅式的基于Mnesia的订阅数据库</li>
<li>为一百万用户生成一个真实的朋友数据集</li>
<li>调整mnesia载入朋友数据</li>
<li>从一个机子打开一百万连接</li>
<li>有一百万连接用户的基准测试</li>
<li>用Libevent + C进行连接处理</li>
<li>最后的思考</li>
</ul>
<p>这个测试的挑战之一是实际上一个测试用机实际上只能打开1M个连接。写一个能接收1M个连接的服务器比创建1M个连接用来测试更容易些，所以这篇文章的相当一部分是关于在一台机器上打开1M个连接的技术 。</p>
<h2>赶快进行我们的发布订阅</h2>
<p>在<a href="http://idisc.javaeye.com/blog/270076" target="_blank">第二部分</a> 我们用路由器给特定用户发送消息。对于聊天/及时通讯系统这是很好的，但是我们有更加有吸引力的事情要做。在我们进行大规模伸缩测试前，让我们再添加一个订阅数据库。我们让应用存储你的朋友是谁，这样， 当你的朋友有些什么事情消息时都会推送给你.</p>
<p>我的意图是把这个用于Last.fm，我能够得到实时的我朋友正在听的歌曲的反馈。他也同样的适合由社会化网络产生的其他信息  Flickr图片上传，Facebook的newsfeed, Twitter的消息，总总。 FriendFeed甚至有一个beta版的实时API，所以这种事确定很热门. (即使我还没有听说过除了Facebook用Erlang做这种事）。</p>
<h2>实现订阅管理器</h2>
<p>我们正实现一个通用订阅管理器，但是我们将把一个人自动签名给其朋友列表中的人 - 这样你可以认为这就是一个朋友数据库。</p>
<p>订阅管理器API：</p>
<ul>
<li>add_subscriptions([{Subscriber, Subscribee},...])</li>
<li>remove_subscriptions([{Subscriber, Subscribee},...])</li>
<li>get_subscribers(User)</li>
</ul>
<p>subsmanager.erl</p>
<div class="dean_ch">
<ol>
<li class="li1">
<div class="de1">-<span class="kw2">module</span> <span class="br0">(</span> subsmanager<span class="br0">)</span> .</div>
</li>
<li class="li1">
<div class="de1">-<span class="kw2">behaviour</span> <span class="br0">(</span> gen_server<span class="br0">)</span> .</div>
</li>
<li class="li1">
<div class="de1">-include<span class="br0">(</span> <span class="st0">&#8220;/usr/local/lib/erlang/lib/stdlib-1.15.4/include/qlc.hrl&#8221;</span> <span class="br0">)</span> .</div>
</li>
<li class="li1">
<div class="de1">-<span class="kw2">export</span> <span class="br0">(</span> <span class="br0">[</span> init/<span class="nu0">1</span> , handle_call/<span class="nu0">3</span> , handle_cast/<span class="nu0">2</span> , handle_info/<span class="nu0">2</span> , terminate/<span class="nu0">2</span> , code_change/<span class="nu0">3</span> <span class="br0">]</span> <span class="br0">)</span> .</div>
</li>
<li class="li2">
<div class="de2">-<span class="kw2">export</span> <span class="br0">(</span> <span class="br0">[</span> add_subscriptions/<span class="nu0">1</span> ,</div>
</li>
<li class="li1">
<div class="de1">         remove_subscriptions/<span class="nu0">1</span> ,</div>
</li>
<li class="li1">
<div class="de1">         get_subscribers/<span class="nu0">1</span> ,</div>
</li>
<li class="li1">
<div class="de1">         first_run/<span class="nu0">0</span> ,</div>
</li>
<li class="li1">
<div class="de1">         stop/<span class="nu0">0</span> ,</div>
</li>
<li class="li2">
<div class="de2">         <span class="kw3">start_link</span> /<span class="nu0">0</span> <span class="br0">]</span> <span class="br0">)</span> .</div>
</li>
<li class="li1">
<div class="de1">-record<span class="br0">(</span> subscription, <span class="br0">{</span> subscriber, subscribee<span class="br0">}</span> <span class="br0">)</span> .</div>
</li>
<li class="li1">
<div class="de1">-record<span class="br0">(</span> state, <span class="br0">{</span> <span class="br0">}</span> <span class="br0">)</span> . <span class="co1">% state is all in mnesia</span></div>
</li>
<li class="li1">
<div class="de1">-define<span class="br0">(</span> <span class="re0">SERVER</span> , global:<span class="me2">whereis_name</span> <span class="br0">(</span> ?<span class="re0">MODULE</span> <span class="br0">)</span> <span class="br0">)</span> .</div>
</li>
<li class="li1">
<div class="de1"> </div>
</li>
<li class="li2">
<div class="de2"><span class="kw3">start_link</span> <span class="br0">(</span> <span class="br0">)</span> -&gt;</div>
</li>
<li class="li1">
<div class="de1">    <span class="me1">gen_server</span> :<span class="kw3">start_link</span> <span class="br0">(</span> <span class="br0">{</span> global, ?<span class="re0">MODULE</span> <span class="br0">}</span> , ?<span class="re0">MODULE</span> , <span class="br0">[</span> <span class="br0">]</span> , <span class="br0">[</span> <span class="br0">]</span> <span class="br0">)</span> .</div>
</li>
<li class="li1">
<div class="de1"> </div>
</li>
<li class="li1">
<div class="de1">stop<span class="br0">(</span> <span class="br0">)</span> -&gt;</div>
</li>
<li class="li1">
<div class="de1">    <span class="me1">gen_server</span> :<span class="kw3">call</span> <span class="br0">(</span> ?<span class="re0">SERVER</span> , <span class="br0">{</span> stop<span class="br0">}</span> <span class="br0">)</span> .</div>
</li>
<li class="li2">
<div class="de2"> </div>
</li>
<li class="li1">
<div class="de1">add_subscriptions<span class="br0">(</span> <span class="re0">SubsList</span> <span class="br0">)</span> -&gt;</div>
</li>
<li class="li1">
<div class="de1">    <span class="me1">gen_server</span> :<span class="kw3">call</span> <span class="br0">(</span> ?<span class="re0">SERVER</span> , <span class="br0">{</span> add_subscriptions, <span class="re0">SubsList</span> <span class="br0">}</span> , infinity<span class="br0">)</span> .</div>
</li>
<li class="li1">
<div class="de1"> </div>
</li>
<li class="li1">
<div class="de1">remove_subscriptions<span class="br0">(</span> <span class="re0">SubsList</span> <span class="br0">)</span> -&gt;</div>
</li>
<li class="li2">
<div class="de2">    <span class="me1">gen_server</span> :<span class="kw3">call</span> <span class="br0">(</span> ?<span class="re0">SERVER</span> , <span class="br0">{</span> remove_subscriptions, <span class="re0">SubsList</span> <span class="br0">}</span> , infinity<span class="br0">)</span> .</div>
</li>
<li class="li1">
<div class="de1"> </div>
</li>
<li class="li1">
<div class="de1">get_subscribers<span class="br0">(</span> <span class="re0">User</span> <span class="br0">)</span> -&gt;</div>
</li>
<li class="li1">
<div class="de1">    <span class="me1">gen_server</span> :<span class="kw3">call</span> <span class="br0">(</span> ?<span class="re0">SERVER</span> , <span class="br0">{</span> get_subscribers, <span class="re0">User</span> <span class="br0">}</span> <span class="br0">)</span> .</div>
</li>
<li class="li1">
<div class="de1"> </div>
</li>
<li class="li2">
<div class="de2"><span class="co1">%%</span></div>
</li>
<li class="li1">
<div class="de1"> </div>
</li>
<li class="li1">
<div class="de1">init<span class="br0">(</span> <span class="br0">[</span> <span class="br0">]</span> <span class="br0">)</span> -&gt;</div>
</li>
<li class="li1">
<div class="de1">    <span class="me1">ok</span> = mnesia:<span class="me2">start</span> <span class="br0">(</span> <span class="br0">)</span> ,</div>
</li>
<li class="li1">
<div class="de1">    io:<span class="kw3">format</span> <span class="br0">(</span> <span class="st0">&#8220;Waiting on mnesia tables..<span class="es0">\n</span> &#8221;</span> ,<span class="br0">[</span> <span class="br0">]</span> <span class="br0">)</span> ,</div>
</li>
<li class="li2">
<div class="de2">    mnesia:<span class="me2">wait_for_tables</span> <span class="br0">(</span> <span class="br0">[</span> subscription<span class="br0">]</span> , <span class="nu0">30000</span> <span class="br0">)</span> ,</div>
</li>
<li class="li1">
<div class="de1">    <span class="re0">Info</span> = mnesia:<span class="me2">table_info</span> <span class="br0">(</span> subscription, all<span class="br0">)</span> ,</div>
</li>
<li class="li1">
<div class="de1">    io:<span class="kw3">format</span> <span class="br0">(</span> <span class="st0">&#8220;OK. Subscription table info: <span class="es0">\n</span> ~w<span class="es0">\n</span> <span class="es0">\n</span> &#8221;</span> ,<span class="br0">[</span> <span class="re0">Info</span> <span class="br0">]</span> <span class="br0">)</span> ,</div>
</li>
<li class="li1">
<div class="de1">    <span class="br0">{</span> ok, #state<span class="br0">{</span> <span class="br0">}</span> <span class="br0">}</span> .</div>
</li>
<li class="li1">
<div class="de1"> </div>
</li>
<li class="li2">
<div class="de2">handle_call<span class="br0">(</span> <span class="br0">{</span> stop<span class="br0">}</span> , _<span class="re0">From</span> , <span class="re0">State</span> <span class="br0">)</span> -&gt;</div>
</li>
<li class="li1">
<div class="de1">    <span class="br0">{</span> stop, stop, <span class="re0">State</span> <span class="br0">}</span> ;</div>
</li>
<li class="li1">
<div class="de1"> </div>
</li>
<li class="li1">
<div class="de1">handle_call<span class="br0">(</span> <span class="br0">{</span> add_subscriptions, <span class="re0">SubsList</span> <span class="br0">}</span> , _<span class="re0">From</span> , <span class="re0">State</span> <span class="br0">)</span> -&gt;</div>
</li>
<li class="li1">
<div class="de1">    <span class="co1">% Transactionally is slower:</span></div>
</li>
<li class="li2">
<div class="de2">    <span class="co1">% F = fun() -&gt;</span></div>
</li>
<li class="li1">
<div class="de1">    <span class="co1">%         [ ok = mnesia:write(S) || S &lt;- SubsList ]</span></div>
</li>
<li class="li1">
<div class="de1">    <span class="co1">%     end,</span></div>
</li>
<li class="li1">
<div class="de1">    <span class="co1">% mnesia:transaction(F),</span></div>
</li>
<li class="li1">
<div class="de1">    <span class="br0">[</span> mnesia:<span class="me2">dirty_write</span> <span class="br0">(</span> <span class="re0">S</span> <span class="br0">)</span> || <span class="re0">S</span> &lt;- <span class="re0">SubsList</span> <span class="br0">]</span> ,</div>
</li>
<li class="li2">
<div class="de2">    <span class="br0">{</span> reply, ok, <span class="re0">State</span> <span class="br0">}</span> ; </div>
</li>
<li class="li1">
<div class="de1"> </div>
</li>
<li class="li1">
<div class="de1">handle_call<span class="br0">(</span> <span class="br0">{</span> remove_subscriptions, <span class="re0">SubsList</span> <span class="br0">}</span> , _<span class="re0">From</span> , <span class="re0">State</span> <span class="br0">)</span> -&gt;</div>
</li>
<li class="li1">
<div class="de1">    <span class="re0">F</span> = fun<span class="br0">(</span> <span class="br0">)</span> -&gt;</div>
</li>
<li class="li1">
<div class="de1">        <span class="br0">[</span> ok = mnesia:<span class="me2">delete_object</span> <span class="br0">(</span> <span class="re0">S</span> <span class="br0">)</span> || <span class="re0">S</span> &lt;- <span class="re0">SubsList</span> <span class="br0">]</span></div>
</li>
<li class="li2">
<div class="de2">    <span class="kw1">end</span> ,</div>
</li>
<li class="li1">
<div class="de1">    mnesia:<span class="me2">transaction</span> <span class="br0">(</span> <span class="re0">F</span> <span class="br0">)</span> ,</div>
</li>
<li class="li1">
<div class="de1">    <span class="br0">{</span> reply, ok, <span class="re0">State</span> <span class="br0">}</span> ;</div>
</li>
<li class="li1">
<div class="de1"> </div>
</li>
<li class="li1">
<div class="de1">handle_call<span class="br0">(</span> <span class="br0">{</span> get_subscribers, <span class="re0">User</span> <span class="br0">}</span> , <span class="re0">From</span> , <span class="re0">State</span> <span class="br0">)</span> -&gt;</div>
</li>
<li class="li2">
<div class="de2">    <span class="re0">F</span> = fun<span class="br0">(</span> <span class="br0">)</span> -&gt;</div>
</li>
<li class="li1">
<div class="de1">        <span class="re0">Subs</span> = mnesia:<span class="me2">dirty_match_object</span> <span class="br0">(</span> #subscription<span class="br0">{</span> subscriber=<span class="st0">‘_’</span> , subscribee=<span class="re0">User</span> <span class="br0">}</span> <span class="br0">)</span> ,</div>
</li>
<li class="li1">
<div class="de1">        <span class="re0">Users</span> = <span class="br0">[</span> <span class="re0">Dude</span> || #subscription<span class="br0">{</span> subscriber=<span class="re0">Dude</span> , subscribee=_<span class="br0">}</span> &lt;- <span class="re0">Subs</span> <span class="br0">]</span> ,</div>
</li>
<li class="li1">
<div class="de1">        gen_server:<span class="me2">reply</span> <span class="br0">(</span> <span class="re0">From</span> , <span class="re0">Users</span> <span class="br0">)</span></div>
</li>
<li class="li1">
<div class="de1">    <span class="kw1">end</span> ,</div>
</li>
<li class="li2">
<div class="de2">    spawn<span class="br0">(</span> <span class="re0">F</span> <span class="br0">)</span> ,</div>
</li>
<li class="li1">
<div class="de1">    <span class="br0">{</span> noreply, <span class="re0">State</span> <span class="br0">}</span> .</div>
</li>
<li class="li1">
<div class="de1"> </div>
</li>
<li class="li1">
<div class="de1">handle_cast<span class="br0">(</span> _<span class="re0">Msg</span> , <span class="re0">State</span> <span class="br0">)</span> -&gt; <span class="br0">{</span> noreply, <span class="re0">State</span> <span class="br0">}</span> .</div>
</li>
<li class="li1">
<div class="de1">handle_info<span class="br0">(</span> _<span class="re0">Msg</span> , <span class="re0">State</span> <span class="br0">)</span> -&gt; <span class="br0">{</span> noreply, <span class="re0">State</span> <span class="br0">}</span> .</div>
</li>
<li class="li2">
<div class="de2"> </div>
</li>
<li class="li1">
<div class="de1">terminate<span class="br0">(</span> _<span class="re0">Reason</span> , _<span class="re0">State</span> <span class="br0">)</span> -&gt;</div>
</li>
<li class="li1">
<div class="de1">    <span class="me1">mnesia</span> :<span class="me2">stop</span> <span class="br0">(</span> <span class="br0">)</span> ,</div>
</li>
<li class="li1">
<div class="de1">    ok.</div>
</li>
<li class="li1">
<div class="de1"> </div>
</li>
<li class="li2">
<div class="de2">code_change<span class="br0">(</span> _<span class="re0">OldVersion</span> , <span class="re0">State</span> , _<span class="re0">Extra</span> <span class="br0">)</span> -&gt;</div>
</li>
<li class="li1">
<div class="de1">    <span class="me1">io</span> :<span class="kw3">format</span> <span class="br0">(</span> <span class="st0">&#8220;Reloading code for ?MODULE<span class="es0">\n</span> &#8221;</span> ,<span class="br0">[</span> <span class="br0">]</span> <span class="br0">)</span> ,</div>
</li>
<li class="li1">
<div class="de1">    <span class="br0">{</span> ok, <span class="re0">State</span> <span class="br0">}</span> .</div>
</li>
<li class="li1">
<div class="de1"> </div>
</li>
<li class="li1">
<div class="de1"><span class="co1">%%</span></div>
</li>
<li class="li2">
<div class="de2"> </div>
</li>
<li class="li1">
<div class="de1">first_run<span class="br0">(</span> <span class="br0">)</span> -&gt;</div>
</li>
<li class="li1">
<div class="de1">    <span class="me1">mnesia</span> :<span class="me2">create_schema</span> <span class="br0">(</span> <span class="br0">[</span> node<span class="br0">(</span> <span class="br0">)</span> <span class="br0">]</span> <span class="br0">)</span> ,</div>
</li>
<li class="li1">
<div class="de1">    ok = mnesia:<span class="me2">start</span> <span class="br0">(</span> <span class="br0">)</span> ,</div>
</li>
<li class="li1">
<div class="de1">    <span class="re0">Ret</span> = mnesia:<span class="me2">create_table</span> <span class="br0">(</span> subscription,</div>
</li>
<li class="li2">
<div class="de2">    <span class="br0">[</span></div>
</li>
<li class="li1">
<div class="de1">     <span class="br0">{</span> disc_copies, <span class="br0">[</span> node<span class="br0">(</span> <span class="br0">)</span> <span class="br0">]</span> <span class="br0">}</span> ,</div>
</li>
<li class="li1">
<div class="de1">     <span class="br0">{</span> attributes, record_info<span class="br0">(</span> fields, subscription<span class="br0">)</span> <span class="br0">}</span> ,</div>
</li>
<li class="li1">
<div class="de1">     <span class="br0">{</span> index, <span class="br0">[</span> subscribee<span class="br0">]</span> <span class="br0">}</span> , <span class="co1">%index subscribee too</span></div>
</li>
<li class="li1">
<div class="de1">     <span class="br0">{</span> type, bag<span class="br0">}</span></div>
</li>
<li class="li2">
<div class="de2">    <span class="br0">]</span> <span class="br0">)</span> ,</div>
</li>
<li class="li1">
<div class="de1">    <span class="re0">Ret</span> .</div>
</li>
</ol>
</div>
<p>几点值得注意的:</p>
<ul>
<li>我包含了qlc.hrl，mnesia用list comprehension做查询时需要，用了绝对路径。那不是最好的方法。</li>
<li><code>get_subscribers</code> 生成另外一个进程且把这个工作委派给他，用<code>gen_server:reply</code> 。这意味这gen_server loop 不能组塞在那个调用上，假如我们抛出大量查找在其上，那么mnesia会慢下来。</li>
<li><code>rr(”subsmanager.erl”).</code> 下面的例子允许你在erl shell中用record定义。把你的record定义写入<code>records.hrl文件并把它包含到你的模块中，这是一种很好的形式，我嵌入它是为了比较简洁。</code></li>
</ul>
<p>现在测试他。<code>first_run()</code> 创建 mnesia schema, 因此首先运行它是很重要的。另一个隐含的问题是在mnesia中数据库只能被创建他的那个节点访问，因此给erl shell 一个名称，关联起来。</p>
<p><code>$ mkdir /var/mnesia<br />
$ erl -boot start_sasl -mnesia dir '"/var/mnesia_data"' -sname subsman<br />
(subsman@localhost)1&gt; c(subsmanager).<br />
{ok,subsmanager}<br />
(subsman@localhost)2&gt; subsmanager:first_run().<br />
...<br />
{atomic,ok}<br />
(subsman@localhost)3&gt; subsmanager:start_link().<br />
Waiting on mnesia tables..<br />
OK. Subscription table info:<br />
[{access_mode,read_write},{active_replicas,[subsman@localhost]},{arity,3},{attributes,[subscriber,subscribee]},{checkpoints,[]},{commit_work,[{index,bag,[{3,{ram,57378}}]}]},{cookie,{{1224,800064,900003},subsman@localhost}},{cstruct,{cstruct,subscription,bag,[],[subsman@localhost],[],0,read_write,[3],[],false,subscription,[subscriber,subscribee],[],[],{{1224,863164,904753},subsman@localhost},{{2,0},[]}}},{disc_copies,[subsman@localhost]},{disc_only_copies,[]},{frag_properties,[]},{index,[3]},{load_by_force,false},{load_node,subsman@localhost},{load_order,0},{load_reason,{dumper,create_table}},{local_content,false},{master_nodes,[]},{memory,288},{ram_copies,[]},{record_name,subscription},{record_validation,{subscription,3,bag}},{type,bag},{size,0},{snmp,[]},{storage_type,disc_copies},{subscribers,[]},{user_properties,[]},{version,{{2,0},[]}},{where_to_commit,[{subsman@localhost,disc_copies}]},{where_to_read,subsman@localhost},{where_to_write,[subsman@localhost]},{wild_pattern,{subscription,’_&#8217;,’_&#8217;}},{{index,3},57378}]</code><br />
<code><br />
{ok,&lt;0.105.0&gt;}<br />
(subsman@localhost)4&gt; rr("subsmanager.erl").<br />
[state,subscription]<br />
(subsman@localhost)5&gt; subsmanager:add_subscriptions([ #subscription{subscriber=alice, subscribee=rj} ]).<br />
ok<br />
(subsman@localhost)6&gt; subsmanager:add_subscriptions([ #subscription{subscriber=bob, subscribee=rj} ]).<br />
ok<br />
(subsman@localhost)7&gt; subsmanager:get_subscribers(rj).<br />
[bob,alice]<br />
(subsman@localhost)8&gt; subsmanager:remove_subscriptions([ #subscription{subscriber=bob, subscribee=rj} ]).<br />
ok<br />
(subsman@localhost)8&gt; subsmanager:get_subscribers(rj).<br />
[alice]<br />
(subsman@localhost)10&gt; subsmanager:get_subscribers(charlie).<br />
[]</code></p>
<p>为测试我们将用整数id值标志用户-但这个测试我用原子(rj, alice, bob)且假设alice和bob都在rj的朋友列表中。非常好mnesia (和ets/dets)不关心你用的什么值-任何Erlang term都可以。这意味着给多种支持的资源升级是很简单的。你可以开始用<code>{user, 123}或</code><code>{photo, 789}描述人们可能订阅的不同的事情</code> , 不用改变subsmanager模块的任何东西。</p>
<h2>Modifying the router to use subscriptions</h2>
<p>取代给特定的用户传递消息，也就是<code>router:send(123, "Hello user 123")，我们将用主题标志消息</code> - 也就是，生成消息的人们(放歌的，上传图片的，等等) - 拥有路由器投递消息给订阅他主题的每个用户。换句话说，将像这样工作：<code>router:send(123, "Hello everyone subscribed to user 123")</code></p>
<p>Updated router.erl:</p>
<div class="dean_ch">
<ol>
<li class="li1">
<div class="de1">-<span class="kw2">module</span> <span class="br0">(</span> router<span class="br0">)</span> .</div>
</li>
<li class="li1">
<div class="de1">-<span class="kw2">behaviour</span> <span class="br0">(</span> gen_server<span class="br0">)</span> .</div>
</li>
<li class="li1">
<div class="de1"> </div>
</li>
<li class="li1">
<div class="de1">-<span class="kw2">export</span> <span class="br0">(</span> <span class="br0">[</span> <span class="kw3">start_link</span> /<span class="nu0">0</span> <span class="br0">]</span> <span class="br0">)</span> .</div>
</li>
<li class="li2">
<div class="de2">-<span class="kw2">export</span> <span class="br0">(</span> <span class="br0">[</span> init/<span class="nu0">1</span> , handle_call/<span class="nu0">3</span> , handle_cast/<span class="nu0">2</span> , handle_info/<span class="nu0">2</span> ,</div>
</li>
<li class="li1">
<div class="de1">     terminate/<span class="nu0">2</span> , code_change/<span class="nu0">3</span> <span class="br0">]</span> <span class="br0">)</span> .</div>
</li>
<li class="li1">
<div class="de1"> </div>
</li>
<li class="li1">
<div class="de1">-<span class="kw2">export</span> <span class="br0">(</span> <span class="br0">[</span> send/<span class="nu0">2</span> , login/<span class="nu0">2</span> , logout/<span class="nu0">1</span> <span class="br0">]</span> <span class="br0">)</span> .</div>
</li>
<li class="li1">
<div class="de1"> </div>
</li>
<li class="li2">
<div class="de2">-define<span class="br0">(</span> <span class="re0">SERVER</span> , global:<span class="me2">whereis_name</span> <span class="br0">(</span> ?<span class="re0">MODULE</span> <span class="br0">)</span> <span class="br0">)</span> .</div>
</li>
<li class="li1">
<div class="de1"> </div>
</li>
<li class="li1">
<div class="de1"><span class="co1">% will hold bidirectional mapping between id &lt;–&gt; pid</span></div>
</li>
<li class="li1">
<div class="de1">-record<span class="br0">(</span> state, <span class="br0">{</span> pid2id, id2pid<span class="br0">}</span> <span class="br0">)</span> .</div>
</li>
<li class="li1">
<div class="de1"> </div>
</li>
<li class="li2">
<div class="de2"><span class="kw3">start_link</span> <span class="br0">(</span> <span class="br0">)</span> -&gt;</div>
</li>
<li class="li1">
<div class="de1">    <span class="me1">gen_server</span> :<span class="kw3">start_link</span> <span class="br0">(</span> <span class="br0">{</span> global, ?<span class="re0">MODULE</span> <span class="br0">}</span> , ?<span class="re0">MODULE</span> , <span class="br0">[</span> <span class="br0">]</span> , <span class="br0">[</span> <span class="br0">]</span> <span class="br0">)</span> .</div>
</li>
<li class="li1">
<div class="de1"> </div>
</li>
<li class="li1">
<div class="de1"><span class="co1">% sends Msg to anyone subscribed to Id</span></div>
</li>
<li class="li1">
<div class="de1">send<span class="br0">(</span> <span class="re0">Id</span> , <span class="re0">Msg</span> <span class="br0">)</span> -&gt;</div>
</li>
<li class="li2">
<div class="de2">    <span class="me1">gen_server</span> :<span class="kw3">call</span> <span class="br0">(</span> ?<span class="re0">SERVER</span> , <span class="br0">{</span> send, <span class="re0">Id</span> , <span class="re0">Msg</span> <span class="br0">}</span> <span class="br0">)</span> .</div>
</li>
<li class="li1">
<div class="de1"> </div>
</li>
<li class="li1">
<div class="de1">login<span class="br0">(</span> <span class="re0">Id</span> , <span class="re0">Pid</span> <span class="br0">)</span> when is_pid<span class="br0">(</span> <span class="re0">Pid</span> <span class="br0">)</span> -&gt;</div>
</li>
<li class="li1">
<div class="de1">    <span class="me1">gen_server</span> :<span class="kw3">call</span> <span class="br0">(</span> ?<span class="re0">SERVER</span> , <span class="br0">{</span> login, <span class="re0">Id</span> , <span class="re0">Pid</span> <span class="br0">}</span> <span class="br0">)</span> .</div>
</li>
<li class="li1">
<div class="de1"> </div>
</li>
<li class="li2">
<div class="de2">logout<span class="br0">(</span> <span class="re0">Pid</span> <span class="br0">)</span> when is_pid<span class="br0">(</span> <span class="re0">Pid</span> <span class="br0">)</span> -&gt;</div>
</li>
<li class="li1">
<div class="de1">    <span class="me1">gen_server</span> :<span class="kw3">call</span> <span class="br0">(</span> ?<span class="re0">SERVER</span> , <span class="br0">{</span> logout, <span class="re0">Pid</span> <span class="br0">}</span> <span class="br0">)</span> .</div>
</li>
<li class="li1">
<div class="de1"> </div>
</li>
<li class="li1">
<div class="de1"><span class="co1">%%</span></div>
</li>
<li class="li1">
<div class="de1"> </div>
</li>
<li class="li2">
<div class="de2">init<span class="br0">(</span> <span class="br0">[</span> <span class="br0">]</span> <span class="br0">)</span> -&gt;</div>
</li>
<li class="li1">
<div class="de1">    <span class="co1">% set this so we can catch death of logged in pids:</span></div>
</li>
<li class="li1">
<div class="de1">    process_flag<span class="br0">(</span> trap_exit, true<span class="br0">)</span> ,</div>
</li>
<li class="li1">
<div class="de1">    <span class="co1">% use ets for routing tables</span></div>
</li>
<li class="li1">
<div class="de1">    <span class="br0">{</span> ok, #state<span class="br0">{</span></div>
</li>
<li class="li2">
<div class="de2">                pid2id = ets:<span class="me2">new</span> <span class="br0">(</span> ?<span class="re0">MODULE</span> , <span class="br0">[</span> bag<span class="br0">]</span> <span class="br0">)</span> ,</div>
</li>
<li class="li1">
<div class="de1">                id2pid = ets:<span class="me2">new</span> <span class="br0">(</span> ?<span class="re0">MODULE</span> , <span class="br0">[</span> bag<span class="br0">]</span> <span class="br0">)</span></div>
</li>
<li class="li1">
<div class="de1">               <span class="br0">}</span></div>
</li>
<li class="li1">
<div class="de1">    <span class="br0">}</span> .</div>
</li>
<li class="li1">
<div class="de1"> </div>
</li>
<li class="li2">
<div class="de2">handle_call<span class="br0">(</span> <span class="br0">{</span> login, <span class="re0">Id</span> , <span class="re0">Pid</span> <span class="br0">}</span> , _<span class="re0">From</span> , <span class="re0">State</span> <span class="br0">)</span> when is_pid<span class="br0">(</span> <span class="re0">Pid</span> <span class="br0">)</span> -&gt;</div>
</li>
<li class="li1">
<div class="de1">    <span class="me1">ets</span> :<span class="me2">insert</span> <span class="br0">(</span> <span class="re0">State</span> #state.pid2id, <span class="br0">{</span> <span class="re0">Pid</span> , <span class="re0">Id</span> <span class="br0">}</span> <span class="br0">)</span> ,</div>
</li>
<li class="li1">
<div class="de1">    ets:<span class="me2">insert</span> <span class="br0">(</span> <span class="re0">State</span> #state.id2pid, <span class="br0">{</span> <span class="re0">Id</span> , <span class="re0">Pid</span> <span class="br0">}</span> <span class="br0">)</span> ,</div>
</li>
<li class="li1">
<div class="de1">    link<span class="br0">(</span> <span class="re0">Pid</span> <span class="br0">)</span> , <span class="co1">% tell us if they exit, so we can log them out</span></div>
</li>
<li class="li1">
<div class="de1">    <span class="co1">%io:format(&#8221;~w logged in as ~w\n&#8221;,[Pid, Id]),</span></div>
</li>
<li class="li2">
<div class="de2">    <span class="br0">{</span> reply, ok, <span class="re0">State</span> <span class="br0">}</span> ;</div>
</li>
<li class="li1">
<div class="de1"> </div>
</li>
<li class="li1">
<div class="de1">handle_call<span class="br0">(</span> <span class="br0">{</span> logout, <span class="re0">Pid</span> <span class="br0">}</span> , _<span class="re0">From</span> , <span class="re0">State</span> <span class="br0">)</span> when is_pid<span class="br0">(</span> <span class="re0">Pid</span> <span class="br0">)</span> -&gt;</div>
</li>
<li class="li1">
<div class="de1">    <span class="me1">unlink</span> <span class="br0">(</span> <span class="re0">Pid</span> <span class="br0">)</span> ,</div>
</li>
<li class="li1">
<div class="de1">    <span class="re0">PidRows</span> = ets:<span class="me2">lookup</span> <span class="br0">(</span> <span class="re0">State</span> #state.pid2id, <span class="re0">Pid</span> <span class="br0">)</span> ,</div>
</li>
<li class="li2">
<div class="de2">    <span class="kw1">case</span> <span class="re0">PidRows</span> <span class="kw1">of</span></div>
</li>
<li class="li1">
<div class="de1">        <span class="br0">[</span> <span class="br0">]</span> -&gt;</div>
</li>
<li class="li1">
<div class="de1">            <span class="me1">ok</span> ;</div>
</li>
<li class="li1">
<div class="de1">        _ -&gt;</div>
</li>
<li class="li1">
<div class="de1">            <span class="re0">IdRows</span> = <span class="br0">[</span> <span class="br0">{</span> <span class="re0">I</span> ,<span class="re0">P</span> <span class="br0">}</span> || <span class="br0">{</span> <span class="re0">P</span> ,<span class="re0">I</span> <span class="br0">}</span> &lt;- <span class="re0">PidRows</span> <span class="br0">]</span> , <span class="co1">% invert tuples</span></div>
</li>
<li class="li2">
<div class="de2">            ets:<span class="me2">delete</span> <span class="br0">(</span> <span class="re0">State</span> #state.pid2id, <span class="re0">Pid</span> <span class="br0">)</span> ,   <span class="co1">% delete all pid-&gt;id entries</span></div>
</li>
<li class="li1">
<div class="de1">            <span class="br0">[</span> ets:<span class="me2">delete_object</span> <span class="br0">(</span> <span class="re0">State</span> #state.id2pid, <span class="re0">Obj</span> <span class="br0">)</span> || <span class="re0">Obj</span> &lt;- <span class="re0">IdRows</span> <span class="br0">]</span> <span class="co1">% and all id-&gt;pid</span></div>
</li>
<li class="li1">
<div class="de1">    <span class="kw1">end</span> ,</div>
</li>
<li class="li1">
<div class="de1">    <span class="co1">%io:format(&#8221;pid ~w logged out\n&#8221;,[Pid]),</span></div>
</li>
<li class="li1">
<div class="de1">    <span class="br0">{</span> reply, ok, <span class="re0">State</span> <span class="br0">}</span> ;</div>
</li>
<li class="li2">
<div class="de2"> </div>
</li>
<li class="li1">
<div class="de1">handle_call<span class="br0">(</span> <span class="br0">{</span> send, <span class="re0">Id</span> , <span class="re0">Msg</span> <span class="br0">}</span> , <span class="re0">From</span> , <span class="re0">State</span> <span class="br0">)</span> -&gt;</div>
</li>
<li class="li1">
<div class="de1">    <span class="re0">F</span> = fun<span class="br0">(</span> <span class="br0">)</span> -&gt;</div>
</li>
<li class="li1">
<div class="de1">        <span class="co1">% get users who are subscribed to Id:</span></div>
</li>
<li class="li1">
<div class="de1">        <span class="re0">Users</span> = subsmanager:<span class="me2">get_subscribers</span> <span class="br0">(</span> <span class="re0">Id</span> <span class="br0">)</span> ,</div>
</li>
<li class="li2">
<div class="de2">        io:<span class="kw3">format</span> <span class="br0">(</span> <span class="st0">&#8220;Subscribers of ~w = ~w<span class="es0">\n</span> &#8221;</span> ,<span class="br0">[</span> <span class="re0">Id</span> , <span class="re0">Users</span> <span class="br0">]</span> <span class="br0">)</span> ,</div>
</li>
<li class="li1">
<div class="de1">        <span class="co1">% get pids of anyone logged in from Users list:</span></div>
</li>
<li class="li1">
<div class="de1">        <span class="re0">Pids0</span> = lists:<span class="me2">map</span> <span class="br0">(</span></div>
</li>
<li class="li1">
<div class="de1">            fun<span class="br0">(</span> <span class="re0">U</span> <span class="br0">)</span> -&gt;</div>
</li>
<li class="li1">
<div class="de1">                <span class="br0">[</span> <span class="re0">P</span> || <span class="br0">{</span> _<span class="re0">I</span> , <span class="re0">P</span> <span class="br0">}</span> &lt;- ets:<span class="me2">lookup</span> <span class="br0">(</span> <span class="re0">State</span> #state.id2pid, <span class="re0">U</span> <span class="br0">)</span> <span class="br0">]</span></div>
</li>
<li class="li2">
<div class="de2">            <span class="kw1">end</span> ,</div>
</li>
<li class="li1">
<div class="de1">            <span class="br0">[</span> <span class="re0">Id</span> | <span class="re0">Users</span> <span class="br0">]</span> <span class="co1">% we are always subscribed to ourselves</span></div>
</li>
<li class="li1">
<div class="de1">        <span class="br0">)</span> ,</div>
</li>
<li class="li1">
<div class="de1">        <span class="re0">Pids</span> = lists:<span class="me2">flatten</span> <span class="br0">(</span> <span class="re0">Pids0</span> <span class="br0">)</span> ,</div>
</li>
<li class="li1">
<div class="de1">        io:<span class="kw3">format</span> <span class="br0">(</span> <span class="st0">&#8220;Pids: ~w<span class="es0">\n</span> &#8221;</span> , <span class="br0">[</span> <span class="re0">Pids</span> <span class="br0">]</span> <span class="br0">)</span> ,</div>
</li>
<li class="li2">
<div class="de2">        <span class="co1">% send Msg to them all</span></div>
</li>
<li class="li1">
<div class="de1">        <span class="re0">M</span> = <span class="br0">{</span> router_msg, <span class="re0">Msg</span> <span class="br0">}</span> ,</div>
</li>
<li class="li1">
<div class="de1">        <span class="br0">[</span> <span class="re0">Pid</span> ! <span class="re0">M</span> || <span class="re0">Pid</span> &lt;- <span class="re0">Pids</span> <span class="br0">]</span> ,</div>
</li>
<li class="li1">
<div class="de1">        <span class="co1">% respond with how many users saw the message</span></div>
</li>
<li class="li1">
<div class="de1">        gen_server:<span class="me2">reply</span> <span class="br0">(</span> <span class="re0">From</span> , <span class="br0">{</span> ok, length<span class="br0">(</span> <span class="re0">Pids</span> <span class="br0">)</span> <span class="br0">}</span> <span class="br0">)</span></div>
</li>
<li class="li2">
<div class="de2">    <span class="kw1">end</span> ,</div>
</li>
<li class="li1">
<div class="de1">    spawn<span class="br0">(</span> <span class="re0">F</span> <span class="br0">)</span> ,</div>
</li>
<li class="li1">
<div class="de1">    <span class="br0">{</span> noreply, <span class="re0">State</span> <span class="br0">}</span> .</div>
</li>
<li class="li1">
<div class="de1"> </div>
</li>
<li class="li1">
<div class="de1"><span class="co1">% handle death and cleanup of logged in processes</span></div>
</li>
<li class="li2">
<div class="de2">handle_info<span class="br0">(</span> <span class="re0">Info</span> , <span class="re0">State</span> <span class="br0">)</span> -&gt;</div>
</li>
<li class="li1">
<div class="de1">    <span class="kw1">case</span> <span class="re0">Info</span> <span class="kw1">of</span></div>
</li>
<li class="li1">
<div class="de1">        <span class="br0">{</span> <span class="st0">‘EXIT’</span> , <span class="re0">Pid</span> , _<span class="re0">Why</span> <span class="br0">}</span> -&gt;</div>
</li>
<li class="li1">
<div class="de1">            <span class="me1">handle_call</span> <span class="br0">(</span> <span class="br0">{</span> logout, <span class="re0">Pid</span> <span class="br0">}</span> , blah, <span class="re0">State</span> <span class="br0">)</span> ;</div>
</li>
<li class="li1">
<div class="de1">        <span class="re0">Wtf</span> -&gt;</div>
</li>
<li class="li2">
<div class="de2">            <span class="me1">io</span> :<span class="kw3">format</span> <span class="br0">(</span> <span class="st0">&#8220;Caught unhandled message: ~w<span class="es0">\n</span> &#8221;</span> , <span class="br0">[</span> <span class="re0">Wtf</span> <span class="br0">]</span> <span class="br0">)</span></div>
</li>
<li class="li1">
<div class="de1">    <span class="kw1">end</span> ,</div>
</li>
<li class="li1">
<div class="de1">    <span class="br0">{</span> noreply, <span class="re0">State</span> <span class="br0">}</span> .</div>
</li>
<li class="li1">
<div class="de1"> </div>
</li>
<li class="li1">
<div class="de1">handle_cast<span class="br0">(</span> _<span class="re0">Msg</span> , <span class="re0">State</span> <span class="br0">)</span> -&gt;</div>
</li>
<li class="li2">
<div class="de2">    <span class="br0">{</span> noreply, <span class="re0">State</span> <span class="br0">}</span> .</div>
</li>
<li class="li1">
<div class="de1">terminate<span class="br0">(</span> _<span class="re0">Reason</span> , _<span class="re0">State</span> <span class="br0">)</span> -&gt;</div>
</li>
<li class="li1">
<div class="de1">    <span class="me1">ok</span> .</div>
</li>
<li class="li1">
<div class="de1">code_change<span class="br0">(</span> _<span class="re0">OldVsn</span> , <span class="re0">State</span> , _<span class="re0">Extra</span> <span class="br0">)</span> -&gt;</div>
</li>
<li class="li1">
<div class="de1">    <span class="br0">{</span> ok, <span class="re0">State</span> <span class="br0">}</span> .</div>
</li>
</ol>
</div>
<p> </p>
<p>这是一个不需要mochiweb的快速测试 - 我用原子代替用户ID, 为清晰忽略了一些输出</p>
<p><code>(subsman@localhost)1&gt; c(subsmanager), c(router), rr("subsmanager.erl").<br />
(subsman@localhost)2&gt; subsmanager:start_link().<br />
(subsman@localhost)3&gt; router:start_link().<br />
(subsman@localhost)4&gt; Subs = [#subscription{subscriber=alice, subscribee=rj}, #subscription{subscriber=bob, subscribee=rj}].<br />
[#subscription{subscriber = alice,subscribee = rj},<br />
#subscription{subscriber = bob,subscribee = rj}]<br />
(subsman@localhost)5&gt; subsmanager:add_subscriptions(Subs).<br />
ok<br />
(subsman@localhost)6&gt; router:send(rj, “RJ did something”).<br />
Subscribers of rj = [bob,alice]<br />
Pids: []<br />
{ok,0}<br />
(subsman@localhost)7&gt; router:login(alice, self()).<br />
ok<br />
(subsman@localhost)8&gt; router:send(rj, “RJ did something”).<br />
Subscribers of rj = [bob,alice]<br />
Pids: [&lt;0.46.0&gt;]<br />
{ok,1}<br />
(subsman@localhost)9&gt; receive {router_msg, M} -&gt; io:format(”~s\n”,[M]) end.<br />
RJ did something<br />
ok</code></p>
<p>这演示了当主题是她订阅的某人 (rj)，alice怎样接收一条消息, 即使这条消息不是直接发送给alice的。输出显示路由器尽可能的标志目标为<code>[alice,bob]</code> 但是消息值传给一个人alice, 因为bob还没有登陆。</p>
<h2>生成一个典型的社会化网络朋友数据集</h2>
<p>我们可以随机的生成大量的朋友关系，但是那样特别不真实。 社会化网络有助于发挥分布规则的力量。社会化网络通常很少有超公众化的用户(<a href="http://twitter.com/barackobama">一些 Twitter 用户</a> 有超过100,000的追随者) 而是很多的人只有少量的几个朋友。Last.fm朋友数据就是个典型 - 他符合<a href="http://en.wikipedia.org/wiki/Barab%C3%A1si-Albert_model">Barabási–Albert 图模型</a> , 因此它就是我用的类型。</p>
<p>为了生成数据集，我用了很出色的<a href="http://cneurocvs.rmki.kfki.hu/igraph/">igraph库</a> 的模块:</p>
<p>fakefriends.py:</p>
<div class="dean_ch">
<ol>
<li class="li1">
<div class="de1"><span class="kw1">import</span> igraph</div>
</li>
<li class="li1">
<div class="de1">g = igraph.<span class="me1">Graph</span> .<span class="me1">Barabasi</span> <span class="br0">(</span> <span class="nu0">1000000</span> , <span class="nu0">15</span> , directed=<span class="kw2">False</span> <span class="br0">)</span></div>
</li>
<li class="li1">
<div class="de1"><span class="kw1">print</span> <span class="st0">&#8220;Edges: &#8220;</span> + <span class="kw2">str</span> <span class="br0">(</span> g.<span class="me1">ecount</span> <span class="br0">(</span> <span class="br0">)</span> <span class="br0">)</span> + <span class="st0">&#8221; Verticies: &#8220;</span> + <span class="kw2">str</span> <span class="br0">(</span> g.<span class="me1">vcount</span> <span class="br0">(</span> <span class="br0">)</span> <span class="br0">)</span></div>
</li>
<li class="li1">
<div class="de1">g.<span class="me1">write_edgelist</span> <span class="br0">(</span> <span class="st0">&#8220;fakefriends.txt&#8221;</span> <span class="br0">)</span></div>
</li>
</ol>
</div>
<p>这产生了用空格分隔的每行2个用户id。这就有了我们要调入subsmanager的朋友关系数据，用户id从1到一百万。</p>
<h2>向mnesia大量调入朋友数据</h2>
<p>这个小模块读fakefriends.txt文件并创建一个订阅记录列表.</p>
<p>readfriends.erl - 读fakefriends.txt创建订阅记录</p>
<div class="dean_ch">
<ol>
<li class="li1">
<div class="de1">-<span class="kw2">module</span> <span class="br0">(</span> readfriends<span class="br0">)</span> .</div>
</li>
<li class="li1">
<div class="de1">-<span class="kw2">export</span> <span class="br0">(</span> <span class="br0">[</span> load/<span class="nu0">1</span> <span class="br0">]</span> <span class="br0">)</span> .</div>
</li>
<li class="li1">
<div class="de1">-record<span class="br0">(</span> subscription, <span class="br0">{</span> subscriber, subscribee<span class="br0">}</span> <span class="br0">)</span> .</div>
</li>
<li class="li1">
<div class="de1"> </div>
</li>
<li class="li2">
<div class="de2">load<span class="br0">(</span> <span class="re0">Filename</span> <span class="br0">)</span> -&gt;</div>
</li>
<li class="li1">
<div class="de1">    <span class="me1">for_each_line_in_file</span> <span class="br0">(</span> <span class="re0">Filename</span> ,</div>
</li>
<li class="li1">
<div class="de1">        fun<span class="br0">(</span> <span class="re0">Line</span> , <span class="re0">Acc</span> <span class="br0">)</span> -&gt;</div>
</li>
<li class="li1">
<div class="de1">            <span class="br0">[</span> <span class="re0">As</span> , <span class="re0">Bs</span> <span class="br0">]</span> = string:<span class="me2">tokens</span> <span class="br0">(</span> string:<span class="me2">strip</span> <span class="br0">(</span> <span class="re0">Line</span> , right, $\n<span class="br0">)</span> , <span class="st0">&#8221; &#8220;</span> <span class="br0">)</span> ,</div>
</li>
<li class="li1">
<div class="de1">            <span class="br0">{</span> <span class="re0">A</span> , _<span class="br0">}</span> = string:<span class="me2">to_integer</span> <span class="br0">(</span> <span class="re0">As</span> <span class="br0">)</span> ,</div>
</li>
<li class="li2">
<div class="de2">            <span class="br0">{</span> <span class="re0">B</span> , _<span class="br0">}</span> = string:<span class="me2">to_integer</span> <span class="br0">(</span> <span class="re0">Bs</span> <span class="br0">)</span> ,</div>
</li>
<li class="li1">
<div class="de1">            <span class="br0">[</span> #subscription<span class="br0">{</span> subscriber=<span class="re0">A</span> , subscribee=<span class="re0">B</span> <span class="br0">}</span> | <span class="re0">Acc</span> <span class="br0">]</span></div>
</li>
<li class="li1">
<div class="de1">        <span class="kw1">end</span> , <span class="br0">[</span> read<span class="br0">]</span> , <span class="br0">[</span> <span class="br0">]</span> <span class="br0">)</span> .</div>
</li>
<li class="li1">
<div class="de1"> </div>
</li>
<li class="li1">
<div class="de1"><span class="co1">% via:&nbsp;<a href="http://www.trapexit.org/Reading_Lines_from_a_File</span></div>
</li>
<p>&#8221; title=&#8221;http://www.trapexit.org/Reading_Lines_from_a_File</span></div>
</li>
<p>&#8221; target=&#8221;_blank&#8221;>http://www.trapexit.org/Reading_Lines_fr&#8230;</a></p>
<li class="li2">
<div class="de2">for_each_line_in_file<span class="br0">(</span> <span class="re0">Name</span> , <span class="re0">Proc</span> , <span class="re0">Mode</span> , <span class="re0">Accum0</span> <span class="br0">)</span> -&gt;</div>
</li>
<li class="li1">
<div class="de1">    <span class="br0">{</span> ok, <span class="re0">Device</span> <span class="br0">}</span> = file:<span class="me2">open</span> <span class="br0">(</span> <span class="re0">Name</span> , <span class="re0">Mode</span> <span class="br0">)</span> ,</div>
</li>
<li class="li1">
<div class="de1">    for_each_line<span class="br0">(</span> <span class="re0">Device</span> , <span class="re0">Proc</span> , <span class="re0">Accum0</span> <span class="br0">)</span> .</div>
</li>
<li class="li1">
<div class="de1"> </div>
</li>
<li class="li1">
<div class="de1">for_each_line<span class="br0">(</span> <span class="re0">Device</span> , <span class="re0">Proc</span> , <span class="re0">Accum</span> <span class="br0">)</span> -&gt;</div>
</li>
<li class="li2">
<div class="de2">    <span class="kw1">case</span> io:<span class="me2">get_line</span> <span class="br0">(</span> <span class="re0">Device</span> , <span class="st0">&#8220;&#8221;</span> <span class="br0">)</span> <span class="kw1">of</span></div>
</li>
<li class="li1">
<div class="de1">        eof  -&gt; <span class="me1">file</span> :<span class="kw3">close</span> <span class="br0">(</span> <span class="re0">Device</span> <span class="br0">)</span> , <span class="re0">Accum</span> ;</div>
</li>
<li class="li1">
<div class="de1">        <span class="re0">Line</span> -&gt; <span class="re0">NewAccum</span> = <span class="re0">Proc</span> <span class="br0">(</span> <span class="re0">Line</span> , <span class="re0">Accum</span> <span class="br0">)</span> ,</div>
</li>
<li class="li1">
<div class="de1">                    for_each_line<span class="br0">(</span> <span class="re0">Device</span> , <span class="re0">Proc</span> , <span class="re0">NewAccum</span> <span class="br0">)</span></div>
</li>
<li class="li1">
<div class="de1">    <span class="kw1">end</span> .</div>
</li>
</ol>
</div>
<p>现在在subsmanager shell中, 你可以从文本中读数据并添加订阅：</p>
<p><code>$ erl -name &nbsp;<a href="mailto:router@minifeeds4.gs2" title="mailto:router@minifeeds4.gs2">router at minifeeds4.gs2</a> +K true +A 128 -setcookie secretcookie -mnesia dump_log_write_threshold 50000 -mnesia dc_dump_limit 40<br />
erl&gt; c(readfriends), c(subsmanager).<br />
erl&gt; subsmanager:first_run().<br />
erl&gt; subsmanager:start_link().<br />
erl&gt; subsmanager:add_subscriptions( readfriends:load(&#8221;fakefriends.txt&#8221;) ).</code></p>
<p>注意这额外的mnesia参数 - 这是避免<strong>** WARNING ** Mnesia is overloaded</strong> 你可能在别的地方看到的警告信息。提到我以前发表的: <a href="http://www.metabrew.com/article/on-bulk-loading-data-into-mnesia/">On bulk loading data into Mnesia</a> 有另外的调入大量数据的方法。最好的解决方案看起来是设置这些选项(在评论中指出的, 谢谢Jacob!) 。<a href="http://www.erlang.org/doc/apps/mnesia/">Mnesia 参考手册</a> 在Configuration参数中包含了很多其他的设置，值得一看.</p>
<h2>调到一百万</h2>
<p>在一台主机上创建一百万个tcp连接是可以的。 我有个感觉就是做这个是用个小集群来模拟大量的客户端连接，可能运行一个像Tsung的真实工具。 甚至调整增加内核内存，增加文件描述符限制，设置本地端口范围到最大值，我们将一直坚持打破临时端口的限制。当建立一个tcp连接时，客户端被分配（或者你可以指定）一个端口，范围在 <code>/proc/sys/net/ipv4/ip_local_port_range里</code> . 假如你手工指定也没什么问题， 用临时端口我们会超出界限。 在第一部分，我们设置这个范围在“1024 65535″之间 - 这就意味这有65535-1024 = 64511个端口可用。他们中的一些将会被别的进程使用，但是我们从没有超过64511个客户连接,因为我们会超出端口界限。</p>
<p>局部端口区间被赋给ip的一段, 因此假如我们是我们输出连接在一个指定的局部端口区间的话我们就能够打开大于64511 个外出连接。</p>
<p>因此让我们弄出17个新的IP地址, 每个让他建立62000个连接 - 给我们总共1,054,000 个连接.</p>
<p><code>$ for i in `seq 1 17`; do echo sudo ifconfig eth0:$i 10.0.0.$i up ; done</code></p>
<p>假如你现在运行<code>ifconfig</code> 你应该看到你的虚拟往里接口: eth0:1, eth0:2 … eth0:17, 每个有不同的IP地址。很显然，你应该选择一个你所需要的地址空间。</p>
<p>现在剩下的就是更改第一部分地道的<code>floodtest工具</code> ，为其指定他应该连接的本地IP…不行的是<a href="http://www.erlang.org/doc/man/http.html">erlang http 客户端</a> 不让你指定源IP。 ibrowse,另一个可选的http客户端库也不行。妈的。</p>
<p><em>&lt;疯狂的想法&gt;</em> <br />
我想到另外的一个选择：建立17对IP - 一个在服务器一个在客户端&#8211; 每对都有自己隔离的 /30 子网。我想假如我随后让客户端连接任何一个给定的服务器IP，他将迫使本地IP在子网上成为这对的另一部分，因为只有本地IP能够达到服务器IP。理论上 ，这将意味这在客户端声明本地源IP将不是必须的 (虽然服务器IP区间需要被指定).我不知道这是否能工作 - 这时听起来可以。最后因它太不正规了所以我决定不试了。<br />
<em>&lt;/疯狂的想法&gt;</em></p>
<p>我也研究了OTP’s <code>http_transport</code> 代码并且想为其加入对指定本地IP的支持。尽管它不是你真正需要的一个特性，但它需要更多的工作。</p>
<p><code>gen_tcp</code> 让你指定源IP ,因此我最终为这个测试用gen_tcp写一个比较粗糙的客户端：</p>
<p>floodtest2.erl</p>
<div class="dean_ch">
<ol>
<li class="li1">
<div class="de1">-<span class="kw2">module</span> <span class="br0">(</span> floodtest2<span class="br0">)</span> .</div>
</li>
<li class="li1">
<div class="de1">-compile<span class="br0">(</span> export_all<span class="br0">)</span> .</div>
</li>
<li class="li1">
<div class="de1">-define<span class="br0">(</span> <span class="re0">SERVERADDR</span> , <span class="st0">&#8220;10.1.2.3&#8243;</span> <span class="br0">)</span> . <span class="co1">% where mochiweb is running</span></div>
</li>
<li class="li1">
<div class="de1">-define<span class="br0">(</span> <span class="re0">SERVERPORT</span> , <span class="nu0">8000</span> <span class="br0">)</span> .</div>
</li>
<li class="li2">
<div class="de2"> </div>
</li>
<li class="li1">
<div class="de1"><span class="co1">% Generate the config in bash like so (chose some available address space):</span></div>
</li>
<li class="li1">
<div class="de1"><span class="co1">% EACH=62000; for i in `seq 1 17`; do echo &#8220;{{10,0,0,$i}, $((($i-1)*$EACH+1)), $(($i*$EACH))}, &#8220;; done</span></div>
</li>
<li class="li1">
<div class="de1"> </div>
</li>
<li class="li1">
<div class="de1">run<span class="br0">(</span> <span class="re0">Interval</span> <span class="br0">)</span> -&gt;</div>
</li>
<li class="li2">
<div class="de2">        <span class="re0">Config</span> = <span class="br0">[</span></div>
</li>
<li class="li1">
<div class="de1"><span class="br0">{</span> <span class="br0">{</span> <span class="nu0">10</span> ,<span class="nu0">0</span> ,<span class="nu0">0</span> ,<span class="nu0">1</span> <span class="br0">}</span> , <span class="nu0">1</span> , <span class="nu0">62000</span> <span class="br0">}</span> ,</div>
</li>
<li class="li1">
<div class="de1"><span class="br0">{</span> <span class="br0">{</span> <span class="nu0">10</span> ,<span class="nu0">0</span> ,<span class="nu0">0</span> ,<span class="nu0">2</span> <span class="br0">}</span> , <span class="nu0">62001</span> , <span class="nu0">124000</span> <span class="br0">}</span> ,</div>
</li>
<li class="li1">
<div class="de1"><span class="br0">{</span> <span class="br0">{</span> <span class="nu0">10</span> ,<span class="nu0">0</span> ,<span class="nu0">0</span> ,<span class="nu0">3</span> <span class="br0">}</span> , <span class="nu0">124001</span> , <span class="nu0">186000</span> <span class="br0">}</span> ,</div>
</li>
<li class="li1">
<div class="de1"><span class="br0">{</span> <span class="br0">{</span> <span class="nu0">10</span> ,<span class="nu0">0</span> ,<span class="nu0">0</span> ,<span class="nu0">4</span> <span class="br0">}</span> , <span class="nu0">186001</span> , <span class="nu0">248000</span> <span class="br0">}</span> ,</div>
</li>
<li class="li2">
<div class="de2"><span class="br0">{</span> <span class="br0">{</span> <span class="nu0">10</span> ,<span class="nu0">0</span> ,<span class="nu0">0</span> ,<span class="nu0">5</span> <span class="br0">}</span> , <span class="nu0">248001</span> , <span class="nu0">310000</span> <span class="br0">}</span> ,</div>
</li>
<li class="li1">
<div class="de1"><span class="br0">{</span> <span class="br0">{</span> <span class="nu0">10</span> ,<span class="nu0">0</span> ,<span class="nu0">0</span> ,<span class="nu0">6</span> <span class="br0">}</span> , <span class="nu0">310001</span> , <span class="nu0">372000</span> <span class="br0">}</span> ,</div>
</li>
<li class="li1">
<div class="de1"><span class="br0">{</span> <span class="br0">{</span> <span class="nu0">10</span> ,<span class="nu0">0</span> ,<span class="nu0">0</span> ,<span class="nu0">7</span> <span class="br0">}</span> , <span class="nu0">372001</span> , <span class="nu0">434000</span> <span class="br0">}</span> ,</div>
</li>
<li class="li1">
<div class="de1"><span class="br0">{</span> <span class="br0">{</span> <span class="nu0">10</span> ,<span class="nu0">0</span> ,<span class="nu0">0</span> ,<span class="nu0">8</span> <span class="br0">}</span> , <span class="nu0">434001</span> , <span class="nu0">496000</span> <span class="br0">}</span> ,</div>
</li>
<li class="li1">
<div class="de1"><span class="br0">{</span> <span class="br0">{</span> <span class="nu0">10</span> ,<span class="nu0">0</span> ,<span class="nu0">0</span> ,<span class="nu0">9</span> <span class="br0">}</span> , <span class="nu0">496001</span> , <span class="nu0">558000</span> <span class="br0">}</span> ,</div>
</li>
<li class="li2">
<div class="de2"><span class="br0">{</span> <span class="br0">{</span> <span class="nu0">10</span> ,<span class="nu0">0</span> ,<span class="nu0">0</span> ,<span class="nu0">10</span> <span class="br0">}</span> , <span class="nu0">558001</span> , <span class="nu0">620000</span> <span class="br0">}</span> ,</div>
</li>
<li class="li1">
<div class="de1"><span class="br0">{</span> <span class="br0">{</span> <span class="nu0">10</span> ,<span class="nu0">0</span> ,<span class="nu0">0</span> ,<span class="nu0">11</span> <span class="br0">}</span> , <span class="nu0">620001</span> , <span class="nu0">682000</span> <span class="br0">}</span> ,</div>
</li>
<li class="li1">
<div class="de1"><span class="br0">{</span> <span class="br0">{</span> <span class="nu0">10</span> ,<span class="nu0">0</span> ,<span class="nu0">0</span> ,<span class="nu0">12</span> <span class="br0">}</span> , <span class="nu0">682001</span> , <span class="nu0">744000</span> <span class="br0">}</span> ,</div>
</li>
<li class="li1">
<div class="de1"><span class="br0">{</span> <span class="br0">{</span> <span class="nu0">10</span> ,<span class="nu0">0</span> ,<span class="nu0">0</span> ,<span class="nu0">13</span> <span class="br0">}</span> , <span class="nu0">744001</span> , <span class="nu0">806000</span> <span class="br0">}</span> ,</div>
</li>
<li class="li1">
<div class="de1"><span class="br0">{</span> <span class="br0">{</span> <span class="nu0">10</span> ,<span class="nu0">0</span> ,<span class="nu0">0</span> ,<span class="nu0">14</span> <span class="br0">}</span> , <span class="nu0">806001</span> , <span class="nu0">868000</span> <span class="br0">}</span> ,</div>
</li>
<li class="li2">
<div class="de2"><span class="br0">{</span> <span class="br0">{</span> <span class="nu0">10</span> ,<span class="nu0">0</span> ,<span class="nu0">0</span> ,<span class="nu0">15</span> <span class="br0">}</span> , <span class="nu0">868001</span> , <span class="nu0">930000</span> <span class="br0">}</span> ,</div>
</li>
<li class="li1">
<div class="de1"><span class="br0">{</span> <span class="br0">{</span> <span class="nu0">10</span> ,<span class="nu0">0</span> ,<span class="nu0">0</span> ,<span class="nu0">16</span> <span class="br0">}</span> , <span class="nu0">930001</span> , <span class="nu0">992000</span> <span class="br0">}</span> ,</div>
</li>
<li class="li1">
<div class="de1"><span class="br0">{</span> <span class="br0">{</span> <span class="nu0">10</span> ,<span class="nu0">0</span> ,<span class="nu0">0</span> ,<span class="nu0">17</span> <span class="br0">}</span> , <span class="nu0">992001</span> , <span class="nu0">1054000</span> <span class="br0">}</span> <span class="br0">]</span> ,</div>
</li>
<li class="li1">
<div class="de1">        start<span class="br0">(</span> <span class="re0">Config</span> , <span class="re0">Interval</span> <span class="br0">)</span> .</div>
</li>
<li class="li1">
<div class="de1"> </div>
</li>
<li class="li2">
<div class="de2">start<span class="br0">(</span> <span class="re0">Config</span> , <span class="re0">Interval</span> <span class="br0">)</span> -&gt;</div>
</li>
<li class="li1">
<div class="de1">        <span class="re0">Monitor</span> = monitor<span class="br0">(</span> <span class="br0">)</span> ,</div>
</li>
<li class="li1">
<div class="de1">        <span class="re0">AdjustedInterval</span> = <span class="re0">Interval</span> / length<span class="br0">(</span> <span class="re0">Config</span> <span class="br0">)</span> ,</div>
</li>
<li class="li1">
<div class="de1">        <span class="br0">[</span> spawn<span class="br0">(</span> fun start/<span class="nu0">5</span> , <span class="br0">[</span> <span class="re0">Lower</span> , <span class="re0">Upper</span> , <span class="re0">Ip</span> , <span class="re0">AdjustedInterval</span> , <span class="re0">Monitor</span> <span class="br0">]</span> <span class="br0">)</span></div>
</li>
<li class="li1">
<div class="de1">          || <span class="br0">{</span> <span class="re0">Ip</span> , <span class="re0">Lower</span> , <span class="re0">Upper</span> <span class="br0">}</span>  &lt;- <span class="re0">Config</span> <span class="br0">]</span> ,</div>
</li>
<li class="li2">
<div class="de2">        ok.</div>
</li>
<li class="li1">
<div class="de1"> </div>
</li>
<li class="li1">
<div class="de1">start<span class="br0">(</span> <span class="re0">LowerID</span> , <span class="re0">UpperID</span> , _, _, _<span class="br0">)</span> when <span class="re0">LowerID</span> == <span class="re0">UpperID</span> -&gt; <span class="me1">done</span> ;</div>
</li>
<li class="li1">
<div class="de1">start<span class="br0">(</span> <span class="re0">LowerID</span> , <span class="re0">UpperID</span> , <span class="re0">LocalIP</span> , <span class="re0">Interval</span> , <span class="re0">Monitor</span> <span class="br0">)</span> -&gt;</div>
</li>
<li class="li1">
<div class="de1">        <span class="me1">spawn</span> <span class="br0">(</span> fun connect/<span class="nu0">5</span> , <span class="br0">[</span> ?<span class="re0">SERVERADDR</span> , ?<span class="re0">SERVERPORT</span> , <span class="re0">LocalIP</span> , <span class="st0">"/test/"</span> ++<span class="re0">LowerID</span> , <span class="re0">Monitor</span> <span class="br0">]</span> <span class="br0">)</span> ,</div>
</li>
<li class="li2">
<div class="de2">        <span class="kw1">receive</span> <span class="kw1">after</span> <span class="re0">Interval</span> -&gt; <span class="me1">start</span> <span class="br0">(</span> <span class="re0">LowerID</span> + <span class="nu0">1</span> , <span class="re0">UpperID</span> , <span class="re0">LocalIP</span> , <span class="re0">Interval</span> , <span class="re0">Monitor</span> <span class="br0">)</span> <span class="kw1">end</span> .</div>
</li>
<li class="li1">
<div class="de1"> </div>
</li>
<li class="li1">
<div class="de1">connect<span class="br0">(</span> <span class="re0">ServerAddr</span> , <span class="re0">ServerPort</span> , <span class="re0">ClientIP</span> , <span class="re0">Path</span> , <span class="re0">Monitor</span> <span class="br0">)</span> -&gt;</div>
</li>
<li class="li1">
<div class="de1">        <span class="re0">Opts</span> = <span class="br0">[</span> binary, <span class="br0">{</span> packet, <span class="nu0">0</span> <span class="br0">}</span> , <span class="br0">{</span> ip, <span class="re0">ClientIP</span> <span class="br0">}</span> , <span class="br0">{</span> reuseaddr, true<span class="br0">}</span> , <span class="br0">{</span> active, false<span class="br0">}</span> <span class="br0">]</span> ,</div>
</li>
<li class="li1">
<div class="de1">        <span class="br0">{</span> ok, <span class="re0">Sock</span> <span class="br0">}</span> = gen_tcp:<span class="me2">connect</span> <span class="br0">(</span> <span class="re0">ServerAddr</span> , <span class="re0">ServerPort</span> , <span class="re0">Opts</span> <span class="br0">)</span> ,</div>
</li>
<li class="li2">
<div class="de2">        <span class="re0">Monitor</span> ! open,</div>
</li>
<li class="li1">
<div class="de1">        <span class="re0">ReqL</span> = io_lib:<span class="kw3">format</span> <span class="br0">(</span> <span class="st0">&#8220;GET ~s<span class="es0">\r</span> <span class="es0">\n</span> Host: ~s<span class="es0">\r</span> <span class="es0">\n</span> <span class="es0">\r</span> <span class="es0">\n</span> &#8221;</span> , <span class="br0">[</span> <span class="re0">Path</span> , <span class="re0">ServerAddr</span> <span class="br0">]</span> <span class="br0">)</span> ,</div>
</li>
<li class="li1">
<div class="de1">        <span class="re0">Req</span> = list_to_binary<span class="br0">(</span> <span class="re0">ReqL</span> <span class="br0">)</span> ,</div>
</li>
<li class="li1">
<div class="de1">        ok = gen_tcp:<span class="me2">send</span> <span class="br0">(</span> <span class="re0">Sock</span> , <span class="br0">[</span> <span class="re0">Req</span> <span class="br0">]</span> <span class="br0">)</span> ,</div>
</li>
<li class="li1">
<div class="de1">        do_recv<span class="br0">(</span> <span class="re0">Sock</span> , <span class="re0">Monitor</span> <span class="br0">)</span> ,</div>
</li>
<li class="li2">
<div class="de2">        <span class="br0">(</span> catch gen_tcp:<span class="kw3">close</span> <span class="br0">(</span> <span class="re0">Sock</span> <span class="br0">)</span> <span class="br0">)</span> ,</div>
</li>
<li class="li1">
<div class="de1">        ok.</div>
</li>
<li class="li1">
<div class="de1"> </div>
</li>
<li class="li1">
<div class="de1">do_recv<span class="br0">(</span> <span class="re0">Sock</span> , <span class="re0">Monitor</span> <span class="br0">)</span> -&gt;</div>
</li>
<li class="li1">
<div class="de1">        <span class="kw1">case</span> gen_tcp:<span class="me2">recv</span> <span class="br0">(</span> <span class="re0">Sock</span> , <span class="nu0">0</span> <span class="br0">)</span> <span class="kw1">of</span></div>
</li>
<li class="li2">
<div class="de2">                <span class="br0">{</span> ok, <span class="re0">B</span> <span class="br0">}</span> -&gt;</div>
</li>
<li class="li1">
<div class="de1">                        <span class="re0">Monitor</span> ! <span class="br0">{</span> bytes, size<span class="br0">(</span> <span class="re0">B</span> <span class="br0">)</span> <span class="br0">}</span> ,</div>
</li>
<li class="li1">
<div class="de1">                        io:<span class="kw3">format</span> <span class="br0">(</span> <span class="st0">&#8220;Recvd ~s<span class="es0">\n</span> &#8221;</span> , <span class="br0">[</span> binary_to_list<span class="br0">(</span> <span class="re0">B</span> <span class="br0">)</span> <span class="br0">]</span> <span class="br0">)</span> ,</div>
</li>
<li class="li1">
<div class="de1">                        io:<span class="kw3">format</span> <span class="br0">(</span> <span class="st0">&#8220;Recvd ~w bytes<span class="es0">\n</span> &#8221;</span> , <span class="br0">[</span> size<span class="br0">(</span> <span class="re0">B</span> <span class="br0">)</span> <span class="br0">]</span> <span class="br0">)</span> ,</div>
</li>
<li class="li1">
<div class="de1">                        do_recv<span class="br0">(</span> <span class="re0">Sock</span> , <span class="re0">Monitor</span> <span class="br0">)</span> ;</div>
</li>
<li class="li2">
<div class="de2">                <span class="br0">{</span> error, closed<span class="br0">}</span> -&gt;</div>
</li>
<li class="li1">
<div class="de1">                        <span class="re0">Monitor</span> ! closed,</div>
</li>
<li class="li1">
<div class="de1">                        closed;</div>
</li>
<li class="li1">
<div class="de1">                <span class="re0">Other</span> -&gt;</div>
</li>
<li class="li1">
<div class="de1">                        <span class="re0">Monitor</span> ! closed,</div>
</li>
<li class="li2">
<div class="de2">                        io:<span class="kw3">format</span> <span class="br0">(</span> <span class="st0">&#8220;Other:~w<span class="es0">\n</span> &#8221;</span> ,<span class="br0">[</span> <span class="re0">Other</span> <span class="br0">]</span> <span class="br0">)</span></div>
</li>
<li class="li1">
<div class="de1">        <span class="kw1">end</span> .</div>
</li>
<li class="li1">
<div class="de1"> </div>
</li>
<li class="li1">
<div class="de1"><span class="co1">% Monitor process receives stats and reports how much data we received etc:</span></div>
</li>
<li class="li1">
<div class="de1">monitor<span class="br0">(</span> <span class="br0">)</span> -&gt;</div>
</li>
<li class="li2">
<div class="de2">        <span class="re0">Pid</span> = spawn<span class="br0">(</span> ?<span class="re0">MODULE</span> , monitor0, <span class="br0">[</span> <span class="br0">{</span> <span class="nu0">0</span> ,<span class="nu0">0</span> ,<span class="nu0">0</span> ,<span class="nu0">0</span> <span class="br0">}</span> <span class="br0">]</span> <span class="br0">)</span> ,</div>
</li>
<li class="li1">
<div class="de1">        timer:<span class="me2">send_interval</span> <span class="br0">(</span> <span class="nu0">10000</span> , <span class="re0">Pid</span> , report<span class="br0">)</span> ,</div>
</li>
<li class="li1">
<div class="de1">        <span class="re0">Pid</span> .</div>
</li>
<li class="li1">
<div class="de1"> </div>
</li>
<li class="li1">
<div class="de1">monitor0<span class="br0">(</span> <span class="br0">{</span> <span class="re0">Open</span> , <span class="re0">Closed</span> , <span class="re0">Chunks</span> , <span class="re0">Bytes</span> <span class="br0">}</span> =<span class="re0">S</span> <span class="br0">)</span> -&gt;</div>
</li>
<li class="li2">
<div class="de2">        <span class="kw1">receive</span></div>
</li>
<li class="li1">
<div class="de1">                report  -&gt; <span class="me1">io</span> :<span class="kw3">format</span> <span class="br0">(</span> <span class="st0">&#8220;{Open, Closed, Chunks, Bytes} = ~w<span class="es0">\n</span> &#8221;</span> ,<span class="br0">[</span> <span class="re0">S</span> <span class="br0">]</span> <span class="br0">)</span> ;</div>
</li>
<li class="li1">
<div class="de1">                open    -&gt; <span class="me1">monitor0</span> <span class="br0">(</span> <span class="br0">{</span> <span class="re0">Open</span> + <span class="nu0">1</span> , <span class="re0">Closed</span> , <span class="re0">Chunks</span> , <span class="re0">Bytes</span> <span class="br0">}</span> <span class="br0">)</span> ;</div>
</li>
<li class="li1">
<div class="de1">                closed  -&gt; <span class="me1">monitor0</span> <span class="br0">(</span> <span class="br0">{</span> <span class="re0">Open</span> , <span class="re0">Closed</span> + <span class="nu0">1</span> , <span class="re0">Chunks</span> , <span class="re0">Bytes</span> <span class="br0">}</span> <span class="br0">)</span> ;</div>
</li>
<li class="li1">
<div class="de1">                chunk   -&gt; <span class="me1">monitor0</span> <span class="br0">(</span> <span class="br0">{</span> <span class="re0">Open</span> , <span class="re0">Closed</span> , <span class="re0">Chunks</span> + <span class="nu0">1</span> , <span class="re0">Bytes</span> <span class="br0">}</span> <span class="br0">)</span> ;</div>
</li>
<li class="li2">
<div class="de2">                <span class="br0">{</span> bytes, <span class="re0">B</span> <span class="br0">}</span> -&gt; <span class="me1">monitor0</span> <span class="br0">(</span> <span class="br0">{</span> <span class="re0">Open</span> , <span class="re0">Closed</span> , <span class="re0">Chunks</span> , <span class="re0">Bytes</span> + <span class="re0">B</span> <span class="br0">}</span> <span class="br0">)</span></div>
</li>
<li class="li1">
<div class="de1">        <span class="kw1">end</span> .</div>
</li>
</ol>
</div>
<p>作为一个初始的测试，我像第一部分描述的那样连接mochiweb应用 - 它简单的每隔10秒给每个客户端发送一条消息。</p>
<p><code>erl&gt; c(floodtest2), floodtest2:run(20).</code></p>
<p><strong>这很快就吃掉了我的内存。<br />
</strong></p>
<p>像那样用gen_tcp打开很多连接吃掉了很多内存。 在没有任何其他调整的情况下我想它需要~36GB的内存以保证正常工作。 我没有兴趣试着优化我的快速破解的erlang http 客户端(在真实世界了,这将是1M个web浏览器), 在手的有多于32GB内存的机子只有那台我们产品数据库用机， 我不能找到一个很好的理由就因为测试这个让last.fm下线：）另外，它看起来它一直只能管理打开64,500个端口 。</p>
<p>从这一点我决定采用值得相信的 <a href="http://monkey.org/~provos/libevent/">libevent</a> , 我很高兴发现有这么一个HTTP API。 新版已经有了一个<code>evhttp_connection_set_local_address函数</code> 。感觉很有希望.</p>
<p>这是采用libevent库用C编写的客户端：</p>
<div class="dean_ch">
<ol>
<li class="li1">
<div class="de1"><span class="co2">#include &lt;sys/types.h&gt;</span></div>
</li>
<li class="li1">
<div class="de1"><span class="co2">#include &lt;sys/time.h&gt;</span></div>
</li>
<li class="li1">
<div class="de1"><span class="co2">#include &lt;sys/queue.h&gt;</span></div>
</li>
<li class="li1">
<div class="de1"><span class="co2">#include &lt;stdlib.h&gt;</span></div>
</li>
<li class="li2">
<div class="de2"><span class="co2">#include &lt;err.h&gt;</span></div>
</li>
<li class="li1">
<div class="de1"><span class="co2">#include &lt;event.h&gt;</span></div>
</li>
<li class="li1">
<div class="de1"><span class="co2">#include &lt;evhttp.h&gt;</span></div>
</li>
<li class="li1">
<div class="de1"><span class="co2">#include &lt;unistd.h&gt;</span></div>
</li>
<li class="li1">
<div class="de1"><span class="co2">#include &lt;stdio.h&gt;</span></div>
</li>
<li class="li2">
<div class="de2"><span class="co2">#include &lt;sys/socket.h&gt;</span></div>
</li>
<li class="li1">
<div class="de1"><span class="co2">#include &lt;netinet/in.h&gt;</span></div>
</li>
<li class="li1">
<div class="de1"><span class="co2">#include &lt;time.h&gt;</span></div>
</li>
<li class="li1">
<div class="de1"><span class="co2">#include &lt;pthread.h&gt;</span></div>
</li>
<li class="li1">
<div class="de1"> </div>
</li>
<li class="li2">
<div class="de2"><span class="co2">#define BUFSIZE 4096</span></div>
</li>
<li class="li1">
<div class="de1"><span class="co2">#define NUMCONNS 62000</span></div>
</li>
<li class="li1">
<div class="de1"><span class="co2">#define SERVERADDR &#8220;10.103.1.43&#8243;</span></div>
</li>
<li class="li1">
<div class="de1"><span class="co2">#define SERVERPORT 8000</span></div>
</li>
<li class="li1">
<div class="de1"><span class="co2">#define SLEEP_MS 10</span></div>
</li>
<li class="li2">
<div class="de2"> </div>
</li>
<li class="li1">
<div class="de1"><span class="kw4">char</span> buf<span class="br0">[</span> BUFSIZE<span class="br0">]</span> ;</div>
</li>
<li class="li1">
<div class="de1"> </div>
</li>
<li class="li1">
<div class="de1"><span class="kw4">int</span> bytes_recvd = <span class="nu0">0</span> ;</div>
</li>
<li class="li1">
<div class="de1"><span class="kw4">int</span> chunks_recvd = <span class="nu0">0</span> ;</div>
</li>
<li class="li2">
<div class="de2"><span class="kw4">int</span> closed = <span class="nu0">0</span> ;</div>
</li>
<li class="li1">
<div class="de1"><span class="kw4">int</span> connected = <span class="nu0">0</span> ;</div>
</li>
<li class="li1">
<div class="de1"> </div>
</li>
<li class="li1">
<div class="de1"><span class="co1">// called per chunk received</span></div>
</li>
<li class="li1">
<div class="de1"><span class="kw4">void</span> chunkcb<span class="br0">(</span> <span class="kw4">struct</span> evhttp_request * req, <span class="kw4">void</span> * arg<span class="br0">)</span></div>
</li>
<li class="li2">
<div class="de2"><span class="br0">{</span></div>
</li>
<li class="li1">
<div class="de1">    <span class="kw4">int</span> s = evbuffer_remove<span class="br0">(</span> req-&gt;input_buffer, &amp;buf, BUFSIZE <span class="br0">)</span> ;</div>
</li>
<li class="li1">
<div class="de1">    <span class="co1">//printf(&#8221;Read %d bytes: %s\n&#8221;, s, &amp;buf);</span></div>
</li>
<li class="li1">
<div class="de1">    bytes_recvd += s;</div>
</li>
<li class="li1">
<div class="de1">    chunks_recvd++;</div>
</li>
<li class="li2">
<div class="de2">    <span class="kw1">if</span> <span class="br0">(</span> connected &gt;= NUMCONNS &amp;&amp; chunks_recvd%<span class="nu0">10000</span> ==<span class="nu0">0</span> <span class="br0">)</span></div>
</li>
<li class="li1">
<div class="de1">        <a href="http://www.opengroup.org/onlinepubs/009695399/functions/printf.html"><span class="kw3">printf</span> </a><span class="br0">(</span> <span class="st0">&#8220;&gt;Chunks: %d<span class="es0">\t</span> Bytes: %d<span class="es0">\t</span> Closed: %d<span class="es0">\n</span> &#8221;</span> , chunks_recvd, bytes_recvd, closed<span class="br0">)</span> ;</div>
</li>
<li class="li1">
<div class="de1"><span class="br0">}</span></div>
</li>
<li class="li1">
<div class="de1"> </div>
</li>
<li class="li1">
<div class="de1"><span class="co1">// gets called when request completes</span></div>
</li>
<li class="li2">
<div class="de2"><span class="kw4">void</span> reqcb<span class="br0">(</span> <span class="kw4">struct</span> evhttp_request * req, <span class="kw4">void</span> * arg<span class="br0">)</span></div>
</li>
<li class="li1">
<div class="de1"><span class="br0">{</span></div>
</li>
<li class="li1">
<div class="de1">    closed++;</div>
</li>
<li class="li1">
<div class="de1"><span class="br0">}</span></div>
</li>
<li class="li1">
<div class="de1"> </div>
</li>
<li class="li2">
<div class="de2"><span class="kw4">int</span> main<span class="br0">(</span> <span class="kw4">int</span> argc, <span class="kw4">char</span> **argv<span class="br0">)</span></div>
</li>
<li class="li1">
<div class="de1"><span class="br0">{</span></div>
</li>
<li class="li1">
<div class="de1">    event_init<span class="br0">(</span> <span class="br0">)</span> ;</div>
</li>
<li class="li1">
<div class="de1">    <span class="kw4">struct</span> evhttp *evhttp_connection;</div>
</li>
<li class="li1">
<div class="de1">    <span class="kw4">struct</span> evhttp_request *evhttp_request;</div>
</li>
<li class="li2">
<div class="de2">    <span class="kw4">char</span> addr<span class="br0">[</span> <span class="nu0">16</span> <span class="br0">]</span> ;</div>
</li>
<li class="li1">
<div class="de1">    <span class="kw4">char</span> path<span class="br0">[</span> <span class="nu0">32</span> <span class="br0">]</span> ; <span class="co1">// eg: &#8220;/test/123&#8243;</span></div>
</li>
<li class="li1">
<div class="de1">    <span class="kw4">int</span> i,octet;</div>
</li>
<li class="li1">
<div class="de1">    <span class="kw1">for</span> <span class="br0">(</span> octet=<span class="nu0">1</span> ; octet&lt;=<span class="nu0">17</span> ; octet++<span class="br0">)</span> <span class="br0">{</span></div>
</li>
<li class="li1">
<div class="de1">        sprintf<span class="br0">(</span> &amp;addr, <span class="st0">&#8220;10.224.0.%d&#8221;</span> , octet<span class="br0">)</span> ;</div>
</li>
<li class="li2">
<div class="de2">        <span class="kw1">for</span> <span class="br0">(</span> i=<span class="nu0">1</span> ;i&lt;=NUMCONNS;i++<span class="br0">)</span> <span class="br0">{</span></div>
</li>
<li class="li1">
<div class="de1">            evhttp_connection = evhttp_connection_new<span class="br0">(</span> SERVERADDR, SERVERPORT<span class="br0">)</span> ;</div>
</li>
<li class="li1">
<div class="de1">            evhttp_connection_set_local_address<span class="br0">(</span> evhttp_connection, &amp;addr<span class="br0">)</span> ;</div>
</li>
<li class="li1">
<div class="de1">            evhttp_set_timeout<span class="br0">(</span> evhttp_connection, <span class="nu0">864000</span> <span class="br0">)</span> ; <span class="co1">// 10 day timeout</span></div>
</li>
<li class="li1">
<div class="de1">            evhttp_request = evhttp_request_new<span class="br0">(</span> reqcb, <span class="kw2">NULL</span> <span class="br0">)</span> ;</div>
</li>
<li class="li2">
<div class="de2">            evhttp_request-&gt;chunk_cb = chunkcb;</div>
</li>
<li class="li1">
<div class="de1">            sprintf<span class="br0">(</span> &amp;path, <span class="st0">&#8220;/test/%d&#8221;</span> , ++connected<span class="br0">)</span> ;</div>
</li>
<li class="li1">
<div class="de1">            <span class="kw1">if</span> <span class="br0">(</span> i%<span class="nu0">100</span> ==<span class="nu0">0</span> <span class="br0">)</span>  <a href="http://www.opengroup.org/onlinepubs/009695399/functions/printf.html"><span class="kw3">printf</span> </a><span class="br0">(</span> <span class="st0">&#8220;Req: %s<span class="es0">\t</span> -&gt;<span class="es0">\t</span> %s<span class="es0">\n</span> &#8221;</span> , addr, &amp;path<span class="br0">)</span> ;</div>
</li>
<li class="li1">
<div class="de1">            evhttp_make_request<span class="br0">(</span> evhttp_connection, evhttp_request, EVHTTP_REQ_GET, path <span class="br0">)</span> ;</div>
</li>
<li class="li1">
<div class="de1">            evhttp_connection_set_timeout<span class="br0">(</span> evhttp_request-&gt;evcon, <span class="nu0">864000</span> <span class="br0">)</span> ;</div>
</li>
<li class="li2">
<div class="de2">            event_loop<span class="br0">(</span> EVLOOP_NONBLOCK <span class="br0">)</span> ;</div>
</li>
<li class="li1">
<div class="de1">            <span class="kw1">if</span> <span class="br0">(</span> connected % <span class="nu0">200</span> == <span class="nu0">0</span> <span class="br0">)</span></div>
</li>
<li class="li1">
<div class="de1">                <a href="http://www.opengroup.org/onlinepubs/009695399/functions/printf.html"><span class="kw3">printf</span> </a><span class="br0">(</span> <span class="st0">&#8220;<span class="es0">\n</span> Chunks: %d<span class="es0">\t</span> Bytes: %d<span class="es0">\t</span> Closed: %d<span class="es0">\n</span> &#8221;</span> , chunks_recvd, bytes_recvd, closed<span class="br0">)</span> ;</div>
</li>
<li class="li1">
<div class="de1">            usleep<span class="br0">(</span> SLEEP_MS*<span class="nu0">1000</span> <span class="br0">)</span> ;</div>
</li>
<li class="li1">
<div class="de1">        <span class="br0">}</span></div>
</li>
<li class="li2">
<div class="de2">    <span class="br0">}</span></div>
</li>
<li class="li1">
<div class="de1">    event_dispatch<span class="br0">(</span> <span class="br0">)</span> ;</div>
</li>
<li class="li1">
<div class="de1">    <span class="kw1">return</span> <span class="nu0">0</span> ;</div>
</li>
<li class="li1">
<div class="de1"><span class="br0">}</span></div>
</li>
</ol>
</div>
<p>更多的参数用#define’s硬编码，这样你通过编辑源码来配置他然后重新编译。</p>
<p>编印运行：<br />
<code>$ gcc -o httpclient httpclient.c -levent<br />
$ ./httpclient</code></p>
<p><strong>这样还是不能打开多余64,500个端口</strong> . 尽管他用了很少的内存。</p>
<p>尽管我指定了本地地址端口还是会超出限制, 临时端口无论是在内核或tcp栈上分配的都会超出2^16。 因此，为了能打开多于64,500个连接, 你需要指定本地地址和本地端口，相应的管理它们。不幸的libevent HTTP API没有选项指定本地端口。我 <a href="http://monkeymail.org/archives/libevent-users/2008-November/001415.html">为 libevent打了补丁</a> 加了一个合适的函数：<br />
<code>void evhttp_connection_set_local_port(struct evhttp_connection *evcon, u_short port);</code> .</p>
<p>这相当不错； libevent编写的被很好, 文档也相当友好。</p>
<p>安装我修改过的libevent, 我可以在the set_local_address下添加如下代码:<br />
<code>evhttp_connection_set_local_port(evhttp_connection, 1024+i);</code></p>
<p>用他替换后, 从不同的地址的多个连接就能用同一端口号，指定本地地址。我重编译客户端让它运行一段时间以验证他能大多2^16限制。</p>
<p><span title="Pun intended">Netstat验证</span> :<br />
<code># netstat -n | awk '/^tcp/ {t[$NF]++}END{for(state in t){print state, t[state]}}’<br />
TIME_WAIT 8<br />
ESTABLISHED 118222</code></p>
<p>这显示多少端口在不同状态被打开。我们最后能够打开多于2^16个连接.</p>
<p>现在我们有了在一台机子上打开百万http连接的工具。它看起来每个连接消耗2KB内存, 加上内核占用的。是时候用它测试百万连接用户的我们的mochiweb comet服务器了。</p>
<h2>C1024K测试-1 百万comet连接</h2>
<p>为了这个测试我用了4台不同配置的服务器。这样的配置可能比测试用的高，但是它是有用的将来会用为产品，这能做一个很变态的测试. 所有这四个服务器都在同一个千兆局域网上，中间用了3个交换机和一个路由器。</p>
<p>一百万测试就像第一、二部分的10k测试，主要不同是更改了客户端，现在是用libevent,c编写，我在一个用了多台机子的正式的分布式erlang设置中运行的a。</p>
<p>服务器1 - 四核 2GHz CPU, 16GB 内存</p>
<ul>
<li>启动订阅管理器</li>
<li>调入好友数据</li>
<li>启动路由器</li>
</ul>
<p>服务器2 - 双通道四核 2.8GHz CPU, 32GB 内存</p>
<ul>
<li>启动mochiweb应用</li>
</ul>
<p>服务器3 - 四核 2GHz CPU, 16GB 内存</p>
<ul>
<li>创建17个真实ip</li>
<li>安装打了补丁的libevent</li>
<li>运行客户端: <code>./httpclient</code> 每秒建立100个连接直到1M</li>
</ul>
<p>服务器4 - 双核 2GHz, 2GB内存</p>
<ul>
<li>运行msggen程序, 向路由器发送大量的消息</li>
</ul>
<p>在猛增到一百万连接期间我测量了mochiweb的内存用量，还有在剩下的时间里：</p>
<p><a href="http://www.metabrew.com/wp-content/uploads/2008/11/mochimem-c1000k.png"><img class="aligncenter size-full wp-image-172" src="http://www.metabrew.com/wp-content/uploads/2008/11/mochimem-c1000k.png" alt="" width="500" height="300" /></a></p>
<p>httpclient在每个连接见加了10ms延时,因此打开一百万连接用了将近3个小时。打开1M连接的mochiweb进程固定地内存用大约为25GB. 运行的服务器都由Ganglia监控, 它测量CPU, 网络和内存用量并且生成漂亮的图片：</p>
<p><a href="http://www.metabrew.com/wp-content/uploads/2008/11/server21.png"><img class="alignnone size-medium wp-image-173" src="http://www.metabrew.com/wp-content/uploads/2008/11/server21.png" alt="" width="131" height="300" /></a></p>
<p>你可以看到它需要大约38GB内存且开始了swap。我猜想这个不同是因为内核为保持打开的连接而基本被耗光的 。当我开始发送消息是就达到了定点。</p>
<p>消息有1000个进程产生，每个进程平均60ms一条消息，总共每秒大约16,666条消息：</p>
<p><code>erl&gt; [ spawn( fun()-&gt;msggen:start(1000000, 10+random:uniform(100), 1000000) end) || I &lt;- lists:seq(1,1000) ].</code></p>
<p>服务器 (server-4) 产生消息看起来如下图所示（Ganglia）:</p>
<p><a href="http://www.metabrew.com/wp-content/uploads/2008/11/msggen.png"><img class="alignnone size-medium wp-image-170" src="http://www.metabrew.com/wp-content/uploads/2008/11/msggen.png" alt="" width="131" height="300" /></a></p>
<p>每秒有10MB的消息发出 - 每秒16,666条消息. 典型的这些消息来自消息总线，应用服务器，或者已存在架构的一部分。</p>
<p>当我开始发送消息时，服务器1的负载(运行订阅管理器和路由器)一直低于1,CPU占用率从0增到5%。</p>
<p>服务器2的CPU (运行mochiweb应用, 有1M个连接) 增长的比较显著：</p>
<p><a href="http://www.metabrew.com/wp-content/uploads/2008/11/server2.png"><img class="alignnone size-medium wp-image-171" src="http://www.metabrew.com/wp-content/uploads/2008/11/server2.png" alt="" width="131" height="300" /></a></p>
<p>自然的，进程当要处理消息是不得不离开休眠状态,内存用量将轻微增加 . 没有消息且所有连接处于打开状态是内存用量的最好时候- 可想的,实际工作时需要更多内存。</p>
<p>从安全方面, mochiweb机器需要40GB内存一打开1M活跃comet连接。30GB用于mochiweb应用,剩下的10GB用于内核.换句话说,每个连接你需要分配40KB。</p>
<p>当用大量连接做各种测试时,我最终对我的sysctl.conf文件做了些修改. 只是部分试错,我真的不知道更多以做出明智的决定关于那个值需要修改的内部原因 . 我的策略是等待问题发生，检测 <code>/var/log/kern.log</code> 看什么神秘的错误被报告, 然后添加听起来很合理的数据. 这是上面测试使用的设置信息：</p>
<pre>net.core.rmem_max = 33554432
net.core.wmem_max = 33554432
net.ipv4.tcp_rmem = 4096 16384 33554432
net.ipv4.tcp_wmem = 4096 16384 33554432
net.ipv4.tcp_mem = 786432 1048576 26777216
net.ipv4.tcp_max_tw_b****ets = 360000
&nbsp;<a href="http://net.core.net" title="http://net.core.
" target="_blank">net.core.net</a>dev_max_backlog = 2500
vm.min_free_kbytes = 65536
vm.swappiness = 0
net.ipv4.ip_local_port_range = 1024 65535</pre>
<p>我将很喜欢学习更多关于linux tcp调优的知识这样我能对这些设置做更明智的决策. 这些可与确定不是优化的, 但是最少它们足够应付1M的连接. 这些更改运行在一个64bit的elang虚拟机上, 字长是8bytes而不是4, 可能可以解释为什么内存用量比我在第二部分做c10k测试时高的多。</p>
<h2>一个用Libevent实现的Erlang C-Node</h2>
<p>在为libevent加入HTTP api后, 它看起来完全合理做1M连接测试相对于用c写的http服务器，因此我们有了比较的基础。</p>
<p>我猜打开内核的poll模型意味这erlang虚拟机能够用epol(或类似的),但是即使是这样显然也需要解决负载问题，我们通过委派连接处理给用libevent实现的c程序或许能减轻负载. 我想重用更多的Erlang代码, 因此让我们尽可能少的用c - 只是在连接处理和HTTP部分。 我也寻找了试用Erlang C 接口的一个理由,因此下面的程序组合了两者。他是一个用C和libevent写的comet http服务器用用整数id标志用户(向我们的mochiweb应用), 且扮演一个Erlang C节点.</p>
<p>它连接一个指定的erlang节点, <code>监听像{123, &lt;&lt;"Hello user 123"&gt;&gt;}的消息然后向用户</code> 123<code>分派</code> “Hello user 123″ , 假如已连接. 那些没有连接用户的消息被丢弃,就像前面的例子。</p>
<p>httpdcnode.c</p>
<div class="dean_ch">
<ol>
<li class="li1">
<div class="de1"><span class="co2">#include &lt;sys/types.h&gt;</span></div>
</li>
<li class="li1">
<div class="de1"><span class="co2">#include &lt;sys/time.h&gt;</span></div>
</li>
<li class="li1">
<div class="de1"><span class="co2">#include &lt;sys/queue.h&gt;</span></div>
</li>
<li class="li1">
<div class="de1"><span class="co2">#include &lt;stdlib.h&gt;</span></div>
</li>
<li class="li2">
<div class="de2"><span class="co2">#include &lt;err.h&gt;</span></div>
</li>
<li class="li1">
<div class="de1"><span class="co2">#include &lt;event.h&gt;</span></div>
</li>
<li class="li1">
<div class="de1"><span class="co2">#include &lt;evhttp.h&gt;</span></div>
</li>
<li class="li1">
<div class="de1"><span class="co2">#include &lt;stdio.h&gt;</span></div>
</li>
<li class="li1">
<div class="de1"><span class="co2">#include &lt;sys/socket.h&gt;</span></div>
</li>
<li class="li2">
<div class="de2"><span class="co2">#include &lt;netinet/in.h&gt;</span></div>
</li>
<li class="li1">
<div class="de1"> </div>
</li>
<li class="li1">
<div class="de1"><span class="co2">#include &#8220;erl_interface.h&#8221;</span></div>
</li>
<li class="li1">
<div class="de1"><span class="co2">#include &#8220;ei.h&#8221;</span></div>
</li>
<li class="li1">
<div class="de1"> </div>
</li>
<li class="li2">
<div class="de2"><span class="co2">#include &lt;pthread.h&gt;</span></div>
</li>
<li class="li1">
<div class="de1"> </div>
</li>
<li class="li1">
<div class="de1"><span class="co2">#define BUFSIZE 1024</span></div>
</li>
<li class="li1">
<div class="de1"><span class="co2">#define MAXUSERS (17*65536) // C1024K</span></div>
</li>
<li class="li1">
<div class="de1"> </div>
</li>
<li class="li2">
<div class="de2"><span class="co1">// List of current http requests by uid:</span></div>
</li>
<li class="li1">
<div class="de1"><span class="kw4">struct</span> evhttp_request * clients<span class="br0">[</span> MAXUSERS<span class="nu0">+1</span> <span class="br0">]</span> ;</div>
</li>
<li class="li1">
<div class="de1"><span class="co1">// Memory to store uids passed to the cleanup callback:</span></div>
</li>
<li class="li1">
<div class="de1"><span class="kw4">int</span> slots<span class="br0">[</span> MAXUSERS<span class="nu0">+1</span> <span class="br0">]</span> ;</div>
</li>
<li class="li1">
<div class="de1"> </div>
</li>
<li class="li2">
<div class="de2"><span class="co1">// called when user disconnects</span></div>
</li>
<li class="li1">
<div class="de1"><span class="kw4">void</span> cleanup<span class="br0">(</span> <span class="kw4">struct</span> evhttp_connection *evcon, <span class="kw4">void</span> *arg<span class="br0">)</span></div>
</li>
<li class="li1">
<div class="de1"><span class="br0">{</span></div>
</li>
<li class="li1">
<div class="de1">    <span class="kw4">int</span> *uidp = <span class="br0">(</span> <span class="kw4">int</span> *<span class="br0">)</span> arg;</div>
</li>
<li class="li1">
<div class="de1">    fprintf<span class="br0">(</span> stderr, <span class="st0">&#8220;disconnected uid %d<span class="es0">\n</span> &#8221;</span> , *uidp<span class="br0">)</span> ;</div>
</li>
<li class="li2">
<div class="de2">    clients<span class="br0">[</span> *uidp<span class="br0">]</span> = <span class="kw2">NULL</span> ;</div>
</li>
<li class="li1">
<div class="de1"><span class="br0">}</span></div>
</li>
<li class="li1">
<div class="de1"> </div>
</li>
<li class="li1">
<div class="de1"><span class="co1">// handles http connections, sets them up for chunked transfer,</span></div>
</li>
<li class="li1">
<div class="de1"><span class="co1">// extracts the user id and registers in the global connection table,</span></div>
</li>
<li class="li2">
<div class="de2"><span class="co1">// also sends a welcome chunk.</span></div>
</li>
<li class="li1">
<div class="de1"><span class="kw4">void</span> request_handler<span class="br0">(</span> <span class="kw4">struct</span> evhttp_request *req, <span class="kw4">void</span> *arg<span class="br0">)</span></div>
</li>
<li class="li1">
<div class="de1"><span class="br0">{</span></div>
</li>
<li class="li1">
<div class="de1">        <span class="kw4">struct</span> evbuffer *buf;</div>
</li>
<li class="li1">
<div class="de1">        buf = evbuffer_new<span class="br0">(</span> <span class="br0">)</span> ;</div>
</li>
<li class="li2">
<div class="de2">        <span class="kw1">if</span> <span class="br0">(</span> buf == <span class="kw2">NULL</span> <span class="br0">)</span> <span class="br0">{</span></div>
</li>
<li class="li1">
<div class="de1">            err<span class="br0">(</span> <span class="nu0">1</span> , <span class="st0">&#8220;failed to create response buffer&#8221;</span> <span class="br0">)</span> ;</div>
</li>
<li class="li1">
<div class="de1">        <span class="br0">}</span></div>
</li>
<li class="li1">
<div class="de1"> </div>
</li>
<li class="li1">
<div class="de1">        evhttp_add_header<span class="br0">(</span> req-&gt;output_headers, <span class="st0">&#8220;Content-Type&#8221;</span> , <span class="st0">&#8220;text/html; charset=utf-8&#8243;</span> <span class="br0">)</span> ;</div>
</li>
<li class="li2">
<div class="de2"> </div>
</li>
<li class="li1">
<div class="de1">        <span class="kw4">int</span> uid = <span class="nu0">-1</span> ;</div>
</li>
<li class="li1">
<div class="de1">        <span class="kw1">if</span> <span class="br0">(</span> strncmp<span class="br0">(</span> evhttp_request_uri<span class="br0">(</span> req<span class="br0">)</span> , <span class="st0">&#8220;/test/&#8221;</span> , <span class="nu0">6</span> <span class="br0">)</span> == <span class="nu0">0</span> <span class="br0">)</span> <span class="br0">{</span></div>
</li>
<li class="li1">
<div class="de1">            uid = atoi<span class="br0">(</span> <span class="nu0">6</span> +evhttp_request_uri<span class="br0">(</span> req<span class="br0">)</span> <span class="br0">)</span> ;</div>
</li>
<li class="li1">
<div class="de1">        <span class="br0">}</span></div>
</li>
<li class="li2">
<div class="de2"> </div>
</li>
<li class="li1">
<div class="de1">        <span class="kw1">if</span> <span class="br0">(</span> uid &lt;= <span class="nu0">0</span> <span class="br0">)</span> <span class="br0">{</span></div>
</li>
<li class="li1">
<div class="de1">            evbuffer_add_printf<span class="br0">(</span> buf, <span class="st0">&#8220;User id not found, try /test/123 instead&#8221;</span> <span class="br0">)</span> ;</div>
</li>
<li class="li1">
<div class="de1">            evhttp_send_reply<span class="br0">(</span> req, HTTP_NOTFOUND, <span class="st0">&#8220;Not Found&#8221;</span> , buf<span class="br0">)</span> ;</div>
</li>
<li class="li1">
<div class="de1">            evbuffer_free<span class="br0">(</span> buf<span class="br0">)</span> ;</div>
</li>
<li class="li2">
<div class="de2">            <span class="kw1">return</span> ;</div>
</li>
<li class="li1">
<div class="de1">        <span class="br0">}</span></div>
</li>
<li class="li1">
<div class="de1"> </div>
</li>
<li class="li1">
<div class="de1">        <span class="kw1">if</span> <span class="br0">(</span> uid &gt; MAXUSERS<span class="br0">)</span> <span class="br0">{</span></div>
</li>
<li class="li1">
<div class="de1">            evbuffer_add_printf<span class="br0">(</span> buf, <span class="st0">&#8220;Max uid allowed is %d&#8221;</span> , MAXUSERS<span class="br0">)</span> ;</div>
</li>
<li class="li2">
<div class="de2">            evhttp_send_reply<span class="br0">(</span> req, HTTP_SERVUNAVAIL, <span class="st0">&#8220;We ran out of numbers&#8221;</span> , buf<span class="br0">)</span> ;</div>
</li>
<li class="li1">
<div class="de1">            evbuffer_free<span class="br0">(</span> buf<span class="br0">)</span> ;</div>
</li>
<li class="li1">
<div class="de1">            <span class="kw1">return</span> ;</div>
</li>
<li class="li1">
<div class="de1">        <span class="br0">}</span></div>
</li>
<li class="li1">
<div class="de1"> </div>
</li>
<li class="li2">
<div class="de2">        evhttp_send_reply_start<span class="br0">(</span> req, HTTP_OK, <span class="st0">&#8220;OK&#8221;</span> <span class="br0">)</span> ;</div>
</li>
<li class="li1">
<div class="de1">        <span class="co1">// Send welcome chunk:</span></div>
</li>
<li class="li1">
<div class="de1">        evbuffer_add_printf<span class="br0">(</span> buf, <span class="st0">&#8220;Welcome, Url: ‘%s’ Id: %d<span class="es0">\n</span> &#8221;</span> , evhttp_request_uri<span class="br0">(</span> req<span class="br0">)</span> , uid<span class="br0">)</span> ;</div>
</li>
<li class="li1">
<div class="de1">        evhttp_send_reply_chunk<span class="br0">(</span> req, buf<span class="br0">)</span> ;</div>
</li>
<li class="li1">
<div class="de1">        evbuffer_free<span class="br0">(</span> buf<span class="br0">)</span> ;</div>
</li>
<li class="li2">
<div class="de2"> </div>
</li>
<li class="li1">
<div class="de1">        <span class="co1">// put reference into global uid-&gt;connection table:</span></div>
</li>
<li class="li1">
<div class="de1">        clients<span class="br0">[</span> uid<span class="br0">]</span> = req;</div>
</li>
<li class="li1">
<div class="de1">        <span class="co1">// set close callback</span></div>
</li>
<li class="li1">
<div class="de1">        evhttp_connection_set_closecb<span class="br0">(</span> req-&gt;evcon, cleanup, &amp;slots<span class="br0">[</span> uid<span class="br0">]</span> <span class="br0">)</span> ;</div>
</li>
<li class="li2">
<div class="de2"><span class="br0">}</span></div>
</li>
<li class="li1">
<div class="de1"> </div>
</li>
<li class="li1">
<div class="de1"> </div>
</li>
<li class="li1">
<div class="de1"><span class="co1">// runs in a thread - the erlang c-node stuff</span></div>
</li>
<li class="li1">
<div class="de1"><span class="co1">// expects msgs like {uid, msg} and sends a a ‘msg’ chunk to uid if connected</span></div>
</li>
<li class="li2">
<div class="de2"><span class="kw4">void</span> cnode_run<span class="br0">(</span> <span class="br0">)</span></div>
</li>
<li class="li1">
<div class="de1"><span class="br0">{</span></div>
</li>
<li class="li1">
<div class="de1">    <span class="kw4">int</span> fd;                                  <span class="coMULTI">/* fd to Erlang node */</span></div>
</li>
<li class="li1">
<div class="de1">    <span class="kw4">int</span> got;                                 <span class="coMULTI">/* Result of receive */</span></div>
</li>
<li class="li1">
<div class="de1">    <span class="kw4">unsigned</span> <span class="kw4">char</span> buf<span class="br0">[</span> BUFSIZE<span class="br0">]</span> ;              <span class="coMULTI">/* Buffer for incoming message */</span></div>
</li>
<li class="li2">
<div class="de2">    ErlMessage emsg;                         <span class="coMULTI">/* Incoming message */</span></div>
</li>
<li class="li1">
<div class="de1"> </div>
</li>
<li class="li1">
<div class="de1">    ETERM *uid, *msg;</div>
</li>
<li class="li1">
<div class="de1"> </div>
</li>
<li class="li1">
<div class="de1">    erl_init<span class="br0">(</span> <span class="kw2">NULL</span> , <span class="nu0">0</span> <span class="br0">)</span> ;</div>
</li>
<li class="li2">
<div class="de2"> </div>
</li>
<li class="li1">
<div class="de1">    <span class="kw1">if</span> <span class="br0">(</span> erl_connect_init<span class="br0">(</span> <span class="nu0">1</span> , <span class="st0">&#8220;secretcookie&#8221;</span> , <span class="nu0">0</span> <span class="br0">)</span> == <span class="nu0">-1</span> <span class="br0">)</span></div>
</li>
<li class="li1">
<div class="de1">        erl_err_quit<span class="br0">(</span> <span class="st0">&#8220;erl_connect_init&#8221;</span> <span class="br0">)</span> ;</div>
</li>
<li class="li1">
<div class="de1"> </div>
</li>
<li class="li1">
<div class="de1">    <span class="kw1">if</span> <span class="br0">(</span> <span class="br0">(</span> fd = erl_connect<span class="br0">(</span> <span class="st0">&#8220;httpdmaster@localhost&#8221;</span> <span class="br0">)</span> <span class="br0">)</span> &lt; <span class="nu0">0</span> <span class="br0">)</span></div>
</li>
<li class="li2">
<div class="de2">        erl_err_quit<span class="br0">(</span> <span class="st0">&#8220;erl_connect&#8221;</span> <span class="br0">)</span> ;</div>
</li>
<li class="li1">
<div class="de1"> </div>
</li>
<li class="li1">
<div class="de1">    fprintf<span class="br0">(</span> stderr, <span class="st0">&#8220;Connected to httpdmaster@localhost<span class="es0">\n</span> <span class="es0">\r</span> &#8221;</span> <span class="br0">)</span> ;</div>
</li>
<li class="li1">
<div class="de1"> </div>
</li>
<li class="li1">
<div class="de1">    <span class="kw4">struct</span> evbuffer *evbuf;</div>
</li>
<li class="li2">
<div class="de2"> </div>
</li>
<li class="li1">
<div class="de1">    <span class="kw1">while</span> <span class="br0">(</span> <span class="nu0">1</span> <span class="br0">)</span> <span class="br0">{</span></div>
</li>
<li class="li1">
<div class="de1">        got = erl_receive_msg<span class="br0">(</span> fd, buf, BUFSIZE, &amp;emsg<span class="br0">)</span> ;</div>
</li>
<li class="li1">
<div class="de1">        <span class="kw1">if</span> <span class="br0">(</span> got == ERL_TICK<span class="br0">)</span> <span class="br0">{</span></div>
</li>
<li class="li1">
<div class="de1">            <span class="kw1">continue</span> ;</div>
</li>
<li class="li2">
<div class="de2">        <span class="br0">}</span> <span class="kw1">else</span> <span class="kw1">if</span> <span class="br0">(</span> got == ERL_ERROR<span class="br0">)</span> <span class="br0">{</span></div>
</li>
<li class="li1">
<div class="de1">            fprintf<span class="br0">(</span> stderr, <span class="st0">&#8220;ERL_ERROR from erl_receive_msg.<span class="es0">\n</span> &#8221;</span> <span class="br0">)</span> ;</div>
</li>
<li class="li1">
<div class="de1">            <span class="kw2">break</span> ;</div>
</li>
<li class="li1">
<div class="de1">        <span class="br0">}</span> <span class="kw1">else</span> <span class="br0">{</span></div>
</li>
<li class="li1">
<div class="de1">            <span class="kw1">if</span> <span class="br0">(</span> emsg.<span class="me1">type</span> == ERL_REG_SEND<span class="br0">)</span> <span class="br0">{</span></div>
</li>
<li class="li2">
<div class="de2">                <span class="co1">// get uid and body data from eg: {123, &lt;&lt;&#8221;Hello&#8221;&gt;&gt;}</span></div>
</li>
<li class="li1">
<div class="de1">                uid = erl_element<span class="br0">(</span> <span class="nu0">1</span> , emsg.<span class="me1">msg</span> <span class="br0">)</span> ;</div>
</li>
<li class="li1">
<div class="de1">                msg = erl_element<span class="br0">(</span> <span class="nu0">2</span> , emsg.<span class="me1">msg</span> <span class="br0">)</span> ;</div>
</li>
<li class="li1">
<div class="de1">                <span class="kw4">int</span> userid = ERL_INT_VALUE<span class="br0">(</span> uid<span class="br0">)</span> ;</div>
</li>
<li class="li1">
<div class="de1">                <span class="kw4">char</span> *body = <span class="br0">(</span> <span class="kw4">char</span> *<span class="br0">)</span> ERL_BIN_PTR<span class="br0">(</span> msg<span class="br0">)</span> ;</div>
</li>
<li class="li2">
<div class="de2">                <span class="kw4">int</span> body_len = ERL_BIN_SIZE<span class="br0">(</span> msg<span class="br0">)</span> ;</div>
</li>
<li class="li1">
<div class="de1">                <span class="co1">// Is this userid connected?</span></div>
</li>
<li class="li1">
<div class="de1">                <span class="kw1">if</span> <span class="br0">(</span> clients<span class="br0">[</span> userid<span class="br0">]</span> <span class="br0">)</span> <span class="br0">{</span></div>
</li>
<li class="li1">
<div class="de1">                    fprintf<span class="br0">(</span> stderr, <span class="st0">&#8220;Sending %d bytes to uid %d<span class="es0">\n</span> &#8221;</span> , body_len, userid<span class="br0">)</span> ;                </div>
</li>
<li class="li1">
<div class="de1">                    evbuf = evbuffer_new<span class="br0">(</span> <span class="br0">)</span> ;</div>
</li>
<li class="li2">
<div class="de2">                    evbuffer_add<span class="br0">(</span> evbuf, <span class="br0">(</span> <span class="kw4">const</span> <span class="kw4">void</span> *<span class="br0">)</span> body, <span class="br0">(</span> size_t<span class="br0">)</span> body_len<span class="br0">)</span> ;</div>
</li>
<li class="li1">
<div class="de1">                    evhttp_send_reply_chunk<span class="br0">(</span> clients<span class="br0">[</span> userid<span class="br0">]</span> , evbuf<span class="br0">)</span> ;</div>
</li>
<li class="li1">
<div class="de1">                    evbuffer_free<span class="br0">(</span> evbuf<span class="br0">)</span> ;</div>
</li>
<li class="li1">
<div class="de1">                <span class="br0">}</span> <span class="kw1">else</span> <span class="br0">{</span></div>
</li>
<li class="li1">
<div class="de1">                    fprintf<span class="br0">(</span> stderr, <span class="st0">&#8220;Discarding %d bytes to uid %d - user not connected<span class="es0">\n</span> &#8221;</span> ,</div>
</li>
<li class="li2">
<div class="de2">                            body_len, userid<span class="br0">)</span> ;                </div>
</li>
<li class="li1">
<div class="de1">                    <span class="co1">// noop</span></div>
</li>
<li class="li1">
<div class="de1">                <span class="br0">}</span></div>
</li>
<li class="li1">
<div class="de1">                erl_free_term<span class="br0">(</span> emsg.<span class="me1">msg</span> <span class="br0">)</span> ;</div>
</li>
<li class="li1">
<div class="de1">                erl_free_term<span class="br0">(</span> uid<span class="br0">)</span> ;</div>
</li>
<li class="li2">
<div class="de2">                erl_free_term<span class="br0">(</span> msg<span class="br0">)</span> ;</div>
</li>
<li class="li1">
<div class="de1">            <span class="br0">}</span></div>
</li>
<li class="li1">
<div class="de1">        <span class="br0">}</span></div>
</li>
<li class="li1">
<div class="de1">    <span class="br0">}</span></div>
</li>
<li class="li1">
<div class="de1">    <span class="co1">// if we got here, erlang connection died.</span></div>
</li>
<li class="li2">
<div class="de2">    <span class="co1">// this thread is supposed to run forever</span></div>
<div class="de2">
<ol>
<li class="li1">
<div class="de1">    <span class="co1">// TODO - gracefully handle failure / reconnect / etc</span></div>
</li>
<li class="li1">
<div class="de1">    pthread_exit<span class="br0">(</span> <span class="nu0">0</span> <span class="br0">)</span> ;</div>
</li>
<li class="li1">
<div class="de1"><span class="br0">}</span></div>
</li>
<li class="li1">
<div class="de1"> </div>
</li>
<li class="li2">
<div class="de2"><span class="kw4">int</span> main<span class="br0">(</span> <span class="kw4">int</span> argc, <span class="kw4">char</span> **argv<span class="br0">)</span></div>
</li>
<li class="li1">
<div class="de1"><span class="br0">{</span></div>
</li>
<li class="li1">
<div class="de1">    <span class="co1">// Launch the thread that runs the cnode:</span></div>
</li>
<li class="li1">
<div class="de1">    pthread_attr_t tattr;</div>
</li>
<li class="li1">
<div class="de1">    pthread_t helper;</div>
</li>
<li class="li2">
<div class="de2">    <span class="kw4">int</span> status;</div>
</li>
<li class="li1">
<div class="de1">    pthread_create<span class="br0">(</span> &amp;helper, <span class="kw2">NULL</span> , cnode_run, <span class="kw2">NULL</span> <span class="br0">)</span> ;</div>
</li>
<li class="li1">
<div class="de1"> </div>
</li>
<li class="li1">
<div class="de1">    <span class="kw4">int</span> i;</div>
</li>
<li class="li1">
<div class="de1">    <span class="kw1">for</span> <span class="br0">(</span> i=<span class="nu0">0</span> ;i&lt;=MAXUSERS;i++<span class="br0">)</span> slots<span class="br0">[</span> i<span class="br0">]</span> =i;</div>
</li>
<li class="li2">
<div class="de2">    <span class="co1">// Launch libevent httpd:</span></div>
</li>
<li class="li1">
<div class="de1">    <span class="kw4">struct</span> evhttp *httpd;</div>
</li>
<li class="li1">
<div class="de1">    event_init<span class="br0">(</span> <span class="br0">)</span> ;</div>
</li>
<li class="li1">
<div class="de1">    httpd = evhttp_start<span class="br0">(</span> <span class="st0">&#8220;0.0.0.0&#8243;</span> , <span class="nu0">8000</span> <span class="br0">)</span> ;</div>
</li>
<li class="li1">
<div class="de1">    evhttp_set_gencb<span class="br0">(</span> httpd, request_handler, <span class="kw2">NULL</span> <span class="br0">)</span> ;</div>
</li>
<li class="li2">
<div class="de2">    event_dispatch<span class="br0">(</span> <span class="br0">)</span> ;</div>
</li>
<li class="li1">
<div class="de1">    <span class="co1">// Not reached, event_dispatch() shouldn’t return</span></div>
</li>
<li class="li1">
<div class="de1">    evhttp_free<span class="br0">(</span> httpd<span class="br0">)</span> ;</div>
</li>
<li class="li1">
<div class="de1">    <span class="kw1">return</span> <span class="nu0">0</span> ;</div>
</li>
<li class="li1">
<div class="de1"><span class="br0">}</span></div>
</li>
</ol>
<p> </p>
<p>最大用户数由#defined定义, 类似提及的mochiweb服务器, 他监听在8000端口，期待着用户用/test/&lt;userid&gt;的形式连接,erlang节点的名称被硬编码，他将为接收消息连接 <code>httpdmaster@localhost</code> , erlang cookie, “secretcookie”. 相应的改变这些。</p>
<p>首先运行连接的erlang节点：<br />
<code>$ erl -setcookie secretcookie -sname httpdmaster@localhost</code></p>
<p>编译运行：<br />
<code>$ gcc -o httpdcnode httpdcnode.c -lerl_interface -lei -levent<br />
$ ./httpdcnode</code></p>
<p>在erlang shell, 检查你能看到隐藏的c-node:<br />
<code>erl&gt; nodes(hidden).<br />
[c1@localhost]</code></p>
<p>现在在你的浏览器中连接 <code>http://localhost:8000/test/123</code> . 你将看到欢迎信息.</p>
<p>现在回到erlang shell - 向C节点发送一条消息：</p>
<p><code>erl&gt; {any, c1@localhost} ! {123, &lt;&lt;"Hello Libevent World"&gt;&gt;}.</code></p>
<p><em>注意我们没有用pid,以此我们用另外一种形式 {procname, node}.我们用 ‘any’ 作为进程名称，它可以被C节点忽略。</em></p>
<p><strong>现在你能够通过erlang传递comet消息, 但是所有的http连接都被用libevent C语言编写的erlang节点管理。</strong></p>
<p>在删除debug打印语句后, 我用同一个上面提到的客户端向http C节点服务器连接1M个用户, 这台机子显示少于10G的内存被使用。服务器进程常驻内存稳定在2G以下：</p>
<p><a href="http://www.metabrew.com/wp-content/uploads/2008/11/mochimem-libevent.png"><img class="aligncenter size-full wp-image-176" src="http://www.metabrew.com/wp-content/uploads/2008/11/mochimem-libevent.png" alt="" width="500" height="300" /></a></p>
<p>因此， 当处理大量连接时相比较webchiweb有很大的节省，对于用libevent服务器进程每个连接的常驻内存低于2KB。所有的都连接后，服务器状态如下：<br />
<code>Mem:  32968672k total,  9636488k used, 23332184k free,      180k buffers</code></p>
<p>每个连接的内核/tcp 栈占了另外8KB内存, 看起来有点高，但是我没有基础对象用来与之比较。libevent-cnode服务器需要多点的工作 . 他还不能聪明的处理从同一个用户来的多连接, 没有锁，假如你在一个消息被分发出去后断开连接，这时就有一个竞争条件存在.</p>
<p>即使这样, 我想这将被普遍化，以这种方式，他允许你<strong>用 Erlang 做很多有意思的事, 有一个C+libevent进程充当一个默默无闻的连接池</strong> . 通过更多的包装代码和回调到erlang，你几乎不需要知道这个怎么运行的-C程序作为一个驱动或者一个C节点运行，一个Erlang包装器能给你一个合适的建筑于libevent的api. (看 <a href="http://www.metabrew.com/article/erlang-libketama-driver-consistent-hashing/">这个</a> ，一个Erlang C驱动). 我将来在这上面会尝试更多。</p>
<h2>最后的思考</h2>
<p>我现在有足够的数据判断假如我为Last.fm发布一个大的伸缩性的comet系统我到底需要多少硬件. 即使每个连接40KB有些浪费但不是过分的-内存很便宜，40GB能支持一个百万用户的系统不过分. 10GB更好. 我将完成这个应用，我将在哪个地方构建发布它，人们可以试用它. 顺着这条路我将整理erlang memcached客户端，我正在用且分发他</p></div>
</li>
</ol>
</div>
]]></content:encoded>
			<wfw:commentRss>http://idisc.blog.techweb.com.cn/archives/10.html/feed</wfw:commentRss>
		</item>
		<item>
		<title>用Mochiweb打造百万级Comet应用，第一部分</title>
		<link>http://idisc.blog.techweb.com.cn/archives/3.html</link>
		<comments>http://idisc.blog.techweb.com.cn/archives/3.html#comments</comments>
		<pubDate>Wed, 21 Jan 2009 07:16:30 +0000</pubDate>
		<dc:creator>idisc</dc:creator>
		
		<category><![CDATA[erlang]]></category>

		<category><![CDATA[comet]]></category>

		<category><![CDATA[mochiweb]]></category>

		<category><![CDATA[小游戏]]></category>

		<guid isPermaLink="false">http://idisc.blog.techweb.com.cn/archives/3.html</guid>
		<description><![CDATA[提示：如有转载请注明作者 小游戏 及出处
原文：A Million-user Comet Application with Mochiweb, Part 1
参考资料：Comet&#8211;基于 HTTP 长连接、无须在浏览器端安装插件的“服务器推”技术为“Comet”
             MochiWeb&#8211;建立轻量级HTTP服务器的Erlang库
      在这个系列中，我将详述我所发现的mochiweb是怎样支持那么巨大的网络连接的，为大家展示怎样用mochiweb构建一个comet应用，这个应用中每个mochiweb连接都被注册到负责为不同用户派送消息的路由器上。最后我们建立一个能够承受百万并发连接的可工作的应用，更重要的我们将知道这样的应用需要多少内存才能使它跑起来。
本部分内容如下：

建立一个基本的comet应用， 它每10秒钟给客户端发送一条消息
调整linux内核参数，使它能够处理大量的TCP连接
写一个能够建立大量网络连接的压力测试工具 (也就是 C10k测试)
检查每个连接到底需要多少内存.

本系列续作将包括怎样建立一个真正的信息路由系统，降低内存使用的技巧，100K和1m并发连接的测试。
基础是你需要知道一些linux命令行操作和一点Erlang知识，否则看不懂别怪我呀，呵呵
写一个Mochiweb测试程序
概括如下:

安装编译Mochiweb
运行: /your-mochiweb-path/scripts/new_mochiweb.erl mochiconntest
cd mochiconntest 之后编辑 src/mochiconntest_web.erl

这部分代码(mochiconntest_web.erl)只是接收连接并且每十秒用块传输方式给客户端发送一个初始的欢迎信息。
mochiconntest_web.erl



-module ( mochiconntest_web) .


-export ( [ start/1 , stop/0 , loop/2 ] ) .


%% 外部API


start( Options ) -&#62;


    { DocRoot , Options1 } = get_option( docroot, Options ) ,


    Loop = fun ( Req ) -&#62;


                   ?MODULE :loop ( Req , DocRoot )


           end ,


    % 设置最大连接数为一百万，缺省2048


    mochiweb_http:start ( [ { max, 1000000 } , { name, ?MODULE } , { loop, Loop } &#124; Options1 ] ) .


 


stop( ) -&#62;


    mochiweb_http :stop ( ?MODULE ) .


 


loop( Req , DocRoot ) -&#62;


    &#8220;/&#8221; ++ Path = Req :get ( path) ,


    case Req :get ( method) of


        Method when Method =:= ‘GET’ ; Method =:= ‘HEAD’ -&#62;


    [...]]]></description>
			<content:encoded><![CDATA[<p>提示：如有转载请注明作者 <a href="http://www.xyx172.com/" target="_blank">小游戏</a> 及出处<br />
原文：<a title="A Million-user Comet Application with Mochiweb, Part 1" rel="bookmark" href="http://www.metabrew.com/article/a-million-user-comet-application-with-mochiweb-part-1/">A Million-user Comet Application with Mochiweb, Part 1</a></p>
<p>参考资料：Comet&#8211;基于 HTTP 长连接、无须在浏览器端安装插件的“服务器推”技术为“Comet”<br />
             MochiWeb&#8211;建立轻量级HTTP服务器的Erlang库</p>
<p>      在这个系列中，我将详述我所发现的mochiweb是怎样支持那么巨大的网络连接的，为大家展示怎样用mochiweb构建一个comet应用，这个应用中每个mochiweb连接都被注册到负责为不同用户派送消息的路由器上。最后我们建立一个能够承受百万并发连接的可工作的应用，更重要的我们将知道这样的应用需要多少内存才能使它跑起来。<br />
本部分内容如下：</p>
<ul>
<li>建立一个基本的comet应用， 它每10秒钟给客户端发送一条消息</li>
<li>调整linux内核参数，使它能够处理大量的TCP连接</li>
<li>写一个能够建立大量网络连接的压力测试工具 (也就是 C10k测试)</li>
<li>检查每个连接到底需要多少内存.</li>
</ul>
<p>本系列续作将包括怎样建立一个真正的信息路由系统，降低内存使用的技巧，100K和1m并发连接的测试。<br />
基础是你需要知道一些linux命令行操作和一点Erlang知识，否则看不懂别怪我呀，呵呵</p>
<h2>写一个Mochiweb测试程序</h2>
<p>概括如下:</p>
<ol>
<li>安装编译Mochiweb</li>
<li>运行: <code>/your-mochiweb-path/scripts/new_mochiweb.erl mochiconntest</code></li>
<li><code>cd mochiconntest</code> 之后编辑 <code>src/mochiconntest_web.erl</code></li>
</ol>
<p>这部分代码(mochiconntest_web.erl)只是接收连接并且每十秒用块传输方式给客户端发送一个初始的欢迎信息。<br />
mochiconntest_web.erl</p>
<div class="dean_ch">
<ol>
<li class="li1">
<div class="de1">-<span class="kw2">module</span> <span class="br0">(</span> mochiconntest_web<span class="br0">)</span> .</div>
</li>
<li class="li1">
<div class="de1">-<span class="kw2">export</span> <span class="br0">(</span> <span class="br0">[</span> start/<span class="nu0">1</span> , stop/<span class="nu0">0</span> , loop/<span class="nu0">2</span> <span class="br0">]</span> <span class="br0">)</span> .</div>
</li>
<li class="li1">
<div class="de1"><span class="co1">%% 外部API</span></div>
</li>
<li class="li1">
<div class="de1">start<span class="br0">(</span> <span class="re0">Options</span> <span class="br0">)</span> -&gt;</div>
</li>
<li class="li2">
<div class="de2">    <span class="br0">{</span> <span class="re0">DocRoot</span> , <span class="re0">Options1</span> <span class="br0">}</span> = get_option<span class="br0">(</span> docroot, <span class="re0">Options</span> <span class="br0">)</span> ,</div>
</li>
<li class="li1">
<div class="de1">    <span class="re0">Loop</span> = fun <span class="br0">(</span> <span class="re0">Req</span> <span class="br0">)</span> -&gt;</div>
</li>
<li class="li1">
<div class="de1">                   ?<span class="re0">MODULE</span> :<span class="me2">loop</span> <span class="br0">(</span> <span class="re0">Req</span> , <span class="re0">DocRoot</span> <span class="br0">)</span></div>
</li>
<li class="li1">
<div class="de1">           <span class="kw1">end</span> ,</div>
</li>
<li class="li1">
<div class="de1">    <span class="co1">% 设置最大连接数为一百万，缺省2048</span></div>
</li>
<li class="li2">
<div class="de2">    mochiweb_http:<span class="me2">start</span> <span class="br0">(</span> <span class="br0">[</span> <span class="br0">{</span> max, <span class="nu0">1000000</span> <span class="br0">}</span> , <span class="br0">{</span> name, ?<span class="re0">MODULE</span> <span class="br0">}</span> , <span class="br0">{</span> loop, <span class="re0">Loop</span> <span class="br0">}</span> | <span class="re0">Options1</span> <span class="br0">]</span> <span class="br0">)</span> .</div>
</li>
<li class="li1">
<div class="de1"> </div>
</li>
<li class="li1">
<div class="de1">stop<span class="br0">(</span> <span class="br0">)</span> -&gt;</div>
</li>
<li class="li1">
<div class="de1">    <span class="me1">mochiweb_http</span> :<span class="me2">stop</span> <span class="br0">(</span> ?<span class="re0">MODULE</span> <span class="br0">)</span> .</div>
</li>
<li class="li1">
<div class="de1"> </div>
</li>
<li class="li2">
<div class="de2">loop<span class="br0">(</span> <span class="re0">Req</span> , <span class="re0">DocRoot</span> <span class="br0">)</span> -&gt;</div>
</li>
<li class="li1">
<div class="de1">    <span class="st0">&#8220;/&#8221;</span> ++ <span class="re0">Path</span> = <span class="re0">Req</span> :<span class="me2">get</span> <span class="br0">(</span> path<span class="br0">)</span> ,</div>
</li>
<li class="li1">
<div class="de1">    <span class="kw1">case</span> <span class="re0">Req</span> :<span class="me2">get</span> <span class="br0">(</span> method<span class="br0">)</span> <span class="kw1">of</span></div>
</li>
<li class="li1">
<div class="de1">        <span class="re0">Method</span> when <span class="re0">Method</span> =:= <span class="st0">‘GET’</span> ; <span class="re0">Method</span> =:= <span class="st0">‘HEAD’</span> -&gt;</div>
</li>
<li class="li1">
<div class="de1">            <span class="kw1">case</span> <span class="re0">Path</span> <span class="kw1">of</span></div>
</li>
<li class="li2">
<div class="de2">                <span class="st0">&#8220;test/&#8221;</span> ++ <span class="re0">Id</span> -&gt;</div>
</li>
<li class="li1">
<div class="de1">                    <span class="re0">Response</span> = <span class="re0">Req</span> :<span class="me2">ok</span> <span class="br0">(</span> <span class="br0">{</span> <span class="st0">&#8220;text/html; charset=utf-8&#8243;</span> ,</div>
</li>
<li class="li1">
<div class="de1">                                      <span class="br0">[</span> <span class="br0">{</span> <span class="st0">"Server"</span> ,<span class="st0">"Mochiweb-Test"</span> <span class="br0">}</span> <span class="br0">]</span> ,</div>
</li>
<li class="li1">
<div class="de1">                                      chunked<span class="br0">}</span> <span class="br0">)</span> ,</div>
</li>
<li class="li1">
<div class="de1">                    <span class="re0">Response</span> :<span class="me2">write_chunk</span> <span class="br0">(</span> <span class="st0">&#8220;Mochiconntest welcomes you! Your Id: &#8220;</span> ++ <span class="re0">Id</span> ++ <span class="st0">&#8220;<span class="es0">\n</span> &#8221;</span> <span class="br0">)</span> ,</div>
</li>
<li class="li2">
<div class="de2">                    <span class="co1">%% router:login(list_to_atom(Id), self()),</span></div>
</li>
<li class="li1">
<div class="de1">                    feed<span class="br0">(</span> <span class="re0">Response</span> , <span class="re0">Id</span> , <span class="nu0">1</span> <span class="br0">)</span> ;</div>
</li>
<li class="li1">
<div class="de1">                _ -&gt;</div>
</li>
<li class="li1">
<div class="de1">                    <span class="re0">Req</span> :<span class="me2">not_found</span> <span class="br0">(</span> <span class="br0">)</span></div>
</li>
<li class="li1">
<div class="de1">            <span class="kw1">end</span> ;</div>
</li>
<li class="li2">
<div class="de2">        <span class="st0">‘POST’</span> -&gt;</div>
</li>
<li class="li1">
<div class="de1">            <span class="kw1">case</span> <span class="re0">Path</span> <span class="kw1">of</span></div>
</li>
<li class="li1">
<div class="de1">                _ -&gt;</div>
</li>
<li class="li1">
<div class="de1">                    <span class="re0">Req</span> :<span class="me2">not_found</span> <span class="br0">(</span> <span class="br0">)</span></div>
</li>
<li class="li1">
<div class="de1">            <span class="kw1">end</span> ;</div>
</li>
<li class="li2">
<div class="de2">        _ -&gt;</div>
</li>
<li class="li1">
<div class="de1">            <span class="re0">Req</span> :<span class="me2">respond</span> <span class="br0">(</span> <span class="br0">{</span> <span class="nu0">501</span> , <span class="br0">[</span> <span class="br0">]</span> , <span class="br0">[</span> <span class="br0">]</span> <span class="br0">}</span> <span class="br0">)</span></div>
</li>
<li class="li1">
<div class="de1">    <span class="kw1">end</span> .</div>
</li>
<li class="li1">
<div class="de1"> </div>
</li>
<li class="li1">
<div class="de1">feed<span class="br0">(</span> <span class="re0">Response</span> , <span class="re0">Path</span> , <span class="re0">N</span> <span class="br0">)</span> -&gt;</div>
</li>
<li class="li2">
<div class="de2">    <span class="kw1">receive</span></div>
</li>
<li class="li1">
<div class="de1">        <span class="co1">%{router_msg, Msg} -&gt;</span></div>
</li>
<li class="li1">
<div class="de1">        <span class="co1">%    Html = io_lib:format(&#8221;Recvd msg #~w: ‘~s’&lt;br/&gt;&#8221;, [N, Msg]),</span></div>
</li>
<li class="li1">
<div class="de1">        <span class="co1">%    Response:write_chunk(Html);</span></div>
</li>
<li class="li1">
<div class="de1">    <span class="kw1">after</span> <span class="nu0">10000</span> -&gt;</div>
</li>
<li class="li2">
<div class="de2">        <span class="re0">Msg</span> = io_lib:<span class="kw3">format</span> <span class="br0">(</span> <span class="st0">&#8220;Chunk ~w for id ~s<span class="es0">\n</span> &#8221;</span> , <span class="br0">[</span> <span class="re0">N</span> , <span class="re0">Path</span> <span class="br0">]</span> <span class="br0">)</span> ,</div>
</li>
<li class="li1">
<div class="de1">        <span class="re0">Response</span> :<span class="me2">write_chunk</span> <span class="br0">(</span> <span class="re0">Msg</span> <span class="br0">)</span></div>
</li>
<li class="li1">
<div class="de1">    <span class="kw1">end</span> ,</div>
</li>
<li class="li1">
<div class="de1">    feed<span class="br0">(</span> <span class="re0">Response</span> , <span class="re0">Path</span> , <span class="re0">N</span> <span class="nu0">+1</span> <span class="br0">)</span> .</div>
</li>
<li class="li1">
<div class="de1"> </div>
</li>
<li class="li2">
<div class="de2"><span class="co1">%%内部API</span></div>
</li>
<li class="li1">
<div class="de1">get_option<span class="br0">(</span> <span class="re0">Option</span> , <span class="re0">Options</span> <span class="br0">)</span> -&gt;</div>
</li>
<li class="li1">
<div class="de1">    <span class="br0">{</span> proplists:<span class="me2">get_value</span> <span class="br0">(</span> <span class="re0">Option</span> , <span class="re0">Options</span> <span class="br0">)</span> , proplists:<span class="me2">delete</span> <span class="br0">(</span> <span class="re0">Option</span> , <span class="re0">Options</span> <span class="br0">)</span> <span class="br0">}</span> .</div>
</li>
</ol>
</div>
<h2>启动Mochiweb应用</h2>
<p><code>make &amp;&amp; ./start-dev.sh</code> <br />
缺省的Mochiweb在所有网卡接口的8000端口上进行监听，假如是在桌面系统上做这些事，你可以使用任何浏览器访问<a href="http://localhost:8000/test/foo">http://localhost:8000/test/foo</a> 进行测试。</p>
<p>这里只是命令行测试：</p>
<pre>$ lynx --source "http://localhost:8000/test/foo"
Mochiconntest welcomes you! Your Id: foo&lt;br/&gt;
Chunk 1 for id foo&lt;br/&gt;
Chunk 2 for id foo&lt;br/&gt;
Chunk 3 for id foo&lt;br/&gt;
^C</pre>
<p>是的,它可以工作。 现在，让我们使劲整它，呵呵。</p>
<h2>调整linux内核参数，使它能够处理大量的TCP连接</h2>
<p>为节省时间我们需要在进行大量并发连接测试之前调整内核的tcp设置参数，否则你的测试将会失败，你将看到大量的<code>Out of socket memory</code> 信息(假如在伪造将得到, <code>nf_conntrack: table full, dropping packet.</code> )<br />
下面的是我用到的sysctl设置 - 你的配置可能不一样，但是大致就是这些：</p>
<pre># General gigabit tuning:
net.core.rmem_max = 16777216
net.core.wmem_max = 16777216
net.ipv4.tcp_rmem = 4096 87380 16777216
net.ipv4.tcp_wmem = 4096 65536 16777216
net.ipv4.tcp_syncookies = 1
# this gives the kernel more memory for tcp
# which you need with many (100k+) open socket connections
net.ipv4.tcp_mem = 50576   64768   98152
&nbsp;<a href="http://net.core.net" title="http://net.core.
" target="_blank">net.core.net</a>dev_max_backlog = 2500
# I was also masquerading the port comet was on, you might not need this
&nbsp;<a href="http://net.ipv4.net" title="http://net.ipv4.
" target="_blank">net.ipv4.net</a>filter.ip_conntrack_max = 1048576</pre>
<p><code>把这些写到</code> <code>/etc/sysctl.conf中然后运行</code> <code>sysctl -p</code> 使其生效。不需要重启，现在你的内核能够处理大量的连接了，yay。</p>
<h2>建立大量连接</h2>
<p>有很多方法可以用. <a href="http://tsung.erlang-projects.org/">Tsung</a> 就十分好, 也有很多其他比较好的工具如ab, httperf, httpload等等可以生成大量的无用请求。 但是它们中任何一款都不适合测试comet应用, 正好我也想找个借口测试一下Erlang的http客户端, 因此我写了一个基本的测试程序用以发起大量的连接。<br />
只是因为你可以但并不意味着你就这样做.. 一个连接就用一个进程确实有点浪费。我用一个进程从文件中调入一批url链接，另一个进程建立连接并接收数据 (当定时器的进程每10秒打印一份报告)。所有从服务器接收来的数据都被丢弃，但是它增加计数，这样我们能够跟踪到底有多少http数据块被传输了。<br />
floodtest.erl</p>
<div class="dean_ch">
<ol>
<li class="li1">
<div class="de1">-<span class="kw2">module</span> <span class="br0">(</span> floodtest<span class="br0">)</span> .</div>
</li>
<li class="li1">
<div class="de1">-<span class="kw2">export</span> <span class="br0">(</span> <span class="br0">[</span> start/<span class="nu0">2</span> , timer/<span class="nu0">2</span> , recv/<span class="nu0">1</span> <span class="br0">]</span> <span class="br0">)</span> .</div>
</li>
<li class="li1">
<div class="de1"> </div>
</li>
<li class="li1">
<div class="de1">start<span class="br0">(</span> <span class="re0">Filename</span> , <span class="re0">Wait</span> <span class="br0">)</span> -&gt;</div>
</li>
<li class="li2">
<div class="de2">    <span class="me1">inets</span> :<span class="me2">start</span> <span class="br0">(</span> <span class="br0">)</span> ,</div>
</li>
<li class="li1">
<div class="de1">    spawn<span class="br0">(</span> ?<span class="re0">MODULE</span> , timer, <span class="br0">[</span> <span class="nu0">10000</span> , self<span class="br0">(</span> <span class="br0">)</span> <span class="br0">]</span> <span class="br0">)</span> ,</div>
</li>
<li class="li1">
<div class="de1">    <span class="re0">This</span> = self<span class="br0">(</span> <span class="br0">)</span> ,</div>
</li>
<li class="li1">
<div class="de1">    spawn<span class="br0">(</span> fun<span class="br0">(</span> <span class="br0">)</span> -&gt; <span class="me1">loadurls</span> <span class="br0">(</span> <span class="re0">Filename</span> , fun<span class="br0">(</span> <span class="re0">U</span> <span class="br0">)</span> -&gt; <span class="re0">This</span> ! <span class="br0">{</span> loadurl, <span class="re0">U</span> <span class="br0">}</span> <span class="kw1">end</span> , <span class="re0">Wait</span> <span class="br0">)</span> <span class="kw1">end</span> <span class="br0">)</span> ,</div>
</li>
<li class="li1">
<div class="de1">    recv<span class="br0">(</span> <span class="br0">{</span> <span class="nu0">0</span> ,<span class="nu0">0</span> ,<span class="nu0">0</span> <span class="br0">}</span> <span class="br0">)</span> .</div>
</li>
<li class="li2">
<div class="de2"> </div>
</li>
<li class="li1">
<div class="de1">recv<span class="br0">(</span> <span class="re0">Stats</span> <span class="br0">)</span> -&gt;</div>
</li>
<li class="li1">
<div class="de1">    <span class="br0">{</span> <span class="re0">Active</span> , <span class="re0">Closed</span> , <span class="re0">Chunks</span> <span class="br0">}</span> = <span class="re0">Stats</span> ,</div>
</li>
<li class="li1">
<div class="de1">    <span class="kw1">receive</span></div>
</li>
<li class="li1">
<div class="de1">        <span class="br0">{</span> stats<span class="br0">}</span> -&gt; <span class="me1">io</span> :<span class="kw3">format</span> <span class="br0">(</span> <span class="st0">&#8220;Stats: ~w<span class="es0">\n</span> &#8221;</span> ,<span class="br0">[</span> <span class="re0">Stats</span> <span class="br0">]</span> <span class="br0">)</span></div>
</li>
<li class="li2">
<div class="de2">        <span class="kw1">after</span> <span class="nu0">0</span> -&gt; <span class="me1">noop</span></div>
</li>
<li class="li1">
<div class="de1">    <span class="kw1">end</span> ,</div>
</li>
<li class="li1">
<div class="de1">    <span class="kw1">receive</span></div>
</li>
<li class="li1">
<div class="de1">        <span class="br0">{</span> http,<span class="br0">{</span> _<span class="re0">Ref</span> ,stream_start,_<span class="re0">X</span> <span class="br0">}</span> <span class="br0">}</span> -&gt;  <span class="me1">recv</span> <span class="br0">(</span> <span class="br0">{</span> <span class="re0">Active</span> <span class="nu0">+1</span> ,<span class="re0">Closed</span> ,<span class="re0">Chunks</span> <span class="br0">}</span> <span class="br0">)</span> ;</div>
</li>
<li class="li1">
<div class="de1">        <span class="br0">{</span> http,<span class="br0">{</span> _<span class="re0">Ref</span> ,stream,_<span class="re0">X</span> <span class="br0">}</span> <span class="br0">}</span> -&gt;          <span class="me1">recv</span> <span class="br0">(</span> <span class="br0">{</span> <span class="re0">Active</span> , <span class="re0">Closed</span> , <span class="re0">Chunks</span> <span class="nu0">+1</span> <span class="br0">}</span> <span class="br0">)</span> ;</div>
</li>
<li class="li2">
<div class="de2">        <span class="br0">{</span> http,<span class="br0">{</span> _<span class="re0">Ref</span> ,stream_end,_<span class="re0">X</span> <span class="br0">}</span> <span class="br0">}</span> -&gt;  <span class="me1">recv</span> <span class="br0">(</span> <span class="br0">{</span> <span class="re0">Active</span> <span class="nu0">-1</span> , <span class="re0">Closed</span> <span class="nu0">+1</span> , <span class="re0">Chunks</span> <span class="br0">}</span> <span class="br0">)</span> ;</div>
</li>
<li class="li1">
<div class="de1">        <span class="br0">{</span> http,<span class="br0">{</span> _<span class="re0">Ref</span> ,<span class="br0">{</span> error,<span class="re0">Why</span> <span class="br0">}</span> <span class="br0">}</span> <span class="br0">}</span> -&gt;</div>
</li>
<li class="li1">
<div class="de1">            <span class="me1">io</span> :<span class="kw3">format</span> <span class="br0">(</span> <span class="st0">&#8220;Closed: ~w<span class="es0">\n</span> &#8221;</span> ,<span class="br0">[</span> <span class="re0">Why</span> <span class="br0">]</span> <span class="br0">)</span> ,</div>
</li>
<li class="li1">
<div class="de1">            recv<span class="br0">(</span> <span class="br0">{</span> <span class="re0">Active</span> <span class="nu0">-1</span> , <span class="re0">Closed</span> <span class="nu0">+1</span> , <span class="re0">Chunks</span> <span class="br0">}</span> <span class="br0">)</span> ;</div>
</li>
<li class="li1">
<div class="de1">        <span class="br0">{</span> loadurl, <span class="re0">Url</span> <span class="br0">}</span> -&gt;</div>
</li>
<li class="li2">
<div class="de2">            <span class="me1">http</span> :<span class="me2">request</span> <span class="br0">(</span> get, <span class="br0">{</span> <span class="re0">Url</span> , <span class="br0">[</span> <span class="br0">]</span> <span class="br0">}</span> , <span class="br0">[</span> <span class="br0">]</span> , <span class="br0">[</span> <span class="br0">{</span> sync, false<span class="br0">}</span> , <span class="br0">{</span> stream, self<span class="br0">}</span> , <span class="br0">{</span> version, <span class="nu0">1.1</span> <span class="br0">}</span> , <span class="br0">{</span> body_format, binary<span class="br0">}</span> <span class="br0">]</span> <span class="br0">)</span> ,</div>
</li>
<li class="li1">
<div class="de1">                recv<span class="br0">(</span> <span class="re0">Stats</span> <span class="br0">)</span></div>
</li>
<li class="li1">
<div class="de1">    <span class="kw1">end</span> .</div>
</li>
<li class="li1">
<div class="de1"> </div>
</li>
<li class="li1">
<div class="de1">timer<span class="br0">(</span> <span class="re0">T</span> , <span class="re0">Who</span> <span class="br0">)</span> -&gt;</div>
</li>
<li class="li2">
<div class="de2">    <span class="kw1">receive</span></div>
</li>
<li class="li1">
<div class="de1">    <span class="kw1">after</span> <span class="re0">T</span> -&gt;</div>
</li>
<li class="li1">
<div class="de1">        <span class="re0">Who</span> ! <span class="br0">{</span> stats<span class="br0">}</span></div>
</li>
<li class="li1">
<div class="de1">    <span class="kw1">end</span> ,</div>
</li>
<li class="li1">
<div class="de1">    timer<span class="br0">(</span> <span class="re0">T</span> , <span class="re0">Who</span> <span class="br0">)</span> .</div>
</li>
<li class="li2">
<div class="de2"> </div>
</li>
<li class="li1">
<div class="de1"><span class="co1">% Read lines from a file with a specified delay between lines:</span></div>
</li>
<li class="li1">
<div class="de1">for_each_line_in_file<span class="br0">(</span> <span class="re0">Name</span> , <span class="re0">Proc</span> , <span class="re0">Mode</span> , <span class="re0">Accum0</span> <span class="br0">)</span> -&gt;</div>
</li>
<li class="li1">
<div class="de1">    <span class="br0">{</span> ok, <span class="re0">Device</span> <span class="br0">}</span> = file:<span class="me2">open</span> <span class="br0">(</span> <span class="re0">Name</span> , <span class="re0">Mode</span> <span class="br0">)</span> ,</div>
</li>
<li class="li1">
<div class="de1">    for_each_line<span class="br0">(</span> <span class="re0">Device</span> , <span class="re0">Proc</span> , <span class="re0">Accum0</span> <span class="br0">)</span> .</div>
</li>
<li class="li2">
<div class="de2"> </div>
</li>
<li class="li1">
<div class="de1">for_each_line<span class="br0">(</span> <span class="re0">Device</span> , <span class="re0">Proc</span> , <span class="re0">Accum</span> <span class="br0">)</span> -&gt;</div>
</li>
<li class="li1">
<div class="de1">    <span class="kw1">case</span> io:<span class="me2">get_line</span> <span class="br0">(</span> <span class="re0">Device</span> , <span class="st0">&#8220;&#8221;</span> <span class="br0">)</span> <span class="kw1">of</span></div>
</li>
<li class="li1">
<div class="de1">        eof  -&gt; <span class="me1">file</span> :<span class="kw3">close</span> <span class="br0">(</span> <span class="re0">Device</span> <span class="br0">)</span> , <span class="re0">Accum</span> ;</div>
</li>
<li class="li1">
<div class="de1">        <span class="re0">Line</span> -&gt; <span class="re0">NewAccum</span> = <span class="re0">Proc</span> <span class="br0">(</span> <span class="re0">Line</span> , <span class="re0">Accum</span> <span class="br0">)</span> ,</div>
</li>
<li class="li2">
<div class="de2">                    for_each_line<span class="br0">(</span> <span class="re0">Device</span> , <span class="re0">Proc</span> , <span class="re0">NewAccum</span> <span class="br0">)</span></div>
</li>
<li class="li1">
<div class="de1">    <span class="kw1">end</span> .</div>
</li>
<li class="li1">
<div class="de1"> </div>
</li>
<li class="li1">
<div class="de1">loadurls<span class="br0">(</span> <span class="re0">Filename</span> , <span class="re0">Callback</span> , <span class="re0">Wait</span> <span class="br0">)</span> -&gt;</div>
</li>
<li class="li1">
<div class="de1">    <span class="me1">for_each_line_in_file</span> <span class="br0">(</span> <span class="re0">Filename</span> ,</div>
</li>
<li class="li2">
<div class="de2">        fun<span class="br0">(</span> <span class="re0">Line</span> , <span class="re0">List</span> <span class="br0">)</span> -&gt;</div>
</li>
<li class="li1">
<div class="de1">            <span class="re0">Callback</span> <span class="br0">(</span> string:<span class="me2">strip</span> <span class="br0">(</span> <span class="re0">Line</span> , right, $\n<span class="br0">)</span> <span class="br0">)</span> ,</div>
</li>
<li class="li1">
<div class="de1">            <span class="kw1">receive</span></div>
</li>
<li class="li1">
<div class="de1">            <span class="kw1">after</span> <span class="re0">Wait</span> -&gt;</div>
</li>
<li class="li1">
<div class="de1">                <span class="me1">noop</span></div>
</li>
<li class="li2">
<div class="de2">            <span class="kw1">end</span> ,</div>
</li>
<li class="li1">
<div class="de1">            <span class="re0">List</span></div>
</li>
<li class="li1">
<div class="de1">        <span class="kw1">end</span> ,</div>
</li>
<li class="li1">
<div class="de1">        <span class="br0">[</span> read<span class="br0">]</span> , <span class="br0">[</span> <span class="br0">]</span> <span class="br0">)</span> .</div>
</li>
</ol>
<p>每个连接我们都要用一个临时的端口，每个端口也是一个文件描述符， 缺省情况下这被限制为1024。为了避免<code>Too many open files问题出现，你需要为你当前shell更改这个限制</code> ，可以通过修改<code>/etc/security/limits.conf</code> ,但是这需要注销再登陆。目前你只需要用sudo修改当前shell就可以了(假如你不想运行在root状态下，调用ulimit后请su回非权限用户）：</p>
<pre>udo bash
# ulimit -n 999999
# erl</pre>
<p>你也可以把临时端口的范围区间增到最大：<br />
<code># echo "1024    65535" &gt; /proc/sys/net/ipv4/ip_local_port_range</code><br />
为压力测试程序生成一个url列表文件<br />
<code>( for i in `seq 1 10000`; do echo "http://localhost:8000/test/$i" ; done ) &gt; /tmp/mochi-urls.txt </code><br />
现在在erlang提示符下你可以编译调用<code>floodtest.erl</code> 了：<br />
<code>erl&gt; c(floodtest).<br />
erl&gt; floodtest:start("/tmp/mochi-urls.txt", 100).</code><br />
这将每秒钟建立十个连接 (也就是每个连接100毫秒).<br />
它将以<code>{Active, Closed, Chunks}的形式输出状态信息</code> ，Active表示已建立连接数， Closed表示因每种原因被终止的连接数，Chunks是mochiweb以块传输模式处理的数据块数。 Closed应该为0，Chunks应该大于Active，因为每个活跃连接接收多个数据块 (10秒一个)。<br />
<strong>10，000个活跃连接的mochiweb进程的固定大小是450MB-也就是每个连接45KB。</strong> CPU占用率就好像预想中的一样微乎其微.</p>
<h2>总结</h2>
<p>第一次尝试是可以理解的。每个连接45KB内存看起来有些高 - 用libevent再做些调整我可以把它做到将近4.5KB每个连接 (只是猜猜, 谁有这方面的经验请留个回复). 如果就代码量和时间效率上对erlang和c做下考量，我想多花点内存还是有情可原的。</p>
<p>后续中，我将建立一个消息路由器 (我们可以把<code>mochiconntest_web.erl中的</code> 25行和41-43行的注释取消 )也探讨一下减少内存用量的方法。我也会分享当100k和1M个连接时的测试结果。</div>
]]></content:encoded>
			<wfw:commentRss>http://idisc.blog.techweb.com.cn/archives/3.html/feed</wfw:commentRss>
		</item>
	</channel>
</rss>
<!-- MKID:0: -->
<!-- MKB:3716 -->
