消息队列(RocketMQ):springcloud结合ONS实现Tcp/Http通信方式的生产者和消费者实例

RocketMQ

新公司的消息队列用的是RocketMQ,但是不是直接使用RocketMq,而是采用了阿里分布式开放消息服务(ONS)

一、阿里分布式开放消息服务(ONS)

ONS(Open Notification Service)即开放消息服务,是基于阿里开源消息中间件MetaQ(RocketMQ)打造的一款云消息产品。实现生产者与消费者的代码稍微和原生的有点差别

二、阿里云中配置RocketMQ

获取下面配置文件中的namesrv_addr,access_key,secret_key 参数

  1. 注册阿里云,然后搜索 消息队列RocketMQ
  2. 购买了一个便宜的资源包
  3. 返回主页创建一个实例在这里插入图片描述
  4. 如果你是基于tcp发送MQ,则将namesrv_addr设置为这个公网接入点在这里插入图片描述
  5. 进入access管理获取access_key和access_secret在这里插入图片描述

三、创建topic和group

Topic:生产者在发送消息和消费者在拉取消息的类别 可以看作每个路的路名
Tag:标签,换句话的意思就是子主题,为用户提供了额外的灵活性。有了标签,来自同一业务模块(同一topic)的具有不同目的的消息可以具有相同的主题和不同的标记
Group:生产/消费组,在集群情况下,新增一台服务,只要配置与之前一致的Group即可加入集群

阿里云中配置:
在这里插入图片描述

四、实现代码

下文中所有代码在git地址中

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.7.RELEASE</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>com.example</groupId>
	<artifactId>demo</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<packaging>war</packaging>
	<name>demo</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</groupId>
			<artifactId>fastjson</artifactId>
			<version>1.2.47</version>
		</dependency>

		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-web</artifactId>
			<version>5.1.6.RELEASE</version>
		</dependency>
		<!--rocketmq  用阿里云的ons去调用rocketmq-->
		<dependency>
			<groupId>com.aliyun.openservices</groupId>
			<artifactId>ons-client</artifactId>
			<version>1.8.4.Final</version>
		</dependency>
	</dependencies>


	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>

</project>

配置文件

server:
    port: 8090
#进入阿里云官网,注册一下,然后购买一下RocketMQ服务,就能看到地址
aliyun:
    mq:
        namesrv_addr: http://MQ_INST_213213213221_BccQ0pjs.mq-internet-access.mq-internet.aliyuncs.com:80 #阿里云RocketMQ地址
        access_key: LTAI4GJhu8qPHPZTssssssseFD  #阿里云个人中心的accesskey
        secret_key: X9Vdllp3ssssssssssssssss   #阿里云个人中心的secret_key
        groupId: GID_TCP_001  #在
        topic: ww_test_topic
        tag: ww_test_tag
        suspend_time_millis: 100
        max_reconsume_times: 20

将生产者和消费者的ProducerBean和ConsumerBean纳入Spring管理,Configuration

package com.example.demo.config.mq;

import com.aliyun.openservices.ons.api.MessageListener;
import com.aliyun.openservices.ons.api.ONSFactory;
import com.aliyun.openservices.ons.api.Producer;
import com.aliyun.openservices.ons.api.PropertyKeyConst;
import com.aliyun.openservices.ons.api.bean.ConsumerBean;
import com.aliyun.openservices.ons.api.bean.ProducerBean;
import com.aliyun.openservices.ons.api.bean.Subscription;
import com.example.demo.mq.AliMqComponent;
import com.example.demo.mq.rece.MqMessageListener;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import java.util.UUID;

/**
 * @Author ww
 * @Date 2020-04-27
 */
@Configuration
public class AliMqConfig {

    @Value("${aliyun.mq.namesrv_addr}")
    private String namesrvAddr;

    @Value("${aliyun.mq.suspend_time_millis}")
    private String suspendTimeMillis;

    @Value("${aliyun.mq.max_reconsume_times}")
    private String maxReconsumeTimes;
    @Value("${aliyun.mq.access_key}")
    private String accessKey;
    @Value("${aliyun.mq.secret_key}")
    private String secretKey;

    @Value("${aliyun.mq.groupId}")
    private String groupId;
    @Value("${aliyun.mq.topic}")
    private String topic;
    @Value("${aliyun.mq.tag}")
    private String tag;
    /**
 *      指定组建的init方法和destroy的几种方法
 *      在new该类的时候 会自动执行initMethod
 *      1:在配置类中 @Bean(initMethod = "init",destroyMethod = "destory")注解指定
 *      2:实现InitializingBean接口重写其afterPropertiesSet方法,实现DisposableBean接口重写destroy方法
 *      3:利用java的JSR250规范中的@PostConstruct标注在init方法上,@PreDestroy标注在destroy注解上
 */

