消息队列(二)RocketMQ

一、SpringBoot整合RocketMQ实战

1.消息消费者

1)pom.xml添加如下依赖

        <dependency>
            <groupId>org.apache.rocketmq</groupId>
            <artifactId>rocketmq-common</artifactId>
            <version>4.2.0</version>
        </dependency>

        <dependency>
        <groupId>org.apache.rocketmq</groupId>
        <artifactId>rocketmq-client</artifactId>
        <version>4.2.0</version>
        </dependency>

2)配置application.properties

server.port=8071
#必须配置name-server不然会有警告日志
rocketmq.name-server=localhost:9876
#以下都是自定义配置在类里面通过@ConfigurationProperties导入
apache.rocketmq.consumer.namesrvAddr=localhost:9876
#group是具有业务逻辑大致相同的一群服务的命名和消息发送端的不需要相同
apache.rocketmq.consumer.group=consumer
apache.rocketmq.consumer.timeout=1000
apache.rocketmq.consumer.retry=3
apache.rocketmq.consumer.maxThread=3
apache.rocketmq.consumer.minThread=3
apache.rocketmq.consumer.MaxSize=10

3)写一个抽象方法配置监听器Consumer ,java

import java.util.List;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.rocketmq.client.consumer.DefaultMQPushConsumer;
import org.apache.rocketmq.client.consumer.listener.ConsumeConcurrentlyContext;
import org.apache.rocketmq.client.consumer.listener.ConsumeConcurrentlyStatus;
import org.apache.rocketmq.client.consumer.listener.MessageListenerConcurrently;
import org.apache.rocketmq.common.message.MessageExt;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

@Component
@ConfigurationProperties(prefix = "apache.rocketmq.consumer")
@Data
@Slf4j
public abstract class Consumer {
	//consumer根据这个topic消费消息
    private final static String TOPIC = "update";
    protected DefaultMQPushConsumer defaultMQPushConsumer;
    private String namesrvAddr;
    private int maxThread;
    private int minThread;
    private int MaxSize;
    private String group;
    private Integer timeout;
    private Integer retry;

    @PostConstruct
    private void init() throws Exception {
        defaultMQPushConsumer = new DefaultMQPushConsumer();
        // 设置MQ地址
        defaultMQPushConsumer.setNamesrvAddr(namesrvAddr);
        // 设置不实用vip通道
        defaultMQPushConsumer.setVipChannelEnabled(false);
        // 设置线程数量
        defaultMQPushConsumer.setConsumeThreadMax(maxThread);
        defaultMQPushConsumer.setConsumeThreadMin(minThread);
		配置一个消费者
        init(defaultMQPushConsumer);
    }

    private void init(DefaultMQPushConsumer defaultMQPushConsumer) throws Exception {

        // 默认情况下不需要设置instanceName,rocketmq会使用ip@pid(pid代表jvm名字)作为唯一标示
        // 如果同一个jvm中,不同的producer需要往不同的rocketmq集群发送消息,需要设置不同的instanceName
        // defaultMQPushConsumer.setInstanceName(mqCfg.getInstanceName());
        defaultMQPushConsumer.setConsumerGroup(group);
        defaultMQPushConsumer.subscribe(TOPIC, "");
        defaultMQPushConsumer.setConsumeMessageBatchMaxSize(getMaxSize());    //每次拉取10条消息
        defaultMQPushConsumer.registerMessageListener(new MessageListenerConcurrently() { // 这里可以抽离出来,添加一个继承MessageListenerConcurrently的类
            @Override
            public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> msgs, ConsumeConcurrentlyContext context) {
                if (null != msgs && !msgs.isEmpty()) {
                    for (MessageExt msg : msgs) {

                        if (TOPIC.equals(msg.getTopic())) {
                            try {
                                //消息体没有sid,或者消息不重复
                                consume(msg, context);

                            } catch (Exception e) {
                                log.error(e.getMessage(), e);
                                //消息重试三次,则返回成功,不要在重新发送了
                                if (msg.getReconsumeTimes() == 3) {
                                    return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
                                }
                                return ConsumeConcurrentlyStatus.RECONSUME_LATER;
                            }
                        } else {
                            // 如果没有return success, consumer会重新消费该消息, 直到return success
                            return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
                        }
                    }
                    // 如果没有return success, consumer会重新消费该消息, 直到return success
                    return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
                }
                return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
            }
        });
        // Consumer对象在使用之前必须要调用start初始化, 初始化一次即可
        defaultMQPushConsumer.start();
    }

    @PreDestroy
    private void shutDown() {
        log.info("消费者:" + defaultMQPushConsumer.getConsumerGroup() + "关闭");
        defaultMQPushConsumer.shutdown();
    }

    protected abstract void consume(MessageExt msg, ConsumeConcurrentlyContext context) throws Exception;

}

4)抽象方法的实现类RocketListener.java,在里面处理核心业务

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.apache.rocketmq.client.consumer.listener.ConsumeConcurrentlyContext;
import org.apache.rocketmq.common.message.MessageExt;
import org.springframework.stereotype.Component;


@Component
@Slf4j
public class RocketListener extends Consumer {
    @Override
    protected void consume(MessageExt msg, ConsumeConcurrentlyContext context) throws Exception {
        //获取返回的字符串
        String result = new String(msg.getBody());
        JSONObject jsonObject = JSON.parseObject(result);
        String id=jsonObject.getString("id");
        JSONObject json=JSON.parseObject(jsonObject.getString("content"));
        log.info("收到*******" + id);
        log.info("内容*******"+json);
    }

}

