【MINA】用mina做業務服之間的通訊,實現業務負載平衡思路

來源:互聯網
上載者:User

標籤:

學習mina目的還是搭建通訊架構,學完mina我們瞭解了如何?用戶端和服務端,也就是一個正常channel我們是知道怎麼建立的

但是問題是,我們應用環境通訊分為兩種

1.前後端通訊

 

其實這個比較好實現,提供一個mina server端,供前端語言通過socket建串連就行,這個通訊就算是ok了,編解碼等通訊解析的細節這裡不講了

以前的遊戲服務端架構業務多用短串連,聊天用長串連,聊天的部分其實就是上面表述的情況

現在是長串連的天下,聊天依舊是長串連,業務也做成長串連,實現了真正意義上的長串連遊戲架構,這其實就表述了一種當下典型架構,

就是後端提供兩個開放的通訊連接埠【即兩個mina server】,供前端的socket串連,一個負責聊天,登入,註冊,另一個負責其他業務,這樣就實現了協議通訊的負載平衡

2.後端的業務服通訊【這是本文的重點】

 

那麼後端的業務就不需要負載平衡嗎?比如job,非同步更新db,活動副本等

當然也是需要的,怎麼做那,先拿1中的做個解釋

                         mainserevr[聊天,登入,註冊]---nodeserver[其他業務]

這兩個mina sever端已經建立起來了,但是兩個server之間還不能通訊,我們有兩個選擇,要麼在mainserevr上起個mina client去連nodeserver,要麼在nodeserver

上起個mina client去連mainserevr,思路肯定是這樣的,一旦這個通道建立了,其實互為server和client的,會有一個iosession被通道持有,只要有這個iosession,

就可以主動write,當然對於通道的另一端可以response,也可以通過取得iosession來主動寫

實現方式,我們在nodeserevr上提供一個mainserverClient這樣一個spring的bean去串連mainserver,這樣在nodeserver上就可以向mainserevr發訊息了

 

3.帶著這個思路設計一下

 

我把遊戲中的業務分為

     public static final String SERVER_TYPE_NODE_STR = "nodeserver";// game nodepublic static final String SERVER_TYPE_MAIN_STR = "mainserver";// 主serverpublic static final String SERVER_TYPE_JOB_STR = "jobserver";// job serverpublic static final String SERVER_TYPE_ASYNCDB_STR = "asyncdbserver";// 非同步DBpublic static final String SERVER_TYPE_ACTIVE_STR = "activityserver";// 活動public static final String SERVER_TYPE_OTHER_STR = "other";// 其他public static final String SERVER_TYPE_GM_STR = "GM";//管理端

 

每次啟動一種server時,首先啟動一次mina serevr,然後啟動多個mina client去串連其他的mina server,

比如啟動nodeserevr 服務端,然後啟動多個client分別串連mainserevr,jobserevr等的服務端,這樣我就可以

在nodeserver上給其他業務serevr發請求了,具體啟動哪些client看需要

 

搞一個啟動server類型的方法

