seq_trace集群消息链跟踪利器
原创文章,转载请注明: 转载自Erlang非业余研究
本文链接地址: seq_trace集群消息链跟踪利器
做过网络集群服务器的的同学都知道,集群服务通常由不同的服务器组成,这些不同角色的服务器组合在一起共同完成了特定的服务。一个服务通常需要一个协调者,和不同的工作者。 协调者负责派发任务,接收工作者的完成情况,最终回馈给用户。举个例子来讲,拨打电话:首先需要确认你的号码是在有效的,同时还要看下你的帐号里面有钱不,还要看下你拨打的电话号码是不是由权限,电话打的时候需要扣钱,等等。 这些服务中间的任何一个环节出问题了,服务就不正常了。那么我们在服务出问题的时候,如何定位问题呢?通常的办法是打日志,在所有的参与服务的节点上打开日志记录,之后到所有的节点上收集日志,集中分析日志,相当的麻烦。
这时候seq_trace来救助了,seq_trace的目标就是能够跟踪一条消息经过的所有环节,最终把路径展现给用户。
铺垫材料:
seq_trace工作原理,请参考这里
ttb对seq_trace的支持参考这里
tdbg对seq_trace的支持参考这里
我们来设计个案例来演示下这个过程:
该服务功能是对一个列表进行消重,翻倍,求和,这3个动作分别由不同的角色做。
集群共有3个节点提供服务, [email protected], [email protected], [email protected],角色设计上比较简单:y先上面提供消重服务,z提供翻倍, x负责最后的求和。
流程是这样的:
1. 用户给x发列表.
2. x收到列表后,转发发给y.
3. y收到后先进行列表消重, 再发给z.
4. z收到后进行列表翻倍, 然后回给y.
5. y收到回给x.
6. x收到后进行最后的加和,回给用户。
我们首先看下我们的测试代码:
$ cat test.erl -module(test). -export([start/0, stop/1, demo/2]). x_fun(Y)-> receive {From, L} -> Y ! {self(), L}, receive {Y, L1} -> From ! {self(), lists:sum(L1)} end, x_fun(Y); quit -> ok end. y_fun(Z)-> receive {From, L} -> Z ! {self(), sets:to_list(sets:from_list(L))}, receive {Z, L1}-> From ! {self(), L1} end, y_fun(Z); quit -> ok end. z_fun()-> receive {From, L} -> From ! {self(), [2 * R || R <-L]}, z_fun(); quit -> ok end. start()-> pong = net_adm:ping('[email protected]'), pong = net_adm:ping('[email protected]'), pong = net_adm:ping('[email protected]'), Z = spawn('[email protected]', fun() -> global:register_name(z_p, self()), z_fun() end), Y = spawn('[email protected]', fun() -> global:register_name(y_p, self()), y_fun(Z) end), X = spawn('[email protected]', fun() -> global:register_name(x_p, self()), x_fun(Y) end), global:sync(), {X, Y, Z}. stop({X,Y,Z})-> [P ! quit || P <- [X,Y,Z]], ok. demo(L, {X,_,_}) when is_list(L) -> X ! {self(), L}, %% io:format("sent ~p~n", [L]), receive {X, _Res} -> %% io:format("got ~p~n",[_Res]), _Res end.
我们首先准备下集群环境,开3个节点Erlang节点:
#terminal 1 $ erl -name [email protected] Erlang R14B04 (erts-5.8.5) 1 [smp:2:2] [rq:2] [async-threads:0] [hipe] [kernel-poll:false] Eshell V5.8.5 (abort with ^G) ([email protected])1> #terminal 2 $ erl -name [email protected] Erlang R14B04 (erts-5.8.5) 1 [smp:2:2] [rq:2] [async-threads:0] [hipe] [kernel-poll:false] Eshell V5.8.5 (abort with ^G) ([email protected])1> #terminal 3 $ erl -name [email protected] Erlang R14B04 (erts-5.8.5) 1 [smp:2:2] [rq:2] [async-threads:0] [hipe] [kernel-poll:false] Eshell V5.8.5 (abort with ^G) ([email protected])1>
好了,现在开始我们的测试:
$ erlc test.erl $ erl -name [email protected] Erlang R14B04 (erts-5.8.5) 1 [smp:2:2] [rq:2] [async-threads:0] [hipe] [kernel-poll:false] Eshell V5.8.5 (abort with ^G) ([email protected])1> Ref=test:start(). {<6584.42.0>,<6585.42.0>,<6586.42.0>} ([email protected])2> test:demo([1,1,3,5],Ref). 18 ([email protected])3> nodes(). ['[email protected]','[email protected]','[email protected]'] ([email protected])4> global:registered_names(). [z_p,y_p,x_p] ([email protected])5> test:stop(Ref). ok
我们看到结果算出来了,参与的节点x,y,z也都看到了,服务的进程名z_p,y_p,x_p也都是正常的,可是我们无法看到消息是如何在集群里面流通的。
神奇的ttb加上seq_trace可以帮我们的忙。原理是用ttb在调用test:demo的时候,给我们的进程设定个token,demo调用把列表通过消息传递给X的时候,seq_trace会在后台把这个token顺着服务链条传递出去,最终消息回到我们手里,我们就知道这个消息所走过的路径,包括节点间的路径。
我们来演示下如果操作:
$ erl -name [email protected] Erlang R14B04 (erts-5.8.5) 1 [smp:2:2] [rq:2] [async-threads:0] [hipe] [kernel-poll:false] Eshell V5.8.5 (abort with ^G) ([email protected])1> Ref=test:start(). {<6584.60.0>,<6585.60.0>,<6586.60.0>} ([email protected])2> ([email protected])2> nodes(). ['[email protected]','[email protected]','[email protected]'] ([email protected])3> global:registered_names(). [z_p,y_p,x_p] ([email protected])5> ttb:tracer(all). {ok,['[email protected]','[email protected]','[email protected]', '[email protected]']} ([email protected])6> ttb:p(self(),call). {ok,[{<0.37.0>,[{matched,'[email protected]',1}]}]} ([email protected])7> ttb:tp(test,demo,ttb:seq_trigger_ms()). {ok,[{matched,'[email protected]',1}, {matched,'[email protected]',1}, {matched,'[email protected]',1}, {matched,'[email protected]',1}, {saved,1}]} ([email protected])8> test:demo([1,1,3,5],Ref),seq_trace:reset_trace(). true ([email protected])9> ttb:stop([fetch]). Stored logs in /Users/yufeng/ttb_upload-20111003-012347 stopped
到现在为止,日志已经保存下来了, 文件名是ttb_upload-20111003-012347, 我们先文本方式格式化下消息流:
([email protected])10> ttb:format("ttb_upload-20111003-012347"). ({<0.37.0>,{erlang,apply,2},'[email protected]'}) call test:demo([1,1,3,5],{<6584.60.0>,<6585.60.0>,<6586.60.0>}) SeqTrace {1317,576219,213176} [0]: ({<0.37.0>,{erlang,apply,2},'[email protected]'}) {<6584.60.0>, {global,z_p}, '[email protected]'} ! {<0.37.0>, [1,1,3,5]} [Serial: {0, 1}] SeqTrace {1317,576219,213398} [0]: ({<6584.60.0>,{global,z_p},'[email protected]'}) << {<0.37.0>,[1,1,3,5]} [Serial: {0, 1}, From: {<0.37.0>, {erlang, apply, 2}, '[email protected]'}] SeqTrace {1317,576219,213409} [0]: ({<6584.60.0>,{global,z_p},'[email protected]'}) {<6585.60.0>, {global,y_p}, '[email protected]'} ! {<6584.60.0>, [1,1,3,5]} [Serial: {1, 2}] SeqTrace {1317,576219,213601} [0]: ({<6585.60.0>,{global,y_p},'[email protected]'}) << {<6584.60.0>,[1,1,3,5]} [Serial: {1, 2}, From: {<6584.60.0>, {global, z_p}, '[email protected]'}] SeqTrace {1317,576219,213640} [0]: ({<6585.60.0>,{global,y_p},'[email protected]'}) {<6586.60.0>, {global,x_p}, '[email protected]'} ! {<6585.60.0>, [3,5,1]} [Serial: {2, 3}] SeqTrace {1317,576219,213814} [0]: ({<6586.60.0>,{global,x_p},'[email protected]'}) << {<6585.60.0>,[3,5,1]} [Serial: {2, 3}, From: {<6585.60.0>, {global, y_p}, '[email protected]'}] SeqTrace {1317,576219,213825} [0]: ({<6586.60.0>,{global,x_p},'[email protected]'}) {<6585.60.0>, {global,y_p}, '[email protected]'} ! {<6586.60.0>, [6,10,2]} [Serial: {3, 4}] SeqTrace {1317,576219,213929} [0]: ({<6585.60.0>,{global,y_p},'[email protected]'}) << {<6586.60.0>,[6,10,2]} [Serial: {3, 4}, From: {<6586.60.0>, {global, x_p}, '[email protected]'}] SeqTrace {1317,576219,213935} [0]: ({<6585.60.0>,{global,y_p},'[email protected]'}) {<6584.60.0>, {global,z_p}, '[email protected]'} ! {<6585.60.0>, [6,10,2]} [Serial: {4, 5}] SeqTrace {1317,576219,214054} [0]: ({<6584.60.0>,{global,z_p},'[email protected]'}) << {<6585.60.0>,[6,10,2]} [Serial: {4, 5}, From: {<6585.60.0>, {global, y_p}, '[email protected]'}] SeqTrace {1317,576219,214062} [0]: ({<6584.60.0>,{global,z_p},'[email protected]'}) {<0.37.0>, {erlang,apply,2}, '[email protected]'} ! {<6584.60.0>, 18} [Serial: {5, 6}] SeqTrace {1317,576219,214241} [0]: ({<0.37.0>,{erlang,apply,2},'[email protected]'}) << {<6584.60.0>,18} [Serial: {5, 6}, From: {<6584.60.0>, {global, z_p}, '[email protected]'}] ok ([email protected])11>
透过消息交互的文本,我们可以看到消息在流动,整个过程都在掌握中,如果哪个环节出问题,也一目了然。
我们还可以用图形方式显示路径,神奇的et可以帮忙,et可以参考这里
演示下这个功能:
([email protected])12> ttb:format("ttb_upload-20111003-012347",[{handler,et}]).
结论: seq_trace+ttb在跟踪消息方面真是无敌!
祝大家玩得开心!
Post Footer automatically generated by wp-posturl plugin for wordpress.