一.初试RocketMQ------发送消息和消费消息的简单案例
项目结构为
看一下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 https://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.3.2.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.test.mq</groupId>
<artifactId>rocketmq</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>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</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.apache.rocketmq</groupId>
<artifactId>rocketmq-client</artifactId>
<version>4.2.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.alibaba/fastjson -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.44</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
先定义一个消息保存的载体,即生产者Producter:
package com.test.mq.rocketmq.producer;
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.apache.rocketmq.client.exception.MQClientException;
import org.apache.rocketmq.client.producer.DefaultMQProducer;
import java.io.Serializable;
import lombok.Data;
import org.apache.rocketmq.common.message.Message;
@Slf4j
public class Producer {
public static void main(String[] args) throws MQClientException {
DefaultMQProducer producer = new DefaultMQProducer("test-group");
producer.setNamesrvAddr("localhost:9876");
producer.setInstanceName("rmq-instance");
producer.start();
try {
for (int i=0;i<100;i++){
User user = new User();
user.setLoginName("abc"+i);
user.setPwd(String.valueOf(i));
Message message = new Message("log-topic", "user-tag",JSON.toJSONString(user).getBytes());
System.out.println("生产者发送消息:"+JSON.toJSONString(user));
producer.send(message);
}
} catch (Exception e) {
e.printStackTrace();
}
producer.shutdown();
}
@Data
static class User implements Serializable {
private String loginName;
private String pwd;
}
}
定义消息的消费者Customer:
package com.test.mq.rocketmq.customer;
import lombok.extern.slf4j.Slf4j;
import org.apache.rocketmq.client.consumer.DefaultMQPushConsumer;
import org.apache.rocketmq.client.consumer.listener.ConsumeConcurrentlyContext;
import org.apache.rocketmq.client.consumer.listener.ConsumeConcurrentlyStatus;
import org.apache.rocketmq.client.consumer.listener.MessageListenerConcurrently;
import org.apache.rocketmq.client.exception.MQClientException;
import org.apache.rocketmq.common.message.MessageExt;
import java.util.List;
@Slf4j
public class Consumer {
public static void main(String[] arg) throws MQClientException {
DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("test-group");
consumer.setNamesrvAddr("localhost:9876");
consumer.setInstanceName("rmq-instance");
consumer.subscribe("log-topic", "user-tag");
consumer.registerMessageListener(new MessageListenerConcurrently() {
public ConsumeConcurrentlyStatus consumeMessage(
List<MessageExt> msgs, ConsumeConcurrentlyContext context) {
for (MessageExt msg : msgs) {
System.out.println("消费者消费数据:"+new String(msg.getBody()));
}
return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
}
});
consumer.start();
}
}
接下来先启动消费者,然后再启动生产者,看一下效果
生产者控制台发送消息:
消费者控制台消费消息
且能发现消费消息的机制 默认是乱序的
二、Spring Boot与RocketMQ整合
整合方式有很多种,但是可以总结为两大类,一类是基于高度封装(使用起来非常简单,通用性高),一类是基于原始开发(没啥可通用性),在这里把这两个类别都讲解一下.
1.先讲一种,基于原始API开发,不要啥封装,只需要引入一个配置文件即可
看一下整体项目结构,主要实现的部分是红色标注的,其它类是第二种封装使用的,
看一下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 https://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.3.2.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.test.mq</groupId>
<artifactId>rocketmq</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>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</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.apache.rocketmq</groupId>
<artifactId>rocketmq-client</artifactId>
<version>4.2.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.alibaba/fastjson -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.44</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
在这里为了简化javabean的操作,引入了lombok组件.定义一个javabean用来承载消息
建立消息体UserContent:
package com.test.mq.rocketmq.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import lombok.experimental.Accessors;
@ToString
@AllArgsConstructor
@EqualsAndHashCode
@Accessors(chain = true)
@Data
public class UserContent {
private String username;
private String pwd;
}
关于这里的注解参数可以具体参考:lombok的常用注解含义
定义生产者:
package com.test.mq.rocketmq.producer;
import com.alibaba.fastjson.JSON;
import com.test.mq.rocketmq.pojo.UserContent;
import org.apache.rocketmq.client.producer.DefaultMQProducer;
import org.apache.rocketmq.client.producer.SendResult;
import org.apache.rocketmq.common.message.Message;
import org.apache.rocketmq.remoting.common.RemotingHelper;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
/**
*
* @Function 模拟用户消息发送
*/
@Component
public class UserProducer {
/**
* 生产者的组名
*/
@Value("${suning.rocketmq.producerGroup}") //注入classpath下面的配置文件中的值到变量中
private String producerGroup;
/**
* NameServer 地址
*/
@Value("${suning.rocketmq.namesrvaddr}")
private String namesrvAddr;
@PostConstruct //作用是相当于servlet的Init功能,在对象的构造器执行完了,就会立马调用该方法
public void produder() {
DefaultMQProducer producer = new DefaultMQProducer(producerGroup);
producer.setNamesrvAddr(namesrvAddr);
try {
producer.start();
for (int i = 0; i < 100; i++) {
UserContent userContent = new UserContent(String.valueOf(i),"abc"+i);
String jsonstr = JSON.toJSONString(userContent);
System.out.println("发送消息:"+jsonstr);
Message message = new Message("user-topic", "user-tag", jsonstr.getBytes(RemotingHelper.DEFAULT_CHARSET));
SendResult result = producer.send(message);
System.err.println("发送响应:MsgId:" + result.getMsgId() + ",发送状态:" + result.getSendStatus());
}
} catch (Exception e) {
e.printStackTrace();
} finally {
producer.shutdown();
}
}
}
这里使用了两个新注解:
@PostConstruct:它的作用是相当于servlet的Init功能,在对象的构造器执行完了,就会立马调用该方法
@Value:注入classpath下面的配置文件中的值到变量中
定义消费者:
package com.test.mq.rocketmq.customer;
import org.apache.rocketmq.client.consumer.DefaultMQPushConsumer;
import org.apache.rocketmq.client.consumer.listener.ConsumeConcurrentlyStatus;
import org.apache.rocketmq.client.consumer.listener.MessageListenerConcurrently;
import org.apache.rocketmq.common.consumer.ConsumeFromWhere;
import org.apache.rocketmq.common.message.MessageExt;
import org.apache.rocketmq.remoting.common.RemotingHelper;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
/**
*
* @Function 模拟用户消息消费
*/
@Component
public class UserConsumer {
/**
* 消费者的组名
*/
@Value("${suning.rocketmq.conumerGroup}")
private String consumerGroup;
/**
* NameServer 地址
*/
@Value("${suning.rocketmq.namesrvaddr}")
private String namesrvAddr;
@PostConstruct
public void consumer() {
System.err.println("init defaultMQPushConsumer");
DefaultMQPushConsumer consumer = new DefaultMQPushConsumer(consumerGroup);
consumer.setNamesrvAddr(namesrvAddr);
try {
consumer.subscribe("user-topic", "user-tag");
consumer.registerMessageListener((MessageListenerConcurrently) (list, context) -> {
try {
for (MessageExt messageExt : list) {
System.err.println("消费消息: " + new String(messageExt.getBody()));//输出消息内容
}
} catch (Exception e) {
e.printStackTrace();
return ConsumeConcurrentlyStatus.RECONSUME_LATER; //稍后再试
}
return ConsumeConcurrentlyStatus.CONSUME_SUCCESS; //消费成功
});
consumer.start();
} catch (Exception e) {
e.printStackTrace();
}
}
}
最后再看一下配置文件:
server.port=8080
# 生产者的组名
suning.rocketmq.producerGroup=user-group
# 消费者的组名
suning.rocketmq.conumerGroup=user-group
# NameServer地址
suning.rocketmq.namesrvaddr=localhost:9876
写一个启动类
package com.test.mq.rocketmq;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class RocketmqApplication {
public static void main(String[] args) {
SpringApplication.run(RocketmqApplication.class, args);
}
}
这个时候直接启动该应用就能看到消息的发送和消费的情况:
其实这样就实现了和springboot的整合,其实也很简单,但是有的人会感觉代码有点啰嗦,而且没啥通用性,如果业务中有多个消息队列的应用,那么就会出现很多重复代码,针对这种情况,可以按照下面这种方式来进行简化,如果不想自己动手的话,可以去找开源的代码,看有没有人实现了类似功能,拿过来直接用,还有就是在熟悉原理之后,也可以自己进行二次封装.