[学习笔记]Spring Cloud Alibaba详细教程

文章目录

    • Nacos服务注册和配置中心
      • Nacos安装
      • Nacos的服务注册
        • 生产者
        • 消费者
      • Nacos与其他注册中心对比
      • Nacos的服务配置中心
        • DataID
        • Group
        • NameSpace
      • Nacos高可用集群
        • 配置MySQL数据库
        • Nacos集群配置
        • 配置Nginx
    • Sentinel流量控制与熔断
      • 流控规则
      • 降级规则
      • 热点限流规则
      • 系统规则
      • SentinelResource配置
        • blockHandler参数
        • fallback与exceptionsToIgnore参数
      • Sentinel整合Feign的降级
      • Sentinel规则的持久化
    • Seata
      • Seata的下载与安装
      • Seata的使用
        • 数据库
        • 订单模块
      • Seata的整体机制

Spring Cloud Alibaba官方文档

Nacos服务注册和配置中心

Nacos: Naming and Configuration Service,也就是注册和配置中心

Nacos等价于Eureka + Config + Bus

Nacos官网

Nacos安装

可以在官网首页找到下载地址

下载完成之后,在nacos目录下的/bin目录中

  • 使用bash startup.sh -m standalone通过单机模式启动nacos,集群模式不用加参数
  • 使用sh shutdown.sh关闭nacos

通过lcoalhost:8848/nacos访问nacos

Nacos的服务注册

生产者

  1. pom

    
    <dependency>
        <groupId>com.alibaba.cloudgroupId>
        <artifactId>spring-cloud-starter-alibaba-nacos-discoveryartifactId>
    dependency>
    
  2. yaml

    server:
      port: 9001
    spring:
      application:
        name: nacos-payment-provider
      cloud:
        nacos:
          discovery:
            server-addr: localhost:8848 # Nacos地址
    management:
      endpoints:
        web:
          exposure:
            include: '*'
    
  3. 主启动类: 注意使用@EnableDiscoveryClient

    @SpringBootApplication
    @EnableDiscoveryClient
    public class PaymentMain9001 {
        public static void main(String[] args) {
            SpringApplication.run(PaymentMain9001.class, args);
        }
    }
    
  4. controller

    @RestController
    public class PaymentController {
        @Value("${server.port}")
        private String serverPort;
    
        @GetMapping("/payment/nacos/{id}")
        public String getNacos(@PathVariable("id") Integer id) {
            return serverPort + "===============" + id;
        }
    }
    

消费者

  1. pom

    
    <dependency>
        <groupId>com.alibaba.cloudgroupId>
        <artifactId>spring-cloud-starter-alibaba-nacos-discoveryartifactId>
    dependency>
    
  2. yaml

    server:
      port: 8080
    
    spring:
      application:
        name: nacos-consumer-order
      cloud:
        nacos:
          discovery:
            server-addr: localhost:8848
    
  3. 主启动

    @SpringBootApplication
    @EnableDiscoveryClient
    public class OrderNacosMain8080 {
        public static void main(String[] args) {
            SpringApplication.run(OrderNacosMain8080.class, args);
        }
    }
    
  4. config: 注意@LoadBalanced,Nacos也是使用Ribbon进行负载均衡

    @Configuration
    public class ApplicationContexConfig {
        @Bean
        @LoadBalanced
        public RestTemplate getRestTemplate() {
            return new RestTemplate();
        }
    
    }
    
  5. controller: 跟Netflix一样调用生产者

    @RestController
    public class OrderNacosController {
        @Resource
        private RestTemplate restTemplate;
    
        private String serverURL = "http://nacos-payment-provider";
    
        @GetMapping("/consumer/payment/nacos/{id}")
        public String getMessage(@PathVariable("id") Integer id) {
            return restTemplate.getForObject(serverURL + "/payment/nacos/" + id, String.class);
        }
    }
    

Nacos与其他注册中心对比

[学习笔记]Spring Cloud Alibaba详细教程_第1张图片

Nacos的服务配置中心

