通过Erlang构建TCP服务器
文章來源:公眾號-智能化IT系統。
走進Erlang
試想一個場景,在一個炎熱的夏天,一群員工進入了一個會議室準備開會,剛進會議室坐下,大家都滿頭大汗,需要等大家都擦完汗,才好開始會議。
會議室的中間有一盒抽紙,于是按照順序,每一個人在這盒抽紙里抽出一張來擦汗,直到最后一個抽出,擦完,OK,會議開始。
在另外一個會議室,同樣的情形,只是沒有抽紙,在每個人的座位上擺了一張紙巾來擦汗,大家就可以一起擦汗了。
兩個場景比較,自然是第二個能夠早點開始會議。回到軟件開發,第一個場景中,那盒抽紙就像我們的共享內存,是共享的就要加鎖,如果不加鎖,大家都來搶,就等著崩潰吧。加了鎖,要按順序一個個的來,自然效率就低了。最好的方式是從根源上解決,就是完全避開共享內存這個概念,改變思路去完成同樣的目標。于是乎有了第二種方式。在這里每一個人相當于一個獨立的進程,各自擦汗,如果紙巾不夠,就問別的進程請求交互紙巾。
第二個場景是快,但是每個人的桌面都放一張紙巾,這也是工作,需要時間啊。不用擔心,在有的技術中,已經為你做好了,你要做的只是轉變思路,用另一種方式解決問題。這里和大家分享的Erlang,一種面向消息的編程語言,就能很好的解決這個問題。
小編知道,第二種場景說起來容易,但是我們很多時候業務復雜度決定了無法這樣解決。小編不是說完全拋棄誰,使用誰,我們可以做的是結合一些特性去思考,可以獨立出一些模塊,一些適用于場景二的模塊,以提升效率。現實中的大型系統往往不是獨立用一種技術完成,而是劃分好模塊,多種技術共用。
案例介紹
本文介紹的案例是TCP網絡服務器的構建,用最原始的方式(非OTP)。其功能很簡單,通過網絡TCP接口接收數據,按照指定的格式解析,并把數據存儲至MongoDB。接口消息的格式有明確規定,每條消息之間用“|”分割,用“#”標識一群消息的結束,消息中的每個字段格式為“字段名:字段值”,每個字段之間以空格分割。
該案例原先是用C#開發,后期隨著數據的增長,一天到了數十億,一連串的問題接連而來。頻繁崩潰,性能跟不上等等。當時單機處理的上限是一天6億。
后來小編在休閑時間用erlang語言實現了同樣的功能,最后測試的性能和穩定性有了不錯的體現,因為當時時間有限,沒有把他優化的很好,但是小編覺得是一個很有意思的使用,所以在此分享。
為什么考慮Erlang呢?(有Erlang經驗者請直接跳過該部分)
當前早就進入了多核時代,一臺服務器的CPU不可能只有一個,系統會否合理的利用是一個問題,大部分系統無法完全的利用,甚至只用了一個CPU,無疑這是對資源的浪費。而Erlang可以讓程序員輕松的解決。
Erlang在國內尚未大范圍的普及,大型的公司和系統能見到的非常少。但是價值不容小看。目前很流行的rabbitMQ ,就是用Erlang開發的。Erlang的特點可以用兩個字形容之:土,快。
土:Erlang語言非常難以上手,甚至被評價為對程序員的眼睛最有殺傷力的語言,一開始編寫會非常頭疼。而且,其數據抽象能力實在不敢恭維,復雜的邏輯使用之會非常難受。
快:即其性能的優異表現了,使用Erlang可以充分的利用上多核資源,效率非常的快。那么為什么其有這種功力呢,有以下幾點特征,供參考:
如果您是一個java或C++等常用語言的開發者,您可以認為Erlang沒有變量,所有的變量只能賦值一次。
Erlang沒有線程的概念,Erlang由輕量級的進程組成。以充分利用多核CPU。
Erlang沒有共享內存的概念,所有的進程通過消息來傳遞信息。這一點就是和文章開端的描述是對應的。
Erlang進程之間傳遞數據非常快
綜合以上的特征,需要高并發,高穩定性,并且中間邏輯簡單的系統,Erlang是非常不錯的選擇。
案例實現
該案例實現的關鍵點將在這里分享,包括關鍵的源碼。
1. 建立TCP socket,并監聽
{ok, Listen} = gen_tcp:listen(28801, [list, {packet, 0}, {reuseaddr, true}, {active, once}]),
這里注意消息接收模式是active once,即半阻塞模式。接收模式的不同對系統影響很大,具體使用需要謹慎。
2. 建立MongoDB的連接,并保存
MongoDB需要下載公共包,mongodb-erlang-refactor,然后編譯,小編閱讀了其中的代碼,部分的MongoDB需要略做修改。
--建立連接
mongo_id_server:init([])
--存儲數據
savemongo(List,Con,Cat) ->
?try
?[mongo:do(unsafe, master, Con, 'SOC_ToBeBuffered', fun() ->
mongo:insert(list_to_atom(Cat), Val)
end) || Val <- List]
?catch
_:_ ->? io:format("Error~p~n",[calendar:local_time()]),
exit("stop")
end.
3.在接收到數據后,開啟進程處理
Erlang創建進程使用Spawn,Pset = spawn(fun tcpReceiverMulBloPool:dataprocess1/0),具體處理邏輯見下。
該案例中沒有用Erlang OTP來開發,所以算是最原始的代碼,現在把主要的邏輯實現的代碼分享如下,因為時間急迫,注釋沒有加,感興趣的可以通過掃描文章下方的二維碼關注公眾號:智能化IT系統,進一步的溝通了解。最終,在筆記本電腦上測試,每天處理的數據量可以達到10億,筆記本的配置是4核,主頻2.5,內存4G,大家可以按照案例的業務場景自己來測試。
公眾號-智能化IT系統。每周都有技術文章推送,包括原創技術干貨,以及技術工作的心得分享。掃描下方關注。
總結
以上是生活随笔為你收集整理的通过Erlang构建TCP服务器的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【案例分享】crontab执行脚本异常问
- 下一篇: 利用Python爬取糗事百科段子信息