RocketMQ
新公司的消息队列用的是RocketMQ,但是不是直接使用RocketMq,而是采用了阿里分布式开放消息服务(ONS)
一、阿里分布式开放消息服务(ONS)
ONS(Open Notification Service)即开放消息服务,是基于阿里开源消息中间件MetaQ(RocketMQ)打造的一款云消息产品。实现生产者与消费者的代码稍微和原生的有点差别
二、阿里云中配置RocketMQ
获取下面配置文件中的namesrv_addr,access_key,secret_key 参数
- 注册阿里云,然后搜索 消息队列RocketMQ
- 购买了一个便宜的资源包
- 返回主页创建一个实例
- 如果你是基于tcp发送MQ,则将namesrv_addr设置为这个公网接入点
- 进入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);
}
}
六、整体加载流程与其参数解释
-
AliMqConfig类被@Configuration标签,将生产者/消费者配置完整参数(ons地址,access_key等参数)后,对生产者/消费者实体用@bean标签,将他们bean动态代理,方便方法中调用生产者/消费者bean。
参数如下:
ProducerBean 生产者类,实现Producer接口。
Properties:生产者/消费者参数列表(本质是个HashTable),会在生产者/消费者实例启动服务的时候,调用ONSFactory.createProducer()方法,将Properties参数传入,并且返回一个Producer
ONS生产者/消费者参数如下:
注:
@Configuration修饰的类被在加载中定义成Spring容器(相当于老版本的xml文件中的)
@Bean 修饰的返回实例(上文中的ProducerBean和ConsumerBean)方法 默认为单例作用域 -
AliMqComponent类: 是用于将AliMqConfig中加载的生产者/消费者bean,将拼接好的Message放入返回bean的send方法中,实现消息的发送
生产者bean发送消息
Message参数如下:
-
消费者:与生产者在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初始化条件 -
消费者监听实现类
返回的Action的参数