RocketMQ:是一款分布式、队列模型的开源消息中间件
CMD命令框执行进入文件bin目录,然后执行‘start mqnamesrv.cmd’,启动NAMESERVER。成功后会弹出如下的提示框,此框勿关闭。
CMD命令框进入至文件bin目录下,然后执行‘start mqbroker.cmd -n 127.0.0.1:9876 autoCreateTopicEnable=true’,启动BROKER。成功后会弹出提示框,此框勿关闭。
注意: 假如弹出提示框提示错误: 找不到或无法加载主类 xxxxxx。打开runbroker.cmd,然后将‘%CLASSPATH%’加上英文双引号。
下载完成之后,进入‘rocketmq-externals\rocketmq-console\src\main\resources’文件夹,打开‘application.properties’修改配置。
进入‘\rocketmq-externals\rocketmq-console’文件夹,执行‘mvn clean package -Dmaven.test.skip=true’,进行编译。编译成功之后,Cmd进入‘target’文件夹,执行‘java -jar rocketmq-console-ng-1.0.0.jar’,启动‘rocketmq-console-ng-1.0.0.jar’。
启动完毕后,在页面上输入地址:127.0.0.1:17890 登录查看
注意: 这里指定了使用2.0.3
版本,为了适配RocketMQ4.5.1
<dependency>
<groupId>org.apache.rocketmqgroupId>
<artifactId>rocketmq-spring-boot-starterartifactId>
<version>2.0.3version>
dependency>
注意: group 必须指定,否者启动项目会报 无法初始化RocketMQ
的错误
rocketmq:
name-server: 127.0.0.1:9876
producer:
#必须指定group
group: test-group
@RestController
@RequestMapping("/admin/shares")
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class ShareAdminController {
private final ShareService shareService;
@PutMapping("/audit/{id}")
public Share auditById(@PathVariable Integer id,@RequestBody ShareAuditDTO shareAuditDTO){
//TODO 认证授
return this.shareService.auditById(id,shareAuditDTO);
}
}
@Transactional(rollbackFor = Exception.class)
public Share auditById(Integer id, ShareAuditDTO auditDTO) {
//1.查询share是否存在,如果不存在或者当前的audit_status != NOT_YET,那么抛异常
Share share = this.shareMapper.selectByPrimaryKey(id);
if(share == null){
throw new IllegalArgumentException("参数非法!该分享不存在!");
}
if(!Objects.equals("NOT_YET", share.getAuditStatus())){
throw new IllegalArgumentException("该分享不是待审核状态!");
}
//2.审核资源,将状态设为PSAA或REJECT
share.setAuditStatus(auditDTO.getAuditStatusEnum().toString());
this.shareMapper.updateByPrimaryKey(share);
//3.如果PASS给用户添加积分,发生消息给rocketmq,让用户中心去消费
UserAddBonusMsgDTO userAddBonusMsgDTO = new UserAddBonusMsgDTO();
userAddBonusMsgDTO.setUserId(share.getUserId());
userAddBonusMsgDTO.setBonus(50);
rocketMQTemplate.convertAndSend("add-bonus", userAddBonusMsgDTO);
return share;
}
AddBonusListener
实现RocketMQListener
,并在onMessage()
方法里编写业务@Service
@RocketMQMessageListener(consumerGroup = "consume-group", topic = "add-bonus")
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class AddBonusListener implements RocketMQListener<UserAddBonusMsgDTO> {
private final UserMapper userMapper;
private final BonusEventLogMapper bonusEventLogMapper;
@Override
public void onMessage(UserAddBonusMsgDTO message) {
//编写业务逻辑
//1.为用户添加积分
Integer userId = message.getUserId();
User user = userMapper.selectByPrimaryKey(userId);
user.setBonus(user.getBonus() + message.getBonus());
this.userMapper.updateByPrimaryKeySelective(user);
//2.记录日志到bonus_event_log表中
BonusEventLog bonusEventLog = new BonusEventLog();
bonusEventLog.setUserId(userId);
bonusEventLog.setValue(user.getBonus());
bonusEventLog.setDescription("投稿通过添加用户积分");
bonusEventLog.setEvent("CONTRIBUTE");
bonusEventLog.setCreateTime(new Date());
this.bonusEventLogMapper.insert(bonusEventLog);
}
}
重构ShareService
里的auditById
方法
public Share auditById(Integer id, ShareAuditDTO auditDTO) {
//1.查询share是否存在,如果不存在或者当前的audit_status != NOT_YET,那么抛异常
Share share = this.shareMapper.selectByPrimaryKey(id);
if(share == null){
throw new IllegalArgumentException("参数非法!该分享不存在!");
}
if(!Objects.equals("NOT_YET", share.getAuditStatus())){
throw new IllegalArgumentException("该分享不是待审核状态!");
}
//3.如果PASS给用户添加积分,发生消息给rocketmq,让用户中心去消费
if(AuditStatusEnum.PASS.equals(auditDTO.getAuditStatusEnum())){
UserAddBonusMsgDTO userAddBonusMsgDTO = new UserAddBonusMsgDTO();
userAddBonusMsgDTO.setUserId(share.getUserId());
userAddBonusMsgDTO.setBonus(50);
this.rocketMQTemplate.sendMessageInTransaction(
"tx-add-bonus-group",
"add-bonus",
MessageBuilder.withPayload(userAddBonusMsgDTO)
.setHeader(RocketMQHeaders.TRANSACTION_ID, UUID.randomUUID().toString())
.setHeader("share_id",id)
.build(),
auditDTO
);
}else{
this.auditByIdInDB(id,auditDTO);
}
return share;
}
ShareService
里添加auditByIdInDB
和auditByIdInWithRocketMqLog
两个方法
@Transactional(rollbackFor = Exception.class)
public void auditByIdInDB(Integer id,ShareAuditDTO auditDTO){
Share share = new Share();
share.setId(id);
share.setAuditStatus(auditDTO.getAuditStatusEnum().toString());
share.setReason(auditDTO.getReason());
this.shareMapper.updateByPrimaryKeySelective(share);
}
@Transactional(rollbackFor = Exception.class)
public void auditByIdInWithRocketMqLog(Integer id,ShareAuditDTO auditDTO,String transactionId){
this.auditByIdInDB(id,auditDTO);
RocketmqTransactionLog rocketmqTransactionLog = new RocketmqTransactionLog();
rocketmqTransactionLog.setTransactionId(transactionId);
rocketmqTransactionLog.setLog("审核分享");
this.rocketmqTransactionLogMapper.insert(rocketmqTransactionLog);
}
添加AddBonusTransactionListener
方法并实现RocketMQLocalTransactionListener
@RocketMQTransactionListener(txProducerGroup = "tx-add-bonus-group")
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class AddBonusTransactionListener implements RocketMQLocalTransactionListener {
private final ShareService shareService;
private final RocketmqTransactionLogMapper rocketmqTransactionLogMapper;
@Override
public RocketMQLocalTransactionState executeLocalTransaction(Message message, Object obj) {
MessageHeaders headers = message.getHeaders();
String transactionId = (String) headers.get(RocketMQHeaders.TRANSACTION_ID);
Integer shareId = Integer.valueOf((String) headers.get("share_id"));
try {
this.shareService.auditByIdInWithRocketMqLog(shareId, (ShareAuditDTO) obj, transactionId);
return RocketMQLocalTransactionState.COMMIT;
}catch (Exception e){
return RocketMQLocalTransactionState.ROLLBACK;
}
}
@Override
public RocketMQLocalTransactionState checkLocalTransaction(Message message) {
MessageHeaders headers = message.getHeaders();
String transactionId = (String) headers.get(RocketMQHeaders.TRANSACTION_ID);
RocketmqTransactionLog rocketmqTransactionLog = new RocketmqTransactionLog();
rocketmqTransactionLog.setTransactionId(transactionId);
RocketmqTransactionLog rocketmqTransactionLog1 = this.rocketmqTransactionLogMapper.selectOne(rocketmqTransactionLog);
if(rocketmqTransactionLog1 != null){
return RocketMQLocalTransactionState.COMMIT;
}
return RocketMQLocalTransactionState.ROLLBACK;
}
}
Spring Cloud Stream是一个框架,用于构建与共享消息传递系统连接的高度可扩展的事件驱动型微服务。
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-stream-rocketmqartifactId>
dependency>
@EnableBinding(Source.class)
spring:
cloud:
stream:
rocketmq:
binder:
name-server: 127.0.0.1:9876
bindings:
output:
# 用来指定topic
destination: add-bonus
@Autowired
private Source source;
@GetMapping("/test-stream")
public String testStream(){
this.source.output()
.send(
MessageBuilder.withPayload("消息体").build()
);
return "success";
}
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-stream-rocketmqartifactId>
dependency>
@EnableBinding(Sink.class)
spring:
cloud:
stream:
rocketmq:
binder:
name-server: 127.0.0.1:9876
bindings:
input:
# 用来指定topic
destination: add-bonus
# 这里的group必须配置
group: binder-group
@Service
@Slf4j
public class TestStreamConsumer {
@StreamListener(Sink.INPUT)
public void receive(String messageBody){
log.info("通过Stream收到了消息:messageBody = {}", messageBody);
}
}
MySource
public interface MySource {
String MY_OUTPUT = "my-output";
@Output(MY_OUTPUT)
MessageChannel output();
}
@EnableBinding(MySource.class)
spring:
cloud:
stream:
rocketmq:
binder:
name-server: 127.0.0.1:9876
bindings:
output:
# 用来指定topic
destination: add-bonus
my-output:
# 用来指定topic
destination: stream-my-topic
@Autowired
private MySource mySource;
@GetMapping("/test-stream-mysource")
public String testStreamMysource(){
this.mySource.output()
.send(
MessageBuilder.withPayload("mySource消息体").build()
);
return "success";
}
MySink
public interface MySink {
String MY_INPUT = "my-input";
@Input(MY_INPUT)
SubscribableChannel input();
}
@EnableBinding({
Sink.class, MySink.class})
spring:
cloud:
stream:
rocketmq:
binder:
name-server: 127.0.0.1:9876
bindings:
input:
# 用来指定topic
destination: add-bonus
# 这里的group必须配置
group: binder-group
my-input:
destination: stream-my-topic
group: my-group
@Service
@Slf4j
public class MyTestStreamConsumer {
@StreamListener(MySink.MY_INPUT)
public void receive(String messageBody){
log.info("MySink通过Stream收到了消息:messageBody = {}", messageBody);
}
}
未完待续!!!
上一篇: 练习Spring Cloud Alibaba —— 4.
下一篇: 练习Spring Cloud Alibaba —— 6.