    /**
     *  tcp 注册生产者Bean
     *
     * @return
     */
    @Bean(initMethod = "start", destroyMethod = "shutdown")
    public ProducerBean getProducer() {
        ProducerBean producerBean = new ProducerBean();
        Properties properties = new Properties();
        properties.put(PropertyKeyConst.GROUP_ID, groupId);
        // AccessKey 阿里云身份验证,在阿里云服务器管理控制台创建
        properties.put(PropertyKeyConst.AccessKey, accessKey);
        // SecretKey 阿里云身份验证,在阿里云服务器管理控制台创建
        properties.put(PropertyKeyConst.SecretKey, secretKey);
        // 设置 TCP 接入域名,进入控制台的实例管理页面的“获取接入点信息”区域查看
        properties.put(PropertyKeyConst.NAMESRV_ADDR, namesrvAddr);
        producerBean.setProperties(properties);
        return producerBean;
    }

    /**
     * tcp 生产者
     *
     * @return
     */
    @Bean(initMethod = "start", destroyMethod = "shutdown")
    public ProducerBean getCommonProducer() {
        ProducerBean producerBean = new ProducerBean();
        Properties properties = new Properties();
        properties.put(PropertyKeyConst.GROUP_ID, groupId);
        // AccessKey 阿里云身份验证,在阿里云服务器管理控制台创建
        properties.put(PropertyKeyConst.AccessKey, accessKey);
        // SecretKey 阿里云身份验证,在阿里云服务器管理控制台创建
        properties.put(PropertyKeyConst.SecretKey, secretKey);
        // 设置 TCP 接入域名,进入控制台的实例管理页面的“获取接入点信息”区域查看
        properties.put(PropertyKeyConst.NAMESRV_ADDR, namesrvAddr);
        properties.put(PropertyKeyConst.InstanceName, UUID.randomUUID().toString());
        producerBean.setProperties(properties);
        return producerBean;
    }

    /**
     * tcp 消费者  MqMessageListener没有放入Spring管理 所以在中间无法直接依赖注入。需要以前放在接口里面
     *
     * @param
     * @return
     */
    @Bean(initMethod = "start", destroyMethod = "shutdown")
    @ConditionalOnBean(name = "aliMqComponent") //如果需要注入其他的类或者接口 需要放在@ConditionalOnBean标签
    public ConsumerBean getCommonConsumer(AliMqComponent aliMqComponent) {
        ConsumerBean consumerBean = new ConsumerBean();
        Properties properties = new Properties();
        properties.put(PropertyKeyConst.GROUP_ID, groupId);
        // AccessKey 阿里云身份验证,在阿里云服务器管理控制台创建
        properties.put(PropertyKeyConst.AccessKey, accessKey);
        // SecretKey 阿里云身份验证,在阿里云服务器管理控制台创建
        properties.put(PropertyKeyConst.SecretKey, secretKey);
        // 设置 TCP 接入域名,进入控制台的实例管理页面的“获取接入点信息”区域查看
        properties.put(PropertyKeyConst.NAMESRV_ADDR, namesrvAddr);
        consumerBean.setProperties(properties);
        //组装订阅者消息
        Map<Subscription, MessageListener> map = new HashMap<Subscription, MessageListener>();
        Subscription subscription = new Subscription();
        subscription.setTopic(topic);//设置需要消费的消息所属的topic
        subscription.setExpression(tag);//设置需要消费的消息所属的tag
        map.put(subscription, new MqMessageListener(aliMqComponent));//MqMessageListener实现MessageListener接口,并且在consume方法中实现消费逻辑
        consumerBean.setSubscriptionTable(map);//将订阅者消息放入consumerBean中,在Spring初始加载该bean的时候,监听MQ中的Topic和tag下的消息
        return consumerBean;
    }

