1.续Seata 概述

JSD-2204-Seata(续)-Sentinel-SpringGateway网关-Day04-LMLPHP

1.1其它模式简介

上次课我们讲解了Seata软件AT模式的运行流程

AT模式的运行有一个非常明显的前提条件,这个条件不满足,就无法使用AT模式

这个条件就是事务分支都必须是操作关系型数据库(mysql\MariaDB\Oracle)

因为关系型数据库才支持提交和回滚,其它非关系型数据库都是直接影响数据(例如Redis)

所以如果我们在业务过程中有一个节点操作的是Redis或其它非关系型数据库时,就无法使用AT模式

除了AT模式之外还有TCC、SAGA 和 XA 事务模式

1.1.1TCC模式

简单来说,TCC模式就是自己编写代码完成事务的提交和回滚

在TCC模式下,我们需要为参与事务的业务逻辑编写一组共3个方法

(prepare\commit\rollback)

prepare:准备

commit:提交

rollback:回滚

  • prepare方法是每个模块都会运行的方法
  • 当所有模块的prepare方法运行都正常时,运行commit
  • 当任意模块运行的prepare方法有异常时,运行rollback

这样的话所有提交或回滚代码都由自己编写

优点:虽然代码是自己写的,但是事务整体提交或回滚的机制仍然可用(仍然由TC来调度)

缺点:每个业务都要编写3个方法来对应,代码冗余,而且业务入侵量大

1.1.2SAGA模式

SAGA模式的思想是对应每个业务逻辑层编写一个新的类,可以设置指定的业务逻辑层方法发生异常时,运行当新编写的类中的代码

这样编写代码不影响已经编写好的业务逻辑代码

一般用于修改已经编写完成的老代码

缺点是每个事务分支都要编写一个类来回滚业务,

会造成类的数量较多,开发量比较大

1.1.3XA模式

支持XA协议的数据库分布式事务,使用比较少

1.2使用Seata

1.2.1配置Seata

cart\stock\order三个模块时需要Seata支持进行事务管理的模块

这三个模块都需要添加下面pom依赖和配置

<!--   Seata和SpringBoot整合依赖     -->
<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>

下面修改cart\stock\order模块的application-dev.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中

同时指定相同的seata地址和端口

1.2.2business模块的配置

business模块作为当前分布式事务模型的触发者

它应该是事务的起点,但是它不连接数据库,所以配置稍有不同

pom文件seata依赖仍然需要,但是只需要seata依赖

<dependency>
    <groupId>io.seata</groupId>
    <artifactId>seata-spring-boot-starter</artifactId>
</dependency>

application-dev.yml是一样的

seata:
  tx-service-group: csmall_group  # 定义分组名称,为了与其它项目区分
  service:
    vgroup-mapping:
      csmall_group: default       # csmall_group分组使用Seata的默认配置完成事务
    grouplist:
      default: localhost:8091     # 配置seata的地址和端口号(8091是默认端口号)

添加完必要的配置之后

要想启动Seata非常简单,只要在起点业务的业务逻辑方法上添加专用的注解即可

添加这个注解的模块就是模型中的TM

他调用的所有远程模块都是RM

@Service
@Slf4j
public class BusinessServiceImpl implements IBusinessService {
    // Dubbo调用order模块的新增订单的方法
    // 单纯的消费者,不需要在类上添加@DubboService
    @DubboReference
    private IOrderService dubboOrderService;

    // Global全局,Transactional事务
    // 一旦编写@GlobalTransactional标记这个方法
    // 就相当于设置了分布式事务的起点,当前模块就是分布式事务模型中的TM
    // 最终效果是由当前方法调用的所有远程服务中对数据库的操作要么都执行,要么都不执行
    @GlobalTransactional
    @Override
    public void buy() {
        // 模拟购买业务
        //  代码略...
    }
}

1.3启动seata

上次课已经将启动的关键步骤寄了笔记

找到seata的解压路径的bin路径下

D:\tools\seata\seata-server-1.4.2\bin

地址栏输入cmd

输入命令如下

D:\tools\seata\seata-server-1.4.2\bin>seata-server.bat -h 127.0.0.1 -m file

然后就可以将服务启动了

先启动Nacos,在seata不关闭的前提下

再启动所有4个服务cart\stock\order\business

