1、NIO超级陷阱

之所以说NIO超级陷阱,就是因为我在本系列开头的那句话,因为使用缺陷导致客户业务系统瘫痪。当然,我对这个问题进行了很深的追踪,包括对MINA源码的深入了解,但其实之所以会出现这个问题,它的根不是MINA的原因,而是JDK底层的问题。

JDK底层在实现nio时,为了能够唤醒等待在io上的线程,在windows平台使用了两个端口建立连接发消息实现。看如下代码:

  1. public class NIOTest {
  2. @Test
  3. public void test1(){
  4. final int MAXSIZE=1000;
  5. Selector [] sels = new Selector[ MAXSIZE];
  6. try{
  7. for( int i = 0 ;i< MAXSIZE ;++i ) {
  8. sels[i] = Selector.open();
  9. Thread.sleep(1000*10);
  10. }
  11. }catch( Exception ex ){
  12. ex.printStackTrace();
  13. }
  14. }
  15. }

也就是说每调用一次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)

  1. /**
  2. * 初始化设置,启动服务
  3. */
  4. public void startServer() {
  5. logger.debug("mina server start");
  6. int port = SysEvnVar.managerPort;
  7. logger.debug("manager端口号:" + port);
  8. IoAcceptor acceptor = new NioSocketAcceptor();
  9. /** 日志设置 */
  10. acceptor.getFilterChain().addLast("logger", new LoggingFilter());
  11. //编码与解码工厂,使用的技术就是JDK提供的ObjectOutStream
  12. ObjectSerializationCodecFactory objsCodec=new ObjectSerializationCodecFactory();
  13. objsCodec.setDecoderMaxObjectSize(DEFAULTDECODER);
  14. objsCodec.setEncoderMaxObjectSize(DEFAULTDECODER);
  15. /** 数据转换,编码设置 */
  16. acceptor.getFilterChain().addLast(
  17. "codec",
  18. new ProtocolCodecFilter(objsCodec));
  19. /** 设置业务处理类 */
  20. acceptor.setHandler(serverHandler);
  21. /** 设置buffer容量 */
  22. acceptor.getSessionConfig().setReadBufferSize(DEFAULTBUFFERSIZE);
  23. /** 设置空闲时间 */
  24. acceptor.getSessionConfig().setIdleTime(IdleStatus.BOTH_IDLE, DEFAULTIDLETIME);
  25. try {
  26. /** 绑定端口 */
  27. acceptor.bind(new InetSocketAddress(port));
  28. logger.info("mina server bind port ("+port+") sucess");
  29. } catch (IOException e) {
  30. logger.error("mina server bind port ("+port+") fail:" + e.getMessage(),e);
  31. }
  32. }

client端(使用同步IO)

  1. public void sendMessageToManager(HyRequest request) {
  2. Socket socket = null;
  3. BufferedInputStream inBuff = null;
  4. OutputStream outBuff = null;
  5. try {
  6. Integer port = Integer.parseInt(hyGlobalConfigureCacheImpl.get(
  7. "managerPort").toString());
  8. String host = hyGlobalConfigureCacheImpl.get("managerIp")
  9. .toString();
  10. socket = new Socket(host, port);
  11. inBuff = new BufferedInputStream(new DataInputStream(
  12. socket.getInputStream()));
  13. outBuff = socket.getOutputStream();
  14. String json = HyJsonUtil.reqeustToJsonStr(request);
  15. //编码开始,使MINA能够解析
  16. IoBuffer buf = IoBuffer.allocate(64);
  17. buf.setAutoExpand(true);
  18. buf.putObject(json);
  19. buf.flip();
  20. byte[] bytes =new byte[buf.limit()];
  21. buf.get(bytes);
  22. //编码结束,直接输出到客户端
  23. outBuff.write(bytes);
  24. outBuff.flush();
  25. if(request.getOperation() != null && !"quit".equals(request.getOperation())){
  26. String allreadstr = new String();
  27. byte[] b = new byte[512 * 1024];
  28. int len;
  29. byte[] tmp = new byte[0];
  30. if ((len = inBuff.read(b)) != -1) {
  31. tmp = new byte[len];
  32. System.arraycopy(b, 0, tmp, 0, len);
  33. }
  34. loger.debug("len:"+len);
  35. //解码开始,byte[]为MINA传过来的数据
  36. IoBuffer in = IoBuffer.wrap(tmp);
  37. Object obj = null;
  38. try {
  39. loger.debug("in:"+in);
  40. obj = in.getObject();
  41. //解码结束
  42. loger.debug("parse success");
  43. } catch (Exception e) {
  44. // TODO Auto-generated catch block
  45. e.printStackTrace();
  46. loger.debug("go to exception");
  47. loger.warn("nio parse exception",e);
  48. obj = "exception";
  49. }
  50. allreadstr = (String) obj;
  51. loger.info("receive message from "
  52. + socket.getRemoteSocketAddress().toString() + ",message:"
  53. + allreadstr);
  54. // 请求消息转换为HyResponse对象
  55. HyResponse response = HyJsonUtil.getHyResponse(allreadstr);
  56. HyRequest hyrequest = new HyRequest();
  57. hyResponseDispatcher = hyClientHandler.getHyResponseDispatcher();
  58. hyResponseDispatcher.responseProcess(hyrequest, response);
  59. if (hyrequest.getOperation() != null) {
  60. sendMessageToManager2(hyrequest);
  61. }
  62. }
  63. } catch (Exception e) {
  64. e.printStackTrace();
  65. } finally {
  66. try {
  67. if(null != outBuff)
  68. outBuff.close();
  69. } catch (IOException e1) {
  70. // TODO Auto-generated catch block
  71. e1.printStackTrace();
  72. }
  73. try {
  74. if(null != inBuff)
  75. inBuff.close();
  76. } catch (IOException e1) {
  77. // TODO Auto-generated catch block
  78. e1.printStackTrace();
  79. }
  80. if (socket != null) {
  81. try {
  82. socket.close();
  83. } catch (IOException e) {
  84. }
  85. }
  86. }
  87. }