    @Autowired
    MqMessageSpringListener mqMessageListener;
    /**
     * tcp 消费者  将MqMessageListener放入Spring管理 可以在逻辑里依赖注入其他类也可以获取配置文件
     *
     * @param
     * @return
     */
    @Bean(initMethod = "start", destroyMethod = "shutdown")
    public ConsumerBean getCommonConsumer() {
        ConsumerBean consumerBean = new ConsumerBean();
        Properties properties = new Properties();
        properties.put(PropertyKeyConst.GROUP_ID, groupId);
        // AccessKey 阿里云身份验证,在阿里云服务器管理控制台创建
        properties.put(PropertyKeyConst.AccessKey, accessKey);
        // SecretKey 阿里云身份验证,在阿里云服务器管理控制台创建
        properties.put(PropertyKeyConst.SecretKey, secretKey);
        // 设置 TCP 接入域名,进入控制台的实例管理页面的“获取接入点信息”区域查看
        properties.put(PropertyKeyConst.NAMESRV_ADDR, namesrvAddr);
        consumerBean.setProperties(properties);
        //组装订阅者消息
        Map<Subscription, MessageListener> map = new HashMap<Subscription, MessageListener>();
        Subscription subscription = new Subscription();
        subscription.setTopic(topic);//设置需要消费的消息所属的topic
        subscription.setExpression(tag);//设置需要消费的消息所属的tag
        map.put(subscription, mqMessageListener);//MqMessageListener实现MessageListener接口,并且在consume方法中实现消费逻辑
        consumerBean.setSubscriptionTable(map);//将订阅者消息放入consumerBean中,在Spring初始加载该bean的时候,监听MQ中的Topic和tag下的消息
        return consumerBean;
    }

}

消费者逻辑

package com.example.demo.mq.rece;

import com.aliyun.openservices.ons.api.Action;
import com.aliyun.openservices.ons.api.ConsumeContext;
import com.aliyun.openservices.ons.api.Message;
import com.aliyun.openservices.ons.api.MessageListener;
import com.example.demo.mq.AliMqComponent;

/**
 * 消费者消费逻辑
 * 该监听被装载消费者bean中 consumer.setSubscriptionTable()
 * @Author ww
 * @Date 2020-04-27
 */
public class MqMessageListener implements MessageListener {

    private AliMqComponent aliMqComponent;
    public MqMessageListener(AliMqComponent aliMqComponent) {
        this.aliMqComponent=aliMqComponent;
    }

    @Override
    public Action consume(Message message, ConsumeContext consumeContext) {

        try {
            String msg = new String(message.getBody(),"UTF-8");
            System.out.println("消息消费成功:"+msg);
            return Action.ReconsumeLater;
            // }
        } catch (Exception e) {
            return Action.ReconsumeLater;
        } finally {
        }
        // return null;
    }


}

发送消息类(1.获取producer 2.将topic和tag设置在Message 3.调用produce.send(Message))

package com.example.demo.mq;

import com.aliyun.openservices.ons.api.Message;
import com.aliyun.openservices.ons.api.Producer;
import com.aliyun.openservices.ons.api.SendResult;
import com.example.demo.config.mq.AliMqConfig;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

/**
 * @Author ww
 * @Date 2020-04-27
 */
@Component
public class AliMqComponent {

    @Autowired
    private AliMqConfig aliMqConfig;

    /**
     * 普通发送消息
     *
     * @param topic 主题
     * @param tag   多个用| * 代表所有
     * @param key
     * @param body
     */
    public SendResult sendMessage(String topic, String tag, String key, byte[] body) {
        Producer producer = aliMqConfig.getProducer();
        Message msg = new Message(topic, tag, body);
        msg.setKey(key);
        try {
            SendResult sendResult = producer.send(msg);
            if (null != sendResult) {
                return sendResult;
            }
            return null;
        } catch (Exception e) {
            e.printStackTrace();
        }

        return null;
    }

    /**
         * 发送延时消息
         *
         * @param topic     主题
         * @param tag       多个用| * 代表所有
         * @param key
         * @param body      内容
         * @param delayTime 延时时间,单位毫秒
         * @return
         */
        public SendResult sendDelayMessage(String topic, String tag, String key, byte[] body, long delayTime) {
            Producer producer = aliMqConfig.getProducer();
            Message msg = new Message(topic, tag, body);
            msg.setKey(key);
            msg.setStartDeliverTime(delayTime);
            try {
                SendResult sendResult = producer.send(msg);
                if (null != sendResult) {
                    return sendResult;
                }
                return null;
            } catch (Exception e) {
                e.printStackTrace();
            }
            return null;
    }

