SpringCloud

概述:

微服务架构是一种架构模式,它提倡将单一应用程序划分成一组小的服务,服务之间互相协调、互相配合,为用户提供最终价值。每个服务运行在独立的进程中,服务与服务间采用轻量级的通信机制互相协作(通常是基于HTTP协议的RESTFul API)。每个服务都围绕着具体业务进行构建,并且能够被独立的部署到生产环境、类生产环境等。另外,应当尽量避免统一的、集中的服务管理机制,对具体的一个服务而言,应根据业务上下文,选择合适的语言、工具对其进行构建

SpringCloudAlibaba介绍:

  • 对SpringCloud的标准实现
  • 以微服务为核心的整体解决方案
  • 开源与平台服务分开维护

image-20220327153557109

测试版本号:

JDK 1.8+

Java 8

Maven 3.8.2

SpringBoot 2.3.2

SpringCloudAlibaba、SpringCloud、SpringBoot各版本对应关系:

image-20220327155340146


RestTemplate模拟分布式架构

包结构:

image-20220327164453885

介绍:

  • Order为订单,Stock为库存
  • Order端口号为8011,Stock端口号为8010
  • Order增加订单方法(add)调用Stock扣除库存方法(reduct)

http://localhost:8011/order/add

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@RestController
@RequestMapping("/order")
public class OrderController {

@Autowired
RestTemplate restTemplate;

@RequestMapping("/add")
public String add(){
System.out.println("下单成功!");
String msg = restTemplate.getForObject("http://localhost:8010/stock/reduct", String.class);
return "hello"+msg;
}
}

http://localhost:8010/stock/reduct

1
2
3
4
5
6
7
8
9
10
@RestController
@RequestMapping("/stock")
public class StockController {

@RequestMapping("/reduct")
public String reduct(){
System.out.println("扣减库存");
return "hello";
}
}

结果:

image-20220327171653063

上述使用 RestTemplate 模拟的分布式架构其实有很大的弊端,请求的 url 是固定的不能更改

1
restTemplate.getForObject("http://localhost:8010/stock/reduct", String.class);

SpringCloudAlibaba环境搭建

组件版本关系:

image-20220327175535680

自测所用版本:

Spring Cloud Alibaba:2.2.5.RELEASE

Spring Boot:2.3.2.RELEASE

Spring Cloud:Hoxton.SR8

Spring Cloud Alibaba会自动管理各组件版本,所以在引入各组件依赖时不需要加版本号

引入各项依赖

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
<!-- Spring Cloud Alibaba的版本管理,通过dependency完成继承 -->
<dependencyManagement>
<dependencies>
<!-- Spring Cloud Alibaba版本管理 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>2.2.5.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>

<!-- Spring Boot版本管理 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.2.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>

<!-- Spring Cloud版本管理 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Hoxton.SR8</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>

Nacos注册中心

下载链接

什么是 Nacos

官方:一个更易于构建云原生应用的动态服务发现服务配置服务管理平台

SOA架构解决了分布式架构中调用关系错综复杂、难以维护的情况,只需要将服务器地址(localhost:8010)更改成对应的服务名即可

而微服务架构结合了SOA架构的这种优点,将其作为一个组件 ====> 注册中心

注册中心就是把服务注册到一个中心中,调用的时候直接写服务名即可,而不需要调用远程服务地址

Nacos的关键特性包括:

  • 服务发现和服务健康监测
  • 动态配置服务
  • 动态DNS服务
  • 服务及其元数据管理

注册中心演变及其设计思想

image-20220328112018730

缺点:需要手动维护服务远程地址

image-20220328112248584

缺点:当服务进行水平扩展,有多个远程地址时,需要在本地实现一个负载均衡机制

image-20220328113553915

缺点:每个服务都需要在Nginx中配置

image-20220330194211779

Nacos核心功能

服务注册: Nacos Client会通过发送REST请求的方式向Nacos Server注册自己的服务,提供自身的元数据,比如ip地址、端口等信息。Nacos Server接收到注册请求后,就会把这些元数据信息存储在一个双层的内存Map中。

服务心跳:在服务注册后,Nacos Client会维护一个定时心跳来持续通知Nacos Server,说明服务一直处于可用状态,防止被剔除。默认5s发送一次心跳。

