You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Hazelcast IQueue高请求下对象堆积致应用性能下降问题

Hazelcast IQueue堆积导致应用性能下降问题排查与解决

问题背景

客户端REST应用通过Hazelcast IQueue与计算器应用通信:REST端将对象加入队列,计算器应用通过监听器接收对象并执行删除操作。正常场景下运行稳定,但当请求间隔小于0.3秒时,队列开始出现对象堆积,堆积量过大时会导致整个应用性能下降。

当前代码与配置

删除队列对象的代码

public void deleteFromQueue(String queueId, Object object) throws NullPointerException, ClassCastException {
    GenericMessage<?> incomeMessage = (GenericMessage<?>) object;
    boolean removed = false;
    removed = hazelcastInstance.getQueue(queueId).remove(object);

    if (!removed) {
        Iterator<Object> iterator = hazelcastInstance.getQueue(queueId).iterator();
        while (iterator.hasNext()) {
            Object next = iterator.next();
            GenericMessage<?> message = (GenericMessage<?>) next;
            if (incomeMessage.getHeaders().getKey().equalsIgnoreCase(message.getHeaders().getKey())) {
                removed = hazelcastInstance.getQueue(queueId).remove(next);
                break;
            }
        }

        if (!removed) {
            if (hazelcastInstance.getQueue(queueId).size() == 1) {
                hazelcastInstance.getQueue(queueId).poll();
            }
        }
    }
}

监听器线程池配置

@Bean(name = "threadPoolTaskExecutor")
public Executor threadPoolTaskExecutor() {
    ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
    executor.setCorePoolSize(5);
    executor.setMaxPoolSize(10);
    executor.setWaitForTasksToCompleteOnShutdown(true);
    executor.setQueueCapacity(15);
    executor.initialize();
    return executor;
}

监控信息

2024-04-30 00:00:01,578 INFO c.h.l.StandardLoggerFactory$StandardLogger [hz.hazelcast_calculator.HealthMonitor] [127.0.0.1]:0000 [NAME] [5.3.6] processors=8, physical.memory.total=31.3G, physical.memory.free=589.2M, swap.space.total=0, swap.space.free=0, heap.memory.used=8.6G, heap.memory.free=13.9G, heap.memory.total=22.5G, heap.memory.max=25.0G, heap.memory.used/total=38.20%, heap.memory.used/max=34.41%, minor.gc.count=142365, minor.gc.time=2012868ms, major.gc.count=2186, major.gc.time=3189851ms, unknown.gc.count=8, unknown.gc.time=38ms, load.process=0.00%, load.system=99.55%, load.systemAverage=14.04, thread.count=439, thread.peakCount=469, cluster.timeDiff=0, event.q.size=0, executor.q.async.size=0, executor.q.client.size=0, executor.q.client.query.size=0, executor.q.client.blocking.size=0, executor.q.query.size=0, executor.q.scheduled.size=0, executor.q.io.size=0, executor.q.system.size=0, executor.q.operations.size=1, executor.q.priorityOperation.size=0, operations.completed.count=1655285419, executor.q.mapLoad.size=0, executor.q.mapLoadAllKeys.size=0, executor.q.cluster.size=0, executor.q.response.size=0, operations.running.count=0, operations.pending.invocations.percentage=0.00%, operations.pending.invocations.count=1, proxy.count=953, clientEndpoint.count=3, connection.active.count=3, client.connection.count=2, connection.count=2

问题分析

  1. 队列删除逻辑性能极差

    • Hazelcast IQueue的remove(Object)是分布式操作,需要遍历整个队列匹配对象,队列堆积时该操作会占用大量集群资源与CPU。
    • 第一次删除失败后再次遍历队列并调用remove(next),双重遍历操作进一步加剧性能损耗。
    • size()方法是分布式状态查询操作,性能低下,且业务逻辑中该判断冗余。
  2. 线程池配置无法应对高并发

    • 核心线程数(5)与最大线程数(10)过低,8核机器无法充分利用CPU资源处理任务。
    • 队列容量(15)过小,高并发场景下任务快速填满队列,导致新任务等待或被拒绝,处理速度跟不上入队速度。
  3. 系统资源已饱和

    • 系统负载load.systemAverage=14.04(8核机器负载超过8即饱和),CPU资源耗尽。
    • GC频繁:Minor GC累计耗时超33分钟,Full GC累计耗时超53分钟,频繁GC导致应用频繁停顿,进一步降低处理效率。
    • 线程数过多(439),上下文切换开销增大,占用额外CPU资源。

解决方案

1. 重构队列消费与删除逻辑

  • 优先使用头部消费:放弃按对象内容删除的逻辑,直接用poll()/take()从队列头部获取任务,处理完成后自动移除元素,避免遍历操作。
  • 改用IMap存储需按Key操作的对象:如果业务必须按消息Key删除,用Hazelcast IMap替代IQueue,将消息Key作为Map的Key,删除操作复杂度为O(1),性能远超队列遍历删除。
  • 移除冗余的size判断:删除hazelcastInstance.getQueue(queueId).size() == 1的逻辑,避免分布式状态查询开销。

2. 调整线程池配置

@Bean(name = "threadPoolTaskExecutor")
public Executor threadPoolTaskExecutor() {
    ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
    // 根据8核机器调整核心线程数与最大线程数
    executor.setCorePoolSize(8);
    executor.setMaxPoolSize(16);
    // 调整队列容量,或设置为无界队列(需监控内存)
    executor.setQueueCapacity(100);
    executor.setWaitForTasksToCompleteOnShutdown(true);
    // 使用CallerRunsPolicy避免任务丢失,同时起到限流作用
    executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
    executor.initialize();
    return executor;
}

3. 优化JVM与GC配置

  • 调整新生代大小,减少Minor GC次数:例如添加JVM参数-Xmn8G(新生代分配8G内存)。
  • 减少临时对象创建:优化消息处理逻辑,避免不必要的对象实例化。

4. 优化Hazelcast配置

  • 若无需高可用性,减少队列备份数量,降低集群同步开销。
  • 若REST与计算器应用部署在同一节点,改用Java本地队列(如LinkedBlockingQueue)替代分布式队列,大幅提升性能。

5. 添加监控与告警

  • 监控队列长度,设置阈值告警,堆积超过阈值时触发限流或扩容操作。
  • 监控GC频率、系统负载与线程数,及时发现资源瓶颈。

内容的提问来源于stack exchange,提问作者Владимир Гичев

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.25 00:28:12