`
idisc
  • 浏览: 36654 次
  • 性别: Icon_minigender_1
  • 来自: 秦皇岛
最近访客 更多访客>>
社区版块
存档分类
最新评论

用Mochiweb打造百万级Comet应用,第一部分

阅读更多

提示:如有转载请注明作者 小游戏 及出处

 

原文:A Million-user Comet Application with Mochiweb, Part 1

参考资料:Comet--基于 HTTP 长连接、无须在浏览器端安装插件的“服务器推”技术为“Comet”

               MochiWeb--建立轻量级HTTP服务器的Erlang库

 

      在这个系列中,我将详述我所发现的mochiweb是怎样支持那么巨大的网络连接的,为大家展示怎样用mochiweb构建一个comet应用,这个应用中每个mochiweb连接都被注册到负责为不同用户派送消息的路由器上。最后我们建立一个能够承受百万并发连接的可工作的应用,更重要的我们将知道这样的应用需要多少内存才能使它跑起来。

 

本部分内容如下:

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

本系列续作将包括怎样建立一个真正的信息路由系统,降低内存使用的技巧,100K和1m并发连接的测试。

基础是你需要知道一些linux命令行操作和一点Erlang知识,否则看不懂别怪我呀,呵呵

写一个Mochiweb测试程序

概括如下:

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

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

 

mochiconntest_web.erl

  1. -module ( mochiconntest_web) .
  2. -export ( [ start/1 , stop/0 , loop/2 ] ) .
  3. %% 外部API
  4. start( Options ) ->
  5.     { DocRoot , Options1 } = get_option( docroot, Options ) ,
  6.     Loop = fun ( Req ) ->
  7.                    ?MODULE :loop ( Req , DocRoot )
  8.            end ,
  9.     % 设置最大连接数为一百万,缺省2048
  10.     mochiweb_http:start ( [ { max, 1000000 } , { name, ?MODULE } , { loop, Loop } | Options1 ] ) .
  11.  
  12. stop( ) ->
  13.     mochiweb_http :stop ( ?MODULE ) .
  14.  
  15. loop( Req , DocRoot ) ->
  16.     "/" ++ Path = Req :get ( path) ,
  17.     case Req :get ( method) of
  18.         Method when Method =:= ‘GET’ ; Method =:= ‘HEAD’ ->
  19.             case Path of
  20.                 "test/" ++ Id ->
  21.                     Response = Req :ok ( { "text/html; charset=utf-8" ,
  22.                                       [ { "Server" ,"Mochiweb-Test" } ] ,
  23.                                       chunked} ) ,
  24.                     Response :write_chunk ( "Mochiconntest welcomes you! Your Id: " ++ Id ++ "\n " ) ,
  25.                     %% router:login(list_to_atom(Id), self()),
  26.                     feed( Response , Id , 1 ) ;
  27.                 _ ->
  28.                     Req :not_found ( )
  29.             end ;
  30.         ‘POST’ ->
  31.             case Path of
  32.                 _ ->
  33.                     Req :not_found ( )
  34.             end ;
  35.         _ ->
  36.             Req :respond ( { 501 , [ ] , [ ] } )
  37.     end .
  38.  
  39. feed( Response , Path , N ) ->
  40.     receive
  41.         %{router_msg, Msg} ->
  42.         %    Html = io_lib:format("Recvd msg #~w: ‘~s’<br/>", [N, Msg]),
  43.         %    Response:write_chunk(Html);
  44.     after 10000 ->
  45.         Msg = io_lib:format ( "Chunk ~w for id ~s\n " , [ N , Path ] ) ,
  46.         Response :write_chunk ( Msg )
  47.     end ,
  48.     feed( Response , Path , N +1 ) .
  49.  
  50. %%内部API
  51. get_option( Option , Options ) ->
  52.     { proplists:get_value ( Option , Options ) , proplists:delete ( Option , Options ) } .

 

启动Mochiweb应用

make && ./start-dev.sh
缺省的Mochiweb在所有网卡接口的8000端口上进行监听,假如是在桌面系统上做这些事,你可以使用任何浏览器访问http://localhost:8000/test/foo 进行测试。

这里只是命令行测试:

$ lynx --source "http://localhost:8000/test/foo"
Mochiconntest welcomes you! Your Id: foo<br/>
Chunk 1 for id foo<br/>
Chunk 2 for id foo<br/>
Chunk 3 for id foo<br/>
^C

是的,它可以工作。 现在,让我们使劲整它,呵呵。

调整linux内核参数,使它能够处理大量的TCP连接

为节省时间我们需要在进行大量并发连接测试之前调整内核的tcp设置参数,否则你的测试将会失败,你将看到大量的Out of socket memory 信息(假如在伪造将得到, nf_conntrack: table full, dropping packet. )

下面的是我用到的sysctl设置 - 你的配置可能不一样,但是大致就是这些:

# 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
net.core.netdev_max_backlog = 2500
# I was also masquerading the port comet was on, you might not need this
net.ipv4.netfilter.ip_conntrack_max = 1048576

把这些写到 /etc/sysctl.conf中然后运行 sysctl -p 使其生效。不需要重启,现在你的内核能够处理大量的连接了,yay。

建立大量连接

有很多方法可以用. Tsung 就十分好, 也有很多其他比较好的工具如ab, httperf, httpload等等可以生成大量的无用请求。 但是它们中任何一款都不适合测试comet应用, 正好我也想找个借口测试一下Erlang的http客户端, 因此我写了一个基本的测试程序用以发起大量的连接。
只是因为你可以但并不意味着你就这样做.. 一个连接就用一个进程确实有点浪费。我用一个进程从文件中调入一批url链接,另一个进程建立连接并接收数据 (当定时器的进程每10秒打印一份报告)。所有从服务器接收来的数据都被丢弃,但是它增加计数,这样我们能够跟踪到底有多少http数据块被传输了。

floodtest.erl

  1. -module ( floodtest) .
  2. -export ( [ start/2 , timer/2 , recv/1 ] ) .
  3.  
  4. start( Filename , Wait ) ->
  5.     inets :start ( ) ,
  6.     spawn( ?MODULE , timer, [ 10000 , self( ) ] ) ,
  7.     This = self( ) ,
  8.     spawn( fun( ) -> loadurls ( Filename , fun( U ) -> This ! { loadurl, U } end , Wait ) end ) ,
  9.     recv( { 0 ,0 ,0 } ) .
  10.  
  11. recv( Stats ) ->
  12.     { Active , Closed , Chunks } = Stats ,
  13.     receive
  14.         { stats} -> io :format ( "Stats: ~w\n " ,[ Stats ] )
  15.         after 0 -> noop
  16.     end ,
  17.     receive
  18.         { http,{ _Ref ,stream_start,_X } } ->  recv ( { Active +1 ,Closed ,Chunks } ) ;
  19.         { http,{ _Ref ,stream,_X } } ->          recv ( { Active , Closed , Chunks +1 } ) ;
  20.         { http,{ _Ref ,stream_end,_X } } ->  recv ( { Active -1 , Closed +1 , Chunks } ) ;
  21.         { http,{ _Ref ,{ error,Why } } } ->
  22.             io :format ( "Closed: ~w\n " ,[ Why ] ) ,
  23.             recv( { Active -1 , Closed +1 , Chunks } ) ;
  24.         { loadurl, Url } ->
  25.             http :request ( get, { Url , [ ] } , [ ] , [ { sync, false} , { stream, self} , { version, 1.1 } , { body_format, binary} ] ) ,
  26.                 recv( Stats )
  27.     end .
  28.  
  29. timer( T , Who ) ->
  30.     receive
  31.     after T ->
  32.         Who ! { stats}
  33.     end ,
  34.     timer( T , Who ) .
  35.  
  36. % Read lines from a file with a specified delay between lines:
  37. for_each_line_in_file( Name , Proc , Mode , Accum0 ) ->
  38.     { ok, Device } = file:open ( Name , Mode ) ,
  39.     for_each_line( Device , Proc , Accum0 ) .
  40.  
  41. for_each_line( Device , Proc , Accum ) ->
  42.     case io:get_line ( Device , "" ) of
  43.         eof  -> file :close ( Device ) , Accum ;
  44.         Line -> NewAccum = Proc ( Line , Accum ) ,
  45.                     for_each_line( Device , Proc , NewAccum )
  46.     end .
  47.  
  48. loadurls( Filename , Callback , Wait ) ->
  49.     for_each_line_in_file ( Filename ,
  50.         fun( Line , List ) ->
  51.             Callback ( string:strip ( Line , right, $\n) ) ,
  52.             receive
  53.             after Wait ->
  54.                 noop
  55.             end ,
  56.             List
  57.         end ,
  58.         [ read] , [ ] ) .
每个连接我们都要用一个临时的端口,每个端口也是一个文件描述符, 缺省情况下这被限制为1024。为了避免Too many open files问题出现,你需要为你当前shell更改这个限制 ,可以通过修改/etc/security/limits.conf ,但是这需要注销再登陆。目前你只需要用sudo修改当前shell就可以了(假如你不想运行在root状态下,调用ulimit后请su回非权限用户):
udo bash
# ulimit -n 999999
# erl

你也可以把临时端口的范围区间增到最大:
# echo "1024 65535" > /proc/sys/net/ipv4/ip_local_port_range

为压力测试程序生成一个url列表文件
( for i in `seq 1 10000`; do echo "http://localhost:8000/test/$i" ; done ) > /tmp/mochi-urls.txt

现在在erlang提示符下你可以编译调用floodtest.erl 了:
erl> c(floodtest).
erl> floodtest:start("/tmp/mochi-urls.txt", 100).

这将每秒钟建立十个连接 (也就是每个连接100毫秒).

它将以{Active, Closed, Chunks}的形式输出状态信息 ,Active表示已建立连接数, Closed表示因每种原因被终止的连接数,Chunks是mochiweb以块传输模式处理的数据块数。 Closed应该为0,Chunks应该大于Active,因为每个活跃连接接收多个数据块 (10秒一个)。


10,000个活跃连接的mochiweb进程的固定大小是450MB-也就是每个连接45KB。 CPU占用率就好像预想中的一样微乎其微.

总结

第一次尝试是可以理解的。每个连接45KB内存看起来有些高 - 用libevent再做些调整我可以把它做到将近4.5KB每个连接 (只是猜猜, 谁有这方面的经验请留个回复). 如果就代码量和时间效率上对erlang和c做下考量,我想多花点内存还是有情可原的。


后续中,我将建立一个消息路由器 (我们可以把mochiconntest_web.erl中的 25行和41-43行的注释取消 )也探讨一下减少内存用量的方法。我也会分享当100k和1M个连接时的测试结果。

分享到:
评论
5 楼 manmoon 2008-11-29  
都是牛人。占座以后看
4 楼 idisc 2008-11-17  
不是重造车轮,只是自己照原文翻译时也可以学习,
3 楼 dogstar 2008-11-17  
http://blog.csdn.net/guo_zanhua/archive/2008/10/26/3152531.aspx
http://blog.csdn.net/Guo_zanhua/archive/2008/11/08/3251504.aspx

已经有人翻译了.翻译三吧
2 楼 sw2wolf 2008-11-17  
不错! 继续
1 楼 idisc 2008-11-14  
第二部分正在翻译中

相关推荐

    mochiweb实例

    Mochiweb是一个轻量级、高效的Web服务器和HTTP库,用Erlang编程语言编写。这个实例将帮助我们理解Mochiweb是如何工作的,并如何使用它来构建一个简单的Web服务器。Erlang是一种并发性极强、容错性高的语言,特别适合...

    erlang mochiweb-test demo

    Mochiweb 是一个用 Erlang 编写的轻量级 Web 服务器和 HTTP 客户端库,它提供了处理 HTTP 请求和响应的能力。这个 "erlang mochiweb-test demo" 压缩包很可能是为了展示如何使用 Mochiweb 在 Erlang 中构建一个简单...

    基于mochiweb的聊天室

    这里我们探讨的是一个使用Erlang编程语言和Mochiweb框架实现的聊天室。Erlang是一种面向并发、分布式计算的语言,因其在处理高并发和容错性方面的优秀特性而被广泛应用于电信、金融以及互联网服务等领域。Mochiweb则...

    mochiweb:MochiWeb 是一个用于构建轻量级 HTTP 服务器的 Erlang 库

    MochiWeb 的最新版本可在MochiWeb 的邮件列表位于 设置 MochiWeb 环境需要 Erlang OTP,可在使用项目创建一个新的 mochiweb:make app PROJECT=project_name 要使用特定目录中的项目创建新的 mochiweb: make app ...

    erlang-rpssl-comet:使用Mochiweb和Comet技术的Erlang网页游戏-Rock-Paper-Scissors-Spock-Lizard

    通常,它是第一人称自己玩的,但这是网络版本。 Web版本使用,该是运行时间很长的HTTP请求,可用于将服务器立即将数据推送到客户端。 每个http请求都是一个单独的Erlang进程。 因为它使用的资源很少,所以我们...

    Erlang的高级特性和应用

    **Erlang 高级特性和应用** Erlang 是一种高级编程语言,以其在并发处理、分布式计算和高可靠性方面的出色性能而闻名。在国内外,Erlang 已经被广泛应用于各种场景,如广告平台、社交网络、云计算、网络游戏以及...

    Erlang高级应用和原理

    在国外,Erlang被广泛应用于Ejabberd即时通讯服务器、RabbitMQ消息队列、CouchDB文档数据库、Mochiweb轻量级Web服务器以及Disco分布式计算框架。 Erlang与传统的操作系统如Unix相比,具有独特的设计哲学。在Unix中...

    erlang web frame

    1. **Mochiweb**:Mochiweb是一个用Erlang编写的轻量级Web服务器和HTTP库。它提供了一个简单的API,允许开发者直接处理HTTP请求和响应。Mochiweb的核心特点包括非阻塞I/O模型,使得它在处理大量并发连接时表现出色。...

    mochiweb:Mochi Media出色的HTTP库的一个分支-可以在https上找到其规范源

    MochiWeb是一个Erlang库... 要使用项目创建新的mochiweb,请执行以下操作:使应用程序PROJECT = project_name 要使用特定目录中的项目创建新的mochiweb:make app PROJECT = project_name PREFIX = $ HOME / projects /

    JavaScript + Delphi + ErLang讲座内容(4)

    【JavaScript + Delphi + ErLang讲座内容(4)】是一个专题讲座的第四部分,主要探讨了如何将三种技术——JavaScript、Delphi和Erlang——整合应用。这个压缩包包含了多个资源,帮助学习者理解这三者的交互和实际应用...

    张琨:教育社交平台的web架构分享

    张琨分享的内容涉及了“三人行网络教育平台”的Web架构设计和实现,本文将围绕这一主题,深入讲解服务器拓扑结构、缓存系统设计、存储设计架构以及具体服务与应用的设计。 首先,总体架构的设计应考虑满足当前业务...

    webmachine:用于构建Web应用程序的基于REST的系统

    Webmachine是一个应用程序层,它在mochiweb提供的出色的按位和HTTP语法管理的基础上增加了HTTP语义意识,并提供了一种简单明了的方式将其连接到应用程序的行为。 可获得更多信息。 您还可以阅读有关Webmachine的...

    erlang实战IP查询服务

    - Mochiweb: 一种轻量级的Erlang Web服务器,可通过SVN检出并放置于 `$ERL_LIB` 目录下自动加入至编译路径。 - GeoIP数据库: 通过脚本从MaxMind获取并解压,存储于`priv`目录下。 - egeoip: 从Google Code获取,...

    CloudFoundry - The building of the Open PaaS Presentation

    **aPaaS(Application Platform as a Service,应用平台即服务)**:一种特定类型的PaaS,更侧重于支持应用程序和相关服务的创建、部署和管理,而不是虚拟机、内存、存储或CPU等基础硬件资源。 #### 三、什么是开放...

    Webmachine.zip

    Webmachine 是一个应用层,为 mochiweb 提供 HTTP 语义的特性,定义一个简单而清晰的连接应用的方式。 标签:Webmachine Web框架

    erlang websocket

    1. **JSON格式**:JSON(JavaScript Object Notation)是一种轻量级的数据交换格式,易于人阅读和编写,同时也易于机器解析和生成。在Erlang中,我们可以使用`jsx`或`jiffy`库来解析和序列化JSON数据。 2. **Erlang...

    heroku-genfsm:一个Heroku Erlang应用程序-Genfsm

    heroku-genfsmAn experimental Erlang app which deployed on HerokuSome Deploy Detail在 Heroku 上部署 Webmachine + Mochiweb + ErlyDTL 组合的 Erlang Web 应用

    Good for restful API

    该框架基于Mochiweb(一款用Erlang编写的Web服务器)构建,旨在帮助开发者轻松构建遵循HTTP语义的服务,同时避免了在业务逻辑中直接处理HTTP相关的复杂性。 ##### 原则 - **默认行为**:Webmachine实现了一些默认...

    使用rebar工具开发erlang工程项目和发布erlang工程项目学习.pdf

    使用 rebar 工具开发 Erlang 工程项目和发布 Erlang 工程项目学习 本文主要介绍了使用 rebar 工具开发 Erlang 工程项目和发布 Erlang 工程项目的方法。rebar 是一个 Erlang 构建工具,可以方便的编译测试 Erlang ...

Global site tag (gtag.js) - Google Analytics