与Spring Cloud Config不同的是,Nacos不需要自己建一个微服务来获得远端配置文件,在Nacos中可以直接添加配置文件.操作如下
[学习笔记]Spring Cloud Alibaba详细教程_第2张图片
微服务中获得配置信息

  1. pom

    
    <dependency>
        <groupId>com.alibaba.cloudgroupId>
        <artifactId>spring-cloud-starter-alibaba-nacos-configartifactId>
    dependency>
    
    <dependency>
        <groupId>com.alibaba.cloudgroupId>
        <artifactId>spring-cloud-starter-alibaba-nacos-discoveryartifactId>
    dependency>
    
  2. bootstrap.yaml

    server:
      port: 3377
    
    spring:
      application:
        name: nacos-config-client
      cloud:
        nacos:
          discovery:
            server-addr: localhost:8848
          config:
            server-addr: localhost:8848 # 配置中心地址
            file-extension: yaml        # 配置文件类型
    

    application.yaml

    spring:
      profiles:
        active: dev # 设置开发环境
    

    远程配置文件名(DataID)的格式要求 ${spring.application.name}-${spring.profiles.active}.${spring.cloud.config.file-extension}

    反过来说,当一个微服务配置内容跟某个远程配置文件的DataID相符,那这个微服务就会使用这个配置文件

  3. 主启动类

    @SpringBootApplication
    @EnableDiscoveryClient
    public class NacosConfigClientMain3377 {
        public static void main(String[] args) {
            SpringApplication.run(NacosConfigClientMain3377.class, args);
        }
    }
    
  4. controller: 不要忘记动态刷新@RefreshScope

    @RestController
    @RefreshScope //动态刷新
    public class ConfigClientController {
        @Value("${config.info}")
        private String configInfo;
    
        @GetMapping("/configInfo")
        public String getConfigInfo() {
            return configInfo;
        }
    }
    

Nacos的远程配置文件发生修改时,为服务直接动态修改,不需要额外的配置

DataID

DataID就是前面提到的配置文件名,格式是 s p r i n g . a p p l i c a t i o n . n a m e − {spring.application.name}- spring.application.name{spring.profiles.active}.${spring.cloud.config.file-extension}

每个服务只能调用复合它自己的配置文件

Group

在新建配置时可以自定义该配置文件的Goup,默认是DEFAULT_GROUP
在这里插入图片描述
微服务可以指定某个组的配置文件,默认选择DEFAULT_GROUP.

spring:
  application:
    name: nacos-config-client
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848
      config:
        server-addr: localhost:8848 # 配置中心地址
        file-extension: yaml        # 配置文件类型
        # 选择分组
        group: DEV_GROUP

NameSpace

NameSpace也叫命名空间

新建命名空间步骤如下:
[学习笔记]Spring Cloud Alibaba详细教程_第3张图片[学习笔记]Spring Cloud Alibaba详细教程_第4张图片
[学习笔记]Spring Cloud Alibaba详细教程_第5张图片
在微服务中可以选择命名空间

spring:
  application:
    name: nacos-config-client
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848
      config:
        server-addr: localhost:8848 # 配置中心地址
        file-extension: yaml        # 配置文件类型
        # 选择分组
        group: DEV_GROUP
        # 选择命名空间
        namespace: bebe2d24-b324-4646-a5b6-075f68feabb7

选择命名空间时要使用命名空间的ID

Nacos高可用集群

Nacos高可用集群示意图
[学习笔记]Spring Cloud Alibaba详细教程_第6张图片

配置MySQL数据库

Nacos自带了一个小数据库,但重启后其中的数据就会消失,要想达到持久化的目标,就需要外接一个数据库.

目前Nacos持久化仅支持MySQL数据库,并且Nacos集群也需要惊Nacos数据库配置成MySQL数据库.

将数据库改成MySQL的配置如下:

  1. 在conf目录中存在一个nacos-mysql.sql
    [学习笔记]Spring Cloud Alibaba详细教程_第7张图片
    内容是这样的
    [学习笔记]Spring Cloud Alibaba详细教程_第8张图片
    按照上面的提示,创建一个名为nacos_config的数据库,并在此库中运行上述sql文件

    以后Nacos的所有信息将存储到这个数据库中

  2. 同样在conf目录下,修改application.properties文件,追加下面一段

    spring.datasource.platform=mysql
    
    db.num=1
    db.url.0=jdbc:mysql://localhost:3306/nacos_devtest?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true
    db.user=你的数据库用户名
    db.password=数据库密码
    

