Spring Cloud Alibaba-(1)搭建项目环境
Spring Cloud Alibaba-(2)Nacos【服务注册与发现、配置管理】
Spring Cloud Alibaba-(3)OpenFeign【服务调用】
Spring Cloud Alibaba-(4)Sentinel【流控和降级】
Spring Cloud Alibaba-(5)Seata【分布式事务】
1.RocketMQ 官网-https://rocketmq.apache.org/zh/
RocketMQ 是一款开源的分布式消息系统,基于高可用分布式集群技术,提供低延时的、高可靠的消息发布与订阅服务。同时,广泛应用于多个领域,包括异步通信解耦、企业解决方案、金融支付、电信、电子商务、快递物流、广告营销、社交、即时通信、移动应用、手游、视频、物联网、车联网等。具有以下特点:
(1)能够保证严格的消息顺序;
(2)提供丰富的消息拉取模式;
(3)高效的订阅者水平扩展能力;
(4)实时的消息订阅机制;
(5)亿级消息堆积能力。
2.RocketMQ 原理
3.RocketMQ 服务端
3.1 下载 (如果5.1.4版本报错,请下载最新版本)
3.2 修改 runbroker.cmd 和 runserver.cmd
3.3 创建ROCKETMQ_HOME环境变量。然后再bin目录新建 startup.bat 文件运行 mqnamesrv.cmd 和 mqbroker.cmd,从而启动RocketMQ服务
@echo off
setlocal
# 获取当前脚本的目录
set "scriptDir=%~dp0"
# 启动 NameServer
echo Starting NameServer...
start "NameServer" "%scriptDir%mqnamesrv.cmd" start
echo NameServer has been started.
# 启动 Broker
# -n 表示 NameServer 地址
# autoCreateTopicEnable=true 表示 Broker开启自动创建Topic,否则报错 No route info of this topic
echo Starting Broker...
start "Broker" "%scriptDir%mqbroker.cmd" start -n localhost:9876 autoCreateTopicEnable=true
echo Broker has been started.
endlocal
4.RocketMQ 控制台
4.1 下载(apache/rocketmq-dashboard)
4.2 IDEA 打开源码,跳过测试,然后打成 jar 包(源码使用jdk8)
4.3 运行RocketMQ DashBoard(cmd 运行 java -Dserver.port=8899 -jar rocketmq-dashboard-1.0.1-SNAPSHOT.jar)
4.4 访问RocketMQ控制台-http://localhost:8899/
5.项目中使用 RocketMQ
5.1 订单微服务生产并发送消息(生产者)
5.1.1 引入 Maven 依赖
<!-- rocketmq -->
<dependency>
<groupId>org.apache.rocketmq</groupId>
<artifactId>rocketmq-spring-boot-starter</artifactId>
<version>2.3.1</version>
</dependency>
<!-- rocketmq-client -->
<dependency>
<groupId>org.apache.rocketmq</groupId>
<artifactId>rocketmq-client</artifactId>
<version>5.1.4</version>
</dependency>
5.1.2 bootstrap.yml 配置 rocketmq
server:
port: 8000
spring:
profiles:
active: public
application:
name: order-service
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/springcloud2024
username: root
password: 123456
cloud:
nacos:
discovery:
server-addr: http://localhost:8848
config:
server-addr: http://localhost:8848
file-extension: yaml
namespace: public
shared-configs:
- data-id: order-service-default.yaml
refresh: true
- data-id: order-service-default-test.yaml
refresh: true
openfeign:
httpclient:
# 连接超时
connection-timeout: 5000
sentinel:
transport:
dashboard: http://localhost:8858
datasource:
flow-rule:
nacos:
server-addr: http://localhost:8848
data-id: order-service-flow-rule
rule-type: flow
# Openfeign 整合 sentinel
feign:
sentinel:
enabled: true
#
#logging:
# level:
# com.dragon.openfeign: debug
seata:
registry:
type: nacos
nacos:
server-addr: http://localhost:8848
application: seata-server
group: SEATA_GROUP
config:
type: nacos
nacos:
server-addr: http://localhost:8848
group: SEATA_GROUP
tx-service-group: default_tx_group
rocketmq:
name-server: http://localhost:9876 # RocketMQ 服务地址
producer:
group: order # 生产者组
5.1.3 订单微服务下单操作使用 RocketMQTemplate 发送消息
package com.dragon.service.impl;
import cn.dev33.satoken.util.SaResult;
import com.alibaba.fastjson.JSONObject;
import com.dragon.entity.Orders;
import com.dragon.entity.Product;
import com.dragon.mapper.OrdersMapper;
import com.dragon.openfeign.ProductServiceFeign;
import com.dragon.service.IOrdersService;
import com.github.yulichang.base.MPJBaseServiceImpl;
import io.seata.spring.annotation.GlobalTransactional;
import org.apache.rocketmq.spring.core.RocketMQTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
import java.time.LocalDateTime;
@Service
public class OrdersServiceImpl extends MPJBaseServiceImpl<OrdersMapper, Orders> implements IOrdersService {
@Qualifier("com.dragon.openfeign.ProductServiceFeign")
@Autowired
private ProductServiceFeign productServiceFeign;
@Autowired
private RocketMQTemplate rocketMQTemplate;
@GlobalTransactional
@Override
public synchronized SaResult add(String productId){
Object obj = productServiceFeign.getById(productId).getData();
// 将 Object 转为 JSONString
String str = JSONObject.toJSONString(obj);
// 将 JSONString 转为 .class
Product product = JSONObject.parseObject(str, Product.class);
product.setStock(product.getStock()-1);
productServiceFeign.saveOrUpdate(product);
Orders order=new Orders();
order.setUserId("1");
order.setProductId(productId);
order.setAmount(1);
order.setPrice(product.getSellPrice());
order.setCreateTime(LocalDateTime.now());
order.setTs(LocalDateTime.now());
this.saveOrUpdate(order);
// // 模拟出现异常
// int i=1/0;
// 向 RocketMQ 的 order-topic 主题发送消息
rocketMQTemplate.convertAndSend("order-topic",order);
return SaResult.ok();
}
}
5.1.4 RocketMQ 控制台查看消息
5.2 用户微服务订阅并消费消息(消费者)
5.2.1 引入 Maven 依赖
<!-- nacos-discovery 服务注册与发现 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!-- rocketmq -->
<dependency>
<groupId>org.apache.rocketmq</groupId>
<artifactId>rocketmq-spring-boot-starter</artifactId>
<version>2.3.1</version>
</dependency>
<!-- rocketmq-client -->
<dependency>
<groupId>org.apache.rocketmq</groupId>
<artifactId>rocketmq-client</artifactId>
<version>5.3.0</version>
</dependency>
5.2.2 bootstrap.yml 配置
server:
port: 8300
spring:
application:
name: user-service
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/springcloud2024
username: root
password: 123456
cloud:
nacos:
discovery:
server-addr: http://localhost:8848
rocketmq:
name-server: http://localhost:9876
5.2.3 用户微服务接收RocketMQ消息,并模拟发送一条短信
5.2.3.1 定义接口,接收消息
package com.dragon.service;
import com.dragon.entity.Orders;
import org.apache.rocketmq.spring.core.RocketMQListener;
// String 是 RocketMQ 消息的类型
public interface SmsService extends RocketMQListener<String> {
}
5.2.3.2 实现接口,消费消息
package com.dragon.service.impl;
import com.alibaba.fastjson2.JSON;
import com.dragon.entity.Orders;
import com.dragon.service.SmsService;
import org.apache.rocketmq.spring.annotation.ConsumeMode;
import org.apache.rocketmq.spring.annotation.MessageModel;
import org.apache.rocketmq.spring.annotation.RocketMQMessageListener;
import org.springframework.stereotype.Service;
@Service
@RocketMQMessageListener(
consumerGroup = "user-group", // 消费者组
topic = "order-topic", // 主题
consumeMode = ConsumeMode.CONCURRENTLY, // 消费模式,CONCURRENTLY(同步消费,默认),ORDERLY(顺序消费)
messageModel = MessageModel.CLUSTERING // 消息模式,CLUSTERING(集群消息,默认),BROADCASTING(广播消息)
)
public class SmsServiceImpl implements SmsService {
@Override
public void onMessage(String msg) {
// 将字符串转为对象类型
Orders orders= JSON.parseObject(msg,Orders.class);
System.out.println("订单消息,"+orders);
System.out.println("接收到了一个订单信息,可以编写消费逻辑,发送一条短信");
}
}
6.RocketMQ 消息类型
6.1 普通消息(Normal Message)
(1)描述:普通消息是最基本的消息类型,没有特殊的语义约束,消息之间也没有任何关联。
(2)特点:
- 生产者将消息发送到 Broker,消费者从 Broker 订阅并消费这些消息。
- 没有顺序约束,生产和消费都是并行进行的。
- 单机性能可以达到十万级别的 TPS(每秒事务处理量)。
(3)发送方式:
1. 单向发送:生产者发送消息后不关心 Broker 是否接收,这种方式最快但可靠性最低。
2. 异步发送:生产者发送消息后立即返回,不等待 Broker 确认,这种方式速度快但可靠性较差。
3. 同步发送:生产者发送消息后等待 Broker 返回确认,这种方式最可靠但也最慢。
@Autowired
private RocketMQTemplate rocketMQTemplate;
// 普通消息
public void test() throws InterruptedException {
// 普通消息:单向消息
// 参数:主题,消息体
rocketMQTemplate.sendOneWay("order-topic","单向消息");
// 普通消息:同步消息
// 参数:主题,消息体,超时时间
rocketMQTemplate.syncSend("order-topic:tag","同步消息",5000);
// 普通消息:异步消息
// 参数:主题,消息体,回调
rocketMQTemplate.asyncSend("order-topic:tag","异步消息",new SendCallback() {
// 成功回调
@Override
public void onSuccess(SendResult sendResult) {
System.out.println(sendResult);
}
// 失败回调
@Override
public void onException(Throwable throwable) {
System.out.println(throwable);
}
});
// 休眠,保证异步消息可以回调
Thread.sleep(900000000);
}
6.2 顺序消息(Orderly Message)
(1)描述:顺序消息保证消息按照发送的顺序进行消费。
(2)类型:
1. 分区有序消息(Partition Orderly Message):消息在同一个队列(Queue)内按照顺序消费。
2. 全局有序消息(Global Orderly Message):整个 Topic 内的消息按照顺序消费。
(3)实现机制:
1. 分区有序消息通过消息 Key 来实现,同一 Key 的消息会被分配到同一个队列中。
2. 全局有序消息需要将消息发送到一个队列中,然后由消费者按照顺序消费。
@Autowired
private RocketMQTemplate rocketMQTemplate;
// 顺序消息
public void test1() throws InterruptedException {
// 顺序消息:单向消息
// 参数:主题,消息体,hashKey(同一个hashKey的消息会分配到同一个队列)
rocketMQTemplate.sendOneWayOrderly("order-topic","单向消息","11");
// 顺序消息:同步消息
// 参数:主题,消息体,hashKey(同一个hashKey的消息会分配到同一个队列)
rocketMQTemplate.syncSendOrderly("order-topic:tag","同步消息","22");
// 顺序消息:异步消息
// 参数:主题,消息体,hashKey(同一个hashKey的消息会分配到同一个队列),回调
rocketMQTemplate.asyncSendOrderly("order-topic:tag","异步消息","33",new SendCallback() {
// 成功回调
@Override
public void onSuccess(SendResult sendResult) {
System.out.println(sendResult);
}
// 失败回调
@Override
public void onException(Throwable throwable) {
System.out.println(throwable);
}
});
// 休眠,保证异步消息可以回调
Thread.sleep(900000000);
}
6.3 延时消息(Delay Message)
(1)描述:延时消息是指消息发送到 Broker 后,在指定的延迟时间之后才会被消费者消费。
(2)应用场景:常用于实现订单超时取消、定时任务等场景。
(3)实现机制:生产者发送消息时可以指定消息的延迟级别,Broker 根据级别计算延迟时间,并在延迟时间过后将消息发布给消费者。
@Autowired
private RocketMQTemplate rocketMQTemplate;
// 延时消息
public void test2(){
// 参数:主题,消息体,延时时间
rocketMQTemplate.syncSendDelayTimeSeconds("order-topic","单向消息",1);
}
6.4 事务消息(Transaction Message)
(1)描述:事务消息主要用于解决分布式事务问题,保证消息的发送与本地事务的一致性。
(2)应用场景:当需要保证消息的发送与本地事务(如数据库操作)的一致性时使用。
(3)实现机制:
1. 生产者发送半消息(Half Message)到 Broker,不立即确认发送。
2. 生产者根据本地事务的结果提交(Commit)或回滚(Rollback)事务消息。
3. Broker 根据事务状态决定是否将消息发送给消费者。