大並發量 socket 通訊的解決方案

來源:互聯網
上載者:User

大並發量socket通訊的解決方案

                       --MINA架構的使用心得及相關要點

目錄

前言... 1

What is MINA.. 2

使用案例:... 2

Apache直屬MINA的子項目:... 3

同類架構:... 3

MINA快速入門... 3

預備知識:... 3

資源下載:... 4

Hello world的關鍵關鍵代碼:... 4

MINA深度瞭解... 5

Mina的應用程式層... 5

MINA的內部流程... 5

選擇MINA的理由... 7

傳統socket編程... 7

改進的Socket編程... 7

使用MINA架構的編程... 8

擴充知識... 9

IoBuffer介面... 9

sendUrgentData()方法的使用... 10

Concurrent包下的一些類... 12

結尾語... 12

前言

筆者之前的工作主要是做java的web端開發,後因工作原因參與了一個國家級的大項目,主要負責其中底層通訊的前置機模組。幾經波折,將該系統完成後,結果在第一輪的測試中就慘敗退回。其根本原因就在於原設計文檔的要求單“通訊機”與“終端”(注一)之間的並發量要達到2W以上的串連通訊,而實際運行並發量只能達到2600個相差了近十倍左右。經過代碼調優、擴充JVM記憶體等等手段,但因基礎資料相差過大,所取得的最佳化效果十分有限。後考慮在根本著手,只有更改整個系統的通訊介面,才有可能達到設計文檔上的要求。某天在某個技術QQ群裡一次討論中,有網友向我推薦了一個架構,這就是本文要介紹的主角-MINA。

注一:前置機分成了三個部分,其設計的結構圖如下所示:

What is MINA

Apache MINA(Multipurpose Infrastructure for Network Applications) 是 Apache 組織一個較新的項目,它為開發高效能和高可用性的網路應用程式提供了非常便利的架構。當前發行的 MINA 版本支援基於 Java NIO 技術的 TCP/UDP 應用程式開發、串口通訊程式(只在最新的預覽版中提供),MINA 所支援的功能也在進一步的擴充中 使用案例:

目前正在使用 MINA 的軟體包括有:Apache Directory Project、AMQP(Advanced Message Queuing Protocol)、RED5 Server(Macromedia Flash Media RTMP)、ObjectRADIUS、Openfire 等等。 Apache直屬MINA的子項目:

FTPServer,AsyncWeb,SSHD

其實在有人推薦了MINA之後,本人就上網google了一把,搜尋一番下來之後才發現自己的眼界過於狹隘了,原來有相同功能的開源架構還真不少,看來以後得繼續多泡論壇和QQ了(有正當理由上班泡罈子,聊QQ了,偷笑一個^_^。。。。。。) 同類架構:

Netty2:具有很好的構架,並且該架構非常有名,使用該架構的項目不少,這意味著Team Dev持續更新的動力非常大。同時Netty2的文檔非常齊全,並且支援JMX。Netty2的缺點就是實現代碼的品質不是非常好,最大的缺點就是只支援普通的TCP。

Cindy:起源於Netty2之後,借鑒了Netty2中MessageRecognizer類的設計,在當前的版本中已經全面支援普通TCP/Secure TCP/UDP單播/UDP多播/Pipe,可以使用同一個模型進行同步/非同步IO處理。Cindy目前缺點是文檔相對較少以及應用的項目比較少。

Grizzl:的設計與一般的nio架構相比是比較不同的,主要不同點在於讀和寫都是採用blocking方式,並且使用臨時selector;執行緒模式高度可配置。效能據說比MINA還高,但是學習曲線很高。QuickServer: http://www.quickserver.org/

Xscocket:是一個輕量級的解決方案,核心思想是屏蔽,簡化nio方式的的開發,並不需要過多的學習。

對於這些架構的基本使用和基礎架構,本人都經過了一番研究,發現這些架構要麼重者過重,要麼輕者過輕。鑒於項目的規模及時間的緊迫,再三比較之下,本人還是選擇了學習曲線低,效能優異的MINA. 至於這些優點是如何體現出來的,本文以下內容將繼續為您解讀。 MINA快速入門

閑話少說,借用大師級的寫書經驗,咱們先來一個hello world。當然在這前我們還是得先做一些準備的。 預備知識:

JAVA NIO

多線程

Socket

以上知識對本文的閱讀理解有一定協助,但並非一定必需。

資源下載:

MINA2.0:暫時分為1.x和2.x兩個主要版本,本文只涉及2.X的版本,至於為什麼只講2.X而不講1.X,比較冠冕堂皇的回答是----因為有一位偉人曾經說過:要以發展的眼光看世界。。。。。。而真實原因嘛。。。。。。咳咳。。。。大家都知道的。。我就不便多言了。下載地址:http://mina.apache.org/downloads.html。項目中使用的是2.03,截止本文發稿為止,最新版本為:2.04

log4j:因為其中缺少log4j的包,所以做實驗的朋友還需要去下一個log4j的包。

開發工具:eclipse

Jdk:1.6x