这样就将就将数据库调整为MySQL

Nacos集群配置

  1. 修改conf/cluster.conf集群配置文件:

    拷贝cluster.conf.example

    cp cluster.conf.example cluster.conf
    

    修改cluster.conf如下
    [学习笔记]Spring Cloud Alibaba详细教程_第9张图片

  2. 修改/bin/startup.sh启动脚本,让它支持-p这个参数,以便用来选择启动的端口

    同样备份一份startup.sh,防止改坏

    修改如下:
    [学习笔记]Spring Cloud Alibaba详细教程_第10张图片
    在这里插入图片描述

配置Nginx

在修改nginx.conf
[学习笔记]Spring Cloud Alibaba详细教程_第11张图片
上述配置完成之后,Nacos集群就会对外暴露1111端口,下次微服务注册Nacos就是用1111端口

Sentinel流量控制与熔断

Sentinel官方文档

Sentinel下载地址

Sentinel也是一个jar包,使用java -jar命令就可以运行

  • 访问Sentinel: localhost:8080

  • username: sentinel

  • password: sentinel

与Hystrix不同的是,Sentinel的配置都是在可视化平台下进行的,降低了配置与代码的耦合度.并且Sentinel提供了大量的限流、熔断规则.

流控规则

新增流量规则
[学习笔记]Spring Cloud Alibaba详细教程_第12张图片
阈值类型

  • QPS: 每秒钟请求的数量

  • 线程数: 同一时间请求的线程数量

流控模式:

  • 直接: 一秒内查询的次数超过单机阈值,直接失败,报默认错误
    [学习笔记]Spring Cloud Alibaba详细教程_第13张图片
  • 关联: 当与A关联的资源B达到阈值后,就限流A自己
    [学习笔记]Spring Cloud Alibaba详细教程_第14张图片
  • 链路: 多个请求调用同一个微服务,

流控效果:

  • 快速失败: 过了阈值直接报错

  • Warm Up(预热): 初始阈值是阈值/冷加载因子(默认为3),经过预热时长之后,慢慢将阈值提升到设置的阈值

    当访问量巨大时,预热的方式能让流量慢慢提高,保护主机

  • 排队等待: 让请求一个一个易均匀的速度获得响应,使用了漏桶算法,超时时间也就是排队的等待时间,等待超时就报错,用于处理间隔性突发流量

降级规则

  • RT(平均响应时间): 当1秒内持续五个请求对应时刻的平均响应时间都超过阈值,那在接下来的时间窗口之内,对这个方法的调用会自动熔断.RT的上限是4900ms,超过此阈值会被算作4900.
    [学习笔记]Spring Cloud Alibaba详细教程_第15张图片
  • 异常比例: 当资源的每秒请求量>=5,并且每秒异常总数占通过量的比值超过阈值,资源进入降级状态,在接下来的窗口时间内,对这个方法的调用都会自动返回.异常比例的阈值范围是[0.0, 1.0]
    [学习笔记]Spring Cloud Alibaba详细教程_第16张图片
  • 异常数: 当资源近一分钟的异常数超过阈值后会进行熔断.由于统计时间窗口是分钟级别的,如果时间窗口小于60s,则结束熔断状态后仍可能进入熔断状态.也就是说时间窗口要大于60秒
    [学习笔记]Spring Cloud Alibaba详细教程_第17张图片

热点限流规则

热点限流就是针对一个请求中的某个参数进行限流

例如下面的代码,我对第0个参数也就是p1进行限流

@GetMapping("/testHotKey")
public String testHotKey(@RequestParam(value = "p1", required = false) String p1,
                         @RequestParam(value = "p2", required = false) String p2) {
    return "===============testHotKey====================";
}

[学习笔记]Spring Cloud Alibaba详细教程_第18张图片
参数例外项

如果你想让限流的参数有不一样的阈值,可以通过参数例外项进行配置
[学习笔记]Spring Cloud Alibaba详细教程_第19张图片

系统规则