服务同步: Nacos Server集群之间会互相同步服务实例,用来保证服务信息的一致性。

服务发观︰股务消费者(Naoos Client)在调用服务提供者的服务时,会发送一个REST清求给Nacos Sever,获取上面注册的服务清单,并且缓存在Nacos Client本地,同时会在Nacos Client本地开启一个定时任务定时拉取服务端最新的注册表信息更新到本地缓存

服务健康检查: Nacos Server会开启一个定时任务用来检查注册服务实例的健康情况,对于超过15s没有收到客户带心练的实例会将它的healhty属性置为false(客户端服务发现时不会发现),如果某个实例超过30秒没有收到心跳,直接剔除该实例(被剔除的实例如果恢复发送心跳则会重新注册)

image-20220330201624941

Nacos配置文件位置:..\nacos-server-2.0.1\nacos\conf\application.properties


搭建Nacos-client服务

1、引入依赖

1
2
3
4
5
<!-- nacos依赖 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>

2、application.yml配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
server:
port: 8020
spring:
#配置服务名
application:
name: order-service
cloud:
nacos:
#配置nacos地址
server-addr: 10.7.89.120:8848
#配置nacos服务登录名和密码
discovery:
username: nacos
password: nocos
#配置命名空间,可以隔离开发环境、测试环境等
namespace: public

3、替换方法中的 localhost:8010 为服务的服务名

1
2
3
4
5
6
@RequestMapping("/add")
public String add(){
System.out.println("下单成功!");
String msg = restTemplate.getForObject("http://stock-service/stock/reduct", String.class);
return "hello"+msg;
}

4、由于nacos进行调用的时候不能自己对服务名进行解析,需要依赖于负载均衡器,所以添加 @LoadBalanced 注解,添加位置如下

1
2
3
4
5
6
7
8
9
10
11
12
13
@SpringBootApplication
public class OrderApplication {
public static void main(String[] args) {
SpringApplication.run(OrderApplication.class);
}

@Bean
@LoadBalanced
public RestTemplate restTemplate(RestTemplateBuilder builder){
RestTemplate restTemplate = builder.build();
return restTemplate;
}
}

nacos默认使用Ribbon的负载均衡机制 image-20220407211431997

这样就可以愉快的访问了

image-20220407210521582

启动多个同一功能服务

image-20220407210956318

这样可以通过不同的端口号启动多个同一功能的服务,后续进行配置可以实现负载均衡

nacos注册中心中产生多个实例

image-20220407212946286


Nacos管理界面介绍

命名空间

用于隔离各种开发环境,可以自己创建命名空间

image-20220407215419567

保护阈值

雪崩保护阈值,可以设置 0~1 之间的值

健康实例数 / 总实例数 < 保护阈值

保护阈值的意义在于

当服务A健康实例数/总实例数 < 保护阈值 的时候,说明健康实例真的不多了,这个时候保护阈值会被触发(状态true)nacos将会把该服务所有的实例信息(健康的+不健康的)全部提供给消费者,消费者可能访问到不健康的实例,请求失败,但这样也⽐造成雪崩要好,牺牲了⼀些请求,保证了整个系统的⼀个可⽤

image-20220407215929401

永久实例

不会因为服务挂了就删除

这样做有一点好处是当一台服务挂了之后还会将挂了的服务拿出来用,虽然不能提供服务了但是可以防止服务雪崩

image-20220407220319594

权重

设置的值越大,为这个微服务分配的流量越大

image-20220408204159207


Nacos配置管理


Nacos集群

集群模式


Ribbon

SpringCloud Ribbon是基于网飞 Ribbon实现的一套客户端的负载均衡工具,Ribbon客户端组件提供一系列的完善的配置,如超时、重试等。通过Load Balancer获取到服务提供的所有机器实例,Ribbon会自动基于某种规则去调用这些服务,Ribbon也可以实现我们自己的负载均衡算法

客户端的负载均衡:客户端会有一个服务器地址列表,在发送请求前通过负载均衡算法选择一个服务器,然后进行访问

服务端的负载均衡:例如nginx,通过nginx进行负载均衡,先发送请求,然后通过负载均衡算法,在多个服务器之间选择一个进行访问;即在服务器端再进行负载均衡

负载均衡策略