監視測試載入器:Oracle JRockit Mission Control 4.0.1 強烈推薦,簡稱JRMC,開發過程中,用它解決了很多效能瓶頸的問題,具體使用方法,因為篇幅所限在此不做詳述,請自行查詢相關文檔。 Hello world的關鍵關鍵代碼:

Server端的Main函數:

   

IoAcceptor acceptor = new NioSocketAcceptor();// 建立監控器

//acceptor.getSessionConfig().setReadBufferSize(2048);

//acceptor.getSessionConfig().setIdleTime(IdleStatus.BOTH_IDLE, 10);

acceptor.getFilterChain().addLast("codec",

     New ProtocolCodecFilter(new TextLineCodecFactory(Charset.forName("UTF-8"),

LineDelimiter.WINDOWS.getValue(), LineDelimiter.WINDOWS.getValue())));//載入解/編碼工廠

acceptor.setHandler(newStandBaowenHandler()); //設定處理類,其主要內容見下。

acceptor.bind(new InetSocketAddress(9998));//綁定的監聽連接埠,可多次綁定,也可同時綁定多個。

StandBaowenHandler的關鍵代碼

public void messageReceived(IoSession session, Object message) throws Exception {           session.getService().getManagedSessions();

              String baowenSrc = message.toString();// 原始報文

              System.out.println(baowenSrc);

       }

鑒於篇幅關係,類沒有寫全,更具體的內容,請大家參考mina壓縮包裡內建的demo。但實際上服務端需要手寫的部分確實只有以上二塊內容,服務端就算是寫好了,由次可以看出mina的入門之快。現在我們來測試。開啟cmd(這個對於同仁們來說,就不用詳述了吧。。。- -。。),輸入telnet 127.0.0.1 9998(這裡的9998要與上面代碼綁定的連接埠一致)隨便輸入一些字元,敲下斷行符號後就會在控制台顯示您所輸入的資訊了。

做得這一步的童鞋可以說對MINA的使用可以說已經初步入門了,是不是覺得太簡單了。這也太假了吧,這就算入門了。呵呵。。。個人認為這就是MINA的強悍之一,簡單的幾行代碼就搞定了一個服務端,要入門簡直是太簡單了。但任何事物都有其二面性,真要把一個東西學好,用好,還要不出錯,我們需要瞭解的東西就太多太多了。下面我們就從MINA架構的底層說起,因為在實際應用當中,要解決一些碰到的痛點及問題,對架構的整個運作體系必須要有個全面深入的瞭解。只有這樣才能在碰到問題時,有目的,有針對性的去找到癥結所在。 MINA深度瞭解 Mina的應用程式層

                                     圖1

一個設計成熟的開源架構,總是會僅可能的減少侵入性,並在整個項目中找到合適的位置,而不應對整個項目的構架設計產生過多的影響,圖1就是MINA的應用程式層示意圖。從圖中和上節的DEMO中我們可以看到,MINA很好的把業務代碼和底層的通訊隔離了開來,我們要做的僅僅是建立好監聽,然後寫上我們需要實現的商務邏輯就OK了。

MINA的內部流程

                                     圖2

(1) IoService:這個介面在一個線程上負責通訊端的建立,擁有自己的Selector,監聽是否有串連被建立。

(2) IoProcessor:這個介面在另一個線程上負責檢查是否有資料在通道上讀寫,也就是說它也擁有自己的Selector,這是與我們使用JAVA NIO 編碼時的一個不同之處,

通常在JAVA NIO 編碼中,我們都是使用一個Selector,也就是不區分IoService與IoProcessor 兩個功能介面。另外,IoProcessor也是MINA架構的核心組件之一.在MINA架構啟動時,會用一個線程池來專門產生線程,來負責調用註冊在IoService 上的過濾器,並在過濾器鏈之後調用IoHandler。在預設情況IoProcessor 會用N+1個線程來輪流詢問監視的連接埠是否有資料傳送,其中n為cpu的核心個數。按一般的多線程設計概念來說,IoProcessor的線程數是越多越好,但實際上並非如此,因為大家都知道,IO的操作是非常佔用資源的,所以項目中的IoProcessor的線程數應該根據實際需要來定,而這個數字可以在產生IoAcceptor對象時進行設定。Eg IoAcceptor acceptor = newNioSocketAcceptor(N);

(3.) IoFilter:這個介面定義一組攔截器,這些攔截器可以包括日誌輸出、黑名單過濾(參見之前程式碼範例的注釋部份),甚至是在過濾器鏈中利用AOP寫上許可權控制(筆者負責的部分沒有涉及到這許可權部分,但根據筆者對架構的理解要實現這一點應該問題不大,有需要的可以自行實驗)。資料的編碼(write 方向)與解碼(read 方向)等功能,其中資料的encode 與decode是最為重要的、也是您在使用Mina 時最主要關注的地方(筆者曾經為了decode的解碼編寫,重寫了不下十幾種實現方式才找到準確無誤適合項目的解碼方法)。

