news 2026/10/20 2:19:43

网关架构:京东京麦Netty长连接网关架构

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
网关架构:京东京麦Netty长连接网关架构

京东京麦Netty长连接网关架构

京东京麦从 HTTP 网关逐步升级为 TCP 长连接网关,最终基于 Netty 4.x + Protobuf 3.x 构建高可用、高性能、高稳定的接入层。相比 HTTP 网关,TCP 网关性能提升 10 倍以上,稳定性也显著增强。

一、TCP 网关的网络结构

客户端通过域名 + 端口访问 TCP 网关,不同域名对应不同运营商的 VIP。VIP 发布在 LVS 上,LVS 把请求转发给 HAProxy,再由 HAProxy 转发到 Netty 的 IP + Port。

客户端

域名 / VIP

LVS DR

HAProxy 1

HAProxy 2

Netty 网关 1

Netty 网关 2

这是典型的高并发接入层标准架构:LVS 做四层转发,HAProxy 做七层转发,Netty 做业务接入。

二、长连接容器架构

Netty 的 Reactor 模型中,每个连接对应一个 Channel,一个 Channel 对应唯一 ChannelPipeline,多个 Handler 串行加入 Pipeline。

TCP 属于传输层,为了给应用层提供稳定服务,京麦在 Netty 之上建立 Session 管理机制:

Channel

Connection

Session

Session Manager

ChannelHandlerContext

每次 Session 请求通过 Proxy 机制调用 Service 层,数据返回后写回 Channel。下行推送时,通过 Session Manager 找到 Active Session,再写入对应 ChannelHandlerContext,实现点对点或广播推送。

三、线程模型

TCP 网关使用三组线程池:

线程池职责
BossGroup接收客户端 TCP 连接
WorkerGroup处理 I/O、系统 Task、定时任务
ExecutorGroup处理加解密、限流、路由、转发业务

Boss Group 作为 Acceptor 接收连接并转发给 Worker Group;Worker Group 负责 Socket 读写;Task Queue 和 Delay Task Queue 负责链路空闲检测、心跳等定时任务。

四、Session 管理源码

Session 创建时会保存 SessionId、创建时间、上次访问时间、Connection 和 SessionListener。

@OverridepublicsynchronizedSessioncreateSession(StringsessionId,ChannelHandlerContextctx){Sessionsession=sessions.get(sessionId);if(session!=null){session.close();}session=newExchangeSession();session.setSessionId(sessionId);session.setValid(true);session.setMaxInactiveInterval(this.getMaxInactiveInterval());session.setCreationTime(System.currentTimeMillis());session.setLastAccessedTime(System.currentTimeMillis());session.setSessionManager(this);session.setConnection(createTcpConnection(session,ctx));for(SessionListenerlistener:essionListeners){session.addSessionListener(listener);}returnsession;}

这里要特别注意断线重连 Channel 的处理。如果 Session 已存在就关闭,可能会误关闭同一个 Channel 上的旧连接。解决方式是在 Channel 中存入 SessionId,每次请求判断 Channel 是否已有 SessionId,从而识别是否为断线重连 Channel。

privateStringgetChannelSessionHook(ChannelHandlerContextctx){returnctx.channel().attr(Constants.SERVER_SESSION_HOOK).get();}privatevoidsetChannelSessionHook(ChannelHandlerContextctx,StringsessionId){ctx.channel().attr(Constants.SERVER_SESSION_HOOK).set(sessionId);}

五、心跳检测

客户端定期发送心跳,服务端更新 Session 最后访问时间。Session 创建后会添加TcpHeartbeatListener,由守护线程轮询 Session 集合,清理过期连接。

Sessionsession=tcpSessionManager.createSession(wrapper.getSessionId(),ctx);session.addSessionListener(tcpHeartbeatListener);session.connect();tcpSessionManager.addSession(session);
publicvoidcheckHeartBeat(){Session[]sessions=tcpSessionManager.getSessions();for(Sessionsession:sessions){if(session.expire()){session.close();logger.info("heart is expire, clear sessionId:"+session.getSessionId());}}}

session.connect()会触发所有 Listener 的sessionCreated事件,心跳监听也是在这一过程中被启动。

六、数据上行与下行

数据上行在channelRead中处理。京麦通过消息格式区分是请求还是应答:

