当前位置:首页 > 单片机 > 架构师社区
[导读]作者:vivo互联网服务器团队-ZhangLin一、业务背景目前移动端的使用场景中会用到大量的消息推送,push消息可以帮助运营人员更高效地实现运营目标(比如给用户推送营销活动或者提醒APP新功能)。对于推送系统来说需要具备以下两个特性:消息秒级送到用户,无延时,支持每秒百万推送,单机百万长连接。支持通知、文本、自定义消息透传等展现形式。正是由于以上原因,对于系统的开发和维护带来了挑战。下图是推送系统的简单描述(API->推送模块->手机)。二、问题背景推送系统中长连接集群在稳定性测试、压力测试阶运行一段时间后随机会出现一个进程挂掉的情况,概率较小(频率为一个月左右发生一次),这会影响部分客户...

作者:vivo互联网服务器团队-Zhang Lin

一、业务背景


目前移动端的使用场景中会用到大量的消息推送,push消息可以帮助运营人员更高效地实现运营目标(比如给用户推送营销活动或者提醒APP新功能)。


对于推送系统来说需要具备以下两个特性:

  • 消息秒级送到用户,无延时,支持每秒百万推送,单机百万长连接。

  • 支持通知、文本、自定义消息透传等展现形式。正是由于以上原因,对于系统的开发和维护带来了挑战。下图是推送系统的简单描述(API->推送模块->手机)。


深入理解Netty-从偶现宕机看Netty流量控制


二、问题背景


推送系统中长连接集群在稳定性测试、压力测试阶运行一段时间后随机会出现一个进程挂掉的情况,概率较小(频率为一个月左右发生一次),这会影响部分客户端消息送到的时效。


推送系统中的长连接节点(Broker系统)是基于Netty开发,此节点维护了服务端和手机终端的长连接,线上问题出现后,添加Netty内存泄露监控参数进行问题排查,观察多天但并未排查出问题。


于长连接节点是Netty开发,为便于读者理解,下面简单介绍一下Netty


三、 Netty介绍


Netty是一个高性能、异步事件驱动的NIO框架,基于Java NIO提供的API实现。它提供了对TCP、UDP和文件传输的支持,作为当前最流行的NIO框架,Netty在互联网领域、大数据分布式计算领域、游戏行业、通信行业等获得了广泛的应用,HBase,Hadoop,Bees,Dubbo等开源组件也基于Netty的NIO框架构建。


四、问题分析


4.1 猜想


最初猜想是长连接数导致的,但经过排查日志、分析代码,发现并不是此原因造成。


长连接数:39万,如下图:


深入理解Netty-从偶现宕机看Netty流量控制


每个channel字节大小1456, 按40万长连接计算,不致于产生内存过大现象。


4.2 查看GC日志


查看GC日志,发现进程挂掉之前频繁full GC(频率5分钟一次),但内存并未降低,怀疑堆外内存泄露。


4.3 分析heap内存情况


ChannelOutboundBuffer对象占将近5G内存,泄露原因基本可以确定:ChannelOutboundBuffer的entry数过多导致,查看ChannelOutboundBuffer的源码可以分析出,是ChannelOutboundBuffer中的数据。


没有写出去,导致一直积压;

ChannelOutboundBuffer内部是一个链表结构。


深入理解Netty-从偶现宕机看Netty流量控制


4.4 从上图分析数据未写出去,为什么会出现这种情况?


代码中实际有判断连接是否可用的情况(Channel.isActive),并且会对超时的连接进行关闭。从历史经验来看,这种情况发生在连接半打开(客户端异常关闭)的情况比较多---双方不进行数据通信无问题。


按上述猜想,测试环境进行重现和测试。

1)模拟客户端集群,并与长连接服务器建立连接,设置客户端节点的防火墙,模拟服务器与客户端网络异常的场景(即要模拟Channel.isActive调用成功,但数据实际发送不出去的情况)。


2)调小堆外内存,持续发送测试消息给之前的客户端。消息大小(1K左右)。


3)按照128M内存来计算,实际上调用9W多次就会出现。


深入理解Netty-从偶现宕机看Netty流量控制


五、问题解决


5.1 启用autoRead机制


当channel不可写时,关闭autoRead;

public void channelReadComplete(ChannelHandlerContext ctx) throws Exception { if (!ctx.channel().isWritable()) { Channel channel = ctx.channel(); ChannelInfo channelInfo = ChannelManager.CHANNEL_CHANNELINFO.get(channel); String clientId = ""; if (channelInfo != null) { clientId = channelInfo.getClientId(); }
LOGGER.info("channel is unwritable, turn off autoread, clientId:{}", clientId); channel.config().setAutoRead(false); }}