    /**
     * 普通发送信息
     * @param key
     * @param body
     * @return
     */
    public String sendCommonMessage(String topic, String tag, String key, byte[] body) {
        Producer producer = aliMqConfig.getCommonProducer();
        Message msg = new Message(topic, tag, body);
        msg.setKey(key);
        try {
            SendResult sendResult = producer.send(msg);
            if (null != sendResult) {
                return sendResult.getMessageId();
            }
            return null;
        } catch (Exception e) {
            e.printStackTrace();
            System.out.println("xxx");
        }

        return null;
    }


}

在同一个项目中生产消息发送到MQ和从MQ中消费该条消息

package com.example.demo.controller;

import com.alibaba.fastjson.JSON;
import com.example.demo.entity.SubmitUserPayOrderRequest;
import com.example.demo.entity.UserPayOrder;
import com.example.demo.mq.AliMqComponent;
import com.example.demo.service.UserPayOrderService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class UserController{
    @Autowired
    private UserPayOrderService userPayOrderService;

    @Autowired
    private AliMqComponent aliMqComponent;
    /**
     * 根据订单号获取商户id
     * @param userOrderId 订单id
     * @return
     */
    @PostMapping("/userOrderMerchantId")
    public String userOrderMerchantId(@RequestParam("userOrderId") String userOrderId){
        UserPayOrder vo=userPayOrderService.userOrderMerchantId(userOrderId);
        //1.向MQ中topic=ww_test_topic tag:ww_test_tag 发送一条消息
        String messageId = aliMqComponent.sendCommonMessage("ww_test_topic","ww_test_tag","ssss",JSON.toJSONBytes(vo));
        //同时MqMessageListener从MQ中监听到消息队列有数据,然后执行消费逻辑
        //结果为:消息消费成功:{"amountOfConsumption":0.01,"channelId":3,"createTime":1576115647000,"discountAmount":0.00,"freeAmount":0.00,"id":"20191212095406497_232109_3042","merchantId":811001,"merchantReceiveAmount":0.01,"payAbleAmount":0.01,"payTime":1576115648000,"payType":0,"status":1,"updateTime":1576115648000,"userPayId":232109}
        return JSON.toJSONString(vo);
    }
  
}

五、Http通信方式的生产者和消费者

pom.xml

		<!-- MQ Http通信方式依赖-->
		<dependency>
			<groupId>com.aliyun.mq</groupId>
			<artifactId>mq-http-sdk</artifactId>
			<version>1.0.1</version>
		</dependency>

application.yml

aliyun:
    mq:
        http:
            instanceId: xx
            access_key: xx
            secret_key: xx
            http_addr: http://cccccccccccccccccccccccccccc.mq-internet-access.mq-internet.aliyuncs.com:80
            group_id: GID_TCP_001
            topic: ww_test_topic
            tag: ww_test_tag

AliMqConfig.java

package com.example.demo.config.mq;

import com.aliyun.mq.http.MQClient;
import com.aliyun.mq.http.MQConsumer;
import com.aliyun.mq.http.MQProducer;
import com.aliyun.mq.http.common.AckMessageException;
import com.aliyun.mq.http.model.Message;
import com.aliyun.mq.http.model.TopicMessage;
import com.aliyun.openservices.ons.api.MessageListener;
import com.aliyun.openservices.ons.api.PropertyKeyConst;
import com.aliyun.openservices.ons.api.bean.ConsumerBean;
import com.aliyun.openservices.ons.api.bean.ProducerBean;
import com.aliyun.openservices.ons.api.bean.Subscription;
import com.example.demo.mq.AliMqComponent;
import com.example.demo.mq.rece.MqMessageListener;
import com.example.demo.mq.rece.MqMessageSpringListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.*;

/**
 * @Author ww
 * @Date 2020-04-27
 */
@Configuration
public class AliMqConfig {

    /**
     * Http 跨平台
     */
    @Value("${aliyun.mq.http.instanceId}")
    private String instanceId;//实例id
    @Value("${aliyun.mq.http.access_key}")
    private String httpAccessKey;
    @Value("${aliyun.mq.http.secret_key}")
    private String httpSecretKey;
    @Value("${aliyun.mq.http.http_addr}")
    private String httpHttpAddr;//http地址
    /**
     * topic、group、tag
     */
    @Value("${aliyun.mq.http.group_id}")
    private String httpGroupId;
    @Value("${aliyun.mq.http.topic}")
    private String httpTopic;
    @Value("${aliyun.mq.http.tag}")
    private String httpTag;