2.消息发送者

1)pom.xml添加如下依赖

        <dependency>
            <groupId>org.apache.rocketmq</groupId>
            <artifactId>rocketmq-common</artifactId>
            <version>4.2.0</version>
        </dependency>

        <dependency>
        <groupId>org.apache.rocketmq</groupId>
        <artifactId>rocketmq-client</artifactId>
        <version>4.2.0</version>
        </dependency>

2)配置application.properties

server.port=8072

apache.rocketmq.provider.namesrvAddr=localhost:9876
apache.rocketmq.provider.group=producer
apache.rocketmq.provider.timeout=1000
apache.rocketmq.provider.retry=3
rocketmq.name-server=localhost:9876

3)封装一个消息实体类Message.java

import lombok.Data;

import java.io.Serializable;

@Data
public class Message<T> implements Serializable {
    private String id;
    private T content;

    @Override
    public String toString() {
        return "{\"id\":" + "\"" + id + "\"" + "," +
                "\"content\":" + content + "}";
    }
}

4)配置一个topic类RocketTopics .java

public class RocketTopics {
     private final static String TOPIC_ONE = "update";

    public static String getTopicOne() {
        return TOPIC_ONE;
    }

}
  1. 配置一个消息发送者RocketmqProducer.java
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.rocketmq.client.exception.MQClientException;
import org.apache.rocketmq.client.producer.DefaultMQProducer;
import org.apache.rocketmq.client.producer.SendCallback;
import org.apache.rocketmq.common.message.Message;
import org.apache.rocketmq.remoting.common.RemotingHelper;
import org.apache.rocketmq.remoting.exception.RemotingException;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import java.io.UnsupportedEncodingException;

@Slf4j
@Service
@ConfigurationProperties(prefix = "apache.rocketmq.provider")
@Data
public class RocketmqProducer {


    private String group;

    private String namesrvAddr;

    private Integer timeout;

    private Integer retry;

    private DefaultMQProducer producer;

    /**
     * 初始化producer
     */
    @PostConstruct
    public void init() {

        producer = new DefaultMQProducer(group);
        this.producer.setNamesrvAddr(namesrvAddr);
        if (this.timeout != null) {
            this.producer.setSendMsgTimeout(timeout);
        }
        // 如果发送消息失败,设置重试次数,默认为2次
        if (this.retry != null) {
            this.producer.setRetryTimesWhenSendFailed(retry);
        }
//		producer.setCreateTopicKey("AUTO_CREATE_TOPIC_KEY");
        try {
            this.producer.start();
            log.info("this.producer is start ! groupName:{},namesrvAddr:{}", group, namesrvAddr);
        } catch (MQClientException e) {
            log.info(e.getMessage());
        }
    }

    /**
     * 释放时自动关闭producer
     */
    @PreDestroy
    public void shutDown() {
        this.producer.shutdown();
    }

    /**
     * 发送消息
     *
     * @param msg          map key:sid(主sessionid) key:subOrderId(订单ID)
     * @param sendCallback
     */
    public void send(String msg, String consumerTopic, String tag, SendCallback sendCallback)
            throws MQClientException, RemotingException, InterruptedException, UnsupportedEncodingException {
        org.apache.rocketmq.common.message.Message message = new Message(consumerTopic, tag, msg.getBytes(RemotingHelper.DEFAULT_CHARSET));
        this.producer.send(message, sendCallback);
    }
}

6 写一个消息发送测试类RocketController .java

import com.alibaba.fastjson.JSONObject;
import com.zzx.rocket_provider.core.RocketTopics;
import com.zzx.rocket_provider.core.RocketmqProducer;
import com.zzx.rocket_provider.entity.Message;
import lombok.extern.slf4j.Slf4j;
import org.apache.rocketmq.client.producer.SendCallback;
import org.apache.rocketmq.client.producer.SendResult;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.UUID;

@RestController
@Slf4j
public class RocketController {

    @Autowired(required = false)
    RocketmqProducer producer;

    @GetMapping("/test")
    public void sendMs() {

        Message<JSONObject> message=new Message<>();
        JSONObject jsonObject=new JSONObject();
        jsonObject.put("username","admin");
        jsonObject.put("password","123456");
        message.setId(String.valueOf(UUID.randomUUID()));
        message.setContent(jsonObject);
        // 发送赛事更新消息MQ
        try {

            producer.send(message.toString(), RocketTopics.getTopicOne(), "", new SendCallback() {

                @Override
                public void onSuccess(SendResult sendResult) {
                   log.info("成功");
                }

                @Override
                public void onException(Throwable e) {
                    log.info(e.getMessage());
                    e.printStackTrace();
                }
            });
        } catch (Exception e) {
            log.info(e.getMessage());
            e.printStackTrace();
        }

    }
}

3. 测试

1)将idea的Allow parallel run √ 去掉就可以在一个idea窗口启动多个项目了
在这里插入图片描述
2)分别启动consumer和provider在浏览器访问localhost:8082/test

  • 可以看到消息发送成功了
    在这里插入图片描述
  • 消息消费端也成收到消息
    在这里插入图片描述
发布了55 篇原创文章 · 获赞 23 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/Z_Vivian/article/details/103362682