首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >MQTT服务接入超时案例:MQTT服务和Netty在异常场景下的保护机制

MQTT服务接入超时案例:MQTT服务和Netty在异常场景下的保护机制

作者头像
博文视点Broadview
发布于 2020-06-10 07:45:35
发布于 2020-06-10 07:45:35
4.4K0
举报

仅以此文献给李林锋新生的爱女。

Netty 4.1提供了MQTT协议栈,基于此可以非常方便地创建MQTT服务,尽管开发简单,但是在实际环境中会面临各种挑战,甚至会面临一些不遵循MQTT规范的端侧设备接入。

如果服务端没有考虑到各种异常场景,很难稳定运行,本文以生产环境MQTT服务无法提供接入服务为例,详细介绍MQTT服务和Netty在异常场景下的保护机制。

  • MQTT服务接入超时问题

1. 生产环境问题现象

生产环境的MQTT服务运行一段时间之后,发现新的端侧设备无法接入,连接超时。分析MQTT服务端日志,没有明显的异常,但是内存占用较高,查看连接数,发现有数十万个TCP连接处于ESTABLISHED状态,实际的MQTT连接数应该在1万个左右,显然这么多连接肯定存在问题。

由于MQTT服务端的内存是按照2万个左右连接数规模配置的,因此当连接数达到数十万个的规模之后,导致了服务端大量SocketChannel积压、内存暴涨、高频率GC和较长的STW时间,对端侧设备的接入造成了很大影响,部分设备MQTT握手超时,无法接入。

2. 连接数膨胀原因分析

通过抓包分析发现,一些端侧设备并没有按照MQTT协议规范进行处理,包括:

(1)客户端发起CONNECT连接,SSL握手成功之后没有按照协议规范继续处理,例如发送PING命令。

(2)客户端发起TCP连接,不做SSL握手,也不做后续处理,导致TCP连接被挂起。

由于服务端是严格按照MQTT规范实现的,上述端侧设备不按规范接入,实际上消息调度不到MQTT应用协议层。MQTT服务端依赖Keep Alive机制进行超时检测,当一段时间接收不到客户端的心跳和业务消息时,就会触发心跳超时,关闭连接。针对上述两种接入场景,由于MQTT的连接流程没有完成,MQTT协议栈不认为这个是合法的MQTT连接,因此心跳保护机制无法对上述TCP连接进行检测。客户端和服务端都没有主动关闭这个连接,导致TCP连接一直保持。

MQTT连接建立过程如下图。

MQTT连接建立过程

3. 无效连接的关闭策略

针对这种不遵循MQTT规范的端侧设备,除了要求对方按照规范修改,服务端还需要做可靠性保护,具体策略如下。

1)端侧设备的TCP连接接入后,启动一个链路检测定时器加入Channel对应的NioEventLoop。

2)链路检测定时器一旦触发,就主动关闭TCP连接。

3)TCP连接完成MQTT协议层的CONNECT之后,删除之前创建的链路检测定时器。

MQTT无效连接检测机制如下图。

MQTT无效连接检测机制

4. 问题总结

生产环境升级版本之后,平稳运行,查看MQTT连接数,稳定在1万个左右,与预期一致,问题得到解决。

对于MQTT服务端,除了要遵循协议规范,还需要对那些不遵循规范的客户端接入进行保护,不能因为一些客户端没按照规范实现,导致服务端无法正常工作。系统的可靠性设计更多的是在异常场景下保护系统的稳定运行。

  • 基于Netty的可靠性设计

从应用场景看,Netty是基础的通信框架,一旦出现问题,轻则需要重启应用,重则可能导致整个业务中断。它的可靠性会影响整个业务集群的数据通信和交换,在以分布式为主的软件架构体系中,通信中断就意味着整个业务中断,分布式架构对通信的可靠性要求非常高。

从运行环境看,Netty会面临恶劣的网络环境,这就要求它自身的可靠性要足够好,平台能够解决的可靠性问题需要由Netty自身来解决,否则会导致上层用户关注过多的底层故障,降低Netty的易用性,同时增加用户的开发和运维成本。