    /**
     * Http发送mq消息
     *
     * @param requestContext 发送内容
     * @param tag
     * @return
     */
    public String sendMessageByHttp(String requestContext, String tag) {
        MQClient mqClient = new MQClient(httpHttpAddr,  httpAccessKey, secretKey);
        MQProducer producer = mqClient.getProducer(instanceId, httpSecretKey);
        TopicMessage topicMessage = new TopicMessage(
                requestContext.getBytes(),
                tag
        );
        TopicMessage pubResultMsg = producer.publishMessage(topicMessage);
        String messageId = pubResultMsg.getMessageId();
        mqClient.close();
        return messageId;
    }
    /**
     * Http消费mq消息
     * //@Bean(initMethod = "start", destroyMethod = "shutdown")注释是因为没有消息 所以会一直报错
     * @param
     * @return
     */
    //@Bean(initMethod = "start", destroyMethod = "shutdown")
    public ConsumerBean getCxUserSynchronizeUserPlatform() {
        MQClient mqClient = new MQClient(httpHttpAddr, httpAccessKey, httpSecretKey);
        MQConsumer consumer = mqClient.getConsumer(instanceId, httpTopic, httpGroupId, null);

        // 在当前线程循环消费消息,建议是多开个几个线程并发消费消息
        do {
            List<Message> messages = null;
            try {
                // 长轮询消费消息
                // 长轮询表示如果topic没有消息则请求会在服务端挂住3s,3s内如果有消息可以消费则立即返回
                messages = consumer.consumeMessage(
                        3,// 一次最多消费3条(最多可设置为16条)
                        3// 长轮询时间3秒(最多可设置为30秒)
                );
            } catch (Throwable e) {
                e.printStackTrace();
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e1) {
                    e1.printStackTrace();
                }
            }
            // 没有消息
            if (messages == null || messages.isEmpty()) {
                continue;
            }

            // 处理业务逻辑
            for (Message message : messages) {
                try {
                    //将传输正文转换成utf-8的json字符串
                    String msg = new String(message.getMessageBodyBytes(), "utf-8");
                }catch (Exception e){

                }

            }

            // Message.nextConsumeTime前若不确认消息消费成功,则消息会重复消费
            // 消息句柄有时间戳,同一条消息每次消费拿到的都不一样
            {
                List<String> handles = new ArrayList<String>();
                for (Message message : messages) {
                    handles.add(message.getReceiptHandle());
                }

                try {
                    consumer.ackMessage(handles);
                } catch (Throwable e) {
                    // 某些消息的句柄可能超时了会导致确认不成功
                    if (e instanceof AckMessageException) {
                        AckMessageException errors = (AckMessageException) e;
                        System.out.println("Ack message fail, requestId is:" + errors.getRequestId() + ", fail handles:");
                        if (errors.getErrorMessages() != null) {
                            for (String errorHandle : errors.getErrorMessages().keySet()) {
                                System.out.println("Handle:" + errorHandle + ", ErrorCode:" + errors.getErrorMessages().get(errorHandle).getErrorCode()
                                        + ", ErrorMsg:" + errors.getErrorMessages().get(errorHandle).getErrorMessage());
                            }
                        }
                        continue;
                    }
                    e.printStackTrace();
                }
            }
        } while (true);
    }

}

这样会阻塞主线程,所以需要重新开一个子线程去处理。

package com.cx.user.config.mq;

import com.alibaba.fastjson.JSONObject;
import com.aliyun.mq.http.MQClient;
import com.aliyun.mq.http.MQConsumer;
import com.aliyun.mq.http.common.AckMessageException;
import com.aliyun.mq.http.model.Message;
import com.cx.user.service.UserPayOrderService;
import com.xz.log.utils.LogTrace;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;

import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import java.util.ArrayList;
import java.util.List;

/**
 * HTTP消费者 新开线程去消费消息。
 *
 * @param
 * @return
 */
@Configuration
public class SyCxOrderInfoHttpMqConsumer implements ServletContextListener {
    /**
     * Http 跨平台
     */
    @Value("${aliyun.mq.http.instanceId}")
    private String instanceId;//实例id
    @Value("${aliyun.mq.http.access_key}")
    private String httpAccessKey;
    @Value("${aliyun.mq.http.secret_key}")
    private String httpSecretKey;
    @Value("${aliyun.mq.http.http_addr}")
    private String httpHttpAddr;//http地址
    /**
     * topic、group、tag
     */
    @Value("${aliyun.mq.http.group_id}")
    private String httpGroupId;
    @Value("${aliyun.mq.http.topic}")
    private String httpTopic;
    @Value("${aliyun.mq.http.tag}")
    private String httpTag;

