RocketMq 本地(Windows)安装配置,事务消息功能

1、描述

windows下RocketMQ安装部署:https://www.jianshu.com/p/4a275e779afa
RocketMQ可视化管理控制台rocketmq-console-ng:https://www.jianshu.com/p/4a275e779afa
启动本地MQ命令:(1)start mqnamesrv.cmd   (2)start mqbroker.cmd -n 127.0.0.1:9876 autoCreateTopicEnable=true

提供事务消息demo代码git: https://github.com/kinber123/rocketMqDemo.git

2、代码

(1)生产者

package com.rocketmq.demo.service.transaction;

import com.rocketmq.demo.config.JmsConfig;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.apache.rocketmq.client.exception.MQClientException;
import org.apache.rocketmq.client.producer.*;
import org.apache.rocketmq.common.message.Message;
import org.apache.rocketmq.remoting.common.RemotingHelper;
import org.springframework.stereotype.Component;

import java.util.concurrent.*;

/**
 * \* @author wcy
 * \* @date: 2020-05-21 9:23
 * \* Description:  发送同步消息
 * \
 */
@Slf4j
@Component
public class TransactionProducer {

    private String producerGroup = "test_tran_producer";


    TransactionMQProducer producer;

    public TransactionProducer() {
        this.start();
    }

    public void start() {
        //1.创建消息生产者producer,并制定生产者组名
        producer = new TransactionMQProducer(producerGroup);
        //2.指定Nameserver地址
        producer.setNamesrvAddr(JmsConfig.NAME_SERVER);
        // 设置超过多大进行compress压缩
        producer.setCompressMsgBodyOverHowmuch(1024 * 10);
        // 设置发送失败的尝试次数。
        producer.setRetryTimesWhenSendFailed(3);
        // 设置如果返回值不是send_ok,是否要重新发送
        producer.setRetryAnotherBrokerWhenNotStoreOK(false);
        // 设置限制最大的文件大小
        producer.setMaxMessageSize(1024*50);
        // 设置默认主题对应的队列数
        producer.setDefaultTopicQueueNums(4);
        // 设置发送超时时间 ms
        producer.setSendMsgTimeout(1000);
        //添加事务监听器
         TransactionListenerImpl transactionListener = new TransactionListenerImpl();
        producer.setTransactionListener(transactionListener);
        // 创建线程池
        ExecutorService executorService = new ThreadPoolExecutor(
                2,
                5,
                100,
                TimeUnit.SECONDS,
                new ArrayBlockingQueue<Runnable>(
                        200),
                new ThreadFactory() {
                    @Override
                    public Thread newThread(Runnable runnable) {
                        Thread thread = new Thread(runnable);
                        thread.setName("client-transaction");
                        return thread;
                    }
                }
        );
        producer.setExecutorService(executorService);
        //3.启动producer
        try {
            producer.start();
        } catch (MQClientException e) {
            log.error(ExceptionUtils.getStackTrace(e));
        }
        log.info("事务消息启动成功");
    }

    /**
     * 生产者生产方法
     *
     * @param topic 主题
     * @param tags  标签,用来给消费者进行过滤的
     * @param keys  作为key
     * @param body  发送的内容
     */
    @SneakyThrows
    public SendResult producerSendMes(String topic, String tags, String keys, String body) {
        Message message = new Message(topic, tags, keys, body.getBytes(RemotingHelper.DEFAULT_CHARSET));
        //发送
        SendResult sendResult = this.producer.sendMessageInTransaction(message, null);
        return sendResult;
    }
    /**
     * 生产者生产方法
     * @param topic 主题
     * @param tags 标签,用来给消费者进行过滤的
     * @param body 发送的内容
     */
    @SneakyThrows
    public SendResult producerSendMes(String topic, String tags, String body) {
        Message message = new Message(topic, tags, body.getBytes(RemotingHelper.DEFAULT_CHARSET));
        //发送
        SendResult sendResult = this.producer.sendMessageInTransaction(message,"hello word !!!");
        return sendResult;
    }