看代码里面的注释,怎么编码和解码,其实我是直接看MINA源码,把MINA的编码和解码方法工具类直接拷到了我们的client端,这样就轻松的实现使用同步IO和MINA进行通信。

NIO框架之MINA源码解析(五):NIO超级陷阱和使用同步IO与MINA通信的更多相关文章

  1. Celery 源码解析五: 远程控制管理

    今天要聊的话题可能被大家关注得不过,但是对于 Celery 来说确实很有用的功能,曾经我在工作中遇到这类情况,就是我们将所有的任务都放在同一个队列里面,然后有一天突然某个同学的代码写得不对,导致大量的 ...

  2. dubbo源码解析五 --- 集群容错架构设计与原理分析

    欢迎来我的 Star Followers 后期后继续更新Dubbo别的文章 Dubbo 源码分析系列之一环境搭建 博客园 Dubbo 入门之二 --- 项目结构解析 博客园 Dubbo 源码分析系列之 ...

  3. ReactiveCocoa源码解析(五) SignalProtocol的observe()、Map、Filter延展实现

    上篇博客我们对Signal的基本实现以及Signal的面向协议扩展进行了介绍, 详细内容请移步于<Signal中的静态属性静态方法以及面向协议扩展>.并且聊了Signal的所有的g功能扩展 ...

  4. ReactiveSwift源码解析(五) SignalProtocol的observe()、Map、Filter延展实现

    上篇博客我们对Signal的基本实现以及Signal的面向协议扩展进行了介绍, 详细内容请移步于<Signal中的静态属性静态方法以及面向协议扩展>.并且聊了Signal的所有的g功能扩展 ...

  5. iOS即时通讯之CocoaAsyncSocket源码解析五

    接上篇:iOS即时通讯之CocoaAsyncSocket源码解析四         原文 前言: 本文为CocoaAsyncSocket Read篇终,将重点涉及该框架是如何利用缓冲区对数据进行读取. ...

  6. Spring 源码解析之DispatcherServlet源码解析(五)

    spring的整个请求流程都是围绕着DispatcherServlet进行的 类结构图 根据类的结构来说DispatcherServlet本身也是继承了HttpServlet的,所有的请求都是根据这一 ...

  7. Mybaits 源码解析 (十)----- 全网最详细,没有之一:Spring-Mybatis框架使用与源码解析

    在前面几篇文章中我们主要分析了Mybatis的单独使用,在实际在常规项目开发中,大部分都会使用mybatis与Spring结合起来使用,毕竟现在不用Spring开发的项目实在太少了.本篇文章便来介绍下 ...

  8. NIO框架之MINA源码解析(四):粘包与断包处理及编码与解码

    1.粘包与段包 粘包:指TCP协议中,发送方发送的若干包数据到接收方接收时粘成一包,从接收缓冲区看,后一包数据的头紧接着前一包数据的尾.造成的可能原因: 发送端需要等缓冲区满才发送出去,造成粘包 接收 ...

  9. NIO框架之MINA源码解析(转)

    http://blog.csdn.net/column/details/nio-mina-source.html http://blog.csdn.net/chaofanwei/article/det ...

随机推荐

  1. java-接口的成员特点

    1.成员变量: - 只能是常量,并且是静态的.公共的. - 默认修饰符:public static final - 建议:自己手动给出. 2.构造方法:接口没有构造方法. 3.成员方法: - 只能是抽 ...

  2. C++学习(二十)(C语言部分)之 函数1

    函数 printf 输出的函数 scanf 输入的函数函数是什么 怎么写 是一组一起执行一个任务的语句 一个程序的基本组成单位是函数 只需要知道函数名字和括号里面要填的内容 就可以调用函数 1.如果代 ...

  3. js中将一个字一个字的打印出来

    第一种方式: setTimeout(function(){ var cc=document.createTextNode(ss[i]) content.appendChild(cc) },3000)

  4. Go Example--超时处理

    package main import ( "fmt" "time" ) func main() { c1 := make(chan string, 1) go ...

  5. Js 判断输入的验证码是否一致

    实现效果: 判断输入的验证码是否一致 如果不同,alert出验证码输入有误~, 输入正确输出登录成功. <!DOCTYPE html> <html lang="en&quo ...

  6. Create-React-App脚手架使用方法

    Facebook 官方推出Create-React-App脚手架,基本可以零配置搭建基于webpack的React开发环境,内置了热更新等功能. 由于create-react-app命令预先安装和配置 ...

  7. java保留2位小数及BigDecimal使用

    java保留两位小数的方法 import java.math.BigDecimal; import java.text.DecimalFormat; import java.text.NumberFo ...

  8. showdoc 开源在线api&&技术文档管理工具

    showdoc 是一个很不错的api 以及技术文档管理工具 环境准备 doker-copose 文件 version: "3" services: doc: image: regi ...

  9. java_生态环境

    Which Java package do I need? Software Developers: JDK (Java SE Development Kit). For Java Developer ...

  10. 编写一个函数实现n^k,使用递归实现

    思路:例如2的3次方.可以分解为2乘2的2次方,而2的2次方又可以分解为2乘2的以此方法,以此类推. #include<stdio.h> int Find_num(int n,int k) ...