安装RocketMQ5.0(官网方法)
1.下载RocketMQ5.0
RocketMQ 的安装包分为两种,二进制包和源码包。 点击这里 下载 Apache RocketMQ 5.1.4的源码包。你也可以从 这里 下载到二进制包。二进制包是已经编译完成后可以直接运行的,源码包是需要编译后运行的。
解压5.1.4的源码包并编译构建二进制可执行文件。
$ unzip rocketmq-all-5.1.4-source-release.zip
$ cd rocketmq-all-5.1.4-source-release/
$ mvn -Prelease-all -DskipTests -Dspotbugs.skip=true clean install -U
$ cd distribution/target/rocketmq-5.1.4/rocketmq-5.1.4
2.启动NameServer
### 启动namesrv
$ nohup sh bin/mqnamesrv &
### 验证namesrv是否启动成功
$ tail -f ~/logs/rocketmqlogs/namesrv.log
The Name Server boot success...
如果自己服务器内存不够大的话可以更改 bin/runserver.sh 中内存信息
3.启动Broker+Proxy
### 先启动broker
$ nohup sh bin/mqbroker -n localhost:9876 --enable-proxy &
### 验证broker是否启动成功, 比如, broker的ip是192.168.1.2 然后名字是broker-a
$ tail -f ~/logs/rocketmqlogs/proxy.log
The broker[broker-a,192.169.1.2:10911] boot success...
至此,一个单节点副本的 RocketMQ 集群已经部署起来了,我们可以利用脚本进行简单的消息收发。
4.关闭服务
$ sh bin/mqshutdown broker
The mqbroker(36695) is running...
Send shutdown request to mqbroker with proxy enable OK(36695)
$ sh bin/mqshutdown namesrv
The mqnamesrv(36664) is running...
Send shutdown request to mqnamesrv(36664) OK
创建Topic
主题必须有消息类型,且只能接收相对应消息类型的消息
Normal:普通消息,消息本身无特殊语义,消息之间也没有任何关联。
FIFO:顺序消息,Apache RocketMQ 通过消息分组MessageGroup标记一组特定消息的先后顺序,可以保证消息的投递顺序严格按照消息发送时的顺序。
Delay:定时/延时消息,通过指定延时时间控制消息生产后不要立即投递,而是在延时间隔后才对消费者可见。
Transaction:事务消息,Apache RocketMQ 支持分布式事务消息,支持应用数据库更新和消息调用的事务一致性保障。
普通消息
sh mqadmin updateTopic -n <nameserver_address> -t <topic_name> -c <cluster_name> -a +message.type=NORMAL
定时/延时消息
sh mqadmin updateTopic -n <nameserver_address> -t <topic_name> -c <cluster_name> -a +message.type=DELAY
顺序消息
sh mqadmin updateTopic -n <nameserver_address> -t <topic_name> -c <cluster_name> -a +message.type=FIFO
事务消息
sh mqadmin updateTopic -n <nameserver_address> -t <topic_name> -c <cluster_name> -a +message.type=TRANSACTION
nameserver_address是9876端口
cluster_name一般为DefaultCluster
实现代码
项目地址
pom依赖
<properties>
<rocketmq.version>5.0.5</rocketmq.version>
</properties>
<dependencies>
<!--rocketmq消息队列-->
<dependency>
<groupId>org.apache.rocketmq</groupId>
<artifactId>rocketmq-client-java</artifactId>
<version>${rocketmq.version}</version>
</dependency>
</dependencies>
yml配置
# TODO endpoints 是Proxy的地址和端口列表,一般是xxx:8081;xxx:8081,并非9876端口。在启动Broker+Proxy的日志中有显示。
rocketmq:
# 生产者配置
producer:
# Endpoints rocketMQ端点
endpoints: 127.0.0.1:8081
# 最大重试次数 默认3
max-attempts: 3
# 主题
topic-list:
- system-message
- user-message
- delay-message
# 消费者配置
consumer:
#是否开启
enabled: true
# Endpoints rocketMQ端点
endpoints: 127.0.0.1:8081
# 官方建议:确保同一组中的每个消费者订阅相同的主题。
group: zcccccy-message-consumer
# 主题
topic-list:
- system-message
- user-message
- delay-message
消费者
配置信息类
/**
* 生产者初始化 生产者连接信息
*
* @projectName: zcccccy
* @packageName: com.zcccccy.extend.rocketmq.producer.config.properties
* @author: zcy
* @createTime: 2024/1/29
* @productName: For Mac IntelliJ IDEA
*/
@Data
@Component
@ConfigurationProperties(prefix = "rocketmq.producer")
public class ProducerProperties {
/** Endpoints rocketMQ端点 */
private String endpoints;
/** 最大重试次数 */
private int maxAttempts = 3;
/** 主题 */
private List<String> topicList;
}
生产者初始化
/**
* 生产者创建
*
* @projectName: zcccccy
* @packageName: com.zcccccy.extend.rocketmq.producer.config
* @author: zcy
* @createTime: 2024/1/29
* @productName: For Mac IntelliJ IDEA
*/
@Configuration
public class ProducerConfig {
private static final Logger log = LoggerFactory.getLogger(ProducerConfig.class);
/**
* 远程调用连接信息
*/
public static Producer producer;
@Autowired
private ProducerProperties producerProperties;
@Bean
public Producer builderProducer() {
log.info("==============》 开始初始化生产者 《===================");
try {
final ClientServiceProvider provider = ClientServiceProvider.loadService();
// 构建连接信息
ClientConfiguration configuration = ClientConfiguration.newBuilder()
// 端点信息
.setEndpoints(producerProperties.getEndpoints())
// 构建
.build();
// 初始化Producer时需要设置通信配置以及预绑定的Topic。
producer = provider.newProducerBuilder()
// TODO 生产者主题预绑定多个,可发送不同主题消息以及不同消息类型消息
// 主题
.setTopics(producerProperties.getTopicList().toArray(new String[0]))
// 连接信息
.setClientConfiguration(configuration)
// 最大重试次数
.setMaxAttempts(producerProperties.getMaxAttempts())
.build();
log.info("==============》 初始化生产者成功:{} ", producer);
} catch (ClientException e) {
log.info("==============》 初始化生产者失败:{} ", e.getMessage());
e.printStackTrace();
}
return producer;
}
}
消息推送实现
目前只对接了普通消息和定时/延时消息
/**
* @projectName: zcccccy
* @packageName: com.zcccccy.extend.rocketmq.producer.service
* @author: zcy
* @createTime: 2024/1/29
* @productName: For Mac IntelliJ IDEA
*/
@Service
public class MessageProducer {
// TODO 注意:RocketMQ5.0在创建Topic时会指定消息类型,默认为普通消息。
// TODO 注意:RocketMQ5.0不允许推送不同与Topic消息类型的消息到Topic
// TODO 本项目只对接了普通消息和定时/延时消息
// 普通消息 : https://rocketmq.apache.org/zh/docs/featureBehavior/01normalmessage
// 定时/延时消息:https://rocketmq.apache.org/zh/docs/featureBehavior/02delaymessage
// 顺序消息:https://rocketmq.apache.org/zh/docs/featureBehavior/03fifomessage
// 事务消息:https://rocketmq.apache.org/zh/docs/featureBehavior/04transactionmessage
// TODO 目前只有同步消息,异步消息后续有时间写
private static final Logger log = LoggerFactory.getLogger(MessageProducer.class);
@Autowired
private Producer producer;
/**
* 发送普通消息
*
* @param topic
* 主题
* @param body
* 消息体
* @return java.lang.String
* @author zcy
* @createTime 2024/1/29 19:22
**/
public String sendMessage(String topic, String body) {
return sendMessage(topic, null, body);
}
/**
* 发送普通消息
*
* @param topic
* 主题
* @param messageKey
* key
* @param body
* 消息体
* @return java.lang.String
* @author zcy
* @createTime 2024/1/29 19:22
**/
public String sendMessage(String topic, String messageKey, String body) {
return sendMessage(topic, "tag", messageKey, body);
}
/**
* 发送普通消息
*
* @param topic
* 主题
* @param tag
* tag
* @param messageKey
* key
* @param body
* 消息体
* @return java.lang.String
* @author zcy
* @createTime 2024/1/29 17:31
**/
public String sendMessage(String topic, String tag, String messageKey, String body) {
log.info("发送消息:【主题】:{}, 【tag】:{},【消息key】:{}【消息体】:{}", topic, tag, messageKey, body);
// 定时/延时消息发送
MessageBuilder messageBuilder = new MessageBuilderImpl();
Message message = messageBuilder.setTopic(topic)
// 设置消息索引键,可根据关键字精确查找某条消息。
.setKeys(messageKey)
// 设置消息Tag,用于消费端根据指定Tag过滤消息。
.setTag(tag)
// 消息体
.setBody(body.getBytes()).build();
try {
// 发送消息,需要关注发送结果,并捕获失败等异常。
SendReceipt sendReceipt = producer.send(message);
log.info("消息发送成功:【消息id】:{}", sendReceipt.getMessageId());
return sendReceipt.getMessageId().toString();
} catch (ClientException e) {
log.info("消息发送失败");
e.printStackTrace();
}
return null;
}
/**
* 发送延时消息
*
* @param topic
* 主题 key
* @param body
* 消息体
* @param duration
* 延时时间/毫秒
* @return java.lang.String
* @author zcy
* @createTime 2024/1/29 17:31
**/
public String sendDelayMessage(String topic, String body, Long duration) {
return sendDelayMessage(topic, null, body, duration);
}
/**
* 发送延时消息
*
* @param topic
* 主题
* @param messageKey
* key
* @param body
* 消息体
* @param duration
* 延时时间/毫秒
* @return java.lang.String
* @author zcy
* @createTime 2024/1/29 17:31
**/
public String sendDelayMessage(String topic, String messageKey, String body, Long duration) {
return sendDelayMessage(topic, "tag", messageKey, body, duration);
}
/**
* 发送延时消息
*
* @param topic
* 主题
* @param tag
* tag
* @param messageKey
* key
* @param body
* 消息体
* @param duration
* 延时时间/毫秒
* @return java.lang.String
* @author zcy
* @createTime 2024/1/29 17:31
**/
public String sendDelayMessage(String topic, String tag, String messageKey, String body, Long duration) {
// 延迟后的时间戳。
Long deliverTimeStamp = System.currentTimeMillis() + duration;
return sendTimedMessage(topic, tag, messageKey, body, deliverTimeStamp);
}
/**
* 发送定时消息
*
* @param topic
* 主题 key
* @param body
* 消息体
* @param deliverTimeStamp
* 时间戳
* @return java.lang.String
* @author zcy
* @createTime 2024/1/29 17:31
**/
public String sendTimedMessage(String topic, String body, Long deliverTimeStamp) {
return sendTimedMessage(topic, null, body, deliverTimeStamp);
}
/**
* 发送定时消息
*
* @param topic
* 主题
* @param messageKey
* key
* @param body
* 消息体
* @param deliverTimeStamp
* 时间戳
* @return java.lang.String
* @author zcy
* @createTime 2024/1/29 17:31
**/
public String sendTimedMessage(String topic, String messageKey, String body, Long deliverTimeStamp) {
return sendTimedMessage(topic, "tag", messageKey, body, deliverTimeStamp);
}
/**
* 发送定时消息
*
* @param topic
* 主题
* @param tag
* tag
* @param messageKey
* key
* @param body
* 消息体
* @param deliverTimeStamp
* 时间戳
* @return java.lang.String
* @author zcy
* @createTime 2024/1/29 17:31
**/
public String sendTimedMessage(String topic, String tag, String messageKey, String body, Long deliverTimeStamp) {
log.info("发送消息:【主题】:{}, 【tag】:{},【消息key】:{}【消息体】:{}【时间戳】:{}", topic, tag, messageKey, body, deliverTimeStamp);
// 定时/延时消息发送
MessageBuilder messageBuilder = new MessageBuilderImpl();
Message message = messageBuilder.setTopic(topic)
// 设置消息索引键,可根据关键字精确查找某条消息。
.setKeys(messageKey)
// 设置消息Tag,用于消费端根据指定Tag过滤消息。
.setTag(tag)
// 时间戳
.setDeliveryTimestamp(deliverTimeStamp)
// 消息体
.setBody(body.getBytes()).build();
try {
// 发送消息,需要关注发送结果,并捕获失败等异常。
SendReceipt sendReceipt = producer.send(message);
log.info("消息发送成功:【消息id】:{}", sendReceipt.getMessageId());
return sendReceipt.getMessageId().toString();
} catch (ClientException e) {
log.info("消息发送失败");
e.printStackTrace();
}
return null;
}
}
消费者
配置信息类
/**
* 消费者初始化 消费者连接信息
*
* @projectName: zcccccy
* @packageName: com.zcccccy.extend.rocketmq.consumer.model
* @author: zcy
* @createTime: 2024/1/27
* @productName: For Mac IntelliJ IDEA
*/
@Data
@Component
@ConfigurationProperties(prefix = "rocketmq.consumer")
public class ConsumerProperties {
/** Endpoints rocketMQ端点 */
private String endpoints;
/** 组名 */
private String group;
/** 主题 */
private List<String> topicList;
}
消费者初始化
/**
* 消费者创建
*
* @projectName: zcccccy
* @packageName: com.zcccccy.extend.rocketmq.consumer.config
* @author: zcy
* @createTime: 2024/1/27
* @productName: For Mac IntelliJ IDEA
*/
@Configuration
@ConditionalOnProperty(value = "rocketmq.consumer.enabled", havingValue = "true")
public class ConsumerConfig {
private static final Logger log = LoggerFactory.getLogger(ConsumerConfig.class);
@Autowired
private ConsumerProperties consumerProperties;
@Bean
public void builderPushConsumer() {
// TODO 本项目消费方式使用PushConsumer消费,如需要SimpleConsumer消费机制请参照官网示例
log.info("==============》 开始初始化消费者 《===================");
// TODO 每个主题对应一种消息类型,所以初始化消费多个主题以达到消费多种类型消息。本项目只对接了普通消息和定时/延时消息。
// 其余消息可参照官网:https://rocketmq.apache.org/zh/docs/domainModel/04message
// 主题构建
Map<String, FilterExpression> topicMap = new HashMap<>();
for (String topic : consumerProperties.getTopicList()) {
// 订阅消息的过滤规则,表示订阅所有Tag的消息。
String tag = "*";
FilterExpression filterExpression = new FilterExpression(tag, FilterExpressionType.TAG);
topicMap.put(topic, filterExpression);
}
try {
final ClientServiceProvider provider = ClientServiceProvider.loadService();
// 连接信息构建
ClientConfiguration clientConfiguration = ClientConfiguration.newBuilder()
// 端点
.setEndpoints(consumerProperties.getEndpoints())
.build();
// 初始化PushConsumer,需要绑定消费者分组ConsumerGroup、通信参数以及订阅关系。
PushConsumer pushConsumer = provider.newPushConsumerBuilder()
//
.setClientConfiguration(clientConfiguration)
// 设置消费者分组。
.setConsumerGroup(consumerProperties.getGroup())
// 设置预绑定的订阅关系。
.setSubscriptionExpressions(topicMap)
// 设置消费监听器。
.setMessageListener(new RocketMQListener())
.build();
// 如果不需要再使用PushConsumer,可关闭该进程。
// pushConsumer.close();
log.info("==============》 初始化消费者成功:{}", pushConsumer);
} catch (ClientException e) {
log.error("==============》 初始化消费者失败:{}", e.getMessage());
}
}
}
消费者监听
/**
* 消息监听
*
* @projectName: zcccccy
* @packageName: com.zcccccy.extend.rocketmq.consumer.listener
* @author: zcy
* @createTime: 2024/1/27
* @productName: For Mac IntelliJ IDEA
*/
@Component
public class RocketMQListener implements MessageListener {
private static final Logger log = LoggerFactory.getLogger(RocketMQListener.class);
/**
* 消息消费 返回success表示消费成功
*
* @param messageView
* @return org.apache.rocketmq.client.apis.consumer.ConsumeResult
* @author zcy
* @createTime 2024/1/28 11:00
**/
@Override
public ConsumeResult consume(MessageView messageView) {
log.info("接收时间:{}", DateUtil.date());
log.info("消费消息:{}", messageView);
// 指定字符集(Charset)
Charset charset = StandardCharsets.UTF_8;
// 将ByteBuffer转换为String
String result = charset.decode(messageView.getBody()).toString();
log.info("消费消息内容:{}", result);
// TODO 处理业务
// 可以通过不同主题做不同处理
// String topic = messageView.getTopic();
if (messageView.getDeliveryAttempt() == 3){
// 重试到最大重试次数可实现其他补救措施
}
// 如果业务处理失败则返回失败
//return ConsumeResult.FAILURE;
// 消费成功返回成功
return ConsumeResult.SUCCESS;
}
}