NIO框架之MINA源码解析(五):NIO超级陷阱和使用同步IO与MINA通信
1、NIO超级陷阱
之所以说NIO超级陷阱,就是因为我在本系列开头的那句话,因为使用缺陷导致客户业务系统瘫痪。当然,我对这个问题进行了很深的追踪,包括对MINA源码的深入了解,但其实之所以会出现这个问题,它的根不是MINA的原因,而是JDK底层的问题。
JDK底层在实现nio时,为了能够唤醒等待在io上的线程,在windows平台使用了两个端口建立连接发消息实现。看如下代码:
- public class NIOTest {
- @Test
- public void test1(){
- final int MAXSIZE=1000;
- Selector [] sels = new Selector[ MAXSIZE];
- try{
- for( int i = 0 ;i< MAXSIZE ;++i ) {
- sels[i] = Selector.open();
- Thread.sleep(1000*10);
- }
- }catch( Exception ex ){
- ex.printStackTrace();
- }
- }
- }
也就是说每调用一次Selector.open(),就会占用两个随机可用端口,相互通信--这就是问题的根源。
当然对于我们的项目来说,这个问题的解决需要分两步,首先限制端口数到用户可接受范围内,这个比较容易,我们可用只调用一次Selector.open()方法即可,使用单利模式;第二步,因为Selector.open()方法每次都是使用的系统当前可用的随机端口,所以就有可能导致占用客户业务端口的情况,因此我们必须把Selector.open()所开的端口限制在一定范围内,最好可以通过代码指定使用哪几个端口,但是直到现在我们都没有找到,如果亲们有方法的话,很感谢能够告诉我....,。
Selector总结如下:
Windows下,Selector.open()会自己和自己建立两条TCP链接。不但消耗了两个TCP连接和端口,同时也消耗了文件描述符。
Linux下,Selector.open()会自己和自己建两条管道。同样消耗了两个系统的文件描述符。
来源于:http://blog.csdn.net/haoel/article/details/2224055
所以,现在我们现在就干脆用传统IO与MINA server通信吧。
2、使用同步IO与MINA通信
其实非常简单,唯一的难点就在于使用同步IO与MINA通信时,怎么编码和解码,还是看下面代码吧。
server 端(用的MINA)
- /**
- * 初始化设置,启动服务
- */
- public void startServer() {
- logger.debug("mina server start");
- int port = SysEvnVar.managerPort;
- logger.debug("manager端口号:" + port);
- IoAcceptor acceptor = new NioSocketAcceptor();
- /** 日志设置 */
- acceptor.getFilterChain().addLast("logger", new LoggingFilter());
- //编码与解码工厂,使用的技术就是JDK提供的ObjectOutStream
- ObjectSerializationCodecFactory objsCodec=new ObjectSerializationCodecFactory();
- objsCodec.setDecoderMaxObjectSize(DEFAULTDECODER);
- objsCodec.setEncoderMaxObjectSize(DEFAULTDECODER);
- /** 数据转换,编码设置 */
- acceptor.getFilterChain().addLast(
- "codec",
- new ProtocolCodecFilter(objsCodec));
- /** 设置业务处理类 */
- acceptor.setHandler(serverHandler);
- /** 设置buffer容量 */
- acceptor.getSessionConfig().setReadBufferSize(DEFAULTBUFFERSIZE);
- /** 设置空闲时间 */
- acceptor.getSessionConfig().setIdleTime(IdleStatus.BOTH_IDLE, DEFAULTIDLETIME);
- try {
- /** 绑定端口 */
- acceptor.bind(new InetSocketAddress(port));
- logger.info("mina server bind port ("+port+") sucess");
- } catch (IOException e) {
- logger.error("mina server bind port ("+port+") fail:" + e.getMessage(),e);
- }
- }
client端(使用同步IO)
- public void sendMessageToManager(HyRequest request) {
- Socket socket = null;
- BufferedInputStream inBuff = null;
- OutputStream outBuff = null;
- try {
- Integer port = Integer.parseInt(hyGlobalConfigureCacheImpl.get(
- "managerPort").toString());
- String host = hyGlobalConfigureCacheImpl.get("managerIp")
- .toString();
- socket = new Socket(host, port);
- inBuff = new BufferedInputStream(new DataInputStream(
- socket.getInputStream()));
- outBuff = socket.getOutputStream();
- String json = HyJsonUtil.reqeustToJsonStr(request);
- //编码开始,使MINA能够解析
- IoBuffer buf = IoBuffer.allocate(64);
- buf.setAutoExpand(true);
- buf.putObject(json);
- buf.flip();
- byte[] bytes =new byte[buf.limit()];
- buf.get(bytes);
- //编码结束,直接输出到客户端
- outBuff.write(bytes);
- outBuff.flush();
- if(request.getOperation() != null && !"quit".equals(request.getOperation())){
- String allreadstr = new String();
- byte[] b = new byte[512 * 1024];
- int len;
- byte[] tmp = new byte[0];
- if ((len = inBuff.read(b)) != -1) {
- tmp = new byte[len];
- System.arraycopy(b, 0, tmp, 0, len);
- }
- loger.debug("len:"+len);
- //解码开始,byte[]为MINA传过来的数据
- IoBuffer in = IoBuffer.wrap(tmp);
- Object obj = null;
- try {
- loger.debug("in:"+in);
- obj = in.getObject();
- //解码结束
- loger.debug("parse success");
- } catch (Exception e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- loger.debug("go to exception");
- loger.warn("nio parse exception",e);
- obj = "exception";
- }
- allreadstr = (String) obj;
- loger.info("receive message from "
- + socket.getRemoteSocketAddress().toString() + ",message:"
- + allreadstr);
- // 请求消息转换为HyResponse对象
- HyResponse response = HyJsonUtil.getHyResponse(allreadstr);
- HyRequest hyrequest = new HyRequest();
- hyResponseDispatcher = hyClientHandler.getHyResponseDispatcher();
- hyResponseDispatcher.responseProcess(hyrequest, response);
- if (hyrequest.getOperation() != null) {
- sendMessageToManager2(hyrequest);
- }
- }
- } catch (Exception e) {
- e.printStackTrace();
- } finally {
- try {
- if(null != outBuff)
- outBuff.close();
- } catch (IOException e1) {
- // TODO Auto-generated catch block
- e1.printStackTrace();
- }
- try {
- if(null != inBuff)
- inBuff.close();
- } catch (IOException e1) {
- // TODO Auto-generated catch block
- e1.printStackTrace();
- }
- if (socket != null) {
- try {
- socket.close();
- } catch (IOException e) {
- }
- }
- }
- }
看代码里面的注释,怎么编码和解码,其实我是直接看MINA源码,把MINA的编码和解码方法工具类直接拷到了我们的client端,这样就轻松的实现使用同步IO和MINA进行通信。
NIO框架之MINA源码解析(五):NIO超级陷阱和使用同步IO与MINA通信的更多相关文章
- Celery 源码解析五: 远程控制管理
今天要聊的话题可能被大家关注得不过,但是对于 Celery 来说确实很有用的功能,曾经我在工作中遇到这类情况,就是我们将所有的任务都放在同一个队列里面,然后有一天突然某个同学的代码写得不对,导致大量的 ...
- dubbo源码解析五 --- 集群容错架构设计与原理分析
欢迎来我的 Star Followers 后期后继续更新Dubbo别的文章 Dubbo 源码分析系列之一环境搭建 博客园 Dubbo 入门之二 --- 项目结构解析 博客园 Dubbo 源码分析系列之 ...
- ReactiveCocoa源码解析(五) SignalProtocol的observe()、Map、Filter延展实现
上篇博客我们对Signal的基本实现以及Signal的面向协议扩展进行了介绍, 详细内容请移步于<Signal中的静态属性静态方法以及面向协议扩展>.并且聊了Signal的所有的g功能扩展 ...
- ReactiveSwift源码解析(五) SignalProtocol的observe()、Map、Filter延展实现
上篇博客我们对Signal的基本实现以及Signal的面向协议扩展进行了介绍, 详细内容请移步于<Signal中的静态属性静态方法以及面向协议扩展>.并且聊了Signal的所有的g功能扩展 ...
- iOS即时通讯之CocoaAsyncSocket源码解析五
接上篇:iOS即时通讯之CocoaAsyncSocket源码解析四 原文 前言: 本文为CocoaAsyncSocket Read篇终,将重点涉及该框架是如何利用缓冲区对数据进行读取. ...
- Spring 源码解析之DispatcherServlet源码解析(五)
spring的整个请求流程都是围绕着DispatcherServlet进行的 类结构图 根据类的结构来说DispatcherServlet本身也是继承了HttpServlet的,所有的请求都是根据这一 ...
- Mybaits 源码解析 (十)----- 全网最详细,没有之一:Spring-Mybatis框架使用与源码解析
在前面几篇文章中我们主要分析了Mybatis的单独使用,在实际在常规项目开发中,大部分都会使用mybatis与Spring结合起来使用,毕竟现在不用Spring开发的项目实在太少了.本篇文章便来介绍下 ...
- NIO框架之MINA源码解析(四):粘包与断包处理及编码与解码
1.粘包与段包 粘包:指TCP协议中,发送方发送的若干包数据到接收方接收时粘成一包,从接收缓冲区看,后一包数据的头紧接着前一包数据的尾.造成的可能原因: 发送端需要等缓冲区满才发送出去,造成粘包 接收 ...
- NIO框架之MINA源码解析(转)
http://blog.csdn.net/column/details/nio-mina-source.html http://blog.csdn.net/chaofanwei/article/det ...
随机推荐
- 01 node.js,npm,es6入门
Node.js安装 1.下载对应你系统的Node.js版本: https://nodejs.org/en/download/ 命令提示符下输入命令 node -v 会显示当前node的版本 快速入门 ...
- python筛选特定文件的信息按照格式输出到txt
最近搞数据库,为了把图片文件的信息导入数据库表中,我开始研究python列出图片文件,其中发现因为IE临时文件里有非常多的不需要的图片,就需要筛选掉一些文件. 最终用python输出了所有需要的图片文 ...
- 51cto-spring boot(一Spring4快速入门)
https://search.maven.org/classic 查找maven依赖的pom.xml文件
- C++中的抽象类
一.抽象类学习笔记 1.virtual修饰函数(虚函数)后面加=0就称为一个纯虚函数,一个类中只要有纯虚函数那么它就是一个抽象类.抽象类不能用来实例化对象,是用来给他的派生类定义好这些框架的,给使用这 ...
- MySQL Inport--导入数据
LOAD DATA INFILE导入数据 语法: LOAD DATA [LOW_PRIORITY | CONCURRENT] [LOCAL] INFILE '' [REPLACE | IGNORE] ...
- 简单 babel plugin 开发-使用lerna 工具
babel在现在的web 应用开发上具有很重要的作用,帮助我们做了好多事情,同时又有 比较多的babel plugin 可以解决我们实际开发上的问题. 以下只是学习下如果编写一个简单的babel pl ...
- tailor 基础模版的使用
对于需要实现共享的html 内容,tailor 提供了基础模版的概念,我们需要操作的就是添加slots ,以及使用slots 做为占位符 环境准备 使用现有的仓库代码 https://github.c ...
- Explicit
Prefixing the explicit keyword to the constructor prevents the compiler from using that constructor ...
- DevExpress 控件使用菜单栏之BarManager
DevExpress 开发的控件有很强的实力,不仅功能丰富,应用简便,而且界面华丽,更可方便定制.对于编程人员来说是个不错的选择.它的菜单栏控件更具代表,完全可以替代开发环境提供的基本控件,而让您编写 ...
- MySQL中视图
视图是指计算机数据库中的视图,是一个虚拟表,其内容由查询定义.同真实的表一样,视图包含一系列带有名称的列和行数据.但是,视图并不在数据库中以存储的数据值集形式存在.行和列数据来自由定义视图的查询所引用 ...