<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    聶永的博客

    記錄工作/學(xué)習(xí)的點(diǎn)點(diǎn)滴滴。

    Tsung筆記之壓測(cè)端資源限制篇

    前言

    這里匯集一下影響tsung client創(chuàng)建用戶數(shù)的各項(xiàng)因素。因?yàn)門sung是IO密集型的應(yīng)用,CPU占用一般不大,為了盡可能的生成更多的用戶,需要考慮內(nèi)存相關(guān)事宜。

    IP & 端口的影響

    1. 系統(tǒng)端口限制

    Linux系統(tǒng)端口為short類型表示,數(shù)值上限為65535。假設(shè)分配壓測(cè)業(yè)務(wù)可用端口范圍為1024 - 65535,不考慮可能還運(yùn)行著其它對(duì)外連接的服務(wù),真正可用端口也就是64000左右(實(shí)際上,一般為了方便計(jì)算,一般直接設(shè)定為50000)。換言之,即在一臺(tái)機(jī)器上一個(gè)IP,可用同時(shí)對(duì)外建立64000網(wǎng)絡(luò)連接。

    若是N個(gè)可用IP,理論上 64000*N,實(shí)際上還需要滿足:

    • 充足內(nèi)存支持
      • tcp接收/發(fā)送緩沖區(qū)不要設(shè)置太大,tsung默認(rèn)分配32K(可以修改成16K,一般夠用了)
      • 一個(gè)粗略估算一個(gè)連接占用80K內(nèi)存,那么10萬(wàn)用戶,將占用約8G內(nèi)存
    • 為多IP的壓測(cè)端分配適合的權(quán)重,以便承擔(dān)更多的終端連接

    另外還需要考慮端口的快速回收等,可以這樣做:

    sysctl -w net.ipv4.tcp_syncookies=1
    sysctl -w net.ipv4.tcp_tw_reuse=1
    sysctl -w net.ipv4.tcp_tw_recycle=1
    sysctl -w net.ipv4.tcp_fin_timeout=30
    sysctl -w net.ipv4.ip_local_port_range="1024 65535"
    
    sysctl -p
    

    若已經(jīng)在 /etc/sysctl.conf 文件中有記錄,則需要手動(dòng)修改

    作為附加,可設(shè)置端口重用:

    <option name="tcp_reuseaddr" value="true"/>
    

    注意,不要設(shè)置下面的可用端口范圍:

    <option name="ports_range" min="1025" max="65535"/>
    

    因?yàn)椴僮飨到y(tǒng)會(huì)自動(dòng)跳過(guò)已經(jīng)被占用本地端口,而Tsung只能夠被動(dòng)通過(guò)錯(cuò)誤進(jìn)行可用端口+1繼續(xù)下一個(gè)連接,有些多余。

    2. IP和端口組合

    每一個(gè)client支持多個(gè)可用IP地址列表

    <client host="client_99" maxusers="120000" weight="2" cpu="8">
        <ip value="10.10.10.99"></ip>
        <ip value="10.10.10.11"></ip>
    </client>
    

    tsung client從節(jié)點(diǎn)開(kāi)始準(zhǔn)備建立網(wǎng)絡(luò)連接會(huì)話時(shí),需要從tsung_controller主節(jié)點(diǎn)獲取具體的會(huì)話信息,其中就包含了客戶端連接需要使用到來(lái)源{LocalIP, LocalPort}二元組。由tsung_controller主節(jié)點(diǎn)完成。

    get_user_param(Client,Config)->
        {ok, IP} = choose_client_ip(Client),
        {ok, Server} = choose_server(Config#config.servers, Config#config.total_server_weights),
        CPort = choose_port(IP, Config#config.ports_range),
        {{IP, CPort}, Server}.
    
    choose_client_ip(#client{ip = IPList, host=Host}) ->
        choose_rr(IPList, Host, {0,0,0,0}).
    
    ......
    
    choose_client_ip(#client{ip = IPList, host=Host}) ->
        choose_rr(IPList, Host, {0,0,0,0}).
    
    choose_rr(List, Key, _) ->
        I = case get({rr,Key}) of
              undefined -> 1 ; % first use of this key, init index to 1
              Val when is_integer(Val) ->
                (Val rem length(List))+1 % round robin
        end,
        put({rr, Key},I),
        {ok, lists:nth(I, List)}.
    
    %% 默認(rèn)不設(shè)置 ports_range 會(huì)直接返回0
    %% 不建議設(shè)置 <option name="ports_range" min="1025" max="65535"/>
    %% 因?yàn)檫@樣存在端口沖突問(wèn)題,除非確實(shí)不存被占用情況
    choose_port(_,_, undefined) ->
        {[],0};
    choose_port(Client,undefined, Range) ->
        choose_port(Client,dict:new(), Range);
    choose_port(ClientIp,Ports, {Min, Max}) ->
        case dict:find(ClientIp,Ports) of
            {ok, Val} when Val =< Max ->
                NewPorts=dict:update_counter(ClientIp,1,Ports),
                {NewPorts,Val};
            _ -> % Max Reached or new entry
                NewPorts=dict:store(ClientIp,Min+1,Ports),
                {NewPorts,Min}
        end.
    

    從節(jié)點(diǎn)建立到壓測(cè)服務(wù)器連接時(shí),就需要指定從主節(jié)點(diǎn)獲取到的本機(jī)IP地址和端口兩元組:

    Opts = protocol_options(Protocol, Proto_opts)  ++ [{ip, IP},{port,CPort}],
    ......
    gen_tcp:connect(Server, Port, Opts, ConnectTimeout).
    

    3. IP自動(dòng)掃描特性

    若從機(jī)單個(gè)網(wǎng)卡綁定了多個(gè)IP,又懶于輸入,可以配置掃描特性:

    <ip scan="true" value="eth0"/>
    

    本質(zhì)上使用shell方式獲取IP地址,并且支持CentOS 6/7。

        /sbin/ip -o -f inet addr show dev eth0
    

    因?yàn)閽呙璞容^慢,Tsung 1.6.1推出了ip_range特性支持。

    Linux系統(tǒng)打開(kāi)文件句柄限制

    系統(tǒng)打開(kāi)文件句柄,直接決定了可以同時(shí)打開(kāi)的網(wǎng)絡(luò)連接數(shù)量,這個(gè)需要設(shè)置大一些,否則,你可能會(huì)在tsung_controller@IP.log文件中看到error_connect_emfile類似文件句柄不夠使用的警告,建議此值要大于 > N * 64000。

    echo "* soft nofile 300000" >> /etc/security/limits.conf
    echo "* hard nofile 300000" >> /etc/security/limits.conf
    

    或者,在Tsung會(huì)話啟動(dòng)腳本文件中明確添加上ulimit -n 300000

    內(nèi)存的影響

    一個(gè)網(wǎng)絡(luò)Socket連接占用不多,但上萬(wàn)個(gè)或數(shù)十萬(wàn)等就不容小覷了,設(shè)置不當(dāng)會(huì)導(dǎo)致內(nèi)存直接成為屏障。

    1. TCP接收、發(fā)送緩存

    Tsung默認(rèn)設(shè)置的網(wǎng)絡(luò)Socket發(fā)送接收緩沖區(qū)為16KB,一般夠用了。

    以TCP為例,某次我手誤為Tcp接收緩存賦值過(guò)大(599967字節(jié)),這樣每一個(gè)網(wǎng)絡(luò)了解至少占用了0.6M內(nèi)存,直接導(dǎo)致在16G內(nèi)存服務(wù)上網(wǎng)絡(luò)連接數(shù)到2萬(wàn)多時(shí),內(nèi)存告急。

    <option name="tcp_snd_buffer" value="16384"></option>
    <option name="tcp_rcv_buffer" value="16384"></option>
    

    此值會(huì)覆蓋Linux系統(tǒng)設(shè)置接收、發(fā)送緩沖大小。

    粗略的默認(rèn)值計(jì)算,一個(gè)網(wǎng)絡(luò)連接發(fā)送緩沖區(qū) + 接收緩沖區(qū),再加上進(jìn)程處理連接堆棧占用,約40多K內(nèi)存,為即計(jì)算方便,設(shè)定建立一個(gè)網(wǎng)絡(luò)連接消費(fèi)50K內(nèi)存。

    先不考慮其它因素,若我們想要從機(jī)模擬10W個(gè)用戶,那么當(dāng)前可用內(nèi)存至少要剩余:50K * 100000 / 1000K = 5000M = 5G內(nèi)存。針對(duì)一般服務(wù)器來(lái)講,完全可滿足要求(剩下事情就是要有兩個(gè)可用IP了)。

    2. Erlang函數(shù)堆棧內(nèi)存占用

    使用Erlang程序?qū)懙膽?yīng)用服務(wù)器,進(jìn)程要存儲(chǔ)堆棧調(diào)用信息,進(jìn)程一多久會(huì)占用大量?jī)?nèi)存,想要服務(wù)更多網(wǎng)絡(luò)連接/任務(wù),需要將不活動(dòng)的進(jìn)程設(shè)置為休眠狀態(tài),以便節(jié)省內(nèi)存,Tsung的壓測(cè)會(huì)話信息若包含thinktime時(shí)間,也要考慮啟用hibernate休眠機(jī)制。

    <option name="hibernate" value="5"></option>
    

    值單位秒,默認(rèn)thinktime超過(guò)10秒后自動(dòng)啟動(dòng),這里修改為5秒。

    XML文件設(shè)置需要注意部分

    1. 日志等級(jí)要調(diào)高一些

    tsung使用error_logger記錄日志,其只適用于真正的異常情況,若當(dāng)一般業(yè)務(wù)調(diào)試類型日志量過(guò)多時(shí),不但耗費(fèi)了大量?jī)?nèi)存,網(wǎng)絡(luò)/磁盤寫入速度跟不上生產(chǎn)速度時(shí),會(huì)導(dǎo)致進(jìn)程堵塞,嚴(yán)重會(huì)拖累整個(gè)應(yīng)用僵死,因此需要在tsung.xml文件中設(shè)置日志等級(jí)要高一些,至少默認(rèn)的notice就很合適。

    2. 不要啟用dump

    dump是一個(gè)耗時(shí)的行為,因此默認(rèn)為false,除非很少的壓測(cè)用戶用于調(diào)試。

    3. 動(dòng)態(tài)屬性太多,會(huì)導(dǎo)致請(qǐng)求超時(shí)

    <option name="file_server" id="userdb" value="/your_path/100w_users.csv"/>
    
    ...
    
    <setdynvars sourcetype="file" fileid="userdb" delimiter=";" order="iter">
        <var name="userid" />
        <var name="nickname" />
    </setdynvars>
    
    ...
    
    <request subst="true">
        <yourprotocol type="hello" uid="%%_userid%%" ack="local">
            Hello, I'm %%_nickname%%
        </yourprotocol>
    </request>
    

    設(shè)定一個(gè)有狀態(tài)的場(chǎng)景,用戶ID儲(chǔ)存在文件中,每一次會(huì)話請(qǐng)求都要從獲取到用戶ID,壓測(cè)用戶一旦達(dá)到百萬(wàn)級(jí)別并且用戶每秒產(chǎn)生速率過(guò)大(比如每秒1000個(gè)用戶),會(huì)經(jīng)常遇到超時(shí)錯(cuò)誤:

    =ERROR REPORT==== 25-Jul-2016::15:14:11 ===
    ** Reason for termination =
    ** {timeout,{gen_server,call,
                            [{global,ts_file_server},{get_next_line,userdb}]}}
    

    這是因?yàn)椋?dāng)tsung client遇到setdynvars指令時(shí),會(huì)直接請(qǐng)求主機(jī)ts_file_server模塊,當(dāng)一時(shí)間請(qǐng)求量巨大,可能會(huì)造成單一模塊處理緩慢,出現(xiàn)超時(shí)問(wèn)題。

    怎么辦:

    1. 降低用戶每秒產(chǎn)生速率,比如300秒用戶生成
    2. 不用從文件中存儲(chǔ)用戶id等信息,采用別的方式

    如何限流/限速

    某些時(shí)候,要避免tsung client壓測(cè)端影響所在服務(wù)器網(wǎng)絡(luò)帶寬IO太擁擠,需要限制流量,其采用令牌桶算法。

    <option name="rate_limit" value="1024"></option>
    
    • 值為KB單位每秒
    • 目前僅對(duì)傳入流量生效

    閥值計(jì)算方式:

    {RateConf,SizeThresh} = case RateLimit of
                                Token=#token_bucket{} ->
                                    Thresh=lists:min([?size_mon_thresh,Token#token_bucket.burst]),
                                    {Token#token_bucket{last_packet_date=StartTime}, Thresh};
                                undefined ->
                                    {undefined, ?size_mon_thresh}
               end,
    

    接收傳入流量數(shù)據(jù),需要計(jì)算:

    handle_info2({gen_ts_transport, _Socket, Data}, wait_ack, State=#state_rcv{rate_limit=TokenParam}) when is_binary(Data)->
        ?DebugF("data received: size=~p ~n",[size(Data)]),
        NewTokenParam = case TokenParam of
                            undefined ->
                                undefined;
                            #token_bucket{rate=R,burst=Burst,current_size=S0, last_packet_date=T0} ->
                                {S1,_Wait}=token_bucket(R,Burst,S0,T0,size(Data),?NOW,true),
                                TokenParam#token_bucket{current_size=S1, last_packet_date=?NOW}
                        end,
        {NewState, Opts} = handle_data_msg(Data, State),
        NewSocket = (NewState#state_rcv.protocol):set_opts(NewState#state_rcv.socket,
                                                           [{active, once} | Opts]),
        case NewState#state_rcv.ack_done of
            true ->
                handle_next_action(NewState#state_rcv{socket=NewSocket,rate_limit=NewTokenParam,
                                                      ack_done=false});
            false ->
                TimeOut = case (NewState#state_rcv.request)#ts_request.ack of
                    global ->
                        (NewState#state_rcv.proto_opts)#proto_opts.global_ack_timeout;
                    _ ->
                        (NewState#state_rcv.proto_opts)#proto_opts.idle_timeout
                end,
                {next_state, wait_ack, NewState#state_rcv{socket=NewSocket,rate_limit=NewTokenParam}, TimeOut}
        end;
    

    下面則是具體的令牌桶算法:

    %% @spec token_bucket(R::integer(),Burst::integer(),S0::integer(),T0::tuple(),P1::integer(),
    %%                    Now::tuple(),Sleep::boolean()) -> {S1::integer(),Wait::integer()}
    
    %% @doc Implement a token bucket to rate limit the traffic: If the
    %%      bucket is full, we wait (if asked) until we can fill the
    %%      bucket with the incoming data
    %%      R = limit rate in Bytes/millisec, Burst = max burst size in Bytes
    %%      T0 arrival date of last packet,
    %%      P1 size in bytes of the packet just received
    %%      S1: new size of the bucket
    %%      Wait: Time to wait
    %% @end
    token_bucket(R,Burst,S0,T0,P1,Now,Sleep) ->
        S1 = lists:min([S0+R*round(ts_utils:elapsed(T0, Now)),Burst]),
        case P1 < S1 of
            true -> % no need to wait
                {S1-P1,0};
            false -> % the bucket is full, must wait
                Wait=(P1-S1) div R,
                case Sleep of
                    true ->
                        timer:sleep(Wait),
                        {0,Wait};
                    false->
                        {0,Wait}
                end
        end.
    

    小結(jié)

    以上簡(jiǎn)單梳理一下影響tsung從機(jī)創(chuàng)建用戶的各項(xiàng)因素,實(shí)際環(huán)境其實(shí)相當(dāng)復(fù)雜,需要一一對(duì)癥下藥才行。

    posted on 2016-07-26 08:47 nieyong 閱讀(4203) 評(píng)論(0)  編輯  收藏 所屬分類: 壓測(cè)

    公告

    所有文章皆為原創(chuàng),若轉(zhuǎn)載請(qǐng)標(biāo)明出處,謝謝~

    新浪微博,歡迎關(guān)注:

    導(dǎo)航

    <2016年7月>
    262728293012
    3456789
    10111213141516
    17181920212223
    24252627282930
    31123456

    統(tǒng)計(jì)

    常用鏈接

    留言簿(58)

    隨筆分類(130)

    隨筆檔案(151)

    個(gè)人收藏

    最新隨筆

    搜索

    最新評(píng)論

    閱讀排行榜

    評(píng)論排行榜

    主站蜘蛛池模板: 67194成手机免费观看| 亚洲高清无码综合性爱视频| 亚洲欧美一区二区三区日产| 免费乱码中文字幕网站| 国产精品免费观看| 亚洲精品色播一区二区| 色噜噜AV亚洲色一区二区| 最近中文字幕完整免费视频ww| 亚洲成av人片在www鸭子| 国产∨亚洲V天堂无码久久久| 毛片A级毛片免费播放| a在线视频免费观看| 亚洲欧美国产精品专区久久| 日本亚洲国产一区二区三区| 99热在线精品免费全部my| 久久久受www免费人成| 亚洲熟妇成人精品一区| 国产偷v国产偷v亚洲高清| 四虎成人免费影院网址| 日本卡1卡2卡三卡免费| 污网站免费在线观看| 亚洲成电影在线观看青青| 狠狠亚洲婷婷综合色香五月排名| 97人伦色伦成人免费视频| 成人精品一区二区三区不卡免费看| 亚洲永久在线观看| 亚洲日本在线看片| 在线观看亚洲成人| 免费人成在线观看播放国产| 在线观看免费人成视频| 四虎影视成人永久免费观看视频 | 亚洲区视频在线观看| 中文字幕精品无码亚洲字| 好爽…又高潮了免费毛片| 日韩在线永久免费播放| 好男人资源在线WWW免费| 亚洲爆乳AAA无码专区| 亚洲区精品久久一区二区三区| 成人免费看吃奶视频网站| 免费看a级黄色片| 亚洲伊人久久大香线蕉|