上一篇讲到ts_config_server:newbeams通过ts_launcher:launch和ts_launcher_static:launch启动本地和远程结点压力客户端,其中ts_launcher用于随机生成用户,ts_launcher_static主要用于静态生成用户。这一篇详细的说明压力客户端是如何启动的。
说明:压力客户端是指一个tsung应用,见TUNG_ROOT/src/tsung/tsung.erl。
newbeams会通过gen_server:cast/2调用回调函数ts_config_server:handle_cast/2,代码见下:
handle_cast({newbeams, HostList}, State=#state{logdir = LogDir, hostname = LocalHost, config = Config}) -> LocalVM = Config#config.use_controller_vm, GetLocal = fun(Host)-> is_vm_local(Host,LocalHost,LocalVM) end, {LocalBeams, RemoteBeams} = lists:partition(GetLocal,HostList), case local_launcher(LocalBeams, LogDir, Config) of Id0 -> Seed=Config#config.seed, Args = set_remote_args(LogDir, Config#config.ports_range), {BeamsIds, LastId} = lists:mapfoldl(fun(A,Acc) -> {{A, Acc}, Acc+1} end, Id0, RemoteBeams), Fun = fun({Host,Id}) -> remote_launcher(Host, Id, Args) end, RemoteNodes = ts_utils:pmap(Fun, BeamsIds), ?LOG("All remote beams started, sync ~n",?NOTICE), global:sync(), StartLaunchers = fun(Node) -> ts_launcher_static:launch({Node,[]}), ts_launcher:launch({Node, [], Seed}) end, lists:foreach(StartLaunchers, RemoteNodes), {noreply, State#state{last_beam_id = LastId}} end;
参数说明:
HostList代表要启动的压力客户端,这个主要由tsung配置文件中的clients段配置。client配置项中有一个cpu属性,当声明这个属性时,会根据cpu的数量生成多个压力客户端。比如:
<clients><client host="memphis" weight="3" maxusers="600"/><clients>
只会生成一个压力客户端,而
<clients><client host="memphis" weight="3" maxusers="600" cpu=”2”/><clients>
则会生成两个压力客户端。
在调用local_laucher之前,GetLocal函数会将压力客户端分成LocalBeams和RemoteBeams,区分这二者的关键是use_controller_vm这个选项,只有这个选项为true时,GetLocal函数才会把与启动tsung结点相同的结点看作是LocalBeams,并通过local_launcher函数在启动tsung的Erlang虚拟机上,再启动一个压力客户端。否则,即使Host在启动tsung的结点上,也会被认为是RemoteBeam。
set_remote_args设置用于启动远程压力客户端的参数,参数内容跟上一篇中tsung.sh使用的启动命令相似。
remote_launcher完成的功能很简单:通过slave:start/3启动远程压力客户端。slave:start/3函数通过Erlang的port机制ssh到远程结点,并运行通过set_remote_args构建的命令来启动远程压力客户端。在erlang的slave手册中,有如下描述:
The user must be allowed to rsh to the remote hosts without being prompted for a password.
这也是为什么在tsung用户手册的依赖那一节中,有如下要求:
for distributed tests, you need an ssh access to remote machines without password.
global:sync函数调用后,远程压力客户端算是启动完毕,但是实际的压力还没有生成。ts_launcher:launch和ts_launcher_static:launch启动实际的压力。这里只分析ts_launcher的实现。
ts_launcher:launch的代码如下:
launch({Node, Arrivals, Seed}) -> ?LOGF("starting on node ~p~n",[[Node]], ?INFO), gen_fsm:send_event({?MODULE, Node}, {launch, Arrivals, Seed})
Node:代表要启动的远程结点主机名;
Arrivals:代码中传递的是空数组;
Seed:随机数种子,默认是当前时间;
gen_fsm:send_event调用会将事件发送到远程结点上,然后在远程结点上启动会话进程。
launch函数的调用栈:wait->wait_static->launcher。
最终launcher通过调用do_launcher启动一个会话进程。do_launcher先通过ts_config_server:get_next_session拿到用户的session信息,然后通过ts_client_sup:start_c hild启动一个会话进程(实际与测试服务器会话还要等一个timer过期会才会真正触发)。
一个会话进程成功启动后,通过change_phase调用确定是在当前phase再继续启动会话进程,还是进入下一个启动phase。
至于一个phase到底启动多少个会话进程,这是由tsung配置文件中load节的arrivalpahse来定义的,根据每个phase配置的每毫秒用户到达速度(两种配置方式:interarrival和arrivalrate,解析时会转换成每毫秒速度)及持续时间来确定(这个值同时受到该phase最大启动会话的限制)。但是最终启动的会话可能不会精确的与这个值相同,这主要是因为生成会话进程的过程本身也会对tsung的控制器产生压力,导致当phase的持续时间已经用完时,期望的会话进程数量还没有达到。
当所有phase的会话都已经启动完毕后,等待所有会话进程完成会话。