Seata事务集成Kafka,实现事务一致性

将kafka消息暂存至ThreadLocal中,seata事务完成后,再发送消息至kafka,如果seata事务失败,不发送消息。

一、 写一个注解拦截类

java 复制代码
@Aspect
@Component
@Slf4j
public class BusinessLogTestAspect {
    @Around("@annotation(com.cloud.util.businesslog.BusinessLogKevin)")
    public Object toLog(ProceedingJoinPoint joinPoint) throws Throwable {
        GlobalTransaction globalTransaction = GlobalTransactionContext.getCurrentOrCreate();
        globalTransaction.begin();
        Object result = null;
        try{
            result = joinPoint.proceed();
            globalTransaction.commit();
            // 发送kafka信息
            List<String> messageList = KafkaThreadLocal.kafkaThreadLocal.get();
            KafkaThreadLocal.kafkaThreadLocal.remove();
            if(ObjectUtil.isNotEmpty(messageList)) {
                //  发送Kafka消息
                KafkaProducer<String, String> producer = null;
                try {
                    producer = createKafkaProducer();
                    for(String message : messageList) {
                         producer.send(new ProducerRecord<>("ustomer", message));
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                } finally {
                    if(producer != null) {
                        producer.close();
                    }
                }
            }

        } catch (Exception e) {
            KafkaThreadLocal.kafkaThreadLocal.remove();
            globalTransaction.rollback();
        }
        System.out.println("localStatus3 role:" + globalTransaction.getGlobalTransactionRole());
        return result;
    }

二、写一个创建kafka生产端的对象

java 复制代码
       private KafkaProducer<String, String> createKafkaProducer() {
        Properties props = new Properties();
        props.put(BOOTSTRAP_SERVERS_CONFIG, "17.30.194.2:9092,172.0.14.28:9093,17.30.19.9:9092");
//        props.put(ENABLE_IDEMPOTENCE_CONFIG, "true");
//        props.put(TRANSACTIONAL_ID_CONFIG, String.valueOf(System.currentTimeMillis()));
        props.put(KEY_SERIALIZER_CLASS_CONFIG, "org.apache.kafka.common.serialization.StringSerializer");
        props.put(VALUE_SERIALIZER_CLASS_CONFIG, "org.apache.kafka.common.serialization.StringSerializer");
        props.put("security.protocol", "SASL_PLAINTEXT");
        props.put("sasl.mechanism", "SCRAM-SHA-256");
        props.put("sasl.jaas.config", "org.apache.kafka.common.security.scram.ScramLoginModule required username='username' password='userpassword';");

        return new KafkaProducer<>(props);
    }

三、将消息暂存至ThreadLocal

java 复制代码
   public class KafkaThreadLocal {
    public static final ThreadLocal<List<String>> kafkaThreadLocal = new ThreadLocal<>();

    public static void send(String message) {
        List<String> messageList = kafkaThreadLocal.get();
        if(ObjectUtil.isEmpty(messageList)) {
            messageList = Lists.newArrayList();
        }
        messageList.add(message);
        kafkaThreadLocal.set(messageList);
    }
}

另外一种办法比较简单:

io.seata.tm.api.transaction.TransactionHook 实现这个接口

io.seata.tm.api.transaction.TransactionHookManager 注册Hook

相关推荐
风吹夏回1 天前
RabbitMQ 核心术语 + Python pika 方法完整讲解
分布式·python·rabbitmq
风吹夏回2 天前
RabbitMQ 三种模式入门:HelloWorld、WorkQueue、PubSub
分布式·rabbitmq·ruby
霸道流氓气质2 天前
分布式追踪与 RequestId 传播完全指南
分布式
cheems95272 天前
[RabbitMQ高级特性] 消息确认机制:从 Ready / Unacked 到 basicAck、basicReject、basicNack 的底层拆解
分布式·rabbitmq·ruby
whaledown2 天前
Kafka 与 Java 消息队列入门:用订单场景理解核心机制
java·kafka·消息队列·springboot
枫华落尽2 天前
【Hadoop01-完全分布式运行模式】
分布式
隔壁阿布都2 天前
ShedLock 分布式定时任务锁框架介绍
spring boot·分布式
文艺倾年2 天前
【强化学习】数学推导专题,20W字总结(十五)
人工智能·分布式·大模型·强化学习·vibecoding
ACP广源盛139246256732 天前
GSV9001S@ACP#1080P 级视频处理芯片,物理 AI 普及终端的高性价比选择
大数据·人工智能·分布式·嵌入式硬件·spark
guslegend2 天前
第1章:初始Kafka
分布式·kafka