“你们的agent占了好多系统的port。把我们的非常多业务系统都给整死了,给我们造成了非常大的损失。要求你们的相关领导下周过来道歉”   --   来自我们的一个客户。

 怎么可能呢,我们都不相信,我们的agent仅仅占一个port啊!

事实胜过雄辩。经过查证。确实是因为我们的agent占了好多系统的port。我看了一下日志。基本把系统可用的port占完了!

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvY2hhb2ZhbndlaQ==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" />

为什么呢?MINA框架私自开的!

因为我们的agent端使用了NIO通信框架MINA,但并没有使用好,造成了这一差点儿毁灭行的灾难。

还是先看代码吧。

/**
* 异步发送消息
* @param agent
* @param request
*/
public void sendMessageToAgent(Agent agent, HyRequest request) {
IoSession session = null;
IoConnector connector=null;
long startTime = System.currentTimeMillis();
try {
// 创建一个非堵塞的客户端程序
connector = new NioSocketConnector();
// 设置链接超时时间
connector.setConnectTimeoutMillis(connectTimeoutMillis); ObjectSerializationCodecFactory objsCodec = new ObjectSerializationCodecFactory();
objsCodec.setDecoderMaxObjectSize(DEFAULTDECODER);
objsCodec.setEncoderMaxObjectSize(DEFAULTDECODER);
ProtocolCodecFilter codecFilter = new ProtocolCodecFilter(
objsCodec);
// 数据转换。编码设置
connector.getFilterChain()
.addLast("codec", codecFilter);
// 消息
connector.setHandler(clientHandler); SocketAddress socketAddress = new InetSocketAddress(
agent.getIpAddr(), agent.getAgentPort());
ConnectFuture future = connector.connect(socketAddress);
future.awaitUninterruptibly();
session = future.getSession();
String json = mapper.writeValueAsString(request);
session.write(json); long endTime = System.currentTimeMillis(); logerr.debug("send-time:" + (endTime - startTime)); } catch (Exception e) {
logerr.error("host:" + agent.getIpAddr() + ", AgentPORT:" + agent.getAgentPort()
+ ", 连接异常..."+e.getMessage());
clientHandler.handlerConnectError(agent, request); }
}
public class MinaClientHandler extends IoHandlerAdapter {
// 日志
private Logger log = Logger.getLogger(getClass()); private MinaResponseProcesser minaResponseProcesser; ObjectMapper mapper=null; @Override
public void messageReceived(IoSession session, Object message)
throws Exception {
String msg = message.toString();
log.info("receive message from " + session.getRemoteAddress().toString() + ",message:" + message);
if(null == mapper){
mapper = new ObjectMapper();
}
//请求消息转换为HyResponse对象
HyResponse response = mapper.readValue(msg, HyResponse.class);
String remoteIp= ((InetSocketAddress)session.getRemoteAddress()).getAddress().getHostAddress();
response.setRemoteIp(remoteIp);
HyRequest request = minaResponseProcesser.processResponse(response);
if(request == null){
//关闭当前session
closeSessionByServer(session,response);
}else{
session.write(mapper.writeValueAsString(request));
}
}
}

上面的逻辑就是,当要发送一个消息时,创建一个新的connector,并获取一个session发送消息后直接返回,在MinaClientHandler类的messageReceived里面处理接受到的响应数据,并进行业务处理。最后假设不须要再次发送请求,则关闭当前session。

事实上出现本文一開始的问题就是在这里造成的。

在出现我们的agent占用大量port后,我们这边的project人员就迅速定位到了这个问题,并非常快修复了。但修复并不理想,但修复过后的代码。

/**
* 异步发送消息
* @param agent
* @param request
*/
public void sendMessageToAgent(Agent agent, HyRequest request) {
IoSession session = null;
IoConnector connector=null;
long startTime = System.currentTimeMillis();
try {
// 创建一个非堵塞的客户端程序
connector = new NioSocketConnector();
// 设置链接超时时间
connector.setConnectTimeoutMillis(connectTimeoutMillis); ObjectSerializationCodecFactory objsCodec = new ObjectSerializationCodecFactory();
objsCodec.setDecoderMaxObjectSize(DEFAULTDECODER);
objsCodec.setEncoderMaxObjectSize(DEFAULTDECODER);
ProtocolCodecFilter codecFilter = new ProtocolCodecFilter(
objsCodec);
// 数据转换,编码设置
connector.getFilterChain()
.addLast("codec", codecFilter);
// 消息
connector.setHandler(clientHandler); SocketAddress socketAddress = new InetSocketAddress(
agent.getIpAddr(), agent.getAgentPort());
ConnectFuture future = connector.connect(socketAddress);
future.awaitUninterruptibly();
session = future.getSession();
String json = mapper.writeValueAsString(request);
session.write(json);
// 等待断开连接
session.getCloseFuture().awaitUninterruptibly();
long endTime = System.currentTimeMillis(); logerr.debug("send-time:" + (endTime - startTime));
//connector.dispose();
} catch (Exception e) {
logerr.error("host:" + agent.getIpAddr() + ", AgentPORT:" + agent.getAgentPort()
+ ", 连接异常..."+e.getMessage());
clientHandler.handlerConnectError(agent, request); }finally{
if(null!=session){
session.close(true);
session=null;
}
if(null !=connector){
connector.dispose();
}
}
}

