SpringCloud+SpringBoot 项目搭建 (四) RocketMq
本篇文章接上一篇:SpringCloud+SpringBoot 项目搭建 (三) MyBatis-Push
SpringCloud+SpringBoot 项目搭建 (四) RocketMq
1. 安装RocketMq环境
下载RocketMq
http://rocketmq.apache.org/release_notes/release-notes-4.3.0/
选择
解压后配置环境变量(根据个人路径自行配置)
cmd进去文件路径bin文件夹中开启服务:
启动NAMESERVER
start mqnamesrv.cmd
启动BROKER
start mqbroker.cmd -n 127.0.0.1:9876 autoCreateTopicEnable=true
出现两个界面
代表启动成功
注意:两个服务命令框不能关闭
2. 安装RocketMQ插件
git下载
https://github.com/apache/rocketmq-externals.git
或
git://github.com/apache/rocketmq-externals.git
或
https://gitee.com/mirrors/RocketMQ-Externals.git
哪个有速度下载哪个
修改插件项目配置
修改rocketmq-console模块下的application.properties
#启动项目的插件项目的端口
server.port=8520
#BROKER配置的端口
rocketmq.config.namesrvAddr=http://127.0.0.1:9876
回到rocketmq-console路径执行maven打包命令
mvn clean package -Dmaven.test.skip=true
mvn clean package -Dmaven.test.skip=true
进入target执行打包好的jar(注意看下打包的版本号)
java -jar rocketmq-console-ng-2.0.0.jar
浏览器打开配置的启动插件端口测试
至此RocketMQ的环境与插件配置完成
3.项目集成RocketMq
老规矩引入pom,注意版本号要和刚才上面配置的服务版本号一致
<rocketmq.version>4.3.0</rocketmq.version>
<dependency>
<groupId>org.apache.rocketmq</groupId>
<artifactId>rocketmq-client</artifactId>
<version>${rocketmq.version}</version>
</dependency>
创建商品和订单表用来测试
CREATE TABLE `product` (
`id` varchar(32) NOT NULL COMMENT '商品id',
`name` varchar(255) DEFAULT NULL COMMENT '商品名称',
`inventory` int(11) DEFAULT NULL COMMENT '库存',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `order` (
`id` varchar(255) NOT NULL COMMENT '订单id',
`product_id` varchar(255) DEFAULT NULL COMMENT '商品id',
`user` bigint(20) DEFAULT NULL COMMENT '用户id',
`number` int(11) DEFAULT NULL COMMENT '数量',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
INSERT INTO product (id,`name`,inventory) values (1,'测试商品1',100)
INSERT INTO product (id,`name`,inventory) values (2,'测试商品2',100)
INSERT INTO product (id,`name`,inventory) values (3,'测试商品3',100)
创建代码生成器生成model,mapper,service
创建一个生成订单的service和controller
@RestController
@RequestMapping("/order")
public class OrderController {
@Autowired
OrderService orderService;
/**
* 测试下单
*/
@GetMapping("/create")
public Object createOrderDoc( String productId, Integer number, Long userId) {
Order order = orderService.create(productId,number,userId);
return order;
}
}
@Log4j2
@Service
public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements OrderService {
@Resource
OrderMapper orderMapper;
@Resource
private DefaultMQProducer defaultMQProducer;
@Override
public Order create(String productId,Integer number, Long userId) {
//创建订单
Order order = new Order();
order.setId((new Random().nextInt(10000))+"");
order.setNumber(number);
order.setUser(userId);
order.setProductId(productId);
int insert = orderMapper.insert(order);
//模拟发送短信通过MQ消费者来发送
Map<String,Object> map = new HashMap<>();
map.put("orderId",order.getId());
map.put("message","创建订单");
Message message = new Message(GlobalConstant.TOPIC, "Tag1", "CreateOrder",JSONObject.toJSONString(map).getBytes());
// 这里用到了这个mq的异步处理,类似ajax,可以得到发送到mq的情况,并做相应的处理
//不过要注意的是这个是异步的
try {
defaultMQProducer.send(message, new SendCallback() {
@Override
public void onSuccess(SendResult sendResult) {
log.info("传输成功");
log.info(JSONObject.toJSON(sendResult));
}
@Override
public void onException(Throwable e) {
log.error("传输失败", e);
}
});
} catch (MQClientException e) {
e.printStackTrace();
} catch (RemotingException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
return insert>0?order:null;
}
}
配置生产者config
spring.application.name=springcloud
rocketmq.producer.groupName=${spring.application.name}
rocketmq.producer.namesrvAddr=127.0.0.1:9876
rocketmq.producer.default=false
注意:上面的namesrvAddr地址需要和上面启动服务的地址一致
@Component
@Getter
@Setter
@ConfigurationProperties(prefix = "rocketmq.producer")
@Configuration
@ToString
public class ProducerConfig {
private String namesrvAddr;
private String groupName;
}
@Slf4j
@Configuration
@EnableConfigurationProperties(ProducerConfig.class)
public class MqConfiguration {
@Autowired
ProducerConfig producerConfig;
@Bean
public DefaultMQProducer defaultMQProducer() throws MQClientException {
DefaultMQProducer defaultMQProducer = new DefaultMQProducer(producerConfig.getGroupName());
defaultMQProducer.setNamesrvAddr(producerConfig.getNamesrvAddr());
defaultMQProducer.setVipChannelEnabled(false);
defaultMQProducer.setRetryTimesWhenSendAsyncFailed(10);
defaultMQProducer.start();
log.info("rocketmq producer server开启成功---------------------------------.");
return defaultMQProducer;
}
}
配置消费者
@Slf4j
@Component
public class MessageMQ {
/**
* 消费者实体对象
*/
private DefaultMQPushConsumer consumer;
@Autowired
MessageService messageService;
/**
* 通过构造函数 实例化对象
*/
public MessageMQ() throws MQClientException {
consumer = new DefaultMQPushConsumer(GlobalConstant.groupName);
consumer.setNamesrvAddr(GlobalConstant.namesrvAddr);
//消费模式:一个新的订阅组第一次启动从队列的最后位置开始消费 后续再启动接着上次消费的进度开始消费
consumer.setConsumeFromWhere(ConsumeFromWhere.CONSUME_FROM_LAST_OFFSET);
//订阅主题和 标签(*代表所有标签)下信息
consumer.subscribe(GlobalConstant.TOPIC, "*");
//注册消费的监听 并在此监听中消费信息,并返回消费的状态信息
consumer.registerMessageListener((MessageListenerConcurrently) (msgs, context) -> {
// msgs中只收集同一个topic,同一个tag,并且key相同的message会把不同的消息分别放置到不同的队列中
try {
for (Message msg : msgs) {
//消费者获取消息 这里只输出 不做后面逻辑处理
String body = new String(msg.getBody(), "utf-8");
log.info("Consumer-获取消息-主题topic为={}, 消费消息为={}", msg.getTopic(), body);
JSONObject object = JSONObject.parseObject(body);
if("CreateOrder".equals(msg.getKeys())){
messageService.send(object.getString("orderId"));
}
}
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
return ConsumeConcurrentlyStatus.RECONSUME_LATER;
}
return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
});
consumer.start();
System.out.println("消费者 启动成功=======");
}
}
编写测试发送消息方法
public interface MessageService {
boolean send(String orderId);
}
@Log4j2
@Service
public class MessageServiceImpl implements MessageService {
@Override
public boolean send(String orderId) {
log.info("发送短信-------"+orderId+"------------");
return true;
}
}
访问controller验证
插件控制器查看
至此RocketMQ就简单的集成到系统中
4.填坑
1.TOPIC一般是服务器设置好 而不能在代码里去新建topic( 如果没有创建好,生产者往该主题发送消息 会报找不到topic错误)
所以需要去控制器创建
2.namesrvAddr必须和启动BROKER 127.0.0.1:9876中配置一样,注意仔细
3.创建环境变量中 路径中请勿有中文和空格!路径中请勿有中文和空格!路径中请勿有中文和空格!重要的事情说三遍 包括Program Files中的空格