Kafka消息积压案例分析

29 篇文章 1 订阅
5 篇文章 0 订阅

案例

一个微服务同一个分组消费同一个topic的kafka消息,不通业务通过key值区分,由于其中一个业务消息量大,偶尔会出现消费滞后的情况,导致当前微服务消费组出现大量消息积压情况,影响业务。

简单的说,即一个单线程消费,一个消息处理完毕,才会处理下一个。

Created with Raphaël 2.3.0 开始 消费者组 消息处理 消费成功? yes

分析

存在消息积压的场景:消息消费

    @Override
    @Consumer(topic = Const.KAFKA_TOPIC, key = Const.TOPIC_KEY)
    public boolean handle(String msg) throws Exception {
        OriginInfo msg = JSON.parseObject(msg, OriginInfo.class);
        return service.process(msg);
    }

其中, service.process(msg)方法流程比较复杂,存在多个查询和入库、更新操作,有时候由于数据库性能,这个过程会变慢,导致整体消息消费跟不上。

建议

持久化方案:消息先入库,再消费,去掉中间处理流程。

Created with Raphaël 2.3.0 开始 消费者组 消息入库 消费成功? yes

消息入库:

    @Override
    @Consumer(topic = Const.KAFKA_TOPIC, key = Const.TOPIC_KEY)
    public boolean handle(String msg) throws Exception {
        OriginInfo msg = JSON.parseObject(msg, OriginInfo.class);
         // 消息入库,后续通过定时调度消费消息
        service.insert(msg);
        return true;
    }

消息处理:可以构建一个单线程池,定时创建一个新线程丢到线程池,已确保消息顺序消费处理。

/**
 * kafka消息异步处理
 * @author loongshawn
 */
public class MsgService implements Runnable {

    private static final Logger logger = LoggerFactory.getLogger(MsgService.class);

    private AService aService;

    public MsgService() {
        this.aService = (AService) SpringContextUtil.getBean("aService");
    }

    /**
     * 消息处理入口
     */
    void process() {
        List<OriginInfo> list = new ArrayList<>();
        try {
            // 依据入库顺序读出消息记录,顺序消费
            list = aService.get();
            if (CollectionUtils.isEmpty(list)) {
                return;
            }
        } catch (Exception e) {
            logger.error("exception {}", e.toString());
        }

        for (OriginInfo item : list) {
            try {
                // 顺序消费
                aService.process(item);
                // 消费完毕,更新消息标记
                aService.update(item);
            } catch (Exception e) {
                logger.error("exception {}", e.toString());
            }
        }
    }

    @Override
    public void run() {
        try {
            this.process();
        } catch (Exception e) {
            logger.error("exception {}", e.toString());
        }
    }
}

优化

由于此样例中呈现的是消息写入库,比如MySQL、Oracle等等,消息量几十万还能应对,但如果应对大规模比如数千万消息要写入库,上述方案就不行了。需要考虑引入缓存方式,降低数据库IO成本。

Created with Raphaël 2.3.0 开始 消费者组 消息缓存 消费成功? yes

三步走方案:

  • kafka消息---->写入redis缓存---->消费结束
  • 读redis缓存---->持久化至数据库
  • 读数据库---->处理消息
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值