学习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 node
public static final String SERVER_TYPE_MAIN_STR = "mainserver";// 主server
public static final String SERVER_TYPE_JOB_STR = "jobserver";// job server
public static final String SERVER_TYPE_ASYNCDB_STR = "asyncdbserver";// 异步DB
public 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需要的bean
if (serverType == NodeSessionMgr.SERVER_TYPE_NODE) {
fileName = "wolf/app_nodeserver.xml";
} else {
fileName = "wolf/app_server.xml";
} //手动启动spring
final ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(
new String[] { xmlFile, fileName }); if (context != null) {
ServiceLocator.getInstance().setApplicationContext(context);
} // 启动socket server
final 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 server
if (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到mainserver
Map<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的client
final 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 server
wolfClient.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里,可以拿到这个iosession
this.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做业务服之间的通信,实现业务负载均衡思路的更多相关文章

  1. (转)nginx利用geo模块做限速白名单以及geo实现全局负载均衡的操作记录

    nginx利用geo模块做限速白名单以及geo实现全局负载均衡的操作记录 原文:http://www.cnblogs.com/kevingrace/p/6165572.html Nginx的geo模块 ...

  2. nginx利用geo模块做限速白名单以及geo实现全局负载均衡的操作记录

    geo指令使用ngx_http_geo_module模块提供的.默认情况下,nginx有加载这个模块,除非人为的 --without-http_geo_module.ngx_http_geo_modu ...

  3. .net做的exe和electron做的exe之间的通信问题

    目前工作遇到个问题: .net做的exe和electron做的exe,之间进行数据通信 目前找到两个相对方便的方法: 1.命名管道 ①.net命名管道资料: 进程间通信 - 命名管道实现 ②elect ...

  4. Android - Fragment (三)不同Fragment之间的通信

    在Fragment的java文件中,可以使用getActivity()来获得调用它的activity, 然后再找到另一个Fragment,进行通信 getActivity().getFragmentM ...

  5. 网络编程之Socket的TCP协议实现客户端与客户端之间的通信

    我认为当你学完某个知识点后,最好是做一个实实在在的小案例.这样才能更好对知识的运用与掌握 如果你看了我前两篇关于socket通信原理的入门文章.我相信对于做出我这个小案列是完全没有问题的!! 既然是小 ...

  6. 死磕nginx系列--使用nginx做负载均衡

    使用nginx做负载均衡的两大模块: upstream 定义负载节点池. location 模块 进行URL匹配. proxy模块 发送请求给upstream定义的节点池. upstream模块解读 ...

  7. 消费者用nginx做负载均衡,提供者用zookeeper自带功能实现负载均衡

    公司的项目基于阿里的Dubbo微服务框架开发.为了符合相关监管部门的安全要求,公司购买了华东1.华东2两套异地服务器,一套是业务服务器,一套是灾备服务器.准备在这两套服务器上实现Dubbo的分布式服务 ...

  8. 用SignalR 2.0开发客服系统[系列4:负载均衡的情况下使用SignalR]

    前言 交流群:195866844 目录: 用SignalR 2.0开发客服系统[系列1:实现群发通讯] 用SignalR 2.0开发客服系统[系列2:实现聊天室] 用SignalR 2.0开发客服系统 ...

  9. Windows平台下利用APM来做负载均衡方案 - 负载均衡(下)

    概述 我们在上一篇Windows平台分布式架构实践 - 负载均衡中讨论了Windows平台下通过NLB(Network Load Balancer) 来实现网站的负载均衡,并且通过压力测试演示了它的效 ...

随机推荐

  1. android 的开源输入法介绍,及 自动触摸的实现方法

    输入法的开源代码见我自己的360云盘里的 openwnn-legacy-android-open-ime.tar.bz2 文件 http://www.pocketmagic.net/injecting ...

  2. HDU 1890--Robotic Sort(Splay Tree)

    题意:每次找出第i大的数的位置p输出,然后将i~p之间的数反转. 题解:每次把要的区间转成一棵子树,然后更新.因为每次将第i小的数转到了了i,所以k次操作后,可知前k个数一定是最小的那k个数,所以以后 ...

  3. python 使用__slots__

    正常情况下,当我们定义了一个class,创建了一个class的实例后,我们可以给该实例绑定任何属性和方法,这就是动态语言的灵活性.先定义class: >>> class Studen ...

  4. python 调用函数

    Python内置了很多有用的函数,我们可以直接调用. 要调用一个函数,需要知道函数的名称和参数,比如求绝对值的函数abs,只有一个参数.可以直接从Python的官方网站查看文档: http://doc ...

  5. [OC Foundation框架 - 12] NSNumber

    1.概念 NSArray,NSDictionary只能放OC对象,不能放入基本数据类型 必须使用包装类NSNumber,把基本数据类型包装成OC对象 不支持自动包装解包   void number() ...

  6. 使用Superprg 下载 AVR/51单片机固件 【worldsing笔记】

    progisp 2.0 可以下载avr.51等单片机,单击下载                      Superprg  单击下载 Superprg软件志峰公司出的专用软件,配合ZF_209使用, ...

  7. gulp 基础运用

    全局安装gulp $npm install --global gulp 作为项目的开发依赖安装 //--save-dev 开发依赖,储存在package.json的devDependencies中,如 ...

  8. 针对 .NET 框架的安全编码指南

      此主题尚未评级 - 评价此主题 发布日期 : 10/9/2004 | 更新日期 : 10/9/2004 Microsoft Corporation 适用于: Microsoft .NET 框架 摘 ...

  9. 解决ecshop在线客户点击无法唤醒QQ问题

    找到default/library/page_footer.lbi中找到QQ代码的相应位置,然后你会发现之前模板里面为什么QQ点击不能对话,是因为QQ客服安装包中的JS代码有的可能是比较旧的代码了. ...

  10. 让Visual Studio 2015 支持ASP.NET MVC4.0.0.1

    近日装上了Visual Studio 2015 ,打开之前vs2013创建的MVC4的项目发现无法编译通过,提示System.Web.MVC,System.Web.WebPages 等找不到,网上搜索 ...