NIO框架之MINA原始碼解析(一):背景
“你們的agent佔了好多系統的埠,把我們的很多業務系統都給整死了,給我們造成了很大的損失,要求你們的相關領導下週過來道歉” -- 來自我們的一個客戶。
怎麼可能呢,我們都不相信,我們的agent只佔一個埠啊!
事實勝過雄辯,經過查證,確實是由於我們的agent佔了好多系統的埠,我看了一下日誌,基本把系統可用的埠占完了!
為什麼呢?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佔用大量埠後,我們這邊的工程人員就迅速定位到了這個問題,並很快修復了,但修復並不理想,但修復過後的程式碼。
/**
* 非同步傳送訊息
* @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。
雖然不會出現程式佔用大量的系統埠這個問題,但會造成另外一個問題-當有一個訊息佇列需要非同步呼叫上面語句傳送訊息時,有原來的非同步(傳送完直接返回,相當於快速併發傳送)變成偽非同步(傳送完訊息後並等待訊息返回處理後返回,相當於順序處理佇列裡面的訊息)。
上面的修改並不是我們想要的結果,但至少修復了佔用大量埠的問題。
由於懷著想徹底修復這個問題的想法,我想還是深入瞭解一下MINA原始碼吧。
相關文章
- NIO框架之MINA原始碼解析(二):mina核心引擎框架原始碼
- NIO框架之MINA原始碼解析(五):NIO超級陷阱和使用同步IO與MINA通訊框架原始碼
- NIO框架之MINA原始碼解析(四):粘包與斷包處理及編碼與解碼框架原始碼
- NIO框架之MINA原始碼解析(三):底層通訊與責任鏈模式應用框架原始碼模式
- mina2原始碼解析原始碼
- BIO到NIO原始碼的一些事兒之NIO 中原始碼
- BIO到NIO原始碼的一些事兒之NIO 上原始碼
- BIO到NIO原始碼的一些事兒之NIO 下 之 Selector原始碼
- Java集合框架之 Java HashMap 原始碼解析Java框架HashMap原始碼
- Java NIO框架Mina、Netty、Grizzly介紹與對比Java框架Netty
- Netty原始碼分析--NIO(一)Netty原始碼
- Spring原始碼之IOC(一)BeanDefinition原始碼解析Spring原始碼Bean
- Android之Mina框架學習Android框架
- mina框架框架
- ARouter路由框架原始碼解析路由框架原始碼
- weex eros框架原始碼解析ROS框架原始碼
- BIO到NIO原始碼的一些事兒之NIO 下 Buffer解讀 下原始碼
- BIO到NIO原始碼的一些事兒之NIO 下 Buffer解讀 上原始碼
- Spring原始碼解析之BeanFactoryPostProcessor(一)Spring原始碼Bean
- BIO到NIO原始碼的一些事兒之BIO原始碼
- tcc分散式事務框架原始碼解析系列(一)之專案結構分散式框架原始碼
- 乾貨分享之Spring框架原始碼解析01-(xml配置解析)Spring框架原始碼XML
- Android八門神器(一):OkHttp框架原始碼解析AndroidHTTP框架原始碼
- RxPermissions 原始碼解析之舉一反三原始碼
- spring原始碼解析之IOC容器(一)Spring原始碼
- Redis原始碼解析之跳躍表(一)Redis原始碼
- Java併發之Semaphore原始碼解析(一)Java原始碼
- [原始碼解析] 並行分散式框架 Celery 之架構 (2)原始碼並行分散式框架架構
- Java併發之ReentrantLock原始碼解析(一)JavaReentrantLock原始碼
- JDK原始碼解析系列之String 之一JDK原始碼
- Andriod 網路框架 OkHttp 原始碼解析框架HTTP原始碼
- 迷你 JS 框架 Hyperapp 原始碼解析JS框架APP原始碼
- Android Volley框架原始碼解析Android框架原始碼
- jQuery原始碼解析之clone()jQuery原始碼
- jQuery原始碼解析之position()jQuery原始碼
- Vue原始碼解析之parseVue原始碼
- LevelDB 原始碼解析之 Arena原始碼
- Dubbo原始碼解析之SPI原始碼