    public TransactionMQProducer getProducer(){
        return producer;
    }

    /**
     * 一般在应用上下文,使用上下文监听器,进行关闭
     */
    public void shutdown() {
        producer.shutdown();
    }
}

(2)消费者

package com.rocketmq.demo.service.transaction;

import com.rocketmq.demo.config.JmsConfig;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.apache.rocketmq.client.consumer.DefaultMQPushConsumer;
import org.apache.rocketmq.client.consumer.listener.ConsumeConcurrentlyStatus;
import org.apache.rocketmq.client.consumer.listener.MessageListenerConcurrently;
import org.apache.rocketmq.client.exception.MQClientException;
import org.apache.rocketmq.common.message.Message;
import org.springframework.stereotype.Component;

import java.io.UnsupportedEncodingException;
/**
 * \* @author wcy
 * \* @date: 2020-05-21 9:23
 * \* Description:  消息的接受者
 * \
 */
@Slf4j
@Component
public class TransactionConsumer {


    /**
     * 消费者组
     */
    public static final String CONSUMER_GROUP = "test_tran_consumer";


    public TransactionConsumer() throws MQClientException {
        // 启动事务消息
        this.consumerInit();
    }

    public void  consumerInit() throws MQClientException {
        //1.创建消费者Consumer,制定消费者组名
        DefaultMQPushConsumer consumer = new DefaultMQPushConsumer(CONSUMER_GROUP);
        //2.指定Nameserver地址
        consumer.setNamesrvAddr(JmsConfig.NAME_SERVER);
        //3.订阅主题Topic和Tag
        consumer.subscribe(JmsConfig.TOPIC, "*");


        //4.设置回调函数,处理消息
        consumer.registerMessageListener((MessageListenerConcurrently) (msgs, context) -> {
            // msgs中只收集同一个topic,同一个tag,并且key相同的message
            // 会把不同的消息分别放置到不同的队列中
            try {
                for (Message msg : msgs) {
                    //消费者获取消息 这里只输出 不做后面逻辑处理
                    String body = new String(msg.getBody(), "utf-8");
                    log.info("TransactionConsumer-获取消息-主题topic为={}, 消费消息为={}", msg.getTopic(), body);
                }
            } catch (UnsupportedEncodingException e) {
                log.error("rocketMq error:{}", ExceptionUtils.getStackTrace(e));
                // 异常重试
                return ConsumeConcurrentlyStatus.RECONSUME_LATER;
            }
            return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
        });
        //5.启动消费者consumer
        consumer.start();
    }

}

(3)事务会查监听器

package com.rocketmq.demo.service.transaction;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.apache.rocketmq.client.producer.LocalTransactionState;
import org.apache.rocketmq.client.producer.TransactionListener;
import org.apache.rocketmq.common.message.Message;
import org.apache.rocketmq.common.message.MessageExt;
import org.springframework.stereotype.Service;

import java.util.concurrent.ConcurrentHashMap;

/**
 * \* @author wcy
 * \* @date: 2020-05-21 9:23
 * \* Description:  实现事务会查监听器类
 * \
 */
@Slf4j
public class TransactionListenerImpl implements TransactionListener {
    private ConcurrentHashMap<String, Integer> localTran = new ConcurrentHashMap<String, Integer>();

    /**
     * 进行事务开始
     */
    private static final Integer BEGIN_LOCAL = 0;
    /**
     * 事务处理成功
     */
    private static final Integer SUCCESS_LOCAL = 1;
    /**
     * 事实处理异常
     */
    private static  final Integer EXCEPTION_LOCAL = 2;