Image

  • RandomRule:随机
  • RoundRobinRule:轮询
  • RetryRobinRule:轮询重试
  • WeightedResponseTimeRule:根据服务器的平均响应时间,时间越短权重越大
  • BestAvailableRule:过滤失效的服务实例的功能,然后顺便找出并发请求最小的服务实例
  • ZoneAvoidanceRule:基于区域和可用性(默认规则)

修改默认负载均衡策略

配置类方式修改

  1. 编写配置类

    注意:Ribbon配置类不能放在@ComponentScan能扫描到的地方

    1
    2
    3
    4
    5
    6
    7
    8
    9
    @Configuration
    public class RibbonRuleConfig{
    //全局配置
    //方法名一定要叫IRule
    @Bean
    public IRule iRule(){
    return new RandomRule();
    }
    }
  2. 启动类添加@RibbonClients注解

    1
    2
    3
    4
    5
    6
    @RibbonClients(value = {
    //name:需要进行负载均衡的服务
    //configuration:配置类
    @RibbonClient(name = "stock-service",configuration = RibbonRuleConfig.class)
    })
    public class OrderApplication{

image-20220411205438167

  1. RestTemplate添加@LoadBalanced注解,表示给RestTemplate添加上负载均衡

    1
    2
    3
    4
    5
    6
    @Bean
    @LoadBalanced
    public RestTemplate restTemplate(RestTemplateBuilder builder){
    RestTemplate restTemplate = builder.build();
    return restTemplate;
    }

配置文件方式修改

1
2
3
4
5
#application.yml
stock-service:
ribbon:
#实现负载均衡的类的全路径
NFLoadBalancerRuleClassName: com.alibaba.cloud.nacos.ribbon.NacosRule

自定义负载均衡策略

实现IRule接口或者实现子类

开启饥饿加载,解决第一次调用慢的问题

1
2
3
4
5
6
ribbon:
eager-load:
#开启饥饿加载
enabled: true
#配置order-service使用ribbon饥饿加载,多个使用逗号分隔,开启服务时就加载stock-service的负载均衡策略
clients: stock-service

LoadBalancer

LoadBalancer是SpringCloud官方自己提供的客户端负载均衡器,用来替代Ribbon

RestTemplate整合LoadBalancer

引入依赖

Image

Image


OpenFegin

  • OpenFegin可以做到使用HTTP请求远程服务时就像调用本地方法一样的体验

  • OpenFegin远程调用会自动集成ribbon负载均衡器和nacos

  • SpringCloudAlibaba中使用OpenFeign时,默认的负载均衡策略是轮询调用。项目启动的时候,会用LoadBalancerFeignClient注册一个feign.Client到ioc容器中,FeignClientFactoryBean会生成一个@FeignClient注解的对应的service实例。

Spring Cloud Alibaba整合OpenFegin

  1. 引入依赖

注意:OpenFegin依赖于Spring Cloud ,需要先添加上Spring Cloud 依赖

1
2
3
4
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
  1. 编写StockFeginService接口
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//name:服务名
//path:服务的调用路径
@FeignClient(name="stock-service",path = "/stock")
public interface StockFeginService {

//服务中方法的调用路径
@RequestMapping("/reduct")
public String reduct();
}

///////////////////////////////////////////////////////////////

@RestController
@RequestMapping("/stock")
public class StockController {
@RequestMapping("/reduct")
public String reduct(){
  1. 删除restTemplate 直接调用StockFeginService接口(与mybatis有些类似)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
@RestController
@RequestMapping("/order")
public class OrderController {

@Autowired
StockFeginService stockFeginService;

@RequestMapping("/add")
public String add(){
System.out.println("下单成功!");
String msg = stockFeginService.reduct();
return "hello"+msg;
}
}
  1. 启动类添加 @EnableFeignClients 注解
1
2
3
4
5
6
7
@SpringBootApplication
@EnableFeignClients
public class OrderApplication {
public static void main(String[] args) {
SpringApplication.run(OrderApplication.class);
}
}

OpenFegin修改负载均衡策略

因为OpenFeign集成了Ribbon,所以OpenFegin修改负载均衡策略与Ribbon中修改负载均衡策略一样,在配置文件中修改或者使用配置类即可


OpenFegin修改日志配置

四种日志级别:

  • NONE【性能最佳,适用于生产】:不记录任何日志(默认值)。
  • BASIC【适用于生产环境追踪问题】︰仅记录请求方法、URL、响应状态代码以及执行时间。
  • HEADERS:记录BASIC级别的基础上,记录请求和响应的header。
  • FULL【比较适用于开发及测试环境定位问题】︰记录请求和响应的header、body和元数据。

全局配置

  1. 定义一个配置类,指定日志级别

    1
    2
    3
    4
    5
    6
    7
    @Configuration
    public class FeginConfig {
    @Bean
    public Logger.Level loggerLevel(){
    return Logger.Level.FULL;
    }
    }
  2. 修改springboot默认日志级别

    image-20220411222340840

局部配置

  1. 定义一个配置类,指定日志级别

    1
    2
    3
    4
    5
    6
    public class FeginConfig {
    @Bean
    public Logger.Level loggerLevel(){
    return Logger.Level.FULL;
    }
    }
  2. 修改OpenFegin接口的@FeignClient注解,添加configuration属性,并指定配置类

    1
    2
    @FeignClient(name="stock-service",path = "/stock",configuration = FeginConfig.class)
    public interface StockFeginService {
  3. 修改springboot默认日志级别


OpenFegin超时时间配置

全局配置

1
2
3
4
5
6
7
@Configuration
public class FeginConfig {
@Bean
public Request.Options options(){
return new Request.Options(5000,10000);
}
}

局部配置

1
2
3
4
5
6
7
8
fegin:
client:
config:
stock-service:
# 连接超时时间,默认2s
connectTimeout: 5000
# 请求处理超时时间,默认5s
readTimeout: 10000

OpenFegin自定义拦截器

1
2
3
public class FeignInterceptor implements RequestInterceptor{

}

全局配置

1
2
3
4
@Bean
public FeignInterceptor feignInerceptor(){
return new FeignInterceptor();
}

局部配置

1
2
3
4
5
6
feign:
client:
config:
stock-service:
requestIntercetor[0]:
#自定义拦截器全路径

Sentinel

  • Sentinel是阿里巴巴开源的,面向分布式服务架构的高可用防护组件

  • 随着微服务的流行,服务和服务之间的稳定性变得越来越重要,sentinel是面向分布式服务架构的流量控制组件,主要以流量为切入点,从限流、流量整形、熔断降级、系统负载保护、热点防护等多个维度来帮助开发者保障微服务的稳定性

服务雪崩

因服务提供者的不可用,导致服务调用者的不可用,并将不可用逐渐放大的过程,就叫服务雪崩

原因:

  • 流量激增:流量激增,导致系统CPU飙高,无法正常处理请求

  • 不稳定服务依赖:慢SQL查询,卡爆连接池;第三方服务不响应,卡满线程池

@SentinelResource

使用@SentinelResource注解可以实现更细粒度的控制 例如:改善接口中资源定义和被流控降级后的处理方法,对某一个方法配置限流等,具体使用请看官方文档,这里使用SpringCloudAlibaba整合Sentinel的dashboard控制台进行流控、熔断设置就不再赘述

使用@SentinelResource注解之前 先引入对应的依赖

1
2
3
4
5
6
<! --如果要使用@sentinelResource-->
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-annotation-aspectj</artifactId>
<version>1.8.0</version>
</dependency>

SpringCloudAlibaba整合Sentinel

jar包下载地址:

1
https://github.com/alibaba/Sentinel/releases

启动控制台命令:

1
java -Dserver.port=8858 -jar sentinel-dashboard-1.8.2.jar
  1. 引入Sentinel依赖
1
2
3
4
5
6
7
8
9
10
11
12
13
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>

<!--若不与SpringCloudAlibaba整合可直接引入sentinel核心库 -->
<!--
<dependency>
<groupId>com.alibaba. csp</groupId>
<artifactId>sentinel-core</artifactId>
<version>1.8.0</version>
</dependency>
-->
  1. 修改yml文件
1
2
3
4
5
spring:
cloud:
sentinel:
transport:
dashboard: 127.0.0.1:8858

阈值类型

QPS流控

QPS:每秒访问次数

QPS流控就是针对每秒访问的次数进行流量控制

image-20220416105539979

线程数

流控模式

直接模式:即对当前要设置的方法进行流控,也是默认的模式

image-20220416112718726

关联模式:当关联的路径超出单机阈值,则对设置的路径进行流控

image-20220416113042939

链路模式:例如 test1==>getUser test2==>getUser 对 test1 进行流控,而 test2 不会受到影响,这就是链路模式

image-20220416113322238

不仅需要增加流控规则 还需要修改yml文件

1
2
3
4
5
cloud:
sentinel:
transport:
dashboard: 127.0.0.1:8858
web-context-unify: false #默认将调用链路收敛了,不展示调用链路

统一异常处理

自定义BlockExceptionHandler的实现类统一处理BlockException

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@Component
public class MyBlockExceptionHandler implements BlockExceptionHandler {

@Override
public void handle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, BlockException e) throws Exception {
if (e instanceof FlowException){
//接口限流
} else if (e instanceof DegradeException) {
//服务降级
} else if (e instanceof ParamFlowException){
//热点参数限流
} else if (e instanceof SystemBlockException){
//触发系统保护规则
} else if (e instanceof AuthorityException){
//授权规则不通过
}
}
...
}

流控效果

直接失败

预热(warm up)

冷启动方式,当前系统长期处于低水位的情况下,当流量突然增加时,直接把系统拉升到高水位可能瞬间把系统压垮。通过冷启动方式,让流量缓慢增加,在一定时间内逐渐增加到阈值上限,避免冷系统被压垮,这种效果主要用于激增流量

冷加载因子默认为3,即请求QPS从3开始,经预热时长逐渐升至设定的QPS阈值

在这里插入图片描述

个人理解此流控效果需要结合redis缓存一起使用

排队等待

排队等待方式会严格控制通过的时间间隔,也即是让请求以匀速的速度通过,这种方式主要用于处理间隔性突发的流量,如果在某一秒有大量的请求到来,而下几秒处于空闲状态,然后又有大量的请求到来,我们希望系统能在空闲的几秒逐渐处理这些请求,而不是第一次就拒绝大量的请求,这种效果主要用于脉冲流量

熔断降级

除了流量控制之外,对调用链路中不稳定的资源进行熔断降级也是保障高可用的重要措施之一。我们需要对不稳定的弱依赖服务调用进行熔断降级,暂时切断不稳定调用,避免局部不稳定因素导致整体的雪崩。熔断降级作为保护自身的手段,通常在调用端进行配置

慢调用比例

image-20220416140121448

最大RT:超过这个时间就是慢调用

比例阈值:慢调用占总请求次数的比例

熔断时长:熔断后持续的时长,超过后进入半开状态

最小请求数:请求次数达到设置的数值之后才有可能熔断

异常比例

image-20220416140616403

比例阈值:异常调用占总请求次数的比例

热点参数流控

何为热点?热点即经常访问的数据。很多时候我们希望统计数据中访问频次最高的数据,并对其访问进行限制

image-20220416154922150

参数索引:参数的索引值

单机阈值:参数公共单机阈值

高级选项中即是对热点参数的具体配置


整合openfeign进行降级

image-20220416142102189
  1. 实现自定义的OpenFegin接口

    1
    2
    3
    4
    5
    6
    7
    @Component
    public class StockFeginServiceFallback implements StockFeginService{
    @Override
    public String reduct() {
    return "服务降级";
    }
    }
  2. 修改yml文件,添加openfegin对sentinel的整合

    1
    2
    3
    fegin:
    sentinel:
    enabled: true
  3. 修改openfegin接口,添加fallback属性

    1
    2
    3
    4
    5
    6
    7
    8
    9
    //name:服务名
    //path:服务的调用路径
    @FeignClient(name="stock-service",path = "/stock",fallback = StockFeginServiceFallback.class)
    public interface StockFeginService {

    //服务中方法的调用路径
    @RequestMapping("/reduct")
    public String reduct();
    }

这样就能整合成功,若出现异常会调用实现类里相应的方法


Seata

微服务情况下,我们可能将一个服务拆分成多个服务,例如下单服务拆分成订单服务和库存服务,这时候spring的声明式事务@Transactional就不管用了,所以产生了分布式事务

Seata是一款开源的分布式事务解决方案,致力于提供高性能和简单易用的分布式事务。Seata将为用户提供了AT、TCC、SAGA和XA事务模式,为用户打造一站式的分布式解决方案

Seata使用的是AT模式

三大角色

TC(事务协调者):维护全局和分支事务的状态,驱动全局事务提交或回滚

TM(事务管理器):定义全局事务的范围:开始全局事务、提交或回滚全局事务

RM(资源管理器):管理分支事务处理的资源,与TC交谈以注册分支事务和报告分支事务的状态,并驱动分支事务提交或回滚


二阶段提交协议

顾名思义,分为两个阶段:Prepare和Commit阶段

Prepare:提交事务请求

  1. 询问协调者向所有参与者发送事务请求,询问是否可执行事务操作,然后等待各个参与者的响应。
  2. 执行各个参与者接收到协调者事务请求后,执行事务操作(例如更新一个关系型数据库表中的记录),并将Undo和Redo信息记录事务日志中。
  3. 响应如果参与者成功执行了事务并写入Undo和Redo信息,则向协调者返回YES响应,否则返回NO响应。当然,参与者也可能宕机,从而不会返回响应.

Commit:正常提交事务

  1. commit请求协调者向所有参与者发送Commit请求。
  2. 事务提交参与者收到Commit 请求后,执行事务提交,提交完成后释放事务执行期占用的所有资源。
  3. 反馈结果参与者执行事务提交后向协调者发送Ack 响应。
  4. 完成事务接收到所有参与者的Ack响应后,完成事务提交。

image-20220417110731200

二阶段协议的问题

  1. 同步阻塞参与者在等待协调者的指令时,其实是在等待其他参与者的响应,在此过程中,参与者是无法进行其他操作的,也就是阻塞了其运行。倘若参与者与协调者之间网络异常导致参与者一直收不到协调者信息,那么会导致参与者一直阻塞下去。
  2. 单点在2PC中,一切请求都来自协调者,所以协调者的地位是至关重要的,如果协调者宕机,那么就会使参与者一直阻塞并一直占用事务资源。
    如果协调者也是分布式,使用选主方式提供服务,那么在一个协调者挂掉后,可以选取另一个协调者继续后续的服务,可以解决单点问题。但是,新协调者无法知道上一个事务的全部状态信息(例如已等待Prepare响应的时长等),所以也无法顺利处理上一个事务。
  3. 数据不一致Commit事务过程中Commit请求Rollback请求可能因为协调者宕机或协调者与参与者网络问题丢失,那么就导致了部分参与者没有收到CommitRollback请求,而其他参与者则正常收到执行了CommitRollback.操作,没有收到请求的参与者则继续阻塞。这时,参与者之间的数据就不再一致了。
    当参与者执行Commit/Rollback后会向协调者发送Ack,然而协调者不论是否收到所有的参与者的Ack,该事务也不会再有其他补救措施了,协调者能做的也就是等待超时后像事务发起者返回一个“我不确定该事务是否成功”。
  4. 环境可靠性依赖协调者Prepare请求发出后,等待响应,然而如果有参与者宕初或与协调者之间的网络中断,都会导致协调者无法收到所有参与者的响应,那么在2PC中,协调者会等待一定时间,然后超时后,会触发事务中断,在这个过程中,协调者和所有其他参与者都是出于阻塞的。这种机制对网络问题常见的现实环境来说太苛刻了。

AT模式

image-20220417150658953

AT模式是一种无入侵的分布式事务解决方案

在AT模式下,用户只需要关注自己的“业务SQL”,用户的“业务SQL”作为一阶段,Seata框架会自动生成事务的二阶段提交和回滚操作

  • 在一阶段,Seata 会拦截业务SQL”,首先解析SQL语义,找到业务SQL”要更新的业务数据,在业务数据被更新前,将其保存成”before inage,然后执行业务SQL”更新业务数据,在业务数据更新之后,再将其保存成aftr image”,最后生成行锁。以上操作全部在一个数据库事务内完成,这样保证了一阶段操作的原子性。

    image-20220417150850450

  • 二阶段如果是提交的话,因为业务SaL在一阶段已经提交至数据库,所以Seata框架只需将一阶段保存的快照数据和行锁删掉,完成数据清理即可

    image-20220417151515226

  • 二阶段如果是回滚的话,Seata就需要回滚一阶段已经执行的”业务SQL,还原业务数据。回滚方式便是用before image”还原业务数据;但在还原前要首先要校验脏写,对比”数据库当前业务数据”和”ater image”,如果两份数据完全一致就说明没有脏写,可以还原业务数据,如果不一致就说明有脏写,出现脏写就需要转人工处理。

    image-20220417151907857


Seata服务搭建

Server端存储模式支持三种:

  • file:单机模式,全局事务会话信息内存中读写并持久化本地文件root.data,性能较高
  • db:高可用模式,全局事务会话信息通过db共享,性能相对差一点
  • redis:性能较高,但存在事务信息丢失风险

这里使用db模式

修改默认配置

打开文件: ..\seata-server-1.3.0\seata\conf\file.conf

修改模式:image-20220417230132143

修改数据库配置:image-20220417230206386

配置Nacos注册中心

打开文件:..\seata-server-1.3.0\seata\conf\registry.conf

修改注册中心:image-20220418215710630

修改nacos配置:image-20220418215516417

配置Nacos配置中心

修改配置中心:image-20220418215654159

修改nacos配置:image-20220418215516417

受限于电脑环境问题 ,服务搭建等到接触到真实业务时再进行学习


Seata分布式事务代码搭建

  1. 引入依赖
1
2
3
4
5
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-alibaba-seata</artifactId>
<version>0.9.0.RELEASE</version>
</dependency>
  1. 在微服务对应的数据库中添加 undo_log 表,各字段就是要插入表的字段
image-20220418223356611

3.修改yml配置文件,配置事务分组

1
2
3
4
cloud:
alibaba:
seata:
tx-service-group: guangzhou
  1. 配置seata注册中心
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
seata:
#注册中心
registry:
type: nacos
nacos:
server-addr: 127.0.0.1:8848 #nacos地址
application: seata-server #seata的服务名
username: nacos
password: nacos
group: seata-group
#配置中心
config:
type: nacos
nacos:
server-addr: 127.0.0.1:8848
username: nacos
password: nacos
group: seata-group

  1. 给需要事务的方法加上**@GlobalTransactional**注解即可

Gateway

所谓的API网关,就是指系统的统一入口,它封装了应用程序的内部结构,为客户端提供统一服务,一些与业务本身功能无关的公共逻辑可以在这里实现。诸如认证、鉴权、监控、路由转发等。

功能特征

  • 动态路由:能够匹配任何请求属性
  • 支持路径重写
  • 集成 springcloud 服务发现功能(nacos)
  • 可集成流控降级功能(sentinel)
  • 可以对路由指定易于编写的断言和过滤器

核心概念

  • 路由:路由时网关中最基础的部分,路由信息包括一个ID,一个目的URI,一组断言工厂,一组Filter组成,如果断言为真,则说明请求的URI和配置的路由匹配
  • 断言:springcloud gateway中的断言函数类型时spring5框架中的ServerWebExchange。断言函数允许开发者去定义匹配Http request中的任何信息,比如请求头和参数等
  • 过滤器:springcloud Gateway中的filter分为Gateway Filter和Global Filter可以对请求和响应进行处理

环境搭建

  1. 创建一个新的springboot项目
  2. 添加依赖
1
2
3
4
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
  1. 修改yml配置文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
server:
port: 8088
spring:
application:
name: api-geteway
cloud:
gateway:
# 路由规则
routes:
- id: order_route # 路由的唯一标识
uri: http://loaclhost:8020 # 需要转发的地址
predicates: # 断言规则,用于路由规则的匹配,只有满足下列条件的才路由
# 路径中包含order-serv
- Path=/order-serv/**
filters: # 过滤器
- StripPrefix=1 # 转发之前去掉第一层路径
# 以这种形式还可以配置多个路由
# - id: stock_route
# ...

这样就可以访问 http://loaclhost:8088/order-serv/order/add 路径然后转发到 http://loaclhost:8020/order/add 路径下了


Gateway整合Nacos

1.添加nacos依赖

2.修改yml文件,添加nacos的配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
gateway:
routes:
- id: order_route
uri: lb://order-service # 需要转发的地址,lb表示使用nacos中的本地负载均衡机制 order-service表示服务名
predicates:
- Path=/order-serv/**
filters:
- StripPrefix=1

# 集成了nacos会将服务名作为断言判断 同时转发时会自动去除服务名这一层,所以路由配置可以不需要写,可以直接使用简化方式,让其自动定位
gateway:
discovery:
locator:
enabled: true

路由断言工厂