2012-11-06 by 谢鸿锋
原创文章,转载请注明:转载自Erlang云中漫步
目录
=================================
=================================
Janus 是一个消息服务器。以tcp方式实现消息传递(主题发布/订阅)。
最优化实现在单播方式下成千上万客户端订阅感兴趣的主题。
目标:Amazon EC2虚拟机环境,客户端在线数20k,消息传递延迟<=2秒。
详细介绍在这里:https://github.com/tolbrino/hotwheels
== 服务器端:<IP: 192.168.67.65> ===============
>erl -pa ebin -env ERL_MAX_PORTS 60000 +P 60000
Eshell V5.9.2 (abort with ^G)
1> application:start(janus).
Listening on port 8081
ok
== ==== 客户端:===============
> bot:test(flashbot, 10000, "192.168.67.65", 8081).
setup: 4218.24ms, good: 10000, bad: 0, run: 7343.00ms
140.0000ms | min
500.0000ms | 3648 - 36.48%
1000.0000ms | 4144 - 41.44%
1500.0000ms | 1001 - 10.01%
2000.0000ms | 0 - 0.00%
2500.0000ms | 226 - 2.26%
3000.0000ms | 499 - 4.99%
3500.0000ms | 482 - 4.82%
3249.0000ms | max
Ok
1、修改janus.app部分源码如下:
免得application:start(janus)时,提示mnesia、inets没启动。
2、修改janus_acceptor.erl à acceptor_init/3函数如下:
一个客户端连接,服务器端产生2个进程,
一个进程与Socket绑定,负责客户端通信消息的收发,如下图中的pid(0.54.0)。
另一个是代理进程,负责对收到的请求进行处理。如下图中的pid(0.57.0)。
janus_transport_sup以simple_one_for_one的方式进行监督。
心跳消息:heartbeat
报文协议
客户端 发 服务器端:
服务器端 发 客户端
报文头部标识/尾部标识作用:a、报文合法性检查;b、拼包、粘包分拆 用途。
进程状态数据变化及消息传递图示
场景:1、启动服务器端程序:application:start(janus). < IP:192.168.67.65>
2、启动客户端测试程序:bot:test(flashbot, 1, “192.168.67.65”, 8081)
3、跟踪上面场景下:a、进程状态数据的变化(仅罗列主要的状态数据);
b、客户端与服务器端之间的消息传递。
启动服务器application:start(janus)后,进程状态数据如下
启动客户端测试程序bot:test(flashbot, 1, “192.168.67.65”, 8081),
客户端与服务器端之间的消息传递及服务器进程主要状态数据变化如下:
修改一个Bug,见下图:
Ide:eclipse + erlide;
erlang tools:appmon、tv、debugger、pman
1、10k客户端订阅一个主题情况下,服务器性能表现出色
> bot:test(flashbot, 10000, "192.168.67.65", 8081).
setup: 4218.24ms, good: 10000, bad: 0, run: 7343.00ms
140.0000ms | min
500.0000ms | 3648 - 36.48%
1000.0000ms | 4144 - 41.44%
1500.0000ms | 1001 - 10.01%
2000.0000ms | 0 - 0.00%
2500.0000ms | 226 - 2.26%
3000.0000ms | 499 - 4.99%
3500.0000ms | 482 - 4.82%
3249.0000ms | max
Ok
2、客户端连接进程以simple_one_for_one方式启动,执行gen_tcp:controlling_process(Socket, Pid)使socket重绑定进程; {active, once}方式数据接收;同时产生一个proxy进程负责消息处理。经典。
为什么要把负责消息收发的进程pid(0.85.0) 与负责消息处理的进程pid(0.87.0)分开,而不合二为一?想下这样一个场景;客户端传递的消息量巨大,服务器端对消息的处理比较复杂而耗时。为避免socket接收缓冲不被消息淹没而丢包。这样就好理解了。
3、心跳消息处理。
4、拼包、拆包处理。
5、主题订阅、发布服务器端处理方式。
hotwheels源码量虽少,作为tcp服务器,麻雀虽小五脏俱全,足够强悍。