当前位置: 首页 > article >正文

物联网接入层技术剖析(四):当epoll遇见MQTT

Netty与高性能网络服务、Linux高并发网络编程实战、从epoll到Netty物联网接入层技术剖析、深入理解I/O多路复用、服务端网络编程进阶指南Netty与物联网当epoll遇见MQTT0 写在前面这个系列写了三篇从 select 到 epoll从内核源码到 Java NIO一路挖下来。但说实话在实际的物联网平台开发中我们很少直接和这些底层 API 打交道。站在它们之上的是 Netty——一个把网络编程的复杂性封装得恰到好处的框架。这篇文章是系列的最后一篇我想把视角拉回到实战层面。聊聊 Netty 是怎么使用 epoll 的在物联网设备接入场景中如何设计一个基于 Netty MQTT 的接入层以及我在项目中积累的一些性能调优经验。1 Netty对epoll的封装Netty 在 Linux 平台上提供了两种 Selector 实现NioEventLoopGroup基于 JDK 的 Selector和EpollEventLoopGroup基于 Netty 自己的 native epoll 封装。为什么要自己封装一套不用 JDK 的呢原因是 JDK 的 Selector 实现虽然底层也是 epoll但中间隔了太多层抽象有些 epoll 的高级特性用不上。Netty 的EpollEventLoopGroup直接通过 JNI 调用 epoll 的系统调用少了中间环节性能更好而且能用上 epoll 特有的功能。比如 ET边沿触发模式。JDK 的 Selector 只支持 LT 模式而 Netty 的 EpollEventLoop 可以配置为 ET 模式EventLoopGroupgroupnewEpollEventLoopGroup(EpollEventLoopGroup.DEFAULT_EVENT_LOOP_THREADS,newDefaultSelectStrategyFactory(),EpollEventLoopGroup.DEFAULT_MAX_PENDING_TASKS,RejectedExecutionHandlers.reject());ServerBootstrapbnewServerBootstrap();b.group(group).channel(EpollServerSocketChannel.class).childHandler(newChannelInitializerSocketChannel(){OverrideprotectedvoidinitChannel(SocketChannelch){// ...}});在物联网平台中如果设备并发量很大使用EpollEventLoopGroupEpollServerSocketChannel可以获得比NioEventLoopGroup更好的性能。实测下来在万级并发连接的场景下吞吐量能提升 10%-20% 左右。不过要注意EpollEventLoopGroup 只能在 Linux 上用。如果你的开发环境是 macOS 或 Windows需要做平台判断开发时用 NioEventLoopGroup生产环境用 EpollEventLoopGroup。Netty 提供了Epoll.isAvailable()方法来做这个判断。2 物联网设备接入层的架构一个典型的物联网平台设备接入层大概长这样设备 → TCP/MQTT → Netty Server → 协议解码 → 业务处理 → 消息队列Netty 在这个架构中承担的是网络通信 协议解析的部分。它负责接收设备的连接、管理连接的生命周期、解码设备上报的协议数据然后把解析后的消息交给上层业务处理。在 Netty 中这个流程是通过 ChannelPipeline 来组织的。Pipeline 是一个处理链每个环节是一个 ChannelHandler。数据从设备端进来依次经过每个 Handler 的处理最终变成业务可以消费的消息对象。3 一个MQTT接入的Pipeline示例以 MQTT 协议为例一个典型的 ChannelPipeline 大概是这样的protectedvoidinitChannel(SocketChannelch){ChannelPipelinepipelinech.pipeline();// 空闲检测60秒没有数据就断开pipeline.addLast(newIdleStateHandler(60,0,0,TimeUnit.SECONDS));// MQTT 编解码器pipeline.addLast(mqtt-decoder,newMqttDecoder());pipeline.addLast(mqtt-encoder,MqttEncoder.INSTANCE);// MQTT 消息处理pipeline.addLast(mqtt-handler,newMqttMessageHandler());// 异常处理pipeline.addLast(exception-handler,newExceptionHandler());}每个 Handler 的职责很清晰IdleStateHandlerNetty 内置的空闲检测 Handler。物联网设备经常会出现假在线的情况——TCP 连接还在但设备已经离线了比如断电、断网但没有发送 DISCONNECT 消息。通过心跳超时检测可以及时清理这些僵尸连接释放资源。MqttDecoder / MqttEncoderMQTT 协议的编解码器。MQTT 是一个二进制协议固定头、可变头、Payload 的格式都有严格定义。Decoder 负责把字节流解析成 MQTT 消息对象Encoder 负责把消息对象编码成字节流发出去。MqttMessageHandler业务 Handler处理解码后的 MQTT 消息。比如处理 CONNECT设备认证、PUBLISH数据上报、SUBSCRIBE订阅主题等。ExceptionHandler统一处理 Pipeline 中的异常避免异常导致连接非正常关闭。4 连接管理物联网平台的命脉在物联网平台中连接管理是最核心也最容易出问题的地方。设备标识与 Session 绑定。每个设备连接上来之后你需要把设备的唯一标识比如设备 ID、Client ID和 Netty 的 Channel 关联起来。通常的做法是用一个 ConcurrentHashMap 来维护这个映射关系ConcurrentHashMapString,ChanneldeviceChannelsnewConcurrentHashMap();// 设备连接成功时deviceChannels.put(deviceId,channel);// 需要给设备下发消息时ChannelchanneldeviceChannels.get(deviceId);if(channel!nullchannel.isActive()){channel.writeAndFlush(message);}连接断开的清理。设备断开连接时不管是正常断开还是异常断开必须及时从映射表中移除否则会内存泄漏。Netty 提供了channelInactive()回调在这里做清理工作OverridepublicvoidchannelInactive(ChannelHandlerContextctx){StringdeviceIdgetDeviceId(ctx.channel());if(deviceId!null){deviceChannels.remove(deviceId);// 通知业务层设备离线deviceOffline(deviceId);}}优雅停机。服务重启时不能直接 kill 进程需要先通知所有设备我要下线了给它们时间重新连接到其他节点。Netty 的EventLoopGroup.shutdownGracefully()可以做到这一点。5 性能调优的几点经验在物联网平台中把 Netty 调到比较理想的性能我总结了几条经验。合理设置 EventLoop 线程数。默认值是 CPU 核心数 * 2。对于物联网场景如果设备数据量不大但连接数很多可以适当减少线程数比如 CPU 核心数因为大部分线程其实都在epoll_wait上睡觉多了反而浪费。如果数据量也很大保持默认值或适当增加。调整 SO_BACKLOG。这是 ServerSocket 的连接队列大小。当瞬时有大量设备同时连接时比如断电恢复后设备集中上线默认的 backlog 可能不够导致连接被拒绝。建议设大一些比如 1024 或 2048。b.option(ChannelOption.SO_BACKLOG,2048);开启 TCP_NODELAY。MQTT 的报文通常很小几十到几百字节。默认情况下Nagle 算法会把小包攒成大包再发这会增加延迟。对于物联网场景实时性往往比吞吐量更重要建议关闭 Nagleb.childOption(ChannelOption.TCP_NODELAY,true);ByteBuffer 池化。Netty 默认使用池化的 ByteBufPooledByteBufAllocator这比每次都创建新的 ByteBuffer 效率高很多。在高并发场景下这个优化能显著减少 GC 压力。确保你没有不小心切换成了非池化的 Allocator。水位线设置。Netty 的 Channel 有 write buffer 的水位线概念。当 write buffer 的字节数超过高水位线时channel.isWritable()会返回 false此时应该暂停写入等 buffer 消耗到低水位线以下再恢复。这个机制可以防止内存被 write buffer 撑爆。b.childOption(ChannelOption.WRITE_BUFFER_HIGH_WATER_MARK,64*1024);b.childOption(ChannelOption.WRITE_BUFFER_LOW_WATER_MARK,32*1024);监控 epoll_wait 的耗时。如果发现 CPU 使用率异常高可以用jstack看一下 EventLoop 线程在干什么。正常情况下EventLoop 线程大部分时间应该阻塞在epoll_wait上。如果发现它在频繁地处理某个 Channel可能是那个 Channel 有大量的数据要读写拖慢了其他 Channel 的处理。6 从epoll到业务完整的数据链路最后让我们把整个系列的知识串成一条完整的数据链路看看一个设备上报的数据是如何从网卡到达业务层的1. 设备通过 TCP 发送 MQTT PUBLISH 报文 2. 网卡收到数据触发硬件中断 3. 内核中断处理程序把数据放入 socket 接收缓冲区 4. socket 的等待队列被唤醒触发 ep_poll_callback 5. ep_poll_callback 把对应的 epitem 挂入 epoll 的就绪链表 rdllist 6. 唤醒在 epoll_wait 上阻塞的 JVM 线程 7. Netty 的 EventLoop 线程从 Selector 中拿到就绪的 Channel 8. 数据经过 PipelineIdleStateHandler → MqttDecoder → MqttMessageHandler 9. MqttDecoder 把字节流解析成 MqttPublishMessage 对象 10. MqttMessageHandler 提取 payload交给业务层处理这十个步骤跨越了硬件中断、内核态、用户态、JVM、Netty 框架、业务逻辑六个层次。理解了这条链路你就理解了物联网平台网络通信的全貌。当然实际生产环境中还有更多的细节需要处理SSL/TLS 加密、设备认证、消息 QoS 保证、集群水平扩展等等。但万变不离其宗底层的网络通信模型就是我们在前面三篇文章中讨论的那些东西。7 写在最后这个系列从 select 的局限性讲起经过 epoll 的原理剖析到 Java NIO Selector 的使用最后落脚在 Netty 和物联网实战。整个过程其实就是在回答一个问题当大量设备同时连接到你的平台时系统是如何高效地处理这些连接的答案的核心就是 epoll 的事件驱动模型。它让一个线程可以轻松管理成千上万个连接让系统资源的消耗与活跃连接数成正比而不是与总连接数成正比。这个特性对于物联网平台来说几乎是不可或缺的。希望这个系列对你有所帮助。如果有什么问题或者想讨论的欢迎留言交流。8 参考资料Java NIO Selector - BaeldungLinux下的I/O复用与epoll详解epoll - 维基百科一文读懂Linux epoll实现原理深入理解 Linux 的 epoll 机制

相关文章:

物联网接入层技术剖析(四):当epoll遇见MQTT

Netty与高性能网络服务、Linux高并发网络编程实战、从epoll到Netty:物联网接入层技术剖析、深入理解I/O多路复用、服务端网络编程进阶指南 Netty与物联网:当epoll遇见MQTT 0 写在前面 这个系列写了三篇,从 select 到 epoll,从内…...

Sunshine游戏串流服务器:打造个人游戏云的终极免费方案

Sunshine游戏串流服务器:打造个人游戏云的终极免费方案 【免费下载链接】Sunshine Self-hosted game stream host for Moonlight. 项目地址: https://gitcode.com/GitHub_Trending/su/Sunshine 想要在任何设备上畅玩PC游戏吗?Sunshine作为一款开源…...

OpenAI通用推理模型攻克近80年数学难题,打脸7个月前虚假突破质疑

【导语:5月21日,OpenAI宣布其一款未对外发布的内部通用推理模型独立完成原创数学证明,推翻了匈牙利数学家保罗埃尔德什1946年提出的“平面单位距离猜想”,这是AI首次独立攻克数学领域核心著名公开难题。】AI攻克近80年几何猜想Ope…...

面试官问CyclicBarrier和CountDownLatch区别?这样回答直接拿Offer(附源码级对比和避坑点)

面试官问CyclicBarrier和CountDownLatch区别?这样回答直接拿Offer(附源码级对比和避坑点) 在Java并发编程的面试中,CyclicBarrier和CountDownLatch的区别几乎是必问的高频考点。但真正能打动面试官的,绝不仅仅是背诵教…...

别再找组策略了!Windows 11家庭版/专业版通用,一条命令搞定密码永不过期

Windows 11密码永不过期终极指南:告别繁琐设置,一条命令解决所有版本难题 每次开机都被"密码即将过期"的提示烦扰?作为Windows 11用户,你可能已经尝试过各种图形界面设置却无功而返。特别是家庭版用户,面对缺…...

Realsense D435i相机标定避坑指南:从棋盘格准备到OpenCV立体校正全流程

Realsense D435i相机标定实战:从硬件配置到立体校正的完整避坑手册 在三维视觉和机器人领域,相机标定是构建精准感知系统的基石。Intel Realsense D435i作为一款广泛使用的深度相机,其标定质量直接影响着SLAM、三维重建等应用的精度。本文将分…...

WinAsar:告别命令行!551KB的Electron asar文件可视化处理神器

WinAsar:告别命令行!551KB的Electron asar文件可视化处理神器 【免费下载链接】WinAsar Portable and lightweight GUI utility to pack and extract asar( Electron archive ) files, Only 551 KB! 项目地址: https://gitcode.com/gh_mirrors/wi/WinA…...

Richard Socher创业公司获6.5亿美元融资,欲让AI自动化研发引领底层范式转移

Richard Socher创业公司获巨额融资一家创业公司获得了GV(Alphabet旗下VC)和Greycroft共同领投的6.5亿美元早期融资,NVIDIA和AMD也参与本轮融资,它的估值达到了46.5亿美元。这家公司的创始人是Richard Socher,他是AI领域…...

别再手动改Hex了!用Vector HexView的/remap命令,5分钟搞定固件地址重映射

嵌入式开发革命:Vector HexView自动化重映射技术实战指南 在汽车电子和物联网设备开发中,固件地址调整如同家常便饭。每当内存布局变更、Bootloader升级或外设地址重新分配时,嵌入式工程师们就不得不面对一项枯燥且容易出错的任务——手动修改…...

Graphormer实战:用最短路径和虚拟节点搞定分子性质预测(附PyTorch代码)

Graphormer实战:从分子结构到性质预测的完整实现指南 在药物发现和材料科学领域,准确预测分子的物理化学性质可以大幅加速研发进程。传统方法依赖昂贵的实验测量或复杂的量子化学计算,而图神经网络(GNN)和Transformer的结合——Graphormer&a…...

5分钟实现OBS多平台同步直播:obs-multi-rtmp插件完全指南

5分钟实现OBS多平台同步直播:obs-multi-rtmp插件完全指南 【免费下载链接】obs-multi-rtmp OBS複数サイト同時配信プラグイン 项目地址: https://gitcode.com/gh_mirrors/ob/obs-multi-rtmp 你是否厌倦了在不同直播平台间来回切换的繁琐操作?obs-…...

城通网盘下载速度慢?3分钟学会ctfileGet终极免费提速方案

城通网盘下载速度慢?3分钟学会ctfileGet终极免费提速方案 【免费下载链接】ctfileGet 获取城通网盘一次性直连地址 项目地址: https://gitcode.com/gh_mirrors/ct/ctfileGet 你是否曾经被城通网盘的龟速下载折磨得抓狂?面对50KB/s的限速、无尽的验…...

QQ音乐加密音频一键解密:qmcdump终极指南

QQ音乐加密音频一键解密:qmcdump终极指南 【免费下载链接】qmcdump 一个简单的QQ音乐解码(qmcflac/qmc0/qmc3 转 flac/mp3),仅为个人学习参考用。 项目地址: https://gitcode.com/gh_mirrors/qm/qmcdump 你是否曾为QQ音乐下…...

ABAQUS结果导出太麻烦?试试这个隐藏技巧,5分钟搞定截面节点应力数据到Excel

ABAQUS结果导出效率革命:5分钟精准捕获截面节点数据的全流程指南 在结构仿真分析领域,数据后处理往往消耗工程师40%以上的工作时间。当我第一次面对ABAQUS中复杂的应力云图,试图提取某个螺栓连接面的节点应力数据时,整整浪费了两天…...

DownKyi:解锁B站8K超高清视频下载的5个核心优势

DownKyi:解锁B站8K超高清视频下载的5个核心优势 【免费下载链接】downkyi 哔哩下载姬downkyi,哔哩哔哩网站视频下载工具,支持批量下载,支持8K、HDR、杜比视界,提供工具箱(音视频提取、去水印等)…...

从‘微软 ORG’到流畅中文NLP:你的zh_core_web_sm模型真的装对了吗?

从‘微软 ORG’到流畅中文NLP:你的zh_core_web_sm模型真的装对了吗? 当你在Spacy中加载zh_core_web_sm模型,运行示例文本"微软准备用十亿美金买下这家英国的创业公司"后,看到"微软"被正确标记为ORG&#xff0…...

保姆级教程:在Gazebo中为你的ROS机器人添加激光雷达和摄像头(附完整xacro配置)

Gazebo传感器配置实战:从激光雷达到摄像头的全流程解析 在机器人仿真开发中,让虚拟机器人"看得见"往往比让它"动起来"更具挑战性。许多开发者精心设计了URDF模型,却在Gazebo中遭遇传感器数据无法显示的困境——激光雷达没…...

从炼丹炉到生产线:在Linux服务器上为Stable Diffusion部署配置PyTorch环境(驱动+CUDA+Anaconda实战)

从炼丹炉到生产线:Linux服务器部署PyTorch环境全流程指南 引言:为什么需要专业化的AI开发环境? 在AI模型开发领域,我们常常把训练模型比作"炼丹"——需要精准控制各种"火候"参数。而要让这个"炼丹炉&quo…...

Flowable工作流回退功能避坑指南:从ruoyi-vue-pro源码看如何优雅处理并行网关

Flowable工作流并行网关回退机制深度解析:从ruoyi-vue-pro看复杂场景解决方案 在业务流程自动化领域,并行网关的处理一直是工作流引擎中最具挑战性的场景之一。当流程需要回退时,并行分支带来的状态管理复杂度会呈指数级增长。传统串行节点的…...

UVa 273 Jack Straws

题目分析 本题的题目背景源自一种名为 “Jack Straws\texttt{Jack Straws}Jack Straws” 的游戏,玩家需要从桌上一堆杂乱摆放的塑料或木质 “稻草” 中逐根取出,而不扰动其他稻草。本题不关心游戏过程,只关心一个问题:给定若干根稻…...

捡垃圾实战:让ESXi 7.0 U3识别老古董Mellanox ConnectX-2 10G网卡(附驱动修改全流程)

老硬件焕新:ESXi 7.0 U3下Mellanox ConnectX-2网卡驱动改造指南 在二手市场以几十元价格淘到的Mellanox ConnectX-2 10G双口网卡,性能依然强劲,却因为官方停止支持而无法在现代虚拟化平台上使用。本文将带你深入探索如何通过驱动改造&#xf…...

Spring Boot项目实战:手把手教你集成银联B2B无卡支付(SM2国密证书版)

Spring Boot实战:银联B2B无卡支付集成全流程解析(SM2国密证书版) 在企业级应用开发中,支付功能是不可或缺的核心模块。银联B2B无卡支付作为国内企业间交易的重要渠道,其安全性和稳定性备受开发者关注。本文将带你从零开…...

CentOS 7上搞定Dell iDRAC Service Module安装报错(附usbutils依赖解决)

CentOS 7上解决Dell iDRAC Service Module安装依赖问题的实战指南 当你在CentOS 7系统上尝试安装Dell iDRAC Service Module时,可能会遇到各种依赖问题导致安装失败。本文将深入剖析最常见的usbutils依赖报错及其解决方案,同时提供一系列实用技巧帮助你顺…...

茉莉花插件:5分钟解决Zotero中文文献管理三大难题

茉莉花插件:5分钟解决Zotero中文文献管理三大难题 【免费下载链接】jasminum A Zotero add-on to retrive CNKI meta data. 一个简单的Zotero 插件,用于识别中文元数据 项目地址: https://gitcode.com/gh_mirrors/ja/jasminum 还在为中文文献管理…...

保姆级教程:在Ubuntu 22.04上配置VNC Server,并用VNC Viewer远程桌面(解决加密报错)

深度解析Ubuntu 22.04 VNC远程桌面配置与加密协议调优实战 在分布式开发和远程协作成为主流的今天,掌握高效的远程桌面技术已成为开发者和运维人员的必备技能。Ubuntu作为最受欢迎的Linux发行版之一,其内置的VNC服务为远程访问提供了原生支持&#xff0c…...

用PyTorch手把手实现PGD对抗训练:从FGM的‘一步到位’到‘小步快跑’的实战代码详解

用PyTorch手把手实现PGD对抗训练:从FGM的‘一步到位’到‘小步快跑’的实战代码详解 对抗训练已成为提升模型鲁棒性的核心技术之一。不同于FGM(Fast Gradient Method)的"一步到位"策略,PGD(Projected Gradie…...

AI Agent智能体技术:从问答到执行的范式革命

标签:AI Agent、大模型、智能体、LangChain、ReAct、Function Calling 📖 前言 2026年5月20日,谷歌I/O 2026大会在美国加州山景城开幕。谷歌CEO桑达尔皮查伊(Sundar Pichai)在大会上宣布:“我们已正式进入’智能体Gemini时代’。”就在同一天,百度Create 2026大会上,…...

模块型OLT跟光模块有什么区别?

模块型OLT跟光模块有什么区别?明明是同一个 SFP 接口,插上去长得也差不多,为什么有的叫“光模块”,有的叫“模块型 OLT”? 它们到底有什么区别?能不能互换?选错了会怎样?同样是 SFP …...

从AB类到C类:拆解Doherty功放里载波与峰值支路的相位“打架”问题及宽带补偿方案

从AB类到C类:拆解Doherty功放里载波与峰值支路的相位“打架”问题及宽带补偿方案 在射频功率放大器设计中,Doherty架构因其高效率特性而备受青睐。然而,当工程师们试图将这种架构扩展到更宽频带时,往往会遇到一个令人头疼的问题—…...

手把手教你用AD9834 DDS模块DIY一个可调信号源(附AD原理图/PCB/程序)

从零构建AD9834 DDS可调信号源:硬件搭建与软件调优全指南 在电子设计与射频实验中,一个稳定可靠的可调信号源是不可或缺的工具。商用信号发生器往往价格昂贵,而基于AD9834 DDS模块的DIY方案,能以极低成本实现0-10MHz频率范围内的高…...