Sentinel 系统自适应限流从整体维度对应用入口流量进行控制,结合应用的 Load、CPU 使用率、总体平均 RT、入口 QPS 和并发线程数等几个维度的监控指标.

  • Load 自适应(仅对 Linux/Unix-like 机器生效):系统的 load1 作为启发指标,进行自适应系统保护。当系统 load1 超过设定的启发值,且系统当前的并发线程数超过估算的系统容量时才会触发系统保护(BBR 阶段)。系统容量由系统的 maxQps * minRt 估算得出。设定参考值一般是 CPU cores * 2.5
  • CPU usage(1.5.0+ 版本):当系统 CPU 使用率超过阈值即触发系统保护(取值范围 0.0-1.0),比较灵敏。
  • 平均 RT:当单台机器上所有入口流量的平均 RT 达到阈值即触发系统保护,单位是毫秒。
  • 并发线程数:当单台机器上所有入口流量的并发线程数达到阈值即触发系统保护。
  • 入口 QPS:当单台机器上所有入口流量的 QPS 达到阈值即触发系统保护。
    [学习笔记]Spring Cloud Alibaba详细教程_第20张图片

SentinelResource配置

blockHandler参数

blockHandler处理配置违规

使用@SentinelResource注解可以自定义错误页面(value)和资源名(blockHandler),没有指定blockHandler就使用系统自带的错误页面.

@GetMapping("/byResource")
@SentinelResource(value = "byResource", blockHandler = "handleException")
public CommonResult byResource() {
    return new CommonResult(200, "===============按资源名限流=========", new Payment(2020L, "serial001"));
}

public CommonResult handleException(BlockException exception) {
    return new CommonResult(444, exception.getClass().getCanonicalName() + "\t 服务不可用");
}

以上配置就是当出现流控或者降级时,调用handleException方法

value中可以是任意值,一般与访问路径相同但不带/

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EF23Rfks-1590731651588)(Spring Cloud Alibaba.assets/截图录屏_选择区域_20200520014355.png)]

上述代码业务逻辑与处理类的耦合性太大,使用blockHandlerClass可以解藕,配置如下

  1. 在另一个类中写出现错误时的方法

    public class CustomerBlockHandler {
        // 注意静态方法,不然Sentinel找不到这个方法
        public static CommonResult handlerException(BlockException exception) {
            return new CommonResult(444, "自定义的错误页面BlockHandleClass", new Payment(2020L, "serial001"));
        }
    }
    
  2. 在@SentinelResource中,使用blockHandlerClass调用这个方法

    @GetMapping("/rateLimit/customerBlockHandle")
    @SentinelResource(value = "customerBlockHandle",
            blockHandlerClass = CustomerBlockHandler.class, //指定处理类
            blockHandler = "handlerException")// 指定该类中的处理方法
    public CommonResult customerBlockHandle() {
        return new CommonResult(200, "=======访问成功======", new Payment(2020L, "serial001"));
    }
    

fallback与exceptionsToIgnore参数

fallback负责业务异常

exceptionsToIgnore可以忽略某个异常的降级

@GetMapping("/consumer/fallback/{id}")
@SentinelResource(value = "fallback",
        fallback = "handlerFallback",
        blockHandler = "blockHandler",
        exceptionsToIgnore = {IllegalArgumentException.class})
public CommonResult<Payment> fallback(@PathVariable("id") Long id) {
    if (id == 4) {
        throw new IllegalArgumentException("存在非法参数");
    }
    CommonResult result = restTemplate.getForObject(SERVER_URL + "/payment/" + id, CommonResult.class);
    if (result.getData() == null) {
        throw new NullPointerException("记录不存在");
    }

    return result;
}

//注意参数应包含业务方法的参数
public CommonResult handlerFallback(@PathVariable Long id, Throwable e) {
    return new CommonResult(444, "业务类出现异常 " + e.getMessage());
}

public CommonResult blockHandler(@PathVariable Long id, BlockException exception) {
    return new CommonResult(445, "操作被限流");
}

fallback = "handlerFallback"出现异常时,会执行handlerFallback()方法

exceptionsToIgnore = {IllegalArgumentException.class},此时IllegalArgumentException异常就不会进入降级页面