利用knife4j访问business模块,否则无法触发事务效果,business模块是seata事务的起点

在windows系统中运行seata可能出现不稳定的情况,重启seata即可解决

根据是否发生随机异常,来判断seata是否有效

OrderServiceImpl在新增订单方法前添加随机发送异常的方法

// 1.减少订单中商品的库存数(要调用stock模块的方法)
// 实例化减少订单业务的DTO对象
StockReduceCountDTO countDTO=new StockReduceCountDTO();
countDTO.setCommodityCode(orderAddDTO.getCommodityCode());
countDTO.setReduceCount(orderAddDTO.getCount());
// dubbo调用stock模块减少库存数的方法
dubboStockService.reduceCommodityCount(countDTO);
// 2.删除订单中选中的购物车的商品(要调用cart模块的方法)
dubboCartService.deleteUserCart(orderAddDTO.getUserId(),
                                orderAddDTO.getCommodityCode());
// 3.执行将orderAddDTO中的信息新增到订单表中的功能
// 实例化一个Order对象
Order order=new Order();
BeanUtils.copyProperties(orderAddDTO,order);
// ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
// 随机发生异常,触发Seata的回顾测试
if(Math.random()<0.5) {
    throw new CoolSharkServiceException(ResponseCode.BAD_REQUEST, "测试异常");
}
// 执行新增
orderMapper.insertOrder(order);
log.info("新增的订单信息为{}",order);

如果seata启动时发送内存不足的错误,可以参考下面的文章解决

springCloud框架搭建(五)使用seata分布式事务_he_lei的博客-CSDN博客_springcloud使用seata

2.Sentinel

官网地址

home

下载地址

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

2.1什么是Sentinel

Sentinel也是Spring Cloud Alibaba的组件

Sentinel英文翻译"哨兵\门卫"

随着微服务的流行,服务和服务之间的稳定性变得越来越重要。Sentinel 以流量为切入点,从流量控制、熔断降级、系统负载保护等多个维度保护服务的稳定性。

2.2为什么需要Sentinel

为了保证服务器运行的稳定性,在请求数到达设计最高值时,将过剩的请求限流,保证在设计的请求数内的请求能够稳定完成处理

  • 丰富的应用场景

    双11,秒杀,12306抢火车票

  • 完备的实时状态监控

    可以支持显示当前项目各个服务的运行和压力状态,分析出每台服务器处理的秒级别的数据

  • 广泛的开源生态

    很多技术可以和Sentinel进行整合,SpringCloud,Dubbo,而且依赖少配置简单

  • 完善的SPI扩展

    Sentinel支持程序设置各种自定义的规则

2.3基本配置

我们的限流针对的是控制器方法

我们找一个简单的模块来测试和观察限流效果

在csmall-stock-webapi模块中

添加sentinel的依赖

<!--    Sentinel 依赖    -->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>

application-dev.yml文件添加配置

cloud:
  sentinel:
    transport:
      dashboard: localhost:8080 # 配置sentinel仪表台的位置
      # 执行限流的端口号,每个项目唯一(别的项目例如cart模块,再设置的话就不能用8721了)
      port: 8721
  nacos:
    discovery:
      # 配置Nacos所在的位置,用于注册时提交信息
      server-addr: localhost:8848

2.4Sentinel启动

JSD-2204-Seata(续)-Sentinel-SpringGateway网关-Day04-LMLPHP

windows同学直接双击start-sentinel.bat文件

mac同学使用下面命令执行jar包

java -jar sentinel-dashboard-1.8.2.jar

启动之后

打开浏览器http://localhost:8080/

会看到下面的界面

JSD-2204-Seata(续)-Sentinel-SpringGateway网关-Day04-LMLPHP 

用户名和密码都是

sentinel

刚开始什么都没有,是空界面

后面我们有控制器的配置就会出现信息了

2.5限流方法

我们以stock模块为例

演示限流的效果

StockController在减少库存的方法上添加限流的注解

@PostMapping("/reduce/count")
@ApiOperation("减少商品库存数")
// @SentinelResource注解标记的控制层方法,会在运行时被Sentinel进行管理
// 在这个控制层方法第一次运行后,可以在Sentinel仪表台界面中设置限流规则
// "减少库存的方法"设置了当前方法在仪表台显示的名称
@SentinelResource("减少库存的方法")
public JsonResult reduceCommodityCount(StockReduceCountDTO stockReduceCountDTO){
    // 调用业务逻辑层
    stockService.reduceCommodityCount(stockReduceCountDTO);
    return JsonResult.ok("库存减少已执行!");
}