    @Override
    public void contextInitialized(ServletContextEvent servletContextEvent) {
    	/**
    	* 在项目加载的时候去新建一个线程去处理
    	*/
        for (int i = 1; i <= 2; i++) {
            int finalI = i;
            new Thread(() -> cxtConsumer(finalI)).start();
        }

    }

    @Override
    public void contextDestroyed(ServletContextEvent servletContextEvent) {

    }

    public void cxtConsumer(int index) {


        MQClient mqClient = new MQClient(scoreHttpAddr, scoreAccessKey, scoreSecretKey);
        MQConsumer consumer = mqClient.getConsumer(instanceId, scoreOrderTopic, scoreGroupId, null);

        // 在当前线程循环消费消息,建议是多开个几个线程并发消费消息
        do {
            List<Message> messages = null;
            try {
                // 长轮询消费消息
                // 长轮询表示如果topic没有消息则请求会在服务端挂住3s,3s内如果有消息可以消费则立即返回
                messages = consumer.consumeMessage(
                        3,// 一次最多消费3条(最多可设置为16条)
                        3// 长轮询时间3秒(最多可设置为30秒)
                );
            } catch (Throwable e) {
                e.printStackTrace();
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e1) {
                    e1.printStackTrace();
                }
            }
            // 没有消息
            if (messages == null || messages.isEmpty()) {
                continue;
            }

            // 处理业务逻辑
            messages.forEach(message ->(System.out.println("xxx")));

            // Message.nextConsumeTime前若不确认消息消费成功,则消息会重复消费
            // 消息句柄有时间戳,同一条消息每次消费拿到的都不一样
            {
                List<String> handles = new ArrayList<>();
                messages.forEach(message -> handles.add(message.getReceiptHandle()));
                try {
                    consumer.ackMessage(handles);
                } catch (Throwable e) {
                    // 某些消息的句柄可能超时了会导致确认不成功
                    if (e instanceof AckMessageException) {
                        AckMessageException errors = (AckMessageException) e;
                        LogTrace.error("消费者积分服务消息", errors);
                        continue;
                    }
                    e.printStackTrace();
                }
            }
        } while (true);

    }
}

六、整体加载流程与其参数解释

  1. AliMqConfig类被@Configuration标签,将生产者/消费者配置完整参数(ons地址,access_key等参数)后,对生产者/消费者实体用@bean标签,将他们bean动态代理,方便方法中调用生产者/消费者bean。
    参数如下:
    ProducerBean 生产者类,实现Producer接口。
    在这里插入图片描述
    Properties:生产者/消费者参数列表(本质是个HashTable),会在生产者/消费者实例启动服务的时候,调用ONSFactory.createProducer()方法,将Properties参数传入,并且返回一个Producer在这里插入图片描述
    ONS生产者/消费者参数如下:
    在这里插入图片描述
    注:
    @Configuration修饰的类被在加载中定义成Spring容器(相当于老版本的xml文件中的)
    @Bean 修饰的返回实例(上文中的ProducerBean和ConsumerBean)方法 默认为单例作用域

  2. AliMqComponent类: 是用于将AliMqConfig中加载的生产者/消费者bean,将拼接好的Message放入返回bean的send方法中,实现消息的发送
    生产者bean发送消息
    在这里插入图片描述
    Message参数如下:
    在这里插入图片描述

  3. 消费者:与生产者在AliMqConfig中配置bean不同。消费者需要在配置好GROUP_ID、AccessKey、SecretKey、NAMESRV_ADDR等参数后去组装一个订阅者的,并且将需要消费的消息的Topic和tag放在Subscription(订阅参数)和MessageListener(消息监听类,一般消费者类都实现该接口,将消费逻辑放在consume方法中)
    在这里插入图片描述
    注:
    @Bean(initMethod = “start”, destroyMethod = “shutdown”) 会在对象创建完成,赋值完成的时候调用initMethod的的方法,也就是Producer/Consumer的start()方法,在该容器(bean所属的类)被关闭后会调用destroyMethod
    单实例bean和多实例bean的区别
    a.单实例是容器启动后创建,多实例是获取时创建对象
    b.初始化initMethod 一样 而销毁不同 单实例是容器关闭调用销毁方法 多实例是手动调用销毁方式
    @ConditionOnBean 条件注解是Spring4提供的一种bean加载特性,主要用于控制配置类和bean初始化条件

  4. 消费者监听实现类在这里插入图片描述
    返回的Action的参数
    在这里插入图片描述

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值