RocketMQ 基础概念以及作用 :http://rocketmq.apache.org/docs/simple-example/
RocketMQ 单机搭建以及使用:https://blog.csdn.net/qq_31289187/article/details/88351235
1、pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.3.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.cn.dl</groupId>
<artifactId>spring-boot-rocketmq</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>spring-boot-rocketmq</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.alibaba.rocketmq</groupId>
<artifactId>rocketmq-client</artifactId>
<version>3.5.8</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.4</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.47</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
2、RocketMqMethod
package com.cn.dl.annotation;
import javax.validation.constraints.NotNull;
import java.lang.annotation.*;
/**
* Created by yanshao on 2019/3/10.
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface RocketMqMethod {
@NotNull
String topic() ;
String tag() default "";
}
@RocketMqMethod :添加到对应处理消息的业务方法上,topic不能null,当SpringBoot启动之后,会扫描到添加该注解的方法
3、RocketMqService
package com.cn.dl.annotation;
import org.springframework.stereotype.Component;
import java.lang.annotation.*;
/**
* Created by yanshao on 2019/3/10.
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Component
public @interface RocketMqService {
String value() default "";
}
@RocketMqService :当SpringBoot启动之后,会从application上下文中获取处理业务的类,从而获取处理业务的方法,注意这里添加了@Component
4、TopicHandlerBean
package com.cn.dl.bean;
import com.cn.dl.handler.RocketMqHandler;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
import java.lang.reflect.Method;
/**
* Created by yanshao on 2019/3/10.
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class TopicHandlerBean implements Serializable{
//处理数据的业务方法
private Method method;
//处理数据类的对象
private Object object;
private String topic;
private String tag;
private RocketMqHandler rocketMqHandler;
}
5、RocketMqConfig
package com.cn.dl.rocketmq;
import com.cn.dl.annotation.*;
import com.cn.dl.bean.TopicHandlerBean;
import com.cn.dl.handler.RocketMqHandler;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import javax.annotation.PostConstruct;
import java.lang.reflect.Method;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
/**
* Created by yanshao on 2019/3/10.
*/
@Component
public class RocketMqConfig implements ApplicationContextAware{
/**
* 上下文信息
* */
private ApplicationContext applicationContext;
private List<TopicHandlerBean> topicBeans = new LinkedList<TopicHandlerBean>();
@PostConstruct
public void init(){
//获取所有添加了@RocketMqService的类
Map<String, Object> rocketMqServices = applicationContext.getBeansWithAnnotation(RocketMqService.class);
for (Map.Entry<String,Object> entry : rocketMqServices.entrySet()){
Method[] methods = entry.getValue().getClass().getDeclaredMethods();
for(Method method : methods){
//获取对应的业务处理类
if(method.isAnnotationPresent(RocketMqMethod.class)){
RocketMqMethod rocketMqHandler = method.getAnnotation(RocketMqMethod.class);
TopicHandlerBean topicBean = TopicHandlerBean.builder()
.method(method)
.object(entry.getValue())
.topic(rocketMqHandler.topic())
.tag(StringUtils.isEmpty(rocketMqHandler.tag()) == true ? "*" : rocketMqHandler.tag())
.rocketMqHandler(new RocketMqHandler())
.build();
topicBeans.add(topicBean);
}
}
}
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
/**
* 获取对应的业务处理类的bean
* */
public List<TopicHandlerBean> getTopicBeans() {
return topicBeans;
}
}
等所有的bean初始化之后,从Application中获取类型为RocketMqService的bean,然后获取处理业务的Object、method、topic、tag,然后封装TopicHandlerBean
6、RocketMqHandler
package com.cn.dl.handler;
import com.alibaba.rocketmq.common.message.MessageExt;
import com.cn.dl.bean.TopicHandlerBean;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
/**
* Created by yanshao on 2019/3/10.
*/
public class RocketMqHandler {
public boolean handler(TopicHandlerBean topicBean, MessageExt ext){
Method method = topicBean.getMethod();
boolean isSuccess = true;
try {
// TODO: 2019/3/10 public boolean panicBuyingHandler(MessageExt messageExt)
isSuccess = (boolean) method.invoke(topicBean.getObject(),ext);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
return isSuccess;
}
}
RocketMqHandler:通过反射调用对应的业务处理方法
7、CommonConstant
package com.cn.dl.config;
import com.alibaba.rocketmq.remoting.common.RemotingHelper;
/**
* Created by yanshao on 2019/3/10.
*/
public interface CommonConstant {
//生产组
String PRODUCER_GROUP = "rocketMQProducer";
//生产组
String CONSUMER_GROUP = "rocketMQConsumer";
//RocketMq server 地址,多个地址之间用分号分割:192.0.0.1:9876;192.0.0.2:9876
String NAMESRV_ADDR = "127.0.0.1:9876";
String TOPIC = "panicBuying";
// String UTF_8 = RemotingHelper.DEFAULT_CHARSET;
}
8、RocketMqConsumer
package com.cn.dl.rocketmq;
import com.alibaba.rocketmq.client.consumer.DefaultMQPushConsumer;
import com.cn.dl.bean.TopicHandlerBean;
import com.cn.dl.config.CommonConstant;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import java.util.List;
/**
* Created by yanshao on 2019/3/10.
*/
@Component
public class RocketMqConsumer extends DefaultMQPushConsumer {
@Autowired
private RocketMqConfig rocketMqConfig;
@Autowired
private RocketMqConsumerListener rocketMqConsumerListener;
/**
* 监听并启动消费者
* */
@PostConstruct
public void start(){
try {
List<TopicHandlerBean> topicBeanList = rocketMqConfig.getTopicBeans();
for(TopicHandlerBean topicHandlerBean : topicBeanList){
rocketMqConsumerListener.addTopicHandlerBeans(topicHandlerBean);
super.setConsumerGroup(CommonConstant.CONSUMER_GROUP);
super.setNamesrvAddr(CommonConstant.NAMESRV_ADDR);
super.subscribe(topicHandlerBean.getTopic(),topicHandlerBean.getTag());
super.setMessageListener(rocketMqConsumerListener);
super.start();
}
}catch (Exception e){
e.printStackTrace();
}
}
/**
* 关闭消费者
* */
@PreDestroy
public void shutdown(){
super.shutdown();
}
}
注册对应的监听者
9、RocketMqConsumerListener
package com.cn.dl.rocketmq;
import com.alibaba.fastjson.JSON;
import com.alibaba.rocketmq.client.consumer.listener.ConsumeConcurrentlyContext;
import com.alibaba.rocketmq.client.consumer.listener.ConsumeConcurrentlyStatus;
import com.alibaba.rocketmq.client.consumer.listener.MessageListenerConcurrently;
import com.alibaba.rocketmq.common.message.MessageExt;
import com.cn.dl.bean.TopicHandlerBean;
import com.cn.dl.handler.RocketMqHandler;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
/**
* 监听topic,并处理业务
* Created by yanshao on 2019/3/10.
*/
@Component
@Slf4j
public class RocketMqConsumerListener implements MessageListenerConcurrently {
private ConcurrentHashMap<String,TopicHandlerBean> topicHandlerBeans = new ConcurrentHashMap<>();
@Override
public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> list, ConsumeConcurrentlyContext consumeConcurrentlyContext) {
for (MessageExt messageExt : list) {
log.info("监听到{}变化:{}",messageExt.getTopic(),JSON.toJSONString(messageExt.getBody()));
boolean result = false;
/**
* 根据topicName获取对应的处理业务方法
* */
TopicHandlerBean topicHandlerBean = topicHandlerBeans.get(messageExt.getTopic());
if (topicHandlerBean != null) {
RocketMqHandler rocketMqHandler = topicHandlerBean.getRocketMqHandler();
result = rocketMqHandler.handler(topicHandlerBean,messageExt);
} else {
log.info("没有找到对应的处理方法:{}", messageExt);
result = true;
}
//如果消费失败,会重新消费
if (!result) {
return ConsumeConcurrentlyStatus.RECONSUME_LATER;
}
}
return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
}
public void addTopicHandlerBeans(TopicHandlerBean topicHandlerBean) {
if(topicHandlerBean != null && ! topicHandlerBeans.containsKey(topicHandlerBean.getTopic())){
// System.out.println("topicHandlerBean >>> " + topicHandlerBean.toString());
topicHandlerBeans.put(topicHandlerBean.getTopic(),topicHandlerBean);
}
}
}
10、RocketMqProducer
package com.cn.dl.rocketmq;
import com.alibaba.fastjson.JSON;
import com.alibaba.rocketmq.client.exception.MQClientException;
import com.alibaba.rocketmq.client.producer.DefaultMQProducer;
import com.alibaba.rocketmq.client.producer.SendResult;
import com.alibaba.rocketmq.common.message.Message;
import com.cn.dl.config.CommonConstant;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
/**
* Created by yanshao on 2019/3/10.
*/
@Component
@Slf4j
public class RocketMqProducer extends DefaultMQProducer{
@PostConstruct
public void init(){
//生产者组
super.setProducerGroup(CommonConstant.PRODUCER_GROUP);
//mq服务地址
super.setNamesrvAddr(CommonConstant.NAMESRV_ADDR);
try {
//启动
super.start();
} catch (MQClientException e) {
e.printStackTrace();
}
}
/**
* 发送消息
* */
public void send(String topic,String msg){
try {
if(StringUtils.isEmpty(msg)){
return;
}
//封装消息
Message message = new Message(topic, "*", msg.getBytes());
// TODO: 2019/3/8 要保证消息顺序不混乱,必须在生产时将消息push到同一个队列
SendResult sendResult = super.send(message, (mqs, meg, arg) -> {
Long id = (Long) arg;
long index = id % mqs.size();
return mqs.get((int)index);
}, 0L);
log.info("topic:{},msg:{},sendResult:{}", topic, msg, JSON.toJSON(sendResult));
}catch (Exception e){
e.printStackTrace();
}
}
@PreDestroy
public void destroy(){
super.shutdown();
}
}
RocketMqProducer :生产者,引入RocketMqProducer ,然后调用send方法即可发送消息,生产者相对简单一点。
11、Order
package com.cn.dl.bean;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
import java.math.BigDecimal;
/**
* Created by yanshao on 2019/3/8.
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class Order implements Serializable{
private String orderNo;
private String productName;
private BigDecimal unitPrice;
private String createDate;
}
12、OrderHandlerService
package com.cn.dl.service;
import com.alibaba.rocketmq.common.message.MessageExt;
import com.cn.dl.annotation.RocketMqMethod;
import com.cn.dl.annotation.RocketMqService;
import com.cn.dl.config.CommonConstant;
import lombok.extern.slf4j.Slf4j;
/**
* Created by yanshao on 2019/3/10.
*/
@RocketMqService
@Slf4j
public class OrderHandlerService {
/**
* 处理订单
* */
@RocketMqMethod(topic = CommonConstant.TOPIC)
public boolean panicBuyingHandler(MessageExt messageExt){
boolean isSuccess = true;
try {
//RemotingHelper.DEFAULT_CHARSET
String msgId = messageExt.getMsgId();
String msg = new String(messageExt.getBody(),"utf-8");
// TODO: 2019/3/10 业务逻辑
log.info("msgId:{},处理订单:{}",msgId,msg);
}catch (Exception e){
isSuccess = false;
e.printStackTrace();
}finally {
return isSuccess;
}
}
}
当需要订阅某个topic时,首先在类上添加@RocketMqService,然后在对应方法加上@RocketMqMethod,业务完成之后,必须返回boolean值,返回false时,会重新处理当前消息。
13、OrderController:测试
package com.cn.dl.controller;
import com.alibaba.fastjson.JSON;
import com.cn.dl.bean.Order;
import com.cn.dl.config.CommonConstant;
import com.cn.dl.rocketmq.RocketMqProducer;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* Created by yanshao on 2019/3/10.
*/
@RestController
@RequestMapping({"/api/order"})
@Slf4j
public class OrderController {
@Autowired
private RocketMqProducer rocketMqProducer;
/**
* 疯狂购物节
* @param order {@link Order}
* @return
* */
@PostMapping("panic_buying")
public String panicBuying(Order order){
//下单信息
log.info("下单信息:{}",order.toString());
//发送消息
rocketMqProducer.send(CommonConstant.TOPIC, JSON.toJSONString(order));
return null;
}
}
测试:
1、127.0.0.1:8080/api/order/panic_buying
2019-03-10 18:29:38.339 INFO 21356 --- [nio-8080-exec-1] com.cn.dl.controller.OrderController : 下单信息:Order(orderNo=JD_01, productName=xiaomi9, unitPrice=2599, createDate=2019-03-10)
2019-03-10 18:29:38.667 INFO 21356 --- [nio-8080-exec-1] com.cn.dl.rocketmq.RocketMqProducer : topic:panicBuying,msg:{"createDate":"2019-03-10","orderNo":"JD_01","productName":"xiaomi9","unitPrice":2599},sendResult:{"regionId":"DefaultRegion","messageQueue":{"queueId":0,"topic":"panicBuying","brokerName":"DESKTOP-2ACCV2S"},"msgId":"C0A82B5C536C18B4AAC22FBDF6CE0000","queueOffset":19,"sendStatus":"SEND_OK","offsetMsgId":"C0A82B5C00002A9F0000000000010DB3"}
2019-03-10 18:29:38.744 INFO 21356 --- [MessageThread_1] c.c.d.rocketmq.RocketMqConsumerListener : 监听到panicBuying变化:"eyJjcmVhdGVEYXRlIjoiMjAxOS0wMy0xMCIsIm9yZGVyTm8iOiJKRF8wMSIsInByb2R1Y3ROYW1lIjoieGlhb21pOSIsInVuaXRQcmljZSI6MjU5OX0="
2019-03-10 18:29:38.744 INFO 21356 --- [MessageThread_1] com.cn.dl.service.OrderHandlerService : msgId:C0A82B5C536C18B4AAC22FBDF6CE0000,处理订单:{"createDate":"2019-03-10","orderNo":"JD_01","productName":"xiaomi9","unitPrice":2599}
2、127.0.0.1:8080/api/order/panic_buying
2019-03-10 18:31:24.987 INFO 21356 --- [nio-8080-exec-4] com.cn.dl.controller.OrderController : 下单信息:Order(orderNo=JD_02, productName=huawei 9, unitPrice=7888, createDate=2019-03-10)
2019-03-10 18:31:24.989 INFO 21356 --- [nio-8080-exec-4] com.cn.dl.rocketmq.RocketMqProducer : topic:panicBuying,msg:{"createDate":"2019-03-10","orderNo":"JD_02","productName":"huawei 9","unitPrice":7888},sendResult:{"regionId":"DefaultRegion","messageQueue":{"queueId":0,"topic":"panicBuying","brokerName":"DESKTOP-2ACCV2S"},"msgId":"C0A82B5C536C18B4AAC22FBF963B0002","queueOffset":21,"sendStatus":"SEND_OK","offsetMsgId":"C0A82B5C00002A9F0000000000010F4D"}
2019-03-10 18:31:25.007 INFO 21356 --- [MessageThread_3] c.c.d.rocketmq.RocketMqConsumerListener : 监听到panicBuying变化:"eyJjcmVhdGVEYXRlIjoiMjAxOS0wMy0xMCIsIm9yZGVyTm8iOiJKRF8wMiIsInByb2R1Y3ROYW1lIjoiaHVhd2VpIDkiLCJ1bml0UHJpY2UiOjc4ODh9"
2019-03-10 18:31:25.009 INFO 21356 --- [MessageThread_3] com.cn.dl.service.OrderHandlerService : msgId:C0A82B5C536C18B4AAC22FBF963B0002,处理订单:{"createDate":"2019-03-10","orderNo":"JD_02","productName":"huawei 9","unitPrice":7888}