多种HTTP服务器:Misultin, Mochiweb, Cowboy, NodeJS 及 Tornadoweb测评
来源:互联网 发布:linux setid 编辑:程序博客网 时间:2024/05/16 18:25
我对HTTP服务器很感兴趣,我花了大量时间来试用它们,还喜欢从不同角度来比较它们。
今天我将用同一个用例对以下不同的HTTP服务器库进行测试:
- Misultin (Erlang)
- Mochiweb (Erlang)
- Cowboy (Erlang)
- NodeJS (V8)
- Tornadoweb (Python)
之所以选择这些库是因为我现在对它们几个最感兴趣。Mochiweb, 是一个稳定的库,在生产环境上使用广泛(估计曾经,或者现在还是,和其它组件一起,被 Facebook 来作聊天服务的后端);Cowboy,一个刚刚诞生不久的库,其开发者在 Erlang 社区里十分活跃;NodeJS,将 javascript 引入服务端,开创了一个充满各种可能的新世界(可与前端共用代码,入门成本低等);还有 Tornadoweb,因为 Python 仍然是我最喜欢的语言之一,另外 Tornadoweb 在各种性能测试和生产环境中都有出色的表现,是 FriendFeed 的后端实现。
关于这个测试,还有两个重要的原则。首先,我并不想做 "Hello World" 类型的测试:对于这类测试,使用静态内容服务器(如 Nginx)就完全可以了。这个测试主要针对动态内容服务器。其次,我计划隔一段时间就关闭 socket,因为现实中是不可能仅靠几个 socket 来承担所有负载压力的。
为了实现上面所说的第二点,我决定使用一个经过修改 的 HttPerf。这是一个来自HP公司的知名的测试工具,使用广泛。它的作用是往服务器发送指定数目的请求,然后根据响应的数目、整个过程中产生错误以及其他信息来生成报告。HttpPerf 最好的一点是,你可以指定一个参数(叫"-num-calls"),来告诉它你在关闭 socket 前一次性要发送多少个请求(就是 socket 连接)。这次测试用到的命令为:
httperf --timeout=5 --client=0/1 --server= --port=8080 --uri=/?value=benchmarks --rate= --send-buffer=4096 --recv-buffer=16384 --num-conns=5000 --num-calls=10
我将速度(--rate)从100开始一直增加到 1200。由于每秒请求数目等于 rate*num-calls,这个测试实际上每秒发送的请求数目由 1000 增加到 12000。总的请求数目等于 num-conns*rate,每次迭代中实际等于 50000。
这个测试实际上是请求服务器:
- 检查GET变量是否设置
- 如果没有,以XML返回错误
- 如果已经设置,在XML中返回
因此,实际上测试的是服务器的:
- header (HTTP头) 分析处理
- querystring (查询串) 分析处理
- 字符串拼接
- socket 实现
服务器是一个双核的1.5G内存的虚拟机系统,安装了 Ubuntu 10.04 LTS,并打上了最新的补丁。/etc/sysctl.conf 已经过优化,主要参数如下:
# Maximum TCP Receive Windownet.core.rmem_max = 33554432# Maximum TCP Send Windownet.core.wmem_max = 33554432# othersnet.ipv4.tcp_rmem = 4096 16384 33554432net.ipv4.tcp_wmem = 4096 16384 33554432net.ipv4.tcp_syncookies = 1# this gives the kernel more memory for tcp which you need with many (100k+) open socket connectionsnet.ipv4.tcp_mem = 786432 1048576 26777216net.ipv4.tcp_max_tw_buckets = 360000net.core.netdev_max_backlog = 2500vm.min_free_kbytes = 65536vm.swappiness = 0net.ipv4.ip_local_port_range = 1024 65535net.core.somaxconn = 65535
/etc/security/limits.conf 文件已经过优化,ulimit -n 设置为 65535。
以下是各个库实现HTTP服务器的代码:
Misultin
-module(misultin_bench).-export([start/1, stop/0, handle_http/1]).start(Port) -> misultin:start_link([{port, Port}, {loop, fun(Req) -> handle_http(Req) end}]).stop() -> misultin:stop().handle_http(Req) -> % get value parameter Args = Req:parse_qs(), Value = misultin_utility:get_key_value("value", Args), case Value of undefined -> Req:ok([{"Content-Type", "text/xml"}], ["<http_test><error>no value specified</error></http_test>"]); _ -> Req:ok([{"Content-Type", "text/xml"}], ["<http_test><value>", Value, "</value></http_test>"]) end.
Mochiweb
-module(mochi_bench).-export([start/1, stop/0, handle_http/1]).start(Port) -> mochiweb_http:start([{port, Port}, {loop, fun(Req) -> handle_http(Req) end}]).stop() -> mochiweb_http:stop().handle_http(Req) -> % get value parameter Args = Req:parse_qs(), Value = misultin_utility:get_key_value("value", Args), case Value of undefined -> Req:respond({200, [{"Content-Type", "text/xml"}], ["<http_test><error>no value specified</error></http_test>"]}); _ -> Req:respond({200, [{"Content-Type", "text/xml"}], ["<http_test><value>", Value, "</value></http_test>"]}) end.
注: 我在这里使用 misultin_utility:get_key_value/2 函数,因为 proplists:get_value/2 太慢了.
Cowboy
-module(cowboy_bench).-export([start/1, stop/0]).start(Port) ->application:start(cowboy),Dispatch = [%% {Host, list({Path, Handler, Opts})}{'_', [{'_', cowboy_bench_handler, []}]}],%% Name, NbAcceptors, Transport, TransOpts, Protocol, ProtoOptscowboy:start_listener(http, 100,cowboy_tcp_transport, [{port, Port}],cowboy_http_protocol, [{dispatch, Dispatch}]).stop() ->application:stop(cowboy).
-module(cowboy_bench_handler).-behaviour(cowboy_http_handler).-export([init/3, handle/2, terminate/2]).init({tcp, http}, Req, _Opts) -> {ok, Req, undefined_state}.handle(Req, State) -> {ok, Req2} = case cowboy_http_req:qs_val(<<"value">>, Req) of {undefined, _} ->cowboy_http_req:reply(200, [{<<"Content-Type">>, <<"text/xml">>}], <<"<http_test><error>no value specified</error></http_test>">>, Req); {Value, _} ->cowboy_http_req:reply(200, [{<<"Content-Type">>, <<"text/xml">>}], ["<http_test><value>", Value, "</value></http_test>"], Req) end, {ok, Req2, State}.terminate(_Req, _State) -> ok.
NodeJS
var http = require('http'), url = require('url');http.createServer(function(request, response) {response.writeHead(200, {"Content-Type":"text/xml"});var urlObj = url.parse(request.url, true);var value = urlObj.query["value"];if (value == ''){response.end("<http_test><error>no value specified</error></http_test>");} else {response.end("<http_test><value>" + value + "</value></http_test>");}}).listen(8080);
Tornadoweb
import tornado.ioloopimport tornado.webclass MainHandler(tornado.web.RequestHandler):def get(self):value = self.get_argument('value', '')self.set_header('Content-Type', 'text/xml')if value == '':self.write("<http_test><error>no value specified</error></http_test>")else:self.write("<http_test><value>" + value + "</value></http_test>")application = tornado.web.Application([(r"/", MainHandler),])if __name__ == "__main__":application.listen(8080)tornado.ioloop.IOLoop.instance().start()
daxiaoming
翻译于 2年前
0人顶
顶 翻译的不错哦!
本文测试的各个库的版本:
- Misultin 0.7.1 (Erlang R14B02)
- Mochiweb 1.5.2 (Erlang R14B02)
- Cowboy master 420f5ba (Erlang R14B02)
- NodeJS 0.4.7
- Tornadoweb 1.2.1 (Python 2.6.5)
各个库都采用默认设置来运行。其中 Erlang 开启了 Kernel Polling。另外,为了让所有库只使用一个CPU,我禁用了SMP。
测试结果
HttPerf 的原始输出文件可从 这里 下载
预期和实际响应图
超时错误图
响应时间图
注:上面的图的Y-轴使用了对数
总响应时间图
由图可知,Tornadoweb 最高约为 1500 请求/秒,NodeJS 为 3000,Mochiweb 为4850, Cowboy 为8600, Misultin 为9700。除了 Misultin 和 Cowboy 没有或者只有少数错误外,其他库在高负载情况下性能下降明显。请注意,这里的“错误”是指超时错误(大于5秒没有响应)。Total responses 指总的响应数目,response times 指的是响应时间。
不得不说,这些结果还是让我比较吃惊的。写到这里,希望各位读者能在代码和测试方法上给我提些意见,以进行更好的测试。欢迎各种意见,我将和其他贡献者一起,在后续的讨论中继续更新这篇文章以及更正相关错误。
注意:文章内容不代表本人观点,请不要由此进行言论攻击。正是由于这些结果太让我惊奇,我才把这篇文章发布出来。
对于这些结果,你的观点是怎样的呢?
更新 (2011-5-16)
现在各种各样的测试正源源不断地冒出来。在你们阅读这些测试报告(也包括我的)的时候,我想着重强调一点:
对于各种测试报告,人们常常误认为:如果一个测试对象在图表中表现越好,那么这个测试对象在所有的应用场景下都是最好的。这种观点绝对,绝对是错误的!
"快速"只是优秀WEB服务器库的众多特点之一:在选择组件进行开发之前,你还必须考虑健状性、功能、易于维护、尽可能接近标准、代码可用性、支持社区、开发速度及其他因素。没有哪个测试是适用于所有情况的。这类测试问题侧重于某一方面:程序运行速度够快、负载能力够强或者尽量少的数据传输量。
因此,请仅将本文测试作为参考的一个因素,而不要武断地下结论。本文引用的库都很优秀,也很有意思,但是你在使用之前,请经过充分的考虑。本文在语言或者在测试方法若有不当,欢迎指正,严厉一些也没关系。
谢谢。
- 多种HTTP服务器:Misultin, Mochiweb, Cowboy, NodeJS 及 Tornadoweb测评
- 多种HTTP服务器:Misultin, Mochiweb, Cowboy, NodeJS 及 Tornadoweb测评
- http服务器(nodejs)
- 创建 nodejs HTTP服务器
- nodejs-构建http服务器
- Erlang cowboy websocket 服务器
- Erlang搭建cowboy服务器
- Erlang cowboy http request生命周期
- 简单cowboy http请求回应
- Nodejs OneAPM测评
- mochiweb 如何处理http请求
- NodeJS与多核HTTP服务器
- NodeJS与多核HTTP服务器
- 使用nodejs创建http服务器
- Erlang cowboy 处理简单的HTTP请求
- nodejs - 简单的http服务器示例
- nodejs (四) 构建HTTP服务器和客户端
- NodeJs创建基本的HTTP服务器
- Unity资源增量更新
- 关于JAVA中流的flush问题
- ungui响应
- 2014,飞天众和团队等你来!
- 黄金数据查询演示示例
- 多种HTTP服务器:Misultin, Mochiweb, Cowboy, NodeJS 及 Tornadoweb测评
- Android Launcher分析和修改5——HotSeat分析
- BeeFramework学习(一):Hello World
- Unity 代码增量更新
- 获取验证码之后出现一个倒计时按钮
- file '/Applications/Xcode 2.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhone
- 使用PullToRefreshListView 区分上拉还是下拉
- jquery 选中某一行
- Android实战技巧:深入解析AsyncTask与thread的区别 THREAD_POOL_EXECUTOR