public static ClassPathXmlApplicationContext start(String serverTypeStr) {try {
//關閉串連池的鉤子線程ProxoolFacade.disableShutdownHook();
//spring 的核心設定檔String xmlFile = "applicationContext.xml";....log.info("啟動 {} server................", serverTypeName);// 設定到系統內容變數System.setProperty(NodeSessionMgr.SERVER_TYPE_KEY, serverType + "");System.setProperty(NodeSessionMgr.SERVER_TYPE_NAME_KEY,serverTypeName);// final ClassPathXmlApplicationContext parent = new// ClassPathXmlApplicationContext(// xmlFile);String fileName = null;
              //這是把spring的住設定檔拆分了一部分內容出來,目前是只載入本server需要的beanif (serverType == NodeSessionMgr.SERVER_TYPE_NODE) {fileName = "wolf/app_nodeserver.xml";} else {fileName = "wolf/app_server.xml";}//手動啟動springfinal ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(new String[] { xmlFile, fileName });if (context != null) {ServiceLocator.getInstance().setApplicationContext(context);}// 啟動socket serverfinal WolfServer server = (WolfServer) ServiceLocator.getSpringBean("wolf_server");server.setServerType(serverType);
//這個調用就是我們熟悉的啟動mina server端server.start();//這個動用做兩件事,選區需要的serevr類型建立mina client串連startClient(server);
//鉤子線程用來監聽應用停止,為了做停止時的後續處理Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {public void run() {_shutdown();}}, "shutdownHookThread"));
              //為了支援web,springMVC,內建一個web serverif (NodeSessionMgr.SERVER_TYPE_MAIN_STR.equalsIgnoreCase(serverTypeStr)) {JettyServer jettyServer = (JettyServer) ServiceLocator.getSpringBean("jettyServer");jettyServer.start();}log.info("start {} end................", serverTypeName);return context;} catch (Exception e) {e.printStackTrace();shutdown();} finally {}return null;}

 

在看下startClient(server);

private static void startClient(WolfServer server) {// asyncdbServer只會被串連,不會主動串連其他server
// 這部分目的是過濾那些不需要主動連比人的serevr,比武我這裡的非同步db,和活動服if (server.getServerType() == NodeSessionMgr.SERVER_TYPE_ASYNCDB|| server.getServerType() == NodeSessionMgr.SERVER_TYPE_ACTIVE) {return;}// 發送game Server ip port到mainserverMap<String, Object> params = new HashMap<String, Object>();params.put("nodeServerIp", server.getIp());params.put("nodeServerPort", server.getPort());params.put("serverType", server.getServerType());//我需要mainserevr的client,就弄個bean在本服final IWolfClientService mainServerClient = (IWolfClientService) ServiceLocator.getSpringBean("mainServerClient");//這個位置其實就是mina的client連server端mainServerClient.init();Object localAddress = mainServerClient.registerNode(params);

         //同上,需要jobserevr的clientfinal IWolfClientService jobServerClient = (IWolfClientService) ServiceLocator.getSpringBean("jobServerClient");if (jobServerClient != null) {jobServerClient.init();Map<String, Object> params1 = new HashMap<String, Object>();params1.putAll(params);jobServerClient.registerNode(params1);}// }.....}

 

再看下WolfClientService.init()

public void init() {if (start)return;if (wolfClient == null) {log.error("wolf client is null");return;}         //mina 的client 串連 mina serverwolfClient.start();if (wolfClient.isConnected())start = true;}

 

再看下wolfclient.start()

/** * 串連一個伺服器,並指定處理接收到的訊息的處理方法 *  */public void start() {// this.context.put("resultMgr", this.resultMgr);logger.info(com.youxigu.dynasty2.i18n.MarkupMessages.getString("WolfClient_9"), processorNum);logger.info(com.youxigu.dynasty2.i18n.MarkupMessages.getString("WolfClient_0"), corePoolSize);logger.info(com.youxigu.dynasty2.i18n.MarkupMessages.getString("WolfClient_4"), maxPoolSize);if (this.serverIp == null || this.serverIp.equals("")) {logger.error(clientName + "沒有配置serverIp,不啟動.........");return;}String threadPrefix = clientName + "[" + this.serverIp + ":"+ this.serverPort + "]";// exector = Executors.newCachedThreadPool(new// NamingThreadFactory(threadPrefix));processor = new SimpleIoProcessorPool<NioSession>(NioProcessor.class,processorNum);// connector = new NioSocketConnector((Executor) exector, processor);connector = new NioSocketConnector(processor);// connector.getSessionConfig().setReuseAddress(true);DefaultIoFilterChainBuilder chain = connector.getFilterChain();if (useLogFilter == 2) {chain.addLast("logging", new LoggingFilter());}// codec filter要放在ExecutorFilter前,因為讀寫同一個socket connection的socket// buf不能並發(事實上主要是讀,寫操作mina已經封裝成一個write Queue)chain.addLast("codec", new ProtocolCodecFilter(codecFactory)); // 設定編碼過濾器// 添加心跳過濾器,用戶端只接受服務端的心跳請求,不發送心跳請求// connector.getSessionConfig().setReaderIdleTime(readIdleTimeOut);// 這裡的KeepAliveFilter必須在codec之後,因為KeepAliveMessageFactoryImpl返回的是Object,如果KeepAliveMessageFactoryImpl返回的是IOBuffer,則可以在codec之前// KeepAliveFilter到底在ExecutorFilter之前好還是之後好,我也不確定KeepAliveFilter filter = new KeepAliveFilter(new KeepAliveMessageFactoryImpl(keepAliveRequestInterval <= 0),IdleStatus.READER_IDLE, new RequestTimeoutCloseHandler(),keepAliveRequestInterval <= 0 ? 600 : keepAliveRequestInterval,30);chain.addLast("ping", filter);// 添加執行線程池executor = new UnorderedThreadPoolExecutor(corePoolSize, maxPoolSize,keepAliveTime, TimeUnit.SECONDS, new NamingThreadFactory(threadPrefix));// 這裡是預先啟動corePoolSize個處理線程executor.prestartAllCoreThreads();chain.addLast("exec", new ExecutorFilter(executor,IoEventType.EXCEPTION_CAUGHT, IoEventType.MESSAGE_RECEIVED,IoEventType.SESSION_CLOSED, IoEventType.SESSION_IDLE,IoEventType.SESSION_OPENED));if (useWriteThreadPool) {executorWrite = new UnorderedThreadPoolExecutor(corePoolSize,maxPoolSize, keepAliveTime, TimeUnit.SECONDS,new NamingThreadFactory(threadPrefix + "write"));executorWrite.prestartAllCoreThreads();chain.addLast("execWrite", new ExecutorFilter(executorWrite,IoEventType.WRITE, IoEventType.MESSAGE_SENT));}// ,logger.isDebugEnabled() ? new// LoggingIoEventQueueHandler("execWrite") : nulls// 配置handler的 logger,在codec之後,列印的是decode前或者encode後的訊息的log// 可以配置在ExecutorFilter之後:是為了在背景工作執行緒中列印log,不是在NioProcessor中列印if (useLogFilter == 1) {chain.addLast("logging", new LoggingFilter());}connector.setHandler(handler);connector.getSessionConfig().setReuseAddress(true);connector.getSessionConfig().setTcpNoDelay(tcpNoDelay);logger.info(com.youxigu.dynasty2.i18n.MarkupMessages.getString("WolfClient_1")+ serverIp + ":" + serverPort);ConnectFuture cf = null;long start = System.currentTimeMillis();while (true) {                        //這地很關鍵,是個無線迴圈,每10秒串連一次,直到可以和服務端建立串連,否則一支迴圈下去cf = connector.connect(serverAddress);// 建立串連cf.awaitUninterruptibly(10000L);if (!cf.isConnected()) {if ((System.currentTimeMillis() - start) > timeout) {throw new RuntimeException(com.youxigu.dynasty2.i18n.MarkupMessages.getString("WolfClient_5")+ serverIp + ":" + serverPort);}if (cf.getException() != null) {logger.error(com.youxigu.dynasty2.i18n.MarkupMessages.getString("WolfClient_6"), serverIp + ":"+ serverPort, cf.getException().getMessage());}try {Thread.sleep(10000);} catch (Exception e) {}continue;}
//這就是終極目標了,我們的目的就是在serevr的用戶端的bean裡,可以拿到這個iosessionthis.setSession(cf.getSession());logger.info(com.youxigu.dynasty2.i18n.MarkupMessages.getString("WolfClient_10")+ serverIp + ":" + serverPort);shutDown = false;if (handler instanceof WolfMessageChain) {WolfMessageChain wmc = WolfMessageChain.class.cast(handler);wmc.init(context);}break;}}

 

這樣後端的業務通訊網就可以輕鬆的建立起來,之後想怎麼通訊就看你的了  

 

【MINA】用mina做業務服之間的通訊,實現業務負載平衡思路

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.