100萬并發連接服務器筆記之Erlang完成1M并發連接目標
前言
使用Erlang語言也寫一個測試和前面大同小異的測試,在100萬個并發連接用戶情況下,就是想觀察一下極顯情況下的表現。
這個測試使用了優秀的Erlang界的明星框架
測試Erlang服務器
運行在VMWare Workstation 9中,64位Centos 6.4系統,分配14.9G內存左右,雙核4個線程,服務器安裝Erlang/OTP R16B,最新版本支持異步代碼熱加載,很贊。
下載安裝
本系統已經提前安裝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
添加到環境變量中(/etc/profile)
export ERL_HOME=/usr/local/erlang export PATH=$ERL_HOME/bin:$PATH
保存生效
source /etc/profile
測試進程創建
這里拷貝《Erlang程序設計》一書提供的processes.erl源碼,稍作修改。
創建一百萬個進程,看看大概花費多少時間。
[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
恩,創建1000萬個進程,每一個進程花費3.4微秒(μs)的CPU時間,相當于4.3微秒(μs)的消耗時間(亦即消耗的真實時間),在一定量的區間內,其值變化,可以看做是一個常量。
初始化小問題
Cowboy初始化需要事項
我們做的簡單程序,使用了非常受歡迎的cowboy框架,其啟動函數:
cowboy:start_http(my_http_listener, 100,
[{port, 8000}],
[{env, [{dispatch, Dispatch}]}]
),
Cowboy默認支持1024個連接,服務器端輸出:
online user 1122 online user 1123
停滯于此,后續的連接只能排隊等候了。
這里設置支持無限個連接好了。
{max_connections, infinity}
話說,Cowboy為Erlang世界的明星產品,絕對值得一試!
Erlang默認創建進程限制
Erlang在我的機器上默認允許創建的線程也是有限的:
erlang:system_info(process_limit).
262144
才26萬個,不夠用。在啟動腳本(start.sh)處添加允許創建的最大線程支持:
#!/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\")."
腳本啟動后現在在erl shell中測試一下:
erlang:system_info(process_limit).
16777216
數量完全夠用了。
開啟erlang的epoll屬性
+K true | false 是否開啟kernel poll,就是epoll;
不開啟,測試過程中,在內存完好情況下,經常會有連接失敗情況。
使用cowboy_req:compact降低內存占用
一旦你從request對象中獲取到足夠的信息,以后不再獲取其附加屬性時,調用compact/1函數可去除無用屬性,起到節省內存作用。
程序里面調用如下:
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}.
在本例中精測壓縮內存效果不明顯,因為 ,測試端輸出的HTTP頭部壓根就沒有幾個。
Cowboy無法處理沒有header的HTTP請求
這里需要牢記,也不能算是BUG,前面的client2.c源碼,就未曾設置HTTP Header元數據,需要做些簡單修改,修改之后的測試端程序文件名為client5.c, 可以到這里 下載client.c。
Cowboy處理長連接
Cowboy很貼心的提供了cowboy_loop_handler
behaviour。在init/3函數中,可以進入休眠狀態,節省內存,消息到達時,被喚醒,值得一贊!
其定義如下:
注意hibernate和timeout參數,按照實際需求返回即可。
htmlfile_handler示范代碼如下:
100萬并發連接達成
測試過程跌跌撞撞的,雖然這中間因為內存問題拋出若干的異常,但也達到100W連接的數量
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 :))
可以看到狀態信息
算一下:
14987952K = 14636M
14987952/1022338 = 14.7K/Connection
未啟動時的內存情況:
total used free shared buffers cached
Mem: 14806 245 14561 0 12 60
-/+ buffers/cache: 172 14634
Swap: 3999 0 3999
啟動后的內存占用情況:
total used free shared buffers cached
Mem: 14806 435 14370 0 12 60
-/+ buffers/cache: 363 14443
Swap: 3999 0 3999
用戶量達到1022338數量后的內存一覽:
total used free shared buffers cached
Mem: 14806 14641 165 0 1 5
-/+ buffers/cache: 14634 172
Swap: 3999 1068 2931
可以看到,當前內存不夠用了,需要虛擬內存配合了。
查看一下當前進程的內存占用
ps -o rss= -p `pgrep -f 'sname testserver'`
4869520
這樣算起來,系統為每一個進程持有 4869520/1022338 = 4.8K 內存。 這個值只是計算物理內存,實際上連虛擬內存都占用了,估計在4.8K-6.8K之間吧。
和C語言相比,內存占用相當大,我虛擬機器分配的15G內存,也僅僅處理達到100萬的連接,已經接近極限時,會發現陸陸續續的有連接失敗。
不得不說的代碼熱加載
運行時系統的代碼熱加載功能,在這個實例中,通過vi修改了htmlfile_handler.erl文件,主要修改內容如下:
io:format("online user ~p :))~n", [NowCount]),
......
io:format("offline user ~p :(( ~n", [NowCount]).
執行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一個文件,然后通知Erlang的運行環境進行代碼熱替換吧。
(testserver@base)4> code:load_file(htmlfile_handler).
查看日志輸出控制臺,可以看到已經生效,同時也保存著到狀態數據等。
非常利于運行時調試,即不傷害在線狀態數據,又能即時修改,贊!但生產環境下,一般都是版本切換,OTP的版本切換,測試或馬上修改bug時,著實有些復雜。
小結
和C相比,處理相同的事情(100萬并發連接),及其簡單,但Erlang會需要更多的內存,廉價的內存可以滿足,只是我的搭建在Vmware中的虛擬機器已經達到了它所要求的極限。
完整的源代碼,可點擊這里下載。
posted on 2013-04-28 17:17 nieyong 閱讀(16888) 評論(1) 編輯 收藏 所屬分類: C1M