    /**
     * - 发送prepare消息成功此方法被回调,该方法用于执行本地事务
     * - @param message 回传的消息,o利用transactionId即可获取到该消息的唯一Id
     * - @param  调用send方法时传递的参数,当send时候若有额外的参数可以传递到send方法中,这里能获取到
     * - @return 返回事务状态,COMMIT:提交 ROLLBACK:回滚 UNKNOW:回调
     */
    @Override
    public LocalTransactionState executeLocalTransaction(Message message, Object o) {
        /// 获取事务Id
        String transactionId = message.getTransactionId();
//        log.info("获取到事务消息Id:{}", transactionId);
        // 业务处理,执行本地事务处理
        try {
            localTran.put(transactionId, BEGIN_LOCAL);
//            log.info("正在执行行业事务处理------开始:{}",transactionId);
            Thread.sleep(1 * 1 * 1000);
//            log.info("正在执行行业事务处理------成功:{}",transactionId);
            localTran.put(transactionId, SUCCESS_LOCAL);
        } catch (InterruptedException e) {
            localTran.put(transactionId, EXCEPTION_LOCAL);
            log.error(ExceptionUtils.getStackTrace(e));
            // 事务重新执行
            return LocalTransactionState.ROLLBACK_MESSAGE;
        }
        return LocalTransactionState.COMMIT_MESSAGE;
    }

    /**
     * - @param messageExt 通过获取transactionId来判断这条消息的本地事务执行状态
     * - @return 返回事务状态,COMMIT:提交 ROLLBACK:回滚 UNKNOW:回调
     */
    @Override
    public LocalTransactionState checkLocalTransaction(MessageExt messageExt) {
        String transactionId = messageExt.getTransactionId();
//        log.info("获取回调事务ID=={}",transactionId);
        Integer state = localTran.get(transactionId);
//        log.info("获取回调事务状态=={}",state);
        switch (state) {
            case 0:
                return LocalTransactionState.UNKNOW;
            case 1:
                return LocalTransactionState.COMMIT_MESSAGE;
            case 2:
                return LocalTransactionState.ROLLBACK_MESSAGE;

        }
        return LocalTransactionState.UNKNOW;
    }
}

(4)JmsConfig 类

package com.rocketmq.demo.config;

/**
 * \* @author wcy
 * \* @date: 2020-05-15 17:42
 * \* Description:  类,安装实际开发这里的信息 都是应该写在配置里,来读取,这里为了方便所以写成常量
 * \
 */
public class JmsConfig {

    /**
     * Name Server 地址,因为是集群部署 所以有多个用 分号 隔开
     */
    public static final String NAME_SERVER = "127.0.0.1:9876";
    /**
     * 主题名称 主题一般是服务器设置好 而不能在代码里去新建topic( 如果没有创建好,生产者往该主题发送消息 会报找不到topic错误)
     */
    public static final String TOPIC = "topic_family";

}

(5)  测试类

package com.rocketmq.demo.controller;

import com.rocketmq.demo.config.JmsConfig;
import com.rocketmq.demo.service.transaction.TransactionProducer;
import lombok.extern.slf4j.Slf4j;
import org.apache.rocketmq.client.producer.SendResult;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.ArrayList;
import java.util.List;

/**
 * \* @author wcy
 * \* @date: 2020-05-15 17:30
 * \* Description:  类
 * \
 */
@Slf4j
@RestController
@RequestMapping(value = "/text")
public class RocketMqController {

    @Autowired
    private TransactionProducer transactionProducer;

    private List<String> mesList;

    /**
     * 初始化消息
     */
    public RocketMqController() {
        mesList = new ArrayList<>();
        mesList.add("小小");
        mesList.add("爸爸");
        mesList.add("妈妈");
        mesList.add("爷爷");
        mesList.add("奶奶");
        mesList.add("外公");
        mesList.add("外婆");

    }


    /**
     * 事务消息MQ
     * @return
     */
    @RequestMapping("/rocketmq/tran")
    public Object callbackTran(){
        for (String s : mesList) {
            //创建生产信息
            SendResult sendResult = transactionProducer.producerSendMes(JmsConfig.TOPIC, "testtag", ("事务消息--小小一家人的称谓:" + s));
            log.info("事务消息输出生产者信息={}",sendResult);
        }
        return "成功";
    }

    /**
     * 测试是否能访问
     * @param test
     * @return
     * @throws Exception
     */
    @RequestMapping("/test")
    public Object test(String test) {
        return "success "+test;
    }
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值