(4.) IoHandler:這個介面負責編寫商務邏輯,也就是接收、發送資料的地方。只本文的代碼執行個體中,可以看到真正的業務代碼只有一句:System.out.println(str);真實項目中當然不可能如此簡單,但如果大家把業務處理寫好,並寫好業務介面,真正要用時,呆需要在此處替換即可,再次見證了MINA分層的徹底。

說了這麼多,以上內容也只能讓大家對MINA有個基礎的瞭解,對於MINA架構優勢的認識可能還不是很多,那麼下面的內容,就此展開對比討論,以便大家對MINA的適用情境及優點有個更全面的瞭解。 選擇MINA的理由 傳統socket編程

在傳統I/O中,最簡單實現高並發伺服器的編程方式就是對每一個客戶開啟一個線程。但是這種方式有如下幾個弊端:

用戶端上限很大的情況下不能及時響應

伺服器硬體資源受限,效能也會急劇下降

受制於作業系統的限制

但這種設計方式優點還是有的:

編碼簡單,實現容易

一定數量的串連效能比較好。

筆者的項目開發中,最開始就是採用的這種方式,寫起來方便,控制起來也方便,但遺憾的是JVM最多隻能開到2K多的線程,就會報- can not create new thread的錯誤。

(實現結構圖見下:)

圖3(一對一的結構圖。) 改進的Socket編程

為瞭解決每一個線程一個串連的模型,筆者最開始想到用多個線程處理N個使用者,這樣既可以保證處理多個使用者的同時,線程開銷降到系統的臨界點。

這樣的方式與前一個模型優勢在於同樣的多線程,但線程數固定,充分運用系統的優勢效能,又不存在多餘的開銷。但是缺點也是顯而易見的:

輪詢的消耗不可避免。

一但產生io阻塞,其阻塞的時間純屬浪費。

客戶數量固定的時候沒有前一模型響應快

編碼更加複雜。

圖4(一對多) 使用MINA架構的編程

為瞭解決上述的矛盾,最終的解決方案只能是非同步NIO,而隨著筆者對JAVA  NIO的研究發現,要實現非同步NIO,並應用到實際項目中,必須對NIO有著比較深刻的瞭解和把握,筆者曾嘗試著利用JAVA 原生 NIO介面寫了一個DEMO(具體的使用方法,感興趣的童鞋可以GOOGLE一把,你會發現用原生NIO寫程式與使用MINA寫程式對比起來是多麼的痛苦。。。。 -_-!!),但由於筆者在這方面的底子過薄,實驗結果不如人意,但要對NIO進行更為深入的學習,時間上面也不允許。直到MINA架構的映入眼帘,以上難題不再是問題。。。。以下是利用MINA的實現方式的一個簡圖。

                                     圖5

其中IoService介面會專門起一個線程來輪詢是否有新的串連產生,一旦有串連產生則通知IoProcessor,而IoProcessor則起n+1個線程來檢查串連是否有資料在上面讀寫(注二)。一旦有串連產生,並有資料讀寫,則通知decode或ENCODE,進行報文的解碼或編碼,將處理後的報文再交給業務類進行業務處理。其中IoProcessor是處理請求的分配,包括選擇Selector,逾時驗證,狀態記錄等。總之這個類和IoService一起配合工作,封裝了NIO底層的實現以及MINA架構內部的功能的支援.由於過於複雜,篇幅所限所以不作詳細講解.

結合執行個體,並根據以上的圖文講解,我們可以很輕易的總結出利用MINA編程的幾個大致步驟:

建立一個實現了IoService介面的類

設定一個實現了IoFilter介面的過濾器(如果有需要的情況下)

設定一個IoHandler 介面實現的處理類,用於處理事件(必須)

對IoService綁定一個連接埠開始工作

關於MINA的大致運行流程及使用步驟,我們就暫時分析到這,具體更細節的關於一些核心類的使用方法及自訂編碼器的方法,大家可以直接參考mina中所帶的幾個案例,寫得非常詳細,足夠解決大家在項目中碰到的大部分問題,接下來要與大家交流的是使用MINA時非常有可能遇到的一些擴充知識。

注二:這一點請特別注意,因IoProcessor也是相當於輪詢機制,這導致在報文過長時,或其它原因導致報文不能一次傳輸完畢的情況下,必須儲存同一串連(在MINA中是以IoSession類產生的對象)下的上一次狀態,這樣才能截取到一個完成的報文,而這也是Decode(編碼器)需要做的核心工作,新手往往就在這上面要跌跟鬥。

擴充知識

這部分的內容要說起來跟MINA的使用關聯不大,但實際情況是用上了MINA架構的項目基本上多多少少都會涉及到這一塊,那就是多線程的編程。多線程的編程曆來是JAVA編程中的重痛點,很多新手碰到此類編程問題,往往都找不出原因所在,甚至一些有多年編程經驗的程式員也會在這上面偶而犯下錯,在這裡筆者也沒有能力通過很短的篇來解說多線程,那麼就向大家介紹幾個類及一些小常識吧,希望能給大家帶來協助。

 

 

 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.