前言
使用Erlang語言也寫一個(gè)測試和前面大同小異的測試,在100萬個(gè)并發(fā)連接用戶情況下,就是想觀察一下極顯情況下的表現(xiàn)。
這個(gè)測試使用了優(yōu)秀的Erlang界的明星框架cowboy,加單易用的接口,避免了我們對HTTP棧再次進(jìn)行閉門造車。
測試Erlang服務(wù)器
運(yùn)行在VMWare Workstation 9中,64位Centos 6.4系統(tǒng),分配14.9G內(nèi)存左右,雙核4個(gè)線程,服務(wù)器安裝Erlang/OTP R16B,最新版本支持異步代碼熱加載,很贊。
下載安裝
本系統(tǒng)已經(jīng)提前安裝JDK,只需要安裝Erlang好了。
安裝依賴
yum install build-essential m4
yum install openssl
yum install openssl-devel
yum install unixODBC
yum install unixODBC-devel
yum -y install openssl make gcc gcc-c++ kernel-devel m4 ncurses-devel openssl-devel
yum install xsltproc fop
源代碼安裝
#wget https://elearning.erlang-solutions.com/binaries/sources/otp_src_R16B.tar.gz
#tar xvf otp_src_R16B.tar.gz
cd otp_src_R16B
./configure --prefix=/usr/local/erlang --enable-hipe --enable-threads --enable-smp-support --enable-kernel-poll
make
make install
添加到環(huán)境變量中(/etc/profile)
export ERL_HOME=/usr/local/erlang export PATH=$ERL_HOME/bin:$PATH
保存生效
source /etc/profile
測試進(jìn)程創(chuàng)建
這里拷貝《Erlang程序設(shè)計(jì)》一書提供的processes.erl源碼,稍作修改。
創(chuàng)建一百萬個(gè)進(jìn)程,看看大概花費(fèi)多少時(shí)間。
[yongboy@base erlang]$ erl +P 10240000
Erlang R16B (erts-5.10.1) [source] [64-bit] [smp:4:4] [async-threads:10] [hipe] [kernel-poll:false]
Eshell V5.10.1 (abort with ^G)
1> processes:max(200000).
Maxmium allowed process is 16777216 Process spawn time=1.1 (2.68) microseconds ok
2> processes:max(200000).
Maxmium allowed process is 16777216 Process spawn time=1.7 (2.33) microseconds ok
3> processes:max(200000).
Maxmium allowed process is 16777216 Process spawn time=1.55 (2.12) microseconds ok
4> processes:max(1000000).
Maxmium allowed process is 16777216 Process spawn time=2.97 (3.967) microseconds ok
5> processes:max(1000000).
Maxmium allowed process is 16777216 Process spawn time=2.4 (2.729) microseconds ok
6> processes:max(1000000).
Maxmium allowed process is 16777216 Process spawn time=2.19 (2.735) microseconds ok
7> processes:max(10000000).
Maxmium allowed process is 16777216 Process spawn time=3.328 (4.2777) microseconds ok
8> processes:max(10000000).
Maxmium allowed process is 16777216 Process spawn time=3.144 (3.1361) microseconds ok
9> processes:max(10000000).
Maxmium allowed process is 16777216 Process spawn time=3.394 (3.2051) microseconds ok
恩,創(chuàng)建1000萬個(gè)進(jìn)程,每一個(gè)進(jìn)程花費(fèi)3.4微秒(μs)的CPU時(shí)間,相當(dāng)于4.3微秒(μs)的消耗時(shí)間(亦即消耗的真實(shí)時(shí)間),在一定量的區(qū)間內(nèi),其值變化,可以看做是一個(gè)常量。
初始化小問題
Cowboy初始化需要事項(xiàng)
我們做的簡單程序,使用了非常受歡迎的cowboy框架,其啟動函數(shù):
cowboy:start_http(my_http_listener, 100,
[{port, 8000}],
[{env, [{dispatch, Dispatch}]}]
),
Cowboy默認(rèn)支持1024個(gè)連接,服務(wù)器端輸出:
online user 1122 online user 1123
停滯于此,后續(xù)的連接只能排隊(duì)等候了。
這里設(shè)置支持無限個(gè)連接好了。
{max_connections, infinity}
話說,Cowboy為Erlang世界的明星產(chǎn)品,絕對值得一試!
Erlang默認(rèn)創(chuàng)建進(jìn)程限制
Erlang在我的機(jī)器上默認(rèn)允許創(chuàng)建的線程也是有限的:
erlang:system_info(process_limit).
262144
才26萬個(gè),不夠用。在啟動腳本(start.sh)處添加允許創(chuàng)建的最大線程支持:
#!/bin/sh
erl +K true +P 10240000 -sname testserver -pa ebin -pa deps/*/ebin -s htmlfilesimple\
-eval "io:format(\"Server start with port 8000 Success!~n\")."
腳本啟動后現(xiàn)在在erl shell中測試一下:
erlang:system_info(process_limit).
16777216
數(shù)量完全夠用了。
開啟erlang的epoll屬性
+K true | false 是否開啟kernel poll,就是epoll;
不開啟,測試過程中,在內(nèi)存完好情況下,經(jīng)常會有連接失敗情況。
使用cowboy_req:compact降低內(nèi)存占用
一旦你從request對象中獲取到足夠的信息,以后不再獲取其附加屬性時(shí),調(diào)用compact/1函數(shù)可去除無用屬性,起到節(jié)省內(nèi)存作用。
程序里面調(diào)用如下:
init(_Any, Req, State) -> NowCount = count_server:welcome(),
io:format("online user ~p :))~n", [NowCount]),
output_first(Req),
Req2 = cowboy_req:compact(Req),
{loop, Req2, State, hibernate}.
在本例中精測壓縮內(nèi)存效果不明顯,因?yàn)?,測試端輸出的HTTP頭部壓根就沒有幾個(gè)。
Cowboy無法處理沒有header的HTTP請求
這里需要牢記,也不能算是BUG,前面的client2.c源碼,就未曾設(shè)置HTTP Header元數(shù)據(jù),需要做些簡單修改,修改之后的測試端程序文件名為client5.c,
可以到這里 下載client.c。
Cowboy處理長連接
Cowboy很貼心的提供了cowboy_loop_handler
behaviour。在init/3函數(shù)中,可以進(jìn)入休眠狀態(tài),節(jié)省內(nèi)存,消息到達(dá)時(shí),被喚醒,值得一贊!
其定義如下:
注意hibernate和timeout參數(shù),按照實(shí)際需求返回即可。
htmlfile_handler示范代碼如下:
100萬并發(fā)連接達(dá)成
測試過程跌跌撞撞的,雖然這中間因?yàn)閮?nèi)存問題拋出若干的異常,但也達(dá)到100W連接的數(shù)量
online user 1022324 :))
online user 1022325 :))
online user 1022326 :))
online user 1022327 :))
online user 1022328 :))
online user 1022329 :))
online user 1022330 :))
online user 1022331 :))
online user 1022332 :))
online user 1022333 :))
online user 1022334 :))
online user 1022335 :))
online user 1022336 :))
online user 1022337 :))
online user 1022338 :))
可以看到狀態(tài)信息

算一下:
14987952K = 14636M
14987952/1022338 = 14.7K/Connection
未啟動時(shí)的內(nèi)存情況:
total used free shared buffers cached
Mem: 14806 245 14561 0 12 60
-/+ buffers/cache: 172 14634
Swap: 3999 0 3999
啟動后的內(nèi)存占用情況:
total used free shared buffers cached
Mem: 14806 435 14370 0 12 60
-/+ buffers/cache: 363 14443
Swap: 3999 0 3999
用戶量達(dá)到1022338數(shù)量后的內(nèi)存一覽:
total used free shared buffers cached
Mem: 14806 14641 165 0 1 5
-/+ buffers/cache: 14634 172
Swap: 3999 1068 2931
可以看到,當(dāng)前內(nèi)存不夠用了,需要虛擬內(nèi)存配合了。
查看一下當(dāng)前進(jìn)程的內(nèi)存占用
ps -o rss= -p `pgrep -f 'sname testserver'`
4869520
這樣算起來,系統(tǒng)為每一個(gè)進(jìn)程持有 4869520/1022338 = 4.8K 內(nèi)存。
這個(gè)值只是計(jì)算物理內(nèi)存,實(shí)際上連虛擬內(nèi)存都占用了,估計(jì)在4.8K-6.8K之間吧。
和C語言相比,內(nèi)存占用相當(dāng)大,我虛擬機(jī)器分配的15G內(nèi)存,也僅僅處理達(dá)到100萬的連接,已經(jīng)接近極限時(shí),會發(fā)現(xiàn)陸陸續(xù)續(xù)的有連接失敗。
不得不說的代碼熱加載
運(yùn)行時(shí)系統(tǒng)的代碼熱加載功能,在這個(gè)實(shí)例中,通過vi修改了htmlfile_handler.erl文件,主要修改內(nèi)容如下:
io:format("online user ~p :))~n", [NowCount]),
......
io:format("offline user ~p :(( ~n", [NowCount]).
執(zhí)行make,編譯
[root@base htmlfilesimple]# make
==> ranch (get-deps)
==> cowboy (get-deps)
==> htmlfilesimple (get-deps)
==> ranch (compile)
==> cowboy (compile)
==> htmlfilesimple (compile)
src/htmlfile_handler.erl:5: Warning: record status is unused
src/htmlfile_handler.erl:29: Warning: variable 'Reason' is unused
Compiled src/htmlfile_handler.erl
很好,非常智能的rebar,自動只編譯了htmlfile_handler.erl一個(gè)文件,然后通知Erlang的運(yùn)行環(huán)境進(jìn)行代碼熱替換吧。
(testserver@base)4> code:load_file(htmlfile_handler).
查看日志輸出控制臺,可以看到已經(jīng)生效,同時(shí)也保存著到狀態(tài)數(shù)據(jù)等。
非常利于運(yùn)行時(shí)調(diào)試,即不傷害在線狀態(tài)數(shù)據(jù),又能即時(shí)修改,贊!但生產(chǎn)環(huán)境下,一般都是版本切換,OTP的版本切換,測試或馬上修改bug時(shí),著實(shí)有些復(fù)雜。
小結(jié)
和C相比,處理相同的事情(100萬并發(fā)連接),及其簡單,但Erlang會需要更多的內(nèi)存,廉價(jià)的內(nèi)存可以滿足,只是我的搭建在Vmware中的虛擬機(jī)器已經(jīng)達(dá)到了它所要求的極限。
完整的源代碼,可點(diǎn)擊這里下載。