Seata概述
什么是Seata
Seata 是一款开源的分布式事务解决方案,致力于在微服务架构下提供高性能和简单易用的分布式事务服务,能够保证一个业务中所有对数据库的操作要么都成功,要么都失败,来保证数据库的数据完整性
事务的4个特性:ACID特性
- 原子性
- 一致性
- 隔离性
- 永久性
Seata保证微服务远程调用业务的原子性
Seata的运行原理(AT模式)
Seata构成部分包含
- 事务协调器TC
- 事务管理器TM
- 资源管理器RM
Seata其它模式简介
除了AT模式之外还有TCC、SAGA 和 XA 事务模式
TCC模式
简单来说,TCC模式就是自己编写代码完成事务的提交和回滚,TCC模式要求我们在每个参与事务的业务中编写一组共3个方法
(prepare\commit\rollback)
prepare:准备 commit:提交 rollback:回滚
- prepare方法是无论事务成功与否都会运行的代码
- commit当整体事务运行成功时运行的方法
- rollback当整体事务运行失败是运行的方法
**优点**:虽然代码是自己写的,但是事务整体提交或回滚的机制仍然可用(仍然由TC来调度)
**缺点**:每个业务都要编写3个方法来对应,代码冗余,而且业务入侵量大
SAGA模式
SAGA模式的思想是对应每个业务逻辑层编写一个新的类,可以设置指定的业务逻辑层方法发生异常时,运行当新编写的类中的代码.
这样编写代码不影响已经编写好的业务逻辑代码,一般用于修改已经编写完成的老代码,缺点是每个事务分支都要编写一个类来回滚业务,会造成类的数量较多,开发量比较大
XA模式
支持XA协议的数据库分布式事务,使用比较少
Seata的使用
(1)生产者添加依赖
<dependency>
<groupId>io.seata</groupId>
<artifactId>seata-spring-boot-starter</artifactId>
</dependency>
<!-- Seata完成分布式事务需要的两个相关依赖(Seata需要下面两个依赖中的资源) -->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
</dependency>
(2)生产者配置yml文件
seata:
tx-service-group: csmall_group # 定义分组名称,一般是为了区分项目
service:
vgroup-mapping:
csmall_group: default # csmall_group组使用默认的Seata配置完成事务
grouplist:
default: localhost:8091 # 设置Seata所在的地址(默认端口号就是8091)
注意同一个事务必须在同一个tx-service-group中
(3)消费者添加依赖
<dependency>
<groupId>io.seata</groupId>
<artifactId>seata-spring-boot-starter</artifactId>
</dependency>
(4)消费者配置yml文件
seata:
tx-service-group: csmall_group # 定义分组名称,一般是为了区分项目
service:
vgroup-mapping:
csmall_group: default # csmall_group组使用默认的Seata配置完成事务
grouplist:
default: localhost:8091 # 设置Seata所在的地址(默认端口号就是8091)
(5)消费者要想启动Seata非常简单,只要在启动业务的业务逻辑方法上添加专用的@GlobalTransactiona
l注解即可
(6)依次启动seata软件=>生产者=>消费者
注意:在windows系统中运行seata可能出现不稳定的情况,重启seata即可解决
Sentinel
Sentinel下载地址:
https://github.com/alibaba/Sentinel/releases
什么是Sentinel(限流、降级)
Sentinel也是Spring Cloud Alibaba的组件
Sentinel英文翻译"哨兵\门卫"
随着微服务的流行,服务和服务之间的稳定性变得越来越重要。Sentinel 以流量为切入点,从流量控制、熔断降级、系统负载保护等多个维度保护服务的稳定性。
Sentinel使用
(1)打开Sentinel软件
(2)添加依赖
<!-- Sentinel依赖 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
(3)配置yml文件
spring:
cloud:
sentinel:
transport:
dashboard: localhost:8080 # 配置sentinel仪表台的位置
# 执行限流的端口,每个项目唯一(别的项目(例如cart)设置的话不能再用8721)
port: 8721
nacos:
discovery:
server-addr: localhost:8848
(4)在要限流的控制器方法前添加@SentinelResource
注解
代码编写完毕,打开浏览器http://localhost:8080/,会看到下面的界面
用户名和密码都是sentinel
自定义限流方法
对与被限流的请求,我们可以自定义限流的处理方法,默认情况下可能不能正确给用户提示,一般情况下,对被限流的请求也要有"服务器忙请重试"或类似的提示,StockController类中@SentinelResource注解中,可以定义处理限流情况的方法:
@SentinelResource(value = "名称",blockHandler = "自定义限流方法名称")
例子:
@PostMapping("/reduce/count")
@ApiOperation("减少商品库存数")
// SentinelResource注解标记的控制器方法,会被Sentinel管理
// 在这个方法第一次运行后,可以在sentinel仪表台界面中设置限流规则
// "减少库存的方法"设置当前方法在仪表台中显示的名称
// blockHandler 指定请求被限流时运行的方法名称
@SentinelResource(value = "减少库存的方法",blockHandler = "blockError")
public JsonResult reduceCommodityCount(StockReduceCountDTO stockReduceCountDTO){
// 调用业务逻辑层
stockService.reduceCommodityCount(stockReduceCountDTO);
return JsonResult.ok("商品库存减少完成!");
}
// Sentinel 自定义限流方法的定义
// 1.访问修饰符必须是public
// 2.返回值类型必须和限流的控制器方法一致
// 3.方法名称必须是限流注解中定义的blockHandler指定的方法名称
// 4.方法参数列表必须和限流的控制器方法一致,而且还要添加一个BlockException类型的参数
public JsonResult blockError(StockReduceCountDTO stockReduceCountDTO, BlockException e){
// 限流方法一般直接返回限流信息即可
return JsonResult.failed(ResponseCode.BAD_REQUEST,"服务器忙,请稍后再试");
}
自定义降级方法
所谓降级就是正常运行控制器方法的过程中,控制器方法发生了异常,Sentinel支持我们运行别的方法来处理异常,或运行别的业务流程处理我们也学习过处理控制器异常的统一异常处理类,和我们的降级处理有类似的地方,但是Sentinel降级方法优先级高,而且针对单一控制器方法编写StockController类中@SentinelResource注解中,可以定义处理降级情况的方法
@SentinelResource(value = "名称",blockHandler = "自定义限流方法名称",fallback = "自定义降级方法名称")
例子:
@PostMapping("/reduce/count")
@ApiOperation("减少商品库存数")
// SentinelResource注解标记的控制器方法,会被Sentinel管理
// 在这个方法第一次运行后,可以在sentinel仪表台界面中设置限流规则
// "减少库存的方法"设置当前方法在仪表台中显示的名称
// blockHandler 指定请求被限流时运行的方法名称
// fallback 指定控制器方法发生异常时,要执行的降级方法名称
@SentinelResource(value = "减少库存的方法",blockHandler = "blockError",
fallback = "fallbackError")
public JsonResult reduceCommodityCount(StockReduceCountDTO stockReduceCountDTO) {
if(Math.random()<0.5){
throw new CoolSharkServiceException(ResponseCode.INTERNAL_SERVER_ERROR,
"抛出随机异常!");
}
// 调用业务逻辑层
stockService.reduceCommodityCount(stockReduceCountDTO);
return JsonResult.ok("商品库存减少完成!");
}
// Sentinel 自定义限流方法的定义
// 1.访问修饰符必须是public
// 2.返回值类型必须和限流的控制器方法一致
// 3.方法名称必须是限流注解中定义的blockHandler指定的方法名称
// 4.方法参数列表必须和限流的控制器方法一致,而且还要添加一个BlockException类型的参数
public JsonResult blockError(StockReduceCountDTO stockReduceCountDTO, BlockException e){
// 限流方法一般直接返回限流信息即可
return JsonResult.failed(ResponseCode.BAD_REQUEST,"服务器忙,请稍后再试");
}
// 这个方法是Sentinel注解中fallback指定的降级方法
// 当原定运行的控制器方法发送异常时,Sentinel会运行下面的方法
// 降级方法中,可以编写一些对正常逻辑的补救措施,使用户受到的损失最少
public JsonResult fallbackError(StockReduceCountDTO stockReduceCountDTO){
return JsonResult.failed(ResponseCode.BAD_REQUEST,"因为运行时发生异常,服务降级");
}
SpringGateway网关
这些框架和SpringCloud Alibaba的对应关系我们要了解
Nacos
对应Eureka
都是注册中心
Dubbo
对应Ribbon+feign
都是实现微服务远程调用的组件
Sentinel
对应Hystrix
都是项目限流熔断降级的组件
Gateway
对应Zuul
都是网关项目
Gateway
框架不是阿里写的,是Spring提供的
什么是网关
网关就是当前微服务项目的"统一入口"
网关的主要功能有:
- 将所有请求统一由经过网关
- 网关可以对这些请求进行检查
- 网关方便记录所有请求的日志
- 网关可以统一将所有请求路由到正确的模块\服务上
(1)创建一个新的模块
(2)添加依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>
(3)配置yml文件
server:
port: 9000
spring:
application:
name: gateway
cloud:
nacos:
discovery:
server-addr: localhost:8848
gateway:
routes: # gateway开始编写路由信息
- id: gateway-shanghai
uri: lb://shanghai
predicates:
- Path=/sh/**
# routes是一个数组类型的变量,yml文件中出现"- ..."表示当前配置的是数组元素
# 如果想访问下面数组元素的uri属性,实际结构为spring.cloud.gateway.routes[0].uri
- id: gateway-beijing # 设置这个路由的名称,名称和项目没有任何关联,只是别和其它的路由名称重复
# uri设置路由的目标
# lb就是loadBalance(负载均衡)的缩写,beijing是beijing项目注册到nacos的名称
uri: lb://beijing
# predicates翻译为断言,断言的意思就是判断某个条件为真时,要做的事情
predicates:
# Path会设置当访问9000端口时如果路径以bj开头,会路由到上面uri配置的路径
# Path就是路径断言,判断路径是否为/bj/开头,如果路径满足就会执行路由
- Path=/bj/**
# localhost:9000/bj/show 路由到 localhost:9001/bj/show
# 当前配置结构找到Path=/bj/**路径为 spring.cloud.gateway.routes[0].predicates[2]
动态路由
网关项目随着微服务数量的增多gateway项目的yml文件配置会越来越多,维护的工作量也会越来越大,所以我们希望gateway能够设计一套默认情况下自动路由到每个模块的路由规则
这样的话,不管当前项目有多少个路由目标,都不需要维护yml文件了
这就是我们SpringGateway的动态路由功能
配置文件中开启即可
server:
port: 9000
spring:
application:
name: gateway
cloud:
nacos:
discovery:
server-addr: localhost:8848
gateway:
discovery:
locator:
# 默认情况gateway不开启动态路由,通过enabled: true开启动态路由
# 路由规则是在9000端口号后面先编写路由目标项目注册到nacos的名称,再编写具体路径
enabled: true
注意: 路由规则是在9000端口号后面先编写路由目标项目注册到nacos的名称,再编写具体路径
Gateway和SpringMvc依赖冲突问题和解决
当一起添加gateway
依赖和web依赖时,默认情况下启动会报错
原因: SpringMvc框架中自带一个Tomcat服务器,而SpringGateway框架中自带一个Netty的服务器,在启动项目时,两个框架中包含的服务器都想占用相同端口,因为争夺端口号和主动权而发生冲突,导致启动服务时报错
解决: 要想能够正常启动必须在yml文件配置
spring:
main:
web-application-type: reactive
添加这个配置之后,会启动netty服务器