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

相关推荐
叶总没有会3 小时前
RabbitMQ高级
分布式·rabbitmq
ABB自动化6 小时前
ABB 500CIM06 (1MRB150077R0111) | REB500 通信接口
大数据·分布式·系统架构·系统安全·模块测试·plc 控制器
ZENERGY-众壹7 小时前
告别本地工控机:分布式光伏 SCADA 架构如何实现云边协同
分布式·架构·光伏运维·能源数字化·电站监控
code_slave(码畜)7 小时前
微服务架构落地:读写分离架构 —— 主从延迟引发的“刚开单查不到”灾难与破局
spring boot·分布式·spring cloud·微服务·云原生·架构·读写分离
JAVA_AI人工智能项目实战8 小时前
分布式大模型训练_学习笔记
人工智能·分布式·语言模型·自然语言处理
starzy19909 小时前
KafkaProducer 源码深度剖析:RecordAccumulator 封装消息流程
大数据·kafka
星夜孤帆9 小时前
Redis缓存击穿与分布式锁实战:专栏目录、源码与实验说明
redis·分布式·缓存
yyk3332410 小时前
Spark介绍
大数据·分布式·spark
成为你的宁宁10 小时前
【基于 SSH 与 JNLP 协议的 Jenkins 分布式构建方案】
分布式·jenkins