Sentinel整合Feign的降级

  1. pom

    <dependency>
        <groupId>org.springframework.cloudgroupId>
        <artifactId>spring-cloud-starter-openfeignartifactId>
    dependency>
    
  2. yaml

    server:
      port: 8480
    spring:
      application:
        name: nacos-order-consumer
      cloud:
        nacos:
          discovery:
            server-addr: localhost:8848
        sentinel:
          transport:
            dashboard: localhost:8080
            port: 8719
    
    #激活sentinel对feign的支持
    feign:
      sentinel:
        enabled: true
    
  3. 主启动类: 使用@EnableFeignClients开启Feign功能

    @SpringBootApplication
    @EnableDiscoveryClient
    //开启feign
    @EnableFeignClients
    public class OrderMain84 {
        public static void main(String[] args) {
            SpringApplication.run(OrderMain84.class, args);
        }
    }
    
  4. service: 直接使用@FeignClient中的fallback参数,指定降级的类,这个类必须是serivce接口的实现类,并且他实现的方法就是降级方法

    //Feign
    @FeignClient(value = "provider-payment-server", fallback = PaymentFallBackServer.class)
    public interface PaymentService {
        @GetMapping("/payment/{id}")
        CommonResult<Payment> payment(@PathVariable("id") Long id);
    }
    
    @Component
    public class PaymentFallBackServer implements PaymentService {
        @Override
        public CommonResult<Payment> payment(Long id) {
            return new CommonResult<>(4444, "==========服务降级===========");
        }
    }
    
  5. controller: 调用service

    @Resource
    private PaymentService paymentService;
    
    @GetMapping("/consumer/payment/{id}")
    public CommonResult<Payment> payment(@PathVariable("id") Long id) {
        return paymentService.payment(id);
    }
    

Sentinel规则的持久化

在Sentinel中配置的规则会随着服务的重启而消失

Sentinel规则的持久化就是将Sentinel的配置放入Nacos保存,访问页面时,Nacos会自动将规则还给Sentinel

  1. pom

    
    <dependency>
        <groupId>com.alibaba.cspgroupId>
        <artifactId>sentinel-datasource-nacosartifactId>
    dependency>
    
  2. yaml

    server:
      port: 8401
    
    spring:
      application:
        name: cloudalibaba-sentinel-service
      cloud:
        nacos:
          discovery:
            # Nacos地址
            server-addr: localhost:8848
        sentinel:
          transport:
            # sentinel地址
            dashboard: localhost:8080
            # 端口被占用会自动+1,直至找到没被占用的端口
            port: 8719
          #持久化配置
          datasource:
            ds1:
              nacos:
                server-addr: localhost:8848
                dataId: cloudalibaba-sentinel-service # 配置文件的DataID
                groupId: DEFAULT_GROUP                # 配置文件的Group
                data_type: json                       # 文件类型
                rule-type: flow                       # 规则类型
    
    
    management:
      endpoints:
        web:
          exposure:
            include: '*'
    
  3. Nacos中新建配置[学习笔记]Spring Cloud Alibaba详细教程_第21张图片

    [
        {
            "resource": "/testA",
        	"limitApp": "default",
        	"grade": 1,
        	"count": 1,
        	"strategy": 0,
        	"controlBehavior": 0,
        	"clusterMode": false
    	}
    ]
    
    • resource: 资源名
    • limitApp: 针对来源
    • grade: 阈值类型, 0表示线程数, 1表示QPS
    • count: 单机阈值
    • strategy: 流控模式, 0表示直接,1表示关联, 2表示链路
    • controlBehavior: 流控效果, 0表示快速失败,1表示WarmUp, 2表示排队等待
    • clusterMode: 是否是集群模式

Seata

分布式事务的解决方案

Seata官网

一个ID

Transaction ID(XID): 全局唯一的事务ID

三个组件

  • TC 事务协调者: 维护全局和分支事务的状态,驱动全局事务提交或回滚。
  • TM 事务管理器: 定义全局事务的范围:开始全局事务、提交或回滚全局事务。
  • RM 资源管理器: 管理分支事务处理的资源,与TC交谈以注册分支事务和报告分支事务的状态,并驱动分支事务提交或回滚。
    [学习笔记]Spring Cloud Alibaba详细教程_第22张图片
    Seata事务的处理过程
  1. TM向TC申请开启一个全局事务,全局事务创建成功并生成一个全局唯一的XID
  2. XID在为服务调用链路的上下文中传播
  3. RM向TC注册分支事务,将其纳入XID对应全局事务的管辖
  4. TM向TC发起针对XID的全局提交或回滚决议
  5. TC调度XID下管辖全部分支事务完成提交或回滚请求

