夢幻之旅

          DEBUG - 天道酬勤

             :: 首頁 :: 新隨筆 :: 聯系 :: 聚合  :: 管理 ::
            671 隨筆 :: 6 文章 :: 256 評論 :: 0 Trackbacks
          最近做的一個項目用到了開源的C/S應用的服務器框架MINA,當初做的時候資料非常少,只能自己不停的測試,總結出了一些規律經驗。

          從網上看的資料上看,這個服務器框架還是比較穩定和支持的并發數還是很不錯的,不過沒有準確的數據,而且我做完的時候也沒有拿到真正的實際環境中測試過,用的時候也發現了很多優點和缺點,使用者可以自己去根據自己的使用需求去衡量是否使用該框架。

          服務器是商業系統很重要的一部分,主要負責數據采集,文件分發,與端機的通信,和自動作業等任務,服務器大多是24小時運行的,因此服務器的實現必須強壯、穩定、安全,而速度雖然也是很重要,不過最重要的還是前三者。服務器框架MINA就是要為這樣的服務器提供了一個網絡應用框架,當然這樣的服務器框架也可以自己去實現。MINA為我們封裝了socket的底層通信實現,提供了日志,線程池等功能,使用起來非常簡單、方便。

          MINA是一個異步框架,是通過網絡事件激發的,它包含兩層:IO層和協議層。首先介紹IO層,要說明的是我用的版本是0.
          8.2,可能不同版本會稍有不同。

          Client產生一個底層IO事件,比如說連接和發送數據包,IoAcceptor執行所有底層IO,將他們翻譯成抽象的IO事件,接著這里可以添加(也可以部添加)一個IoFilters對IO事件進行過濾,并把翻譯過的事件或過濾過的事件和關聯的IoSession 發送給IoHandler。IoSession是一個有效的網絡連接會話,此會話將一直保持連接,除非網絡斷開或用戶主動斷開連接(session.close()),用戶可以通過IoSession獲得有關該會話連接的信息和配置會話的對象和屬性;IoHandler是網絡事件的監聽器,也就是說當有網絡事件發生時會通知IoHandler,用戶不用去主動接受數據。用戶只要實現此接口愛干嗎干嗎去吧。IoFilter:Io過濾器,對Io事件進行過濾,比如添加日志過濾器和線程池過濾器。

          使用說明:

          import java.util.logging.Level;

          import org.apache.mina.common.ByteBuffer;
          import org.apache.mina.common.IdleStatus;
          import org.apache.mina.common.SessionConfig;
          import org.apache.mina.io.IoHandlerAdapter;
          import org.apache.mina.io.IoSession;
          import org.apache.mina.io.socket.SocketSessionConfig;
          import org.apache.mina.util.SessionLog;

          public class ServerHandler extends IoHandlerAdapter {

            
          public ServerHandler() {

           }


           
          public void dataRead(IoSession session, ByteBuffer buffer) throws Exception {

          //當有數據讀入時此方法被調用,數據封裝在ByteBuffer中,可以用以下方法對出buffer的數據,ByteBuffer的數據讀出后內存中就沒有了。
          //String message = "";
          //  byte[] bytes = new byte[rb.remaining()];
          //  int j = 0;                                                                   //  while (rb.hasRemaining()) {
           
          //  bytes[j++] = rb.get();
           
          // }
           
          //  message = new String(bytes); 

          //接著可以進行邏輯處理

          }


           
          public void dataWritten(IoSession session, Object mark) throws Exception {

          //當數據被寫入通道時此方法被調用,實際就是調用了session.write(IoSession,Object)方法
            SessionLog.log(Level.INFO,session,mark.toString());//必要時打印所寫入的內容,mark的內容就是session.write(session,mark)中的第二個參數
           }


           
          public void exceptionCaught(IoSession session, Throwable arg1)
             
          throws Exception {

          //當出現異常時此方法被調用,從而進行各種異常處理,該方法可以捕捉網絡異常(如連接非正常關閉)和所有其他方法產生的異常,這里要注意如果客戶端要保持與服務器端的連接時不要在這里馬上重新連接不然會拋出CancelKeyException運行期異常直接導致程序死掉(特別是與服務器端有超過兩個連接時一定會發生并且此異常無法捕獲),建議的方法是啟動一個單獨的線程來完成與服務器端的重新連接,還有要注意的是如果網絡是正常關閉的,比如說是客戶端正常關閉連接,而此時服務器端是不愿意關閉的話,這個異常本方法是捕捉不了的,因此只能在session.close()方法中處理這種情況。
            session.close();
            
           }


           
          public void sessionClosed(IoSession session) throws Exception {

          //當網絡連接被關閉是此方法被調用
            SessionLog.log(Level.INFO,session,"Close a Session");//必要時打印出信息
           }


           
          public void sessionCreated(IoSession session) throws Exception {

          //當網絡連接被創建時此方法被調用(這個肯定在sessionOpened(IoSession session)方法之前被調用),這里可以對Socket設置一些網絡參數
            SessionConfig cfg = session.getConfig();
            
          if (cfg instanceof SocketSessionConfig) {
             ((SocketSessionConfig) cfg).setSessionReceiveBufferSize(
          2048);
             ((SocketSessionConfig) cfg).setKeepAlive(
          true);
             ((SocketSessionConfig) cfg).setSoLinger(
          true0);
             ((SocketSessionConfig) cfg).setTcpNoDelay(
          true);
             ((SocketSessionConfig) cfg).setWriteTimeout(
          1000 * 5);
            }

           }


           
          public void sessionIdle(IoSession arg0, IdleStatus arg1) throws Exception {
            
          // 當網絡通道空閑時此方法被調用,在這里可以判斷是讀空閑、寫空閑還是兩個都空閑,以便做出正確的處理

          一半的網絡通訊程序都要與服務器端保持長連接,所以這里可以發一下網絡測試數據以保持與服務器端的連接
           }


           
          public void sessionOpened(IoSession session) throws Exception {

          //當網絡連接被打開時此方法被調用,這里可以對session設置一些參數或者添加一些IoFilter的實現,也可以對客戶端做一些認證之類的工作
            session.getConfig().setIdleTime(IdleStatus.BOTH_IDLE, 60);
           }


          }


          //啟動監聽連接的服務器

          import org.apache.mina.common.*;
          import org.apache.mina.io.*;
          import org.apache.mina.io.filter.*;
          import org.apache.mina.registry.*;

          public class Server{
              
          /** Choose your favorite port number. */
              
          private static final int PORT = 8080;
              
              
          public static void main( String[] args ) throws Exception
              
          {
                  ServiceRegistry registry 
          = new SimpleServiceRegistry();

          //可以添加各種過濾器,比如線程池過濾器,增加一個線程池處理來自不同的連接

             IoAcceptor ioAcceptor 
          = registry.getIoAcceptor();
             IoThreadPoolFilter ioThreadPoolFilter 
          = new IoThreadPoolFilter();
             ioThreadPoolFilter.setMaximumPoolSize(
          10);
             ioThreadPoolFilter.start();
             ioAcceptor.getFilterChain().addLast(
          "IoThreadPool",
               ioThreadPoolFilter);
                 
          // Bind
                  Service service = new Service( "serviceName",
          TransportType.SOCKET, PORT );
                  registry.bind( service, 
          new ServerHandler() );

                  System.out.println( 
          "Listening on port " + PORT );
              }

          }


          //如果是連接服務器的可以如下啟動連接請求

          import org.apache.mina.io.filter.IoThreadPoolFilter;
          import org.apache.mina.io.socket.SocketConnector;
          import java.net.InetSocketAddress;

          public class Client{
                
          public static void main( String[] args ) throws Exception
              
          {
              
          private static final int CONNECT_TIMEOUT = 3//設置超時連接時間

          //可以添加各種過濾器,比如線程池過濾器,增加一個線程池處理來自不同的連接

          IoThreadPoolFilter ioThreadPoolFilter 
          = new IoThreadPoolFilter();
             ioThreadPoolFilter.setMaximumPoolSize(
          10);
             ioThreadPoolFilter.start();
             SocketConnector connector 
          = new SocketConnector();

             connector.getFilterChain().addFirst(
          "threadPool",
               ioThreadPoolFilter);

          //初始化客戶端的監聽處理器
             ClientHandler clientHandler = new ClientHandler();  

            InetSocketAddress address 
          = new InetSocketAddress("serverIp",serverPort);

            
          try {

          connector.connect(address, CONNECT_TIMEOUT,
                  clientHandler);      

          System.out.println(
          "connect sucessfully!");

             }
           catch(Exception e){

              System.err.println(
          "Failed to connect.");

          }
             
          }


          如果一個協議非常復雜,如果只用一個Io層是非常復雜的,因為IO層沒有幫助你分離‘message解析’和‘實際的業務邏輯,MINA提供了一個協議層來解決這個問題。

           

          使用協議層必須實現5個接口:ProtocolHandler, ProtocolProvider, ProtocolCodecFactory, ProtocolEncoder, 和 ProtocolDecoder:

          第一步:實現ProtocolDecoder和ProtocolEncoder,當有IO事件時便先調用這兩個類的方法

          import org.apache.mina.common.ByteBuffer;
          import org.apache.mina.protocol.ProtocolDecoderOutput;
          import org.apache.mina.protocol.ProtocolSession;
          import org.apache.mina.protocol.ProtocolViolationException;
          import org.apache.mina.protocol.codec.MessageDecoder;
          import org.apache.mina.protocol.codec.MessageDecoderResult;
          import java.util.*;

          public class ServerDecoder implements MessageDecoder {

           
          public ServerTranInfoDecoder() {

           }



           
          public MessageDecoderResult decodable(ProtocolSession session, ByteBuffer in) {

          //對接受的數據判斷是否與協議相同,如果相同返回MessageDecoderResult.OK,否則返回MessageDecoderResult.NOT_OK,這里如果要從ByteBuffer讀出數據,需要重新用ByteBuffer.put(ByteBuffer)放回內存中,以便decode方法使用;
            return MessageDecoderResult.OK;
            
          return MessageDecoderResult.NOT_OK;
           }


            
          public MessageDecoderResult decode(ProtocolSession session, ByteBuffer in,
             ProtocolDecoderOutput out) 
          throws ProtocolViolationException {

          //根據協議將介紹到的數據(放在ByteBuffer中)組裝成相對應的實體,調用out.write(Object)方法發送給協議層進行業務邏輯的處理,如果成功返回MessageDecoderResult.OK,否則返回MessageDecoderResult.NOT_OK;

          out.write(object);

            }


          import org.apache.mina.common.ByteBuffer;
          import org.apache.mina.protocol.ProtocolEncoderOutput;
          import org.apache.mina.protocol.ProtocolSession;
          import org.apache.mina.protocol.ProtocolViolationException;
          import org.apache.mina.protocol.codec.MessageEncoder;
          import java.util.*;

          public class ServerEncoder implements MessageEncoder{
           
          public static Set TYPES;
           
              
          public ServerEncoder(){
               
              }

             
              
          public Set getMessageTypes(){
               HashSet set 
          = new HashSet();
               set.add(Send.
          class);  //增加要進行編碼的實體類
               TYPES = Collections.unmodifiableSet( set );
                  
          return TYPES; 
              }

           

          public void encode( ProtocolSession session, Object message, ProtocolEncoderOutput out )
                                                              
          throws ProtocolViolationException {
              
          //將回應報文實體message編碼層returnStr后發送到客戶端  

            
          byte[] bytes = returnStr.getBytes();
            ByteBuffer rb 
          = ByteBuffer.allocate(bytes.length);
            rb.put(bytes);
            rb.flip();
            out.write(rb);
             }

          }


          第二步:實現ProtocolCodecFactory

          import org.apache.mina.protocol.codec.DemuxingProtocolCodecFactory;

          public class ServerProtocolCodecFactory extends DemuxingProtocolCodecFactory {
           
          public ServerProtocolCodecFactory(boolean server) {
            
          if (server) {
             
          super.register(ServerDecoder.class);
             
          super.register(ServerEncoder.class);
            }

           }

          }


          第三步:實現ProtocolHandler,在有IO事件發生后,經過decode和encode的處理后就把協議實體交個這個處理器進行業務邏輯的處理,因此實現了協議解釋和業務邏輯的分離,它與IoHandler非常相似,不過這里處理的是經過編碼與解碼后的對象實體。

          import org.apache.mina.common.IdleStatus;
          import org.apache.mina.protocol.ProtocolSession;
          import org.apache.mina.util.SessionLog;
          import org.apache.mina.protocol.handler.DemuxingProtocolHandler;

          public class ServerSessionHandler extends DemuxingProtocolHandler {
           
          public ServerSessionHandler() {
           }


           
          public void sessionCreated(ProtocolSession session) throws Exception {
           }


           
          public void sessionOpened(ProtocolSession session) throws Exception {
              session.getConfig().setIdleTime(IdleStatus.BOTH_IDLE, 
          60);
            }


           
          public void sessionClosed(ProtocolSession session) {
            }


            
          public void messageReceived(ProtocolSession session, Object message)
             
          throws Exception {

          //根據解碼后的message,進行業務邏輯的處理
          session.close();
             }


           
          public void messageSent(ProtocolSession session, Object message) {
             }


            
          public void sessionIdle(ProtocolSession session, IdleStatus status)
             
          throws Exception {

          //網絡出現空閑時進行處理,并關掉連接
              session.close();
            }


           
          public void exceptionCaught(ProtocolSession session, Throwable cause) {
            cause.printStackTrace();

          //處理所有Handler方法拋出的異常,和Mina架構拋出的異常,并關掉連接
              session.close();
           }

          }


          第四步:實現ProtocolProvider

          import org.apache.mina.protocol.*;

          public class ServerProtocolProvider implements ProtocolProvider {
           
          private static final ProtocolCodecFactory CODEC_FACTORY = new SemsProtocolCodecFactory(
             
          true);

           
          private static final ProtocolHandler HANDLER = new ServerSessionHandler();

           
          public ServerProtocolProvider() {

           }


           
          public ProtocolCodecFactory getCodecFactory() {
            
          return CODEC_FACTORY;
           }


           
          public ProtocolHandler getHandler() {
            
          return HANDLER;
           }

          }


          這樣協議層便完成了,啟動時跟IO層的差不多,不過我們還可以在IO層和協議層用兩個線程池,如下:

          public class Server {
           
          //服務器的監聽端口號
           public static final int SERVER_PORT = 8000;

           
          public static void main(String[] args) {
              
              
          //進行服務器的相關配置
             ServiceRegistry registry = new SimpleServiceRegistry();
             IoProtocolAcceptor protocolAcceptor 
          = (IoProtocolAcceptor) registry
               .getProtocolAcceptor(TransportType.SOCKET);
             ProtocolThreadPoolFilter protocolThreadPoolFilter 
          = new ProtocolThreadPoolFilter();
             protocolThreadPoolFilter.setMaximumPoolSize(
          10);
             protocolThreadPoolFilter.start();
             protocolAcceptor.getFilterChain().addLast(
          "IoProtocolThreadPool",
               protocolThreadPoolFilter);

             IoAcceptor ioAcceptor 
          = protocolAcceptor.getIoAcceptor();
             IoThreadPoolFilter ioThreadPoolFilter 
          = new IoThreadPoolFilter();
             ioThreadPoolFilter.setMaximumPoolSize(
          10);
             ioThreadPoolFilter.start();
             ioAcceptor.getFilterChain().addLast(
          "IoThreadPool",
               ioThreadPoolFilter);

             Service service 
          = new Service("TranServer", TransportType.SOCKET,
               SERVER_PORT);

             
          //綁定了剛剛實現的ServerProtocolProvider
             registry.bind(service, new ServerProtocolProvider());

             }


          整個MINA框架經常用到的就是這些了,這樣的事件觸發框架和兩層框架使用起來非常方便,不過這種異步框架還是有些非常明顯的缺陷:

          第一,MINA只會為每個Session分配一個線程,也就是只能一個一個事件按順序執行,就算你在某個方法執行時產生了新的事件,比如收到新的數據,MINA也會先將該事件緩沖起來,所以你在執行某個方法時是不可能執行dataRead方法的,所以MINA框架是不會阻塞的,要想在一個邏輯方法中實現交互是實現不了的,因此要想出另外的實現方法。

          第二,如果客戶端發完一個數據給服務器就想馬上得到回復,而不等整個業務邏輯執行完,也是實現不到的,因為MINA框架要將整個接收事件處理完了,再把回復信息發給客戶端。

          第三,如果MINA是作為服務器端等待連接的,當客戶端正常關閉后業務邏輯也可繼續正常執行,但是如果MINA是連接服務器的客戶端,則當服務器關閉后,MINA的session也會關閉。

          最后要說明的是MINA使用的線程池是用Leader
          /Followers Tread Pool實現的,默認最大支持2G的線程。當然MINA框架是開源的,用戶可以根據自己的需要改寫代碼,而其MINA的功能也是不斷可以擴展的。

          以上是我使用MINA的經驗總結,其實MINA的相關文檔和例子也介紹了很多了,我這里算是一個總結吧,不過有很多地方只是我的個人見解,不一定正確,如果有不對的,希望高手可以提出。
          http://riddickbryant.iteye.com/blog/564415
          posted on 2011-09-05 00:40 HUIKK 閱讀(4621) 評論(0)  編輯  收藏 所屬分類: java Net
          主站蜘蛛池模板: 绥阳县| 龙江县| 铅山县| 安阳市| 华阴市| 苏州市| 岚皋县| 探索| 如皋市| 太保市| 莱西市| 格尔木市| 通山县| 翁源县| 安宁市| 肇庆市| 丹江口市| 淳化县| 郸城县| 金沙县| 旺苍县| 偃师市| 监利县| 梅河口市| 仁布县| 怀化市| 曲沃县| 南郑县| 通许县| 伊通| 阳春市| 卢龙县| 汉阴县| 衢州市| 资中县| 阿克陶县| 盐城市| 禹州市| 东至县| 铜山县| 保德县|