Netty的可靠性如此重要,它的任何故障都可能导致业务中断,产生巨大的经济损失。因此,Netty在版本的迭代中不断加入新的可靠性特性来满足用户日益增长的高可靠和健壮性需求。

1. 业务定制I/O异常

在大多数场景下,当底层网络发生故障的时候,应该由底层的NIO框架负责释放资源,处理异常等,上层的业务应用不需要关心底层的处理细节。但是,在一些特殊的场景下,用户可能需要关心这些异常,并针对这些异常进行定制处理,例如:

(1)客户端的断连和重连机制。

(2)消息的缓存重发。

(3)在接口日志中详细记录故障细节。

(4)运维相关功能,例如告警、触发邮件/短信等。

Netty的处理策略是,发生I/O异常时,底层的资源由它负责释放,同时将异常堆栈信息以事件的形式通知给上层用户,由用户对异常进行定制。这种处理机制既保证了异常处理的安全性,也向上层提供了灵活的定制能力。Netty异常通知接口定义如下图。

Netty异常通知接口定义

2. 链路的有效性检测

当网络发生单通、连接被防火墙挡住、长时间GC或者通信线程发生非预期异常时,链路会不可用且不易被及时发现。特别是如果异常发生在凌晨业务低谷期间,当早晨业务高峰到来时,由于链路不可用导致瞬间大批量业务失败或者超时,这将对系统的可靠性产生重大的威胁。

从技术层面看,要解决链路的可靠性问题,必须周期性地对链路进行有效性检测。目前最流行和通用的做法就是心跳检测。

心跳检测机制分为三个层面。

(1)TCP层面的心跳检测,即TCP的Keep-Alive机制,它的作用域是整个TCP协议栈。

(2)协议层的心跳检测,主要存在于长连接协议中,例如MQTT。

(3)应用层的心跳检测,它主要由各业务产品通过约定方式定时给对方发送心跳消息实现。心跳检测的目的就是确认当前链路是否可用,对方是否活着并且能够正常接收和发送消息。

作为高可靠的NIO框架,Netty也提供了心跳检测机制,利用IdleStateHandler可以方便地实现业务层的心跳检测。

3. 内存保护

NIO通信的内存保护主要集中在如下几点。

1)链路总数的控制:每条链路都包含接收和发送缓冲区,链路个数太多容易导致内存溢出。

2)单个缓冲区的上限控制:防止非法长度或者消息过大导致内存溢出。

3)缓冲区内存释放:防止因为缓冲区使用不当导致的内存泄漏。

4)NIO消息发送队列的长度上限控制。

防止内存池泄漏

为了提升内存的利用率,Netty提供了内存池和对象池。但是,基于缓存池实现以后需要对内存的申请和释放进行严格的管理,否则很容易导致内存泄漏。

如果不采用内存池技术实现,每次对象都以方法的局部变量形式被创建,用完之后,只要不再继续引用它,JVM会自动释放。但是,一旦引入内存池机制,对象的生命周期将由内存池负责管理,通常是全局引用(含线程级缓存),如果不显式释放,JVM是不会回收这部分内存的。对于从内存池申请的对象,使用完毕一定要及时释放,防止内存泄漏。

缓冲区溢出保护

当我们对消息进行解码的时候,需要创建缓冲区(Netty的ByteBuf)。缓冲区的创建方式通常有两种。

1)容量预分配,在实际读写过程中如果不够再扩展。

2)根据协议消息长度创建缓冲区。

在实际的商用环境中,如果遇到畸形码流攻击、协议消息编码异常、消息丢包等问题,可能会解析到一个超长的长度字段。我曾经遇到过类似问题,报文长度字段值竟然超过2GB,由于代码的一个分支没有对长度上限进行有效保护,导致内存溢出。系统重启几秒后再次发生内存溢出,幸好及时定位到问题的根本原因,没有造成严重的事故。

