一、背景
我正在建设一个租房平台,进行基于租房业务的架构实践。微服务选型使用的spring cloud体系,同时基于不同业务领域上下文进行工程划分,其中需要分析不同上下文之间如何通过事件串联,哪些场景需要通过mq进行解耦,哪些需要同步调用,因此在不同工程系统间的调用中,需要有mq的参与。另外一方面我之前没怎么接触过开源的mq系统,这里正好需要一款开源的mq中间件进行实战。经过慎重考虑我决定选用rocketmq作为租房业务平台的消息中间件做业务模拟实战。
二、rocketmq版本
rocketmq自阿里捐赠给apache基金会孵化完成之后,版本相对稳定,这里选取的是rocketmq-all-4.7.1版本,下载链接如下:http://rocketmq.apache.org/dowloading/releases/,当前最新版本为4.8.0。
三、windows 部署
3.1 下载
我们需要从官网上或者从github上找到对应版本的安装包,下载链接:https://archive.apache.org/dist/rocketmq/4.7.1/rocketmq-all-4.7.1-bin-release.zip
3.2 windows本地安装
下面进行安装,将下载的安装包解压到当前文件夹,如下图所示:
此时安装完毕~~~
3.3 windows部署
安装完成之后,需要对rocketmq进行部署,这里通过命令行就可以启动。首先进入到安装目录中的bin目录:
E:\programfiles\softtools\rocketmq\rocketmq47\bin
然后右键打开git bash窗口,执行如下面命令:
start mqnamesrv.cmd
start mqbroker.cmd -n 127.0.0.1:9876 autoCreateTopicEnable=true
效果如下图所示:
四、插件下载&部署
部署完成之后,我们可以通过管理控制台进行消息主题等元数据的管理,追踪消息的消费情况,rocketmq的管理控制台不在rocketmq的安装包里,因此可以从下面的链接进行下载:
https://github.com/apache/rocketmq-externals.git
这里是个git项目,因此我们可以自己编译下,并打成jar包,在打包之前调整下配置文件:
,启动也非常简单,在存放目录右侧打开git bash命令行窗口,输入 java -jar rocketmq-console-ng-1.0.1.jar即可看到启动完成的日志。同时浏览器打开访问http://127.0.0.1:8099/#/则可看到控制台信息。
五、springboot整合
5.1 pom.xml配置
上述操作完成之后我们看一下如何基于springboot进行整合,首先看一下pom.xml依赖:
<!--默认springboot版本 -->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.8.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<!--默认springcloud版本 -->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Greenwich.SR3</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.1.8.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>2.1.0.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<!--rocketmq start-->
<dependency>
<groupId>org.apache.rocketmq</groupId>
<artifactId>rocketmq-spring-boot-starter</artifactId>
<version>2.1.0</version>
</dependency>
<!--rocketmq end-->
5.2 消息生产者服务配置连接rocketmq
首先看application.yml中的配置:
server:
port: 8052
spring:
profiles:
active: mqproducer
然后新增一个配置文件,名为application-elasticsearch.yml,配置内容是:
rocketmq:
name-server: x.x.x.x:9876 # 自己的RocketMQ服务地址
producer:
send-message-timeout: 3000
group: ${spring.application.name}
#这里定义总的房源域的topic
room_topic: snailapp_room_topic
#这里通过不同的tag区分不同业务场景,点赞,收藏,关注业务
roomlike_tag: ${rocketmq.producer.room_topic}:roomlike
#这里通过不同的tag区分不同业务场景,预约看房业务
roommeeting_tag: ${rocketmq.producer.room_topic}:roommeeting
#这里通过不同的tag区分不同业务场景,中介,代理人发布房源,消息
roomadd_tag: ${rocketmq.producer.room_topic}:roomadd
extNameServer: 127.0.0.1:9876
定义ExtRocketMQTemplate类:
//作为springbean
@ExtRocketMQTemplateConfiguration(nameServer = "${rocketmq.extNameServer}")
public class ExtRocketMQTemplate extends RocketMQTemplate {
}
package com.lightsnail.app.room.manager.controller;
import com.alibaba.fastjson.JSON;
import com.coderman.utils.response.ResultDataDto;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.lightsnail.app.room.model.vo.RoomSourceVO;
import org.apache.rocketmq.client.producer.SendCallback;
import org.apache.rocketmq.spring.core.RocketMQTemplate;
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.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import javax.annotation.Resource;
import javax.websocket.SendResult;
import java.util.HashMap;
import java.util.Map;
/**
* description: SnailRoomController <br>
* date: 2020/7/15 23:43 <br>
* author: coderman <br>
* version: 1.0 <br>
*
*/
@RestController
public class SnailRoomController {
@Autowired
private RestTemplate restTemplate;
@Resource(name = "extRocketMQTemplate")
private RocketMQTemplate extRocketMQTemplate;
/**
* 房源新增的mq tag
*/
@Value("${rocketmq.producer.roomadd_tag}")
private String roomAddTag;
private Logger logger = LoggerFactory.getLogger(SnailRoomController.class);
/**
* 推送房源到数据库和es
* 这里持久化到数据库
* @param roomSourceVO
*/
@PostMapping(value = "/snail/pushroom")
public ResultDataDto<Long> pushRoomSource(@RequestBody RoomSourceVO roomSourceVO){
logger.info("logger = "+JSON.toJSONString(roomSourceVO));
ResultDataDto<Long> resultDataDto = restTemplate.postForObject("http://lightsnail-room-core/roomSource/add" ,roomSourceVO, ResultDataDto.class);
logger.info("resultDataDto = "+JSON.toJSONString(resultDataDto));
if(resultDataDto.isSuccess()){
//todo 发送mq消息
/**
* 这里设置发送异步消息
*/
Map<String,String> roomMap = new HashMap<>();
roomMap.put("sourceId",resultDataDto.getData().toString());
roomMap.put("cityId",roomSourceVO.getCityId().toString());
logger.info("roomAddMsgBody = {}",JSON.toJSONString(roomMap));
extRocketMQTemplate.asyncSend(roomAddTag, JSON.toJSONString(roomMap),new SendCallback() {
@Override
public void onSuccess(org.apache.rocketmq.client.producer.SendResult sendResult) {
logger.info("sendResult = {}",JSON.toJSONString(sendResult));
}
@Override
public void onException(Throwable e) {
//异常回调
logger.error("error call back ",e);
}
});
}
return resultDataDto;
}
}
5.3 消息消费者服务配置连接rocketmq
首先看application.yml中的配置:
server:
port: 8078
spring:
profiles:
active: rocketmqconsumer
然后新增一个配置文件,名为application-elasticsearch.yml,配置内容是:
rocketmq:
name-server: 172.17.54.209:9876 # 自己的RocketMQ服务地址
consumer:
send-message-timeout: 3000
group: ${spring.application.name}
#这里定义总的房源域的topic
room_topic: snailapp_room_topic
#这里通过不同的tag区分不同业务场景
#这里通过不同的tag区分不同业务场景,中介,代理人发布房源,消息
roomadd_tag: roomadd
extNameServer: 127.0.0.1:9876
消费端代码:
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.lightsnail.app.room.es.mq;
import com.alibaba.fastjson.JSON;
import com.coderman.utils.response.ResultDataDto;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.lightsnail.app.room.es.bean.RoomSourceTemplate;
import com.lightsnail.app.room.es.repository.RoomSourceRepository;
import org.apache.rocketmq.spring.annotation.RocketMQMessageListener;
import org.apache.rocketmq.spring.core.RocketMQListener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import java.util.Map;
/**
* StringConsumer
* 这里统一使用json协议进行消息体的编码和解码
* 点赞,收藏业务的消息消费处理
*/
@Service
@RocketMQMessageListener(nameServer = "${rocketmq.extNameServer}",
topic = "${rocketmq.consumer.room_topic}",
consumerGroup = "${rocketmq.consumer.group}",
selectorExpression = "${rocketmq.consumer.roomadd_tag}"
)
public class JSONStringConsumer implements RocketMQListener<String> {
protected Logger logger = LoggerFactory.getLogger(JSONStringConsumer.class);
@Autowired
private RestTemplate restTemplate;
@Autowired
private RoomSourceRepository roomSourceRepository;
@Override
public void onMessage(String message) {
logger.info("message = "+message);
Map<String,String> mqMap = (Map<String,String>)JSON.parse(message);
logger.info("roomMq = "+JSON.toJSONString(mqMap));
String url = "http://lightsnail-room-core/roomSource/get?id="+mqMap.get("sourceId")+"&cityId="+mqMap.get("cityId");
ResultDataDto resultDataDto = restTemplate.getForObject(url , ResultDataDto.class);
logger.info("resultDataDto = {}",JSON.toJSONString(resultDataDto));
if(resultDataDto.isSuccess()){
ObjectMapper mapper = new ObjectMapper();
//这里拿到房源id发送mq,由lightsnail-user-crm进行消费,存储房源id-发布人id关联关系,避免通过发布人查询
//另外模拟通过发送mq,由lightsnail-room-es模块消费,调用lightsnail-room-core同步数据
RoomSourceTemplate roomSourceTemplate = mapper.convertValue(resultDataDto.getData(), RoomSourceTemplate.class);
if(roomSourceTemplate != null){
RoomSourceTemplate roomSourceTemplate1 = roomSourceRepository.save(roomSourceTemplate);
logger.info("roomSourceTemplate1 = {}",JSON.toJSONString(roomSourceTemplate1));
}
}
}
}
分别启动服务端和消费端即可模拟消息发送和消费。