SpringBoot-RocketMQ整合(二)

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}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

燕少༒江湖

给我一份鼓励!谢谢!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值