当数据可写时开启autoRead;

@Overridepublic void channelWritabilityChanged(ChannelHandlerContext ctx) throws Exception{ Channel channel = ctx.channel(); ChannelInfo channelInfo = ChannelManager.CHANNEL_CHANNELINFO.get(channel); String clientId = ""; if (channelInfo != null) { clientId = channelInfo.getClientId(); } if (channel.isWritable()) { LOGGER.info("channel is writable again, turn on autoread, clientId:{}", clientId); channel.config().setAutoRead(true); }}

说明:

深入理解Netty-从偶现宕机看Netty流量控制


autoRead的作用是更精确的速率控制,如果打开的时候Netty就会帮我们注册读事件。当注册了读事件后,如果网络可读,则Netty就会从channel读取数据。那如果autoread关掉后,则Netty会不注册读事件。


这样即使是对端发送数据过来了也不会触发读事件,从而也不会从channel读取到数据。当recv_buffer满时,也就不会再接收数据。


5.2 设置高低水位

serverBootstrap.option(ChannelOption.WRITE_BUFFER_WATER_MARK, new WriteBufferWaterMark(1024 * 1024, 8 * 1024 * 1024));
注:高低水位配合后面的isWritable使用

5.3 增加channel.isWritable()的判断


channel是否可用除了校验channel.isActive()还需要加上channel.isWrite()的判断,isActive只是保证连接是否激活,而是否可写由isWrite来决定。

private void writeBackMessage(ChannelHandlerContext ctx, MqttMessage message) { Channel channel = ctx.channel(); //增加channel.isWritable()的判断 if (channel.isActive()
本站声明: 本文章由作者或相关机构授权发布,目的在于传递更多信息,并不代表本站赞同其观点,本站亦不保证或承诺内容真实性等。需要转载请联系该专栏作者,如若文章内容侵犯您的权益,请及时联系本站删除。
换一批
延伸阅读

LED驱动电源的输入包括高压工频交流(即市电)、低压直流、高压直流、低压高频交流(如电子变压器的输出)等。

关键字: 驱动电源

在工业自动化蓬勃发展的当下,工业电机作为核心动力设备,其驱动电源的性能直接关系到整个系统的稳定性和可靠性。其中,反电动势抑制与过流保护是驱动电源设计中至关重要的两个环节,集成化方案的设计成为提升电机驱动性能的关键。

关键字: 工业电机 驱动电源

LED 驱动电源作为 LED 照明系统的 “心脏”,其稳定性直接决定了整个照明设备的使用寿命。然而,在实际应用中,LED 驱动电源易损坏的问题却十分常见,不仅增加了维护成本,还影响了用户体验。要解决这一问题,需从设计、生...

关键字: 驱动电源 照明系统 散热

根据LED驱动电源的公式,电感内电流波动大小和电感值成反比,输出纹波和输出电容值成反比。所以加大电感值和输出电容值可以减小纹波。

关键字: LED 设计 驱动电源

电动汽车(EV)作为新能源汽车的重要代表,正逐渐成为全球汽车产业的重要发展方向。电动汽车的核心技术之一是电机驱动控制系统,而绝缘栅双极型晶体管(IGBT)作为电机驱动系统中的关键元件,其性能直接影响到电动汽车的动力性能和...

关键字: 电动汽车 新能源 驱动电源

在现代城市建设中,街道及停车场照明作为基础设施的重要组成部分,其质量和效率直接关系到城市的公共安全、居民生活质量和能源利用效率。随着科技的进步,高亮度白光发光二极管(LED)因其独特的优势逐渐取代传统光源,成为大功率区域...

关键字: 发光二极管 驱动电源 LED

LED通用照明设计工程师会遇到许多挑战,如功率密度、功率因数校正(PFC)、空间受限和可靠性等。

关键字: LED 驱动电源 功率因数校正

在LED照明技术日益普及的今天,LED驱动电源的电磁干扰(EMI)问题成为了一个不可忽视的挑战。电磁干扰不仅会影响LED灯具的正常工作,还可能对周围电子设备造成不利影响,甚至引发系统故障。因此,采取有效的硬件措施来解决L...

关键字: LED照明技术 电磁干扰 驱动电源

开关电源具有效率高的特性,而且开关电源的变压器体积比串联稳压型电源的要小得多,电源电路比较整洁,整机重量也有所下降,所以,现在的LED驱动电源

关键字: LED 驱动电源 开关电源

LED驱动电源是把电源供应转换为特定的电压电流以驱动LED发光的电压转换器,通常情况下:LED驱动电源的输入包括高压工频交流(即市电)、低压直流、高压直流、低压高频交流(如电子变压器的输出)等。

关键字: LED 隧道灯 驱动电源
关闭