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

相关推荐
阿卡好可爱5 小时前
Lua 热更新不生效:改完文件线上仍是旧逻辑,package.loaded 与 upvalue 重绑定详解
数据结构·分布式·后端·架构·lua
我不会起名字3221 天前
消费回调固定 return nil:错误传播与 Kafka 位点提交的区别
后端·mongodb·kafka·go·im·故障排查
qq_452396231 天前
第十篇:《Kafka 安全加固:SASL、SSL、ACL 与多租户隔离》
安全·kafka·ssl
用户3610588626122 天前
KafkaProducer 元数据管理:send() 卡住时,它在等什么
kafka
方方洛2 天前
ray教程-28-附录G-CICD与Slurm与云上调度
人工智能·分布式·机器学习
空奈qwq2 天前
【Kafka 从入门到实战】从手写一个队列到生产级消息中间件:架构、分区、消费组、零拷贝全解析
分布式·架构·kafka
山甫aa3 天前
锁一篇搞定:乐观锁、悲观锁、CAS、数据库锁与分布式锁【从零开始的 Web 后端学习】
java·数据库·分布式·后端·学习·web
严肃小白兔4 天前
Cloudflare Durable Objects 支持自动重试:分布式调用如何避免重复执行?
分布式
用户3610588626124 天前
深度拆解 Kafka 源码:Producer 构造器装配了哪些核心组件
kafka