nacos\seata\sentinel要启动

重启stock服务(其它服务都可以停掉)

如果不运行knife4j测试,sentinel的仪表盘不会有任何信息

在第一次运行了减少库存方法之后,sentinel的仪表盘才会出现nacos-stock的信息

选中这个信息点击"簇点链路"

找到我们编写的"减少库存的方法"点"+流控"

设置流控规则

我们先设置QPS为1也就是每秒请求数超过1时,进行限流

然后我们可以快速双击knife4j减少库存的方法,触发它的流控效果

JSD-2204-Seata(续)-Sentinel-SpringGateway网关-Day04-LMLPHP

 

这样的流控没有正确的消息提示

我们需要自定义方法进行正确的提示给用户看到

2.6自定义限流方法

对与被限流的请求,我们可以自定义限流的处理方法

默认情况下可能不能正确给用户提示,一般情况下,对被限流的请求也要有"服务器忙请重试"或类似的提示

StockController类中@SentinelResource注解中,可以自定义处理限流情况的方法

@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.INTERNAL_SERVER_ERROR,
                                "服务器忙,请稍后再试");
}

重启stock-webapi模块

再次尝试被限流,观察被限流的提示

2.7QPS与并发线程数

  • QPS:是每秒请求数

    单纯的限制在一秒内有多少个请求访问控制器方法

  • 并发线程数:是当前正在使用服务器资源请求线程的数量

    限制的是使用当前服务器的线程数

2.8自定义降级方法

所谓降级就是正常运行控制器方法的过程中

控制器方法发生了异常,Sentinel支持我们运行别的方法来处理异常,或运行别的业务流程处理

我们也学习过处理控制器异常的统一异常处理类,和我们的降级处理有类似的地方

但是Sentinel降级方法优先级高,而且针对单一控制器方法编写

StockController类中@SentinelResource注解中,可以定义处理降级情况的方法

@PostMapping("/reduce/count")
@ApiOperation("减少商品库存数")
// @SentinelResource注解标记的控制层方法,会在运行时被Sentinel进行管理
// 在这个控制层方法第一次运行后,可以在Sentinel仪表台界面中设置限流规则
// "减少库存的方法"设置了当前方法在仪表台显示的名称
// blockHandler是指定限流时运行方法的配置
// fallback 是指当控制器方法运行发生异常时,运行的降级方法的名称
@SentinelResource(value = "减少库存的方法",blockHandler = "blockError",
                    fallback = "fallbackError")
public JsonResult reduceCommodityCount(StockReduceCountDTO stockReduceCountDTO){

    // 测试Sentinel降级
    if(Math.random()<0.5){
        // 随机抛出异常,抛出的异常会被降级方法处理
        throw new CoolSharkServiceException(ResponseCode.BAD_REQUEST,"随机异常");
    }
    // 调用业务逻辑层
    stockService.reduceCommodityCount(stockReduceCountDTO);
    return JsonResult.ok("库存减少已执行!");
}

// 限流方法略.....

// 降级方法:上面@SentinelResource中fallback指定的降级方法
// 声明格式:基本和限流方法相同,方法参数不需要添加异常类型
// 当控制器方法运行发送异常时,Sentinel会自动调用这个方法
// 实际业务中,可以是新版的业务发生异常,然后转而运行老版代码的机制
public JsonResult fallbackError(StockReduceCountDTO stockReduceCountDTO){
    // 因为没有老版本代码可用,所以也是返回错误信息
    return JsonResult.failed(ResponseCode.INTERNAL_SERVER_ERROR,
                "运行发生异常,服务降级!");
}

重启csmall-stock模块测试

当发生随机异常时,就运行降级方法

当没有发生随机异常时,就正常运行!

2.8.1课堂作业

为business模块控制器的buy方法添加Sentinel流控和降级的功能

流控时输出"服务器忙",降级时输出"服务降级"

1.pom文件

2.yml(port属性不能和stock模块的相同8722)

3.修改控制器代码(注解,流控和降级方法)

3.SpringGateway网关