Netty提供了编解码框架,因此对于解码缓冲区的上限保护就显得非常重要,在实际项目中主要通过如下两种方式对缓冲区进行保护。

1)创建ByteBuf时对它的容量上限进行保护性设置,如下。

ByteBuf容量上限保护

2)在消息解码的时候,对消息长度进行判断,如果超过最大容量,则抛出解码异常,拒绝分配内存,以LengthFieldBasedFrameDecoder的decode方法为例进行说明,代码如下:

3. 消息发送队列积压保护

Netty的NIO消息发送队列ChannelOutboundBuffer并没有容量上限,它会随着消息的积压自动扩展,直到达到0x7fffffff。

如果对方处理速度比较慢,会导致TCP滑窗长时间为0;如果消息发送方发送速度过快或者一次批量发送消息量过大,会导致ChannelOutboundBuffer的内存膨胀,可能会使系统的内存溢出。

建议业务配置合适的高水位(writeBufferWaterMark)对消息发送速度进行控制,同时在发送业务消息时,调用Channel的isWritable方法判断Channel是否可写,如果不可写则不要继续发送,否则会导致发送队列积压,出现OOM异常。

  • 总 结

可靠性设计的关键在于对非预期异常场景的保护,应用层协议栈会考虑应用协议异常时通信双方应该怎么正确处理异常,但是对于那些不遵循协议规范实现的客户端,协议规范是无法强制约束对方的,特别是在物联网应用中,面对各种厂家的不同终端设备接入,服务端需要应对各种异常。只有可靠性做得足够好,MQTT服务才能更从容地应对海量设备的接入。

推荐阅读

《Netty进阶之路:跟着案例学Netty》

Netty将Java NIO接口封装,提供了全异步编程方式,是各大Java项目的网络应用开发必备神器。本书作者李林锋是国内Netty技术的先行者和布道者,本书是他继《Netty权威指南》之后的又一力作。

