老年代又占用100%了,顺便发现了vertx-redis-client 的bug

周五中午陆续收到线上广告检索服务某些节点内存不足的报警,这个服务在我们的广告系统中非常重要,所以需要快速止损,立马重启有问题的节点,可是这个时间点真是尴尬,刚好去食堂吃饭了,还好同事机灵在手机上登录云平台,把有问题的节点重启了

 

回到工位后,又陆续收到了相同的报警信息,开始和同事一起排查,找到占用内存过高的进程后,通过查看 gc 情况,发现新生代和老年代都满了,内存得不到释放,遇到这种问题就摩拳擦掌,想排查出来,可是毕竟这个服务会影响线上收入,所以我们当时的处理流程如下:

1.先把有问题的节点重启,不影响线上收入

2.留一个问题节点用于排查问题【注意,当问题信息搜集完后,把该问题节点先暂时从集群中摘除】

 

通过 jmap -histo pid |more 查看内存中占用较大的对象都有什么,发现有个广告创意相关的对象占用挺多,所以需要生成此时的dump文件,查看引用信息

 

jmap -dump:live,format=b,file=adsearch.dump pid

 

生成dump文件和下载到本地是个耗时的过程......

 

下载到本地后,我用了 visualvm 分析 dump 文件

 

发现有大量Promotion对象【上面说的广告创意对象】,在PrequenceControlService【用于频控的服务】中需要读redis ,因为我们的服务是异步的【用vertx实现】,所以用了配套的 vertx-redis-client, 版本是3.6.3

 

通过 dump 文件和源代码可知 redis 的 Command 堆积到了 LinkedList 中

在 RedisConnection 这个类中,发现 send 方法不断的往waitling的LinkedList中添加,且添加进去的一直没有得到响应,导致内存暴涨,直至gc

 

这个版本客户端的设计是有 bug 的,如果连接建立,但 redis server 没有及时响应,请求会一直往队列中添加,但是3.6.3版本的  vertx-redis-client 并没有对添加操作做容量限制,导致内存占用太多

 

正确的应该有容量限制的,如果超过了最大值,就报错,这样可以保证内存不被撑爆【画外音:但还是解决不了连接不返回的问题】

去github上看 vertx-redis-client 的最新版,发现已经优化了,去掉了RedisConnection,取而代之的是 RedisClient

 

 

 

 

可以看到在最新版的客户端中,往队列中添加命令时做了容量的限制与判断,默认大小是2048

 

所以我们的服务升级相应的 jar 包就好了,但是并不能根本解决问题,如果连接再出问题,还是得不到响应,服务还是不可用

 

至于 redis server 端发生的细节,我还不太清楚,我同事在跟进,大概是因为 client 到 redis server 之间有一层代理,clent与代理之间的链接建立成功后,在 server 端 dba切换节点了,导致连接出了问题,报警时间点和dba切库时间点完全吻合,我们已经和 dba 反映了情况,他们不切节点,就不会发生问题,具体细节还待下周我同事和dba同学一起排查

 

通过这次线上问题的排查,总结如下:

1.先把有问题的节点重启,不影响线上收入

2.留一台,用于排查问题【注意,问题要用的信息搜集完后,把该节点从集群中摘除】

3.用 linux 常用命令和工具进行进行排查

4.时刻关注项目中用到的开源组件的issue和版本更新,以便了解老版本中的一些bug

 

对排查jvm相关问题感兴趣的朋友可以查看我之前的文章

cpu使用率过高和jvm old占用过高排查过程

频繁FGC的真凶原来是它

 

 

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
您好!对于使用 Vert.x-MQTT 和 Protobuf 进行编解码,您可以按照以下步骤进行操作: 1. 首先,您需要确保已经在您的项目中添加了 Vert.x-MQTT 和 Protobuf 的相关依赖。 2. 创建一个 Protobuf 定义文件(.proto 文件),用于定义您的消息结构。例如,假设您的消息结构如下所示: ``` syntax = "proto3"; message MyMessage { string field1 = 1; int32 field2 = 2; } ``` 3. 使用 Protobuf 编译器将 .proto 文件编译为 Java 类。您可以使用以下命令行命令: ``` protoc --java_out=<output_directory> <your_proto_file.proto> ``` 此命令将生成相应的 Java 类文件,用于在代码中进行消息的编码和解码。 4. 在 Vert.x-MQTT 中使用编码器和解码器。您可以创建一个类来实现 Vert.x-MQTT 的 `MqttMessageCodec` 接口,并在其中实现编码和解码逻辑。在这个类中,您可以使用 Protobuf 生成的类来进行消息的序列化和反序列化。 以下是一个简单的示例: ```java import io.vertx.mqtt.MqttMessageCodec; import io.vertx.mqtt.messages.MqttPublishMessage; import com.example.protobuf.MyMessage; public class MyMessageCodec implements MqttMessageCodec<MyMessage> { @Override public void encodeToWire(Buffer buffer, MyMessage myMessage) { byte[] payload = myMessage.toByteArray(); // 在此处将 payload 写入到 buffer 中 } @Override public MyMessage decodeFromWire(int pos, Buffer buffer) { // 从 buffer 中读取 payload,并将其反序列化为 MyMessage 对象 byte[] payload = buffer.getBytes(pos, buffer.length()); return MyMessage.parseFrom(payload); } @Override public MyMessage transform(MqttPublishMessage mqttPublishMessage) { // 将 MqttPublishMessage 转换为 MyMessage 对象 byte[] payload = mqttPublishMessage.payload().getBytes(); return MyMessage.parseFrom(payload); } @Override public MqttPublishMessage transform(MyMessage myMessage) { // 将 MyMessage 对象转换为 MqttPublishMessage return MqttPublishMessage.create(myMessage.toByteArray(), false, QualityOfService.AT_MOST_ONCE, false); } } ``` 5. 最后,在您的 Vert.x-MQTT 代码中,使用您自定义的消息编解码器。示例如下: ```java MqttServerOptions options = new MqttServerOptions(); options.setCodecs(new MyMessageCodec()); MqttServer mqttServer = MqttServer.create(vertx, options); mqttServer.endpointHandler(endpoint -> { endpoint.publishHandler(message -> { // 处理收到的消息 MyMessage myMessage = message.payload(); // ... }); }).listen(); ``` 这样,您就可以使用 Vert.x-MQTT 和 Protobuf 进行消息的编码和解码了。 请注意,上述示例是一个简单示例,您可能需要根据您的实际需求进行适当的修改和调整。希望对您有所帮助!如果您有任何其他问题,请随时提问。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值