3.1奈非框架简介

早期(2020年前)奈非提供的微服务组件和框架受到了很多开发者的欢迎

这些框架和SpringCloud Alibaba的对应关系我们要了解

现在还有很多旧项目维护是使用奈非框架完成的微服务架构

Nacos对应Eureka都是注册中心

Dubbo对应Ribbon+feign都是实现微服务远程RPC调用的组件

Sentinel对应Hystrix都是做项目限流熔断降级的组件

Gateway对应Zuul都是网关组件

Gateway框架不是阿里写的,是Spring提供的

3.2什么是网关

"网"指网络,"关"指关口或关卡

网关:就是指网络中的关口\关卡

网关就是当前微服务项目的"统一入口"

程序中的网关就是当前微服务项目对外界开放的统一入口

所有外界的请求都需要先经过网关才能访问到我们的程序

提供了统一入口之后,方便对所有请求进行统一的检查和管理

JSD-2204-Seata(续)-Sentinel-SpringGateway网关-Day04-LMLPHP

 

网关项目git地址

jtzhanghl/gateway-demo

网关的主要功能有

  • 将所有请求统一经过网关
  • 网关可以对这些请求进行检查
  • 网关方便记录所有请求的日志
  • 网关可以统一将所有请求路由到正确的模块\服务上

路由的近义词就是"分配"

3.3Spring Gateway简介

我们使用Spring Gateway作为当前项目的网关框架

Spring Gateway是Spring自己编写的,也是SpringCloud中的组件

SpringGateway官网

Spring Cloud Gateway

网关项目git地址

jtzhanghl/gateway-demo

3.4简单网关演示

SpringGateway网关是一个依赖,不是一个软件

所以我们要使用它的话,必须先创建一个SpringBoot项目

这个项目也要注册到Nacos注册中心,因为网关项目也是微服务项目的一个组成部分

beijing和shanghai是编写好的两个项目

gateway项目就是网关项目,需要添加相关配置

<dependencies>
    <!--   Gateway依赖   -->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-gateway</artifactId>
    </dependency>
    <!--   Nacos依赖   -->
    <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>
</dependencies>

我们从yml文件配置开始添加

server:
  port: 9000
spring:
  application:
    name: gateway
  cloud:
    nacos:
      discovery:
        # 网关也是微服务项目的一部分,所以也要注册到Nacos
        server-addr: localhost:8848
    gateway:
      # routes是一个数组,数组中的数据使用"-"开头表示数据中的一个对象
      routes:   # 开始编写Gateway路由配置
          # 当前路由的名称,和任何其他名称没有关联,只是不能和后面再出现的路由名称重复
        - id: gateway-beijing
          # 当匹配当前路由设置时,访问指定的服务器名称(Nacos注册的服务器名称)
          # lb是LoadBalance的缩写,是负载均衡的调用
          uri: lb://beijing
          # 编写断言配置,断言的意思就是满足指定条件时运行某些事情
          # predicates:断言
          predicates:
            # 断言中我们编写当路径满足指定条件时
            # 当请求路径以/bj/开头时,就会路由到上面设置好的beijing服务器运行
            # ↓ P大写!!!!!!!!
            - Path=/bj/**
      # spring.cloud.gateway.routes[0].uri
      # spring.cloud.gateway.routes[0].predicates[0]

随笔

Dubbo报错

报错信息特别长的

  • 删除nacos配置列表中的所有信息(可以先选每页显示100条,在执行全删)

    然后停掉所有服务重启

  • consumer:
      # 当本项目启动时,是否检查当前项目需要的所有Dubbo服务是否是可用状态
      # 我们设置它的值为false,表示项目启动时不检查,所需的服务是否可用
      check: false
      timeout: 50000
    
  • 报错信息特别长,但是不影响运行的

    是因为当前计算机wifi网卡配置或防火墙软件导致的,可以无视

路由规则解释

如果路径是 /base/business开头的, 就去找nacos-business服务器

如果路径是 /base/cart开头的, 就去找nacos-cart服务器

如果路径是 /base/order开头的, 就去找nacos-order服务器

如果路径是 /base/stock开头的, 就去找nacos-stock服务器

gateway项目

如果路径是 /bj开头 就去找beijing服务器

如果路径是 /sh开头 就去找shanghai服务器

08-25 11:45