本书内容精选自1000多个一线业务实际案例,真正从原理到实践全景式讲解Netty项目实践,快速领悟Netty专家花大量时间积累的经验,助你提高编程水平及分析解决问题的能力。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2019-03-05,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 博文视点Broadview 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
Netty 总结篇
Netty一个主要的目标就是促进“关注点分离”:使业务逻辑从网络基础设施应用程序中分离。不仅仅是Netty框架,其他框架的设计目的也大都是为了使业务程序和底层技术解耦,使程序员更加专注于业务逻辑实现,提高开发质量和效率。Netty为什么性能如此之高,主要是其内部的Reactor模型机制。
luoxn28
2019/12/12
1.3K0
Netty 总结篇
跟着源码学IM(十一):一套基于Netty的分布式高可用IM详细设计与实现(有源码)
本文将要分享的是如何从零实现一套基于Netty框架的分布式高可用IM系统,它将支持长连接网关管理、单聊、群聊、聊天记录查询、离线消息存储、消息推送、心跳、分布式唯一ID、红包、消息同步等功能,并且还支持集群部署。
JackJiang
2023/06/09
1.4K0
跟着源码学IM(十一):一套基于Netty的分布式高可用IM详细设计与实现(有源码)
一篇文章,读懂Netty的高性能架构之道
Netty是一个高性能、异步事件驱动的NIO框架,它提供了对TCP、UDP和文件传输的支持,作为一个异步NIO框架,Netty的所有IO操作都是异步非阻塞的,通过Future-Listener机制,用户可以方便的主动获取或者通过通知机制获得IO操作结果。
Spark学习技巧
2021/03/05
9240
一篇文章,读懂Netty的高性能架构之道
netty原理分析
Netty是一个高性能、异步事件驱动的NIO框架,它提供了对TCP、UDP和文件传输的支持,作为一个异步NIO框架,Netty的所有IO操作都是异步非阻塞的,通过Future-Listener机制,用户可以方便的主动获取或者通过通知机制获得IO操作结果。
Java架构师历程
2019/03/08
9560
netty原理分析
基于Netty实现海量接入的推送服务技术要点
一.引言 1关于本次话题 最近很多从事移动互联网和物联网开发的同学给我发邮件或者微博私信我,咨询推送服务相关的问题。问题五花八门,在帮助大家答疑解惑的过程中,我也对问题进行了总结,大概可以归纳为如下几类: Netty是否可以做推送服务器? 如果使用Netty开发推送服务,一个服务器最多可以支撑多少个客户端? 使用Netty开发推送服务遇到的各种技术问题。 由于咨询者众多,关注点也比较集中,我希望通过本文的案例分析和对推送服务设计要点的总结,帮助大家在实际工作中少走弯路。 2关于推送服务 移
用户1263954
2018/06/22
1.6K0
Netty十年感悟
最近在某高校做技术交流时,有个博士加我微信时发现他是《Netty权威指南》的读者,研究生做项目时涉及到网络通信于是买了这本书边学习边实践。2022年ArchSummit架构师峰会杭州站讲师互动环节,来自阿里、小红书、得物等很多平台中间件的90后架构师说在上学时就读过《Netty权威指南》或者学习过我写的Netty系列技术文章,受益很多。
博文视点Broadview
2024/04/02
2670
Netty十年感悟
【大牛经验】Java NIO通信框架在电信领域的实践
Java NIO通信框架在电信领域的实践 1. 华为电信软件技术架构演进 1.1. 电信软件 从广义上看电信软件的范围非常广,细分实际可以分为两大类:系统软件和业务应用软件。 系统软件包括路由器底层的信令机软件、手机操作系统等,业务应用软件主要包括客户关系管理CRM、网上营业厅、融合计费OCS和各类消息网关,例如短信网关、彩信网关等。 本文重点介绍电信业务应用软件的技术变迁历史,以及华为电信软件架构演进和Java NIO框架在技术变迁中起到的关键作用。 1.2. 华为电信软件的技术演进史 1.2.1. C和
Java帮帮
2018/03/15
1.9K0
【大牛经验】Java NIO通信框架在电信领域的实践
使用Netty,我们到底在开发些什么?
在java界,netty无疑是开发网络应用的拿手菜。你不需要太多关注复杂的nio模型和底层网络的细节,使用其丰富的接口,可以很容易的实现复杂的通讯功能。
xjjdog
2019/09/24
8400
使用Netty,我们到底在开发些什么?
Netty高性能之道
1. 背景 1.1. 惊人的性能数据 最近一个圈内朋友通过私信告诉我,通过使用Netty4 + Thrift压缩二进制编解码技术,他们实现了10W TPS(1K的复杂POJO对象)的跨节点远程服务调用。相比于传统基于Java序列化+BIO(同步阻塞IO)的通信框架,性能提升了8倍多。 事实上,我对这个数据并不感到惊讶,根据我5年多的NIO编程经验,通过选择合适的NIO框架,加上高性能的压缩二进制编解码技术,精心的设计Reactor线程模型,达到上述性能指标是完全有可能的。 下面我们就一起来看下Netty是如
用户1263954
2018/01/30
1.4K0
Netty高性能之道
膨胀了!我要手写QQ底层!(附源码)
一直想写一篇关于im即时通讯分享的文章,无奈工作太忙,很难抽出时间。今天终于从公司离职了,打算好好休息几天再重新找工作,趁时间空闲,决定静下心来写一篇文章,毕竟从前辈那里学到了很多东西。工作了五年半,这三四年来一直在做社交相关的项目,有直播、即时通讯、短视频分享、社区论坛等产品,深知即时通讯技术在一个项目中的重要性,本着开源分享的精神,也趁这机会总结一下,所以写下这篇文章,文中有不对之处欢迎批评与指正。
小林C语言
2019/07/16
1.7K0
膨胀了!我要手写QQ底层!(附源码)
2021最新版BAT大厂Netty面试题集(有详尽答案)
一个高性能、异步事件驱动的 NIO 框架,它提供了对 TCP、UDP 和文件传输的支持 使用更高效的 socket 底层,对 epoll 空轮询引起的 cpu 占用飙升在内部进行了处理,避免 了直接使用 NIO 的陷阱,简化了 NIO 的处理方式。 采用多种 decoder/encoder 支持,对 TCP 粘包/分包进行自动化处理 可使用接受/处理线程池,提高连接效率,对重连、心跳检测的简单支持 可配置IO线程数、TCP参数, TCP接收和发送缓冲区使用直接内存代替堆内存,通过内存 池的方式循环利用 ByteBuf 通过引用计数器及时申请释放不再引用的对象,降低了 GC 频率 使用单线程串行化的方式,高效的 Reactor 线程模型 大量使用了 volitale、使用了 CAS 和原子类、线程安全类的使用、读写锁的使用
JavaEdge
2021/10/18
9690
Netty初级应用之通讯框架分析
原文:www.cnblogs.com/scy251147/p/10498008.html
良月柒
2020/06/22
5160
Netty初级应用之通讯框架分析
Netty框架整体架构及源码知识点
Netty是一个高性能、异步事件驱动的NIO框架,它提供了对TCP、UDP和文件传输的支持。作为当前最流行的NIO框架,Netty在互联网领域、大数据分布式计算领域、游戏行业、通信行业等获得了广泛的应用,一些业界著名的开源组件也基于Netty的NIO框架构建。
用户4447430
2019/05/04
9500
Netty相关知识汇总
1)、TCP面向连接(如打电话要先拨号建立连接);UDP是无连接的,即发送数据之前不需要建立连接。
用户3467126
2019/10/08
1K0
Netty相关知识汇总
Netty高性能之道
【导读】传统RPC性能差的原因有三个,一是网络传输方式是同步阻塞的,二是Java原生序列化性能差,无法跨语言使用,序列化之后体积大等,三是线程模型会占用大量系统资源。所以今天来看以下Netty的高性能是如何建立的?
Liusy
2020/09/01
7450
Netty 系列之 Netty 高性能之道
最近一个圈内朋友通过私信告诉我,通过使用 Netty4 + Thrift 压缩二进制编解码技术,他们实现了 10W TPS(1K 的复杂 POJO 对象)的跨节点远程服务调用。相比于传统基于 Java 序列化 +BIO(同步阻塞 IO)的通信框架,性能提升了 8 倍多。
chengcheng222e
2021/11/04
7720
Netty权威指南_算法笔记上机指南pdf
fd:file descriptor,文件描述符。linux内核将所有外部设备都看作一个文件来操作,对文件的读写会调用内核提供的命令,返回一个文件描述符。对一个socket的读写也会有相应的socket fd。描述符就是一个指向内核中结构体的数字。
全栈程序员站长
2022/09/20
1.4K0
Netty权威指南_算法笔记上机指南pdf
自已开发IM有那么难吗?手把手教你自撸一个Andriod版简易IM (有源码)
一直想写一篇关于im即时通讯分享的文章,无奈工作太忙,很难抽出时间。今天终于从公司离职了,打算好好休息几天再重新找工作,趁时间空闲,决定静下心来写一篇文章,毕竟从前辈那里学到了很多东西。
JackJiang
2019/07/22
1.2K0
Netty入门之WebSocket初体验
BIO即同步阻塞模式一请求一应答的通信模型,该模型最大的问题就是缺乏弹性伸缩能力,当客户端并发访问量增加后,服务端的线程个数和客户端并发访问数呈1:1的正比关系,由于线程是JAVA虚拟机非常宝贵的系统资源,当线程数膨胀之后,系统的性能将急剧下降,随着并发访问量的继续增大,系统会发生线程堆栈溢出、创建新线程失败等问题,并最终导致进程宕机或者僵死,不能对外提供服务。
端碗吹水
2020/09/23
1.1K0
Netty入门之WebSocket初体验
Netty一文深入
通过2个位置指针来协助缓冲区的读写,读使用 readerIndex,写使用 writerIndex。
趣学程序-shaofeer
2020/07/17
8280
Netty一文深入
推荐阅读
相关推荐
Netty 总结篇
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档