publicvoidchannelRead(ChannelHandlerContextctx,Objecto)throwsException{try{if(oinstanceofMessageBuf.JMTransfer){SystemMessagesMsg=generateSystemMessage(ctx);MessageBuf.JMTransfermessage=(MessageBuf.JMTransfer)o;if(message.getFormat()==SEND){MessageWrapperwrapper=proxy.invoke(sMsg,message);if(wrapper!=null){this.receive(ctx,wrapper);}}if(message.getFormat()==REPLY){notify.reply(message);}}else{logger.warn("TcpServerHandler channelRead message is not proto.");}}catch(Exceptione){logger.error("TcpServerHandler handler error.",e);throwe;}}

数据下行通过 MQ 广播到所有服务器,每台服务器判断消息目标是否在本地 Session 中。点对点推送也先广播,再由目标服务器判断并下发。

下行通知如果异步方法需要获取返回值,需要把异步转同步。NotifyFuture实现java.util.concurrent.Future,通过超时和 seq 关联响应。

publicvoidreply(MessageBuf.JMTransfermessage)throwsException{try{longseq=message.getSeq();finalNotifyFuturefuture=this.futureMap.get(seq);if(future!=null){future.setSuccess(true);futureMap.remove(seq);}}catch(Exceptione){throwe;}}

写数据时不要用cf.await阻塞等待写入结果。它偶发出现BlockingOperationException,原因是 Netty 会检查当前线程是否就是该 Channel 的 EventLoop,如果是则可能判定死锁。正确做法是使用writeAndFlush加ChannelFutureListener。

ChannelFuturecf=cxt.writeAndFlush(message);cf.addListener(newChannelFutureListener(){@OverridepublicvoidoperationComplete(ChannelFuturefuture)throwsPushException{if(future.isSuccess()){logger.debug("send success.");}else{thrownewPushException("Failed to send message.");}Throwablecause=future.cause();if(cause!=null){thrownewPushException(cause);}}});

七、总结

京东京麦 TCP 网关的核心不是 Netty API 的简单使用,而是把连接、会话、心跳、上下行通知、异步回调和线程模型组合成一套可运维的长连接系统。Session 管理解决应用层状态,心跳解决连接有效性,Channel 属性解决断线重连,异步 Future 和 EventLoop 约束解决线程安全与死锁。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/20 2:18:48

西门子Comfort触摸屏缺少面板映像,装了TIA为什么还下载不了?

Comfort触摸屏下载提示缺少面板映像,先核对电脑是否装了项目需要的映像,别仅凭报错判断屏坏。速捷工控根据西门子官方资料,帮你把软件版本、项目设备版本和实际面板分开核对。1. 先记全报错,再分别记录三种版本信息下载失败先别慌…

作者头像 李华
网站建设 2026/10/20 2:18:07

Pr插件一键安装包:28000款转场+磨皮+特效多合一

资源概述 这是一套面向 Adobe 视频剪辑软件的插件整合包,将常用的转场特效、人像处理、动态模糊等效果插件打包在一起,提供一键安装程序,降低手动配置各插件的门槛。 配套资源链接 查看配套资源 从目录结构来看,资源主要围绕 Pr …

作者头像 李华
网站建设 2026/10/20 2:17:42

CS计算机专业保研

五大院系!CS保研er上岸南京大学有哪些选择? 五大院系!CS保研er上岸南京大学有哪些选择?_南大cs有什么所-CSDN博客 保研分享:双非软件学渣 - 985CS 保研分享:双非软件学渣 - 985CS_双非cs保研大工-CSDN博客 …

作者头像 李华
网站建设 2026/10/20 2:17:42

金融 RAG 落地案例实战:一个知识库从 0 到 1 的真实路径

个人主页&#xff1a;> for_ever_love__ <&#xff08;欢迎各位大佬莅临&#x1f60a;&#xff09; 其他栏目: > 大模型开发从0到1 < 其他栏目: > iOS项目总结大全 < 其他栏目: > 我想学python了 < 其他栏目: > iOS UI < 文章目录金融 RAG 落地案…

作者头像 李华
网站建设 2026/10/20 2:17:39

Agent Skills 系列 04:SKILL.md 指令写法,从能用变成好用

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华