仅仅改了一个地方。就是在发送完消息后,加了一个等待断开连接语句和finally语句块-关闭session和connector。

尽管不会出现程序占用大量的系统port这个问题。但会造成另外一个问题-当有一个消息队列须要异步调用上面语句发送消息时,有原来的异步(发送完直接返回,相当于高速并发发送)变成伪异步(发送完消息后并等待消息返回处理后返回,相当于顺序处理队列里面的消息)。

上面的改动并非我们想要的结果,但至少修复了占用大量port的问题。

因为怀着想彻底修复这个问题的想法,我想还是深入了解一下MINA源代码吧。

NIO框架之MINA源代码解析(一):背景的更多相关文章

  1. NIO框架之MINA源代码解析(二):mina核心引擎

    NIO框架之MINA源代码解析(一):背景 MINA的底层还是利用了jdk提供了nio功能,mina仅仅是对nio进行封装.包含MINA用的线程池都是jdk直接提供的. MINA的server端主要有 ...

  2. NIO框架之MINA源码解析(五):NIO超级陷阱和使用同步IO与MINA通信

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

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

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

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

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

  5. (一)Mina源代码解析之总体架构

    Apache Mina Server 是一个网络通信应用框架.也就是说,它主要是对基于TCP/IP.UDP/IP协议栈的通信框架(当然,也能够提供JAVA 对象的序列化服务.虚拟机管道通信服务等).M ...

  6. NIO框架Mina学习

    前言: 找了篇文章看了看,nio框架数Mina用的最多! 代码: 服务端: package com.mina; import java.net.InetSocketAddress; import ja ...

  7. NIO通讯框架之Mina

          在两三年前,阿堂在技术博客(http://blog.sina.com.cn/heyitang)上曾经写过"JAVA新I/O学习系列笔记(1)"和"JAVA新I ...

  8. 【原创】NIO框架入门(二):服务端基于MINA2的UDP双向通信Demo演示

    前言 NIO框架的流行,使得开发大并发.高性能的互联网服务端成为可能.这其中最流行的无非就是MINA和Netty了,MINA目前的主要版本是MINA2.而Netty的主要版本是Netty3和Netty ...

  9. 【原创】NIO框架入门(四):Android与MINA2、Netty4的跨平台UDP双向通信实战

    概述 本文演示的是一个Android客户端程序,通过UDP协议与两个典型的NIO框架服务端,实现跨平台双向通信的完整Demo. 当前由于NIO框架的流行,使得开发大并发.高性能的互联网服务端成为可能. ...

随机推荐

  1. tp框架引入第三方sdk的经验总结

    tp框架开发常用到第三方的接口,这时候需要引入第三方的sdk.例如:微信扫码支付sdk,阿里大于的淘宝sdk等等 首先到官网上下载对应php的sdk文件,通常会有至少一个实例代码. 1 新建一个控制器 ...

  2. 洛谷——P3258 [JLOI2014]松鼠的新家

    https://www.luogu.org/problem/show?pid=3258 题目描述 松鼠的新家是一棵树,前几天刚刚装修了新家,新家有n个房间,并且有n-1根树枝连接,每个房间都可以相互到 ...

  3. 基于ORA-12170 TNS 连接超时解决办法详解

    转自原文 基于ORA-12170 TNS 连接超时解决办法详解 1.开始----程序-----Oracle------配置和移植工具-----Net Manager----本地----服务命名---o ...

  4. java 日期和字符串互转,依据当天整天时间 得到当天最后一秒的日期时间

    java 日期和字符串互转.依据当天整天时间   得到当天最后一秒的日期时间 package com.hi; import java.text.DateFormat; import java.text ...

  5. HDU 4309 Contest 1

    最大流建图.开始以为旧桥有1000座,没敢用枚举,后来看看题目发现了只是十二座.枚举桥的状态没问题. 对于隧道的容量W,可以虚拟出第三个结点表示,如u->v.增加一个点p,u->p(INF ...

  6. HDU Train Problem I (STL_栈)

    Problem Description As the new term comes, the Ignatius Train Station is very busy nowadays. A lot o ...

  7. leetcode第一刷_Subsets II

    要求子集,有很现成的方法.N个数.子集的个数是2^N.每一个元素都有在集合中和不在集合中两种状态,这些状态用[0,pow(2,N)]中每一个数来穷举,假设这个数中的第i位为1,说明当前集合中包括源数组 ...

  8. yii自己定义CLinkPager分页

    在components中自己定义LinkPager.并继承CLinkPager 代码例如以下: <? php /** * CLinkPager class file. * * @author l ...

  9. vue引入swiper vue使用swiper vue脚手架使用swiper /引入js文件/引入css文件

    vue引入swiper  vue使用swiper  vue脚手架使用swiper /引入js文件/引入css文件 ------------------------------------------- ...

  10. win32编程 画图

    void cDefense::DrawAll() { HDC hDc = GetDC(m_hWnd);//获取客户区窗口,如果该值为NULL,GetDC则获整个屏幕的窗口. HDC dcMem = C ...