Seata的下载与安装

在Seata官网就有下载入口

Seata的安装与配置

  1. 修改conf目录下的file.conf文件

    • 自定义事务组的名称
      [学习笔记]Spring Cloud Alibaba详细教程_第23张图片
    • 修改存储模式为db并配置数据库连接信息
      [学习笔记]Spring Cloud Alibaba详细教程_第24张图片
  2. 新建数据库seata

  3. 在seata库中建表,建表文件在conf/db_store.sql

  4. 修改registry.conf,指明注册中心并修改连接信息,这里使用nacos
    [学习笔记]Spring Cloud Alibaba详细教程_第25张图片

  5. 启动: 先启动Nacos,然后启动Seata: 运行bin中的seata-server.sh

Seata的使用

现在模拟一个下订单的业务逻辑,要同时进行添加订单,减少库存和修改账户信息

数据库

  1. 建库

    新建三个数据库: seata_order seata_storage seata_account, 分别存储订单、库存和账户信息

  2. 建表

    每个库中都有一张存储信息的表
    [学习笔记]Spring Cloud Alibaba详细教程_第26张图片
    [学习笔记]Spring Cloud Alibaba详细教程_第27张图片
    [学习笔记]Spring Cloud Alibaba详细教程_第28张图片

  3. 回滚日志表

    在conf目录中有一个db_undo_log.sql文件,这个文件可以生成回滚日志表,每个库中都要生成一个回滚日志表,也就是说每个库都运行一次这个文件.

订单模块

使用@GlobalTransactional注解启用Seata,参数name是唯一ID,参数rollbackFor表示出现什么异常时回滚。

@Service
@Slf4j
public class OrderServiceImpl implements OrderService {
    @Resource
    private OrderDao orderDao;
    @Resource
    private AccountService accountService;
    @Resource
    private StorageService storageService;

    @Override
    @GlobalTransactional(name = "create-order", rollbackFor = Exception.class)
    public void create(Order order) {
        log.info("===========>开始新建订单");
        orderDao.creat(order);

        log.info("=============>调用库存微服务,减少库存");
        storageService.decrease(order.getProductId(), order.getCount());

        log.info("===========>调用账户微服务,扣钱");
        accountService.decrease(order.getUserId(), order.getMoney());

        log.info("=============>修改订单状态");
        orderDao.update(order.getUserId(), 0);

        log.info("==============>订单完成");
    }
}

这里演示了执行订单服务的代码,这个server中同时调用了库存、账户和订单三个微服务中的数据库,当某个调用发生异常后,Seata能将所有相关数据库都回滚到原始状态。

Seata的整体机制

Seata共有 AT、TCC、SAGA 和 XA 四种事务模式,默认使用AT模式

  • 一阶段: 业务数据和回滚日志记录在同一个本地事务中提交,释放本地锁和连接资源
  • 二阶段:
    • 提交异步化, 非常快速完成
    • 回滚通过一阶段的回滚日志进行反向补偿

详细过程

一阶段,seata拦截"业务SQL"

  1. 解析SQL语句,找到要更新到业务数据,在该数据被更新前将其保存成"before image"
  2. 执行"业务SQL"更新业务数据,在业务数据更新之后将其保存成"after image",并生成行锁
  3. 向数据库提交业务SQL
    [学习笔记]Spring Cloud Alibaba详细教程_第29张图片
    在第一阶段中,Seata会将所有SQL操作进行快照记录

二阶段:

  • 如果SQL执行顺利,在一阶段提交完SQL之后,Seata框架会将一阶段保存的快照数据和行锁删掉
  • 如果需要回滚,Seata就会回滚一阶段已经执行的业务SQL,还原业务数据.Seata会根据"vefore image"还原业务数据,在这之前要进行校验脏写,通过对比数据库当前业务数据和"after image",如果二者不一致,说明该数据有脏写,这时候将不能进行自动还原,需要通过人工方式处理.
    [学习笔记]Spring Cloud Alibaba详细教程_第30张图片

你可能感兴趣的:([学习笔记]Spring Cloud Alibaba详细教程)