Kafka 不难,只是你用得不对

本文分享使用 Kafka 的一些经典模式。有时你感觉 Kafka 好难搞,可能是因为不了解这些模式。
让我们从基础开始:
1.每个事件类型一个主题
反模式:
orders-service-topic
shipping-service-topic
analytics-service-topic
每个服务都有自己的主题?不不不,你要这么搞,那就不是事件驱动设计了,这种设计只能看做是多了些步骤的远程过程调用(RPC)。
正模式:
order.created
order.shipped
order.cancelled
主题应该表示一个什么什么事件,多个服务可以对同一事件做出反应,这样你的架构才能保持松耦合。
2.消费者组 = 一个逻辑工作单元
消费者组很简单:一个组 = 一个合理的业务目的。
但很多团队都把这一点搞砸了。他们为每个实例创建一个消费者组,更糟糕的是,还会为不同的任务重复使用同一个组。
正模式:
- inventory-updater 仓库更新器
- email-sender 邮件发送器
- analytics-processor 分析处理器
这些中的每一个都是独立的组。它们都使用 order.created 并独立完成自己的工作。
这将所有内容解耦。它还具有出色的扩展性——Kafka 会在同一组内的消费者之间对分区进行负载均衡。
3.避免无状态的链式事件
假设你收到一个 order.created 的消息,然后你甚至没有检查任何内容就立即发布一个 order.verified 的消息。这很危险。不要把 Kafka 变成一个愚蠢的消息传递者。
正模式:事件 + 状态。
不要为了模拟工作流程而将五个事件串联在一起,而是让事件触发一个拥有逻辑和状态的服务,并且仅在必要时发出下一个事件。
这样可以避免意外的无限循环、幽灵事件和令人困惑的重放错误。
4.使用发件箱模式
有没有试过在插入数据库后向 Kafka 发送消息,结果 Kafka 调用失败?现在你的数据库已更新……但 Kafka 从未收到该消息。恭喜你——你刚刚制造了一个不一致性!
发件箱模式来解决这个问题
将事件作为同一事务的一部分存储在你的数据库中,如下所示:
BEGIN;
INSERT INTO orders (...) VALUES (...);
INSERT INTO outbox (event_type, payload) VALUES ('order.created', '{...}');
COMMIT;
然后运行一个后台任务,读取 outbox 表并将事件发送到 Kafka。
原子!可靠!可重放!
5.采用退避重试
人们认为“Kafka 让一切都可重放”,所以他们滥用它。他们只是不假思索地重新消费失败的事件。
但如果错误是由于下游系统宕机导致的呢?(比如A服务消费Kafka消息时要调用B服务做一些逻辑,此时B服务挂了,进而导致最终未能成功消费)
使用带有指数退避的重试队列,或者使用像Kafka Streams / Debezium这样内置错误处理功能的框架。
或者手动创建如下主题存放消费失败的消息,延迟重试:
- order.created.retry.1m
- order.created.retry.10m
- order.created.dlq
巴辉特提醒:这种 retry 队列的模式很有意思,之前我也未曾关注过。通常来讲,建议一个 topic + 一个 consumer group 作为颗粒度创建一套 retry 队列,因为事件可能有多个 consumer group 来消费。这个知识有点绕,大家可以通过 GPT 了解更多细节。
举例,比如 100 条事件里只有 1 条消费失败,如果持续重试这一条,持续失败,就会影响后面的事件消费。
当然,如果你们的业务逻辑就必须得严格要求顺序,那另当别论,case by case 来看哈。
6. Schema 要全局统一
如果您的事件看起来像这样:
{
"id": 123,
"user": "john",
"total": 100
}
这样没问题……直到有人添加了一个新字段:
{
"id": 123,
"user": "john",
"total": 100,
"vip": true
}
现在,你的使用者程序出现故障。或者更糟的是—— 悄无声息地出现异常行为。
巴辉特提醒:这个意思是说,consumer 不知道事件格式发生变化,可能会引起故障,需要一个机制,让所有人知道消息格式变了,而且消息格式需要兼容性。
使用 Avro/Protobuf + Schema Registry,你会得到:
- 向前/向后兼容性
- 严格类型标注
- 演进支持
当团队壮大时,你会感谢自己的这种做法。
简单的Kafka架构图
+------------------+
| Order Service |
|------------------|
| Emits: order.created |
+--------+---------+
|
v
+--------+---------+
| Kafka |
|-------------------|
| Topics: |
| - order.created |
| - order.retry |
| - order.dlq |
+--------+----------+
|
+-----------+------------+
| | |
v v v
+-----------+ +--------------+ +------------------+
|Inventory | |Email Service | |Analytics Service |
|Updater | |(Consumer) | |(Consumer) |
+-----------+ +--------------+ +------------------+
每个服务都是一个独立的消费者组,对相同的事件做出反应。发件箱模式(未显示)在生产者端实现。
最终想法
Kafka 并不难。你跳过了那些让事情变得易于处理的模式,结果反而把事情弄复杂了。
你越早遵循这些原则,Kafka就越早成为一个健壮、可扩展的事件驱动系统的支柱,而不是凌晨3点令人头疼的调试难题。
原文:https://codingplainenglish.medium.com/kafka-is-hard-because-you-keep-ignoring-these-patterns-588b2ebac3c0
Kafka 不难,只是你用得不对的更多相关文章
- *循环-01. 求整数段和【help】
/* * Main.c * 循环-01. 求整数段和 * Created on: 2014年6月18日 * Author: Boomkeeper ***测试木有通过**** */ #include & ...
- 配置eureka 老是报错connected time out 或者 refused connected
报错信息总是连接错误,我指定了端口号,却不按照我指定的端口进行访问,而是访问eureka-server 的端口号是8761 ,这是因为配置有问题. 查看 类 EurekaClientConfigBea ...
- 基于Kubernetes在AWS上部署Kafka时遇到的一些问题
作者:Jack47 转载请保留作者和原文出处 欢迎关注我的微信公众账号程序员杰克,两边的文章会同步,也可以添加我的RSS订阅源. 交代一下背景:我们的后台系统是一套使用Kafka消息队列的数据处理管线 ...
- kafka性能参数和压力测试揭秘
转自:http://blog.csdn.net/stark_summer/article/details/50203133 上一篇文章介绍了Kafka在设计上是如何来保证高时效.大吞吐量的,主要的内容 ...
- kafka集群安装部署
kafka集群安装 使用的版本 系统:centos6.5 centos6.7 jdk:1.7.0_79 zookeeper:3.4.9 kafka:2.10-0.10.1.0 一.环境准备[只列,不具 ...
- Kafka使用入门教程
转载自http://www.linuxidc.com/Linux/2014-07/104470.htm 介绍 Kafka是一个分布式的.可分区的.可复制的消息系统.它提供了普通消息系统的功能,但具有自 ...
- 【原创】Kafka producer原理 (Scala版同步producer)
本文分析的Kafka代码为kafka-0.8.2.1.另外,由于Kafka目前提供了两套Producer代码,一套是Scala版的旧版本:一套是Java版的新版本.虽然Kafka社区极力推荐大家使用J ...
- Kafka 分布式环境搭建
这篇文章将介绍如何搭建kafka环境,我们会从单机版开始,然后逐渐往分布式扩展.单机版的搭建官网上就有,比较容易实现,这里我就简单介绍下即可,而分布式的搭建官网却没有描述,我们最终的目的还是用分布式来 ...
- kafka入门:简介、使用场景、设计原理、主要配置及集群搭建(转)
问题导读: 1.zookeeper在kafka的作用是什么? 2.kafka中几乎不允许对消息进行"随机读写"的原因是什么? 3.kafka集群consumer和producer状 ...
- kafka入门教程链接
http://www.aboutyun.com/forum.php?mod=viewthread&tid=12882 经典入门教程 1.Kafka独特设计在什么地方?2.Kafka如何搭建及创 ...
随机推荐
- 小程序简单 tab 切换实现
也是终于找到了数据可视化的最佳载体, 用小程序来做可视化简直完美. 尤其对于像我这种搞数据的, 数据分析, 数据报表, 可视化一直是一个巨大难题, 当我认识的最终的方案还是要用前端的时候, 感觉还有麻 ...
- MFC窗口闪烁问题
本文引自:<VC窗口闪烁问题的解决> 概述 一般的windows复杂的界面需要使用多层窗口而且要用贴图来美化,所以不可避免在窗口移动或者改变大小时候出现闪烁. 闪烁产生的原因 原因一: 如 ...
- Longest Consecutive Sequence——LeetCode进阶路
原题链接https://leetcode.com/problems/longest-consecutive-sequence/ 题目描述 Given an unsorted array of inte ...
- RBMQ与odoo15的集成
背景:在对接物联网设备时候常用的协议就是:MQTT.AMQ.https.还有WebSocket,此案例就是针对接物联网设备传输的消息的消费 原理:通过新建守护线程的方式来启动mq服务,来消费设备平台端 ...
- sqlite:No module named _sqlite3
执行代码报错:"sqlite:No module named _sqlite3" 执行环境说明 某台服务器上执行DrissionPage相关程序报错,本机没有问题. 解决说明 本机 ...
- PicGo使用简明教程及踩坑记录
PicGo使用简明教程及踩坑记录 PicGo使用 我现在用的博客的记录方式是Typora+PicGo+阿里云oss,这一套配置好后就非常方便了,可以快捷上传图片到云服务器,并且阿里云的速度也是我试过的 ...
- java实现linux文件的解压缩(确保md5sum一致)
package com.xlkh.device.utils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java. ...
- 数据安全新战场,EasyMR为企业筑起“安全防线”
2020年1月,时间跨度长达14年的,微软2.5亿条客户服务和支持记录在网上泄露: 同年4月,微盟发生史上最贵"删库跑路"事件,造成微盟市值一夜之间缩水约24亿港币: 今年7月,网 ...
- 从零开始实现简易版Netty(一) MyNetty Reactor模式
从零开始实现简易版Netty(一) MyNetty Reactor模式 自从18年作为一个java程序员入行以来,所接触到的大量组件如dubbo.rocketmq.redisson等都是基于netty ...
- Java学习篇(四)—— Java 多线程
如何创建一个线程? Java创建线程有两种方法,这里对三种方法做一个梳理,方便理解. 实现Runnable接口和run()方法 Java的接口就是一种协议,约定了想要被统一管理的类要遵循的协议.在Ja ...