SpringCloud Alibaba

SpringCloud Alibaba

0.入门

  • 服务限流降级:默认支持 Servlet、Feign、RestTemplate、Dubbo 和 RocketMQ 限流降级功能的接入,可以在运行时通过控制台实时修改限流降级规则,还支持查看限流降级 Metrics 监控。
    服务注册与发现:适配 Spring Cloud 服务注册与发现标准,默认集成了 Ribbon 的支持。
  • 分布式配置管理:支持分布式系统中的外部化配置,配置更改时自动刷新。
  • 消息驱动能力:基于 Spring Cloud Stream 为微服务应用构建消息驱动能力。
  • 阿里云对象存储:阿里云提供的海量、安全、低成本、高可靠的云存储服务。支持在任何应用、任何时间、任何地点存储和访问任意类型的数据。
  • 分布式任务调度:提供秒级、精准、高可靠、高可用的定时(基于 Cron 表达式)任务调度服务。同时提供分布式的任务执行模型,如网格任务。网格任务支持海量子任务均匀分配到所有 Worker(schedulerx-client)上执行。

Spring Cloud Alibaba 致力于提供微服务开发的一站式解决方案。此项目包含开发分布式应用微服务的必需组件,方便开发者通过 Spring Cloud 编程模型轻松使用这些组件来开发分布式应用服务。 依托 Spring Cloud Alibaba,您只需要添加一些注解和少量配置,就可以将 Spring Cloud 应用接入阿里微服务解决方案,通过阿里中间件来迅速搭建分布式应用系统。

1.Nacos服务注册和配置中心

1.1 简介

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

Nacos: Dynamic Naming and Configuration Service

Nacos = Eureka+Config +Bus

下载解压 ------ 直接运行bin目录下的startup.cmd ----访问http://localhost:8848/nacos

默认账号密码都是nacos

1.2 服务注册中心

服务提供者

父工程POM

<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-alibaba-dependencies</artifactId>
    <version>2.1.0.RELEASE</version>
    <type>pom</type>
    <scope>import</scope>
</dependency>

子POM

<!--SpringCloud ailibaba nacos -->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>

YML

server:
  port: 9001

spring:
  application:
    name: nacos-payment-provider
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848 #配置Nacos地址

management:
  endpoints:
    web:
      exposure:
        include: '*'

主启动 @EnableDiscoveryClient 业务类

服务消费者

POM nacos支持负载均衡 ------ Ribbon

YML

server:
  port: 83

spring:
  application:
    name: nacos-order-consumer
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848

#消费者将要去访问的微服务名称(注册成功进nacos的微服务提供者)
service-url:
  nacos-user-service: http://nacos-payment-provider

主启动 @EnableDiscoveryClient ----- 业务类RestTemplate ----- 配置类

@Configuration
public class ApplicationContextConfig {
    @Bean
    @LoadBalanced
    public RestTemplate getRestTemplate() {
        return new RestTemplate();
    }
}

服务注册中心对比

Nacos 支持AP和CP模式的切换

何时选择使用何种模式?

  • 如果不需要存储服务级别的信息且服务实例是通过nacos-client注册,并能够保持心跳上报,那么就可以选择AP模式。
    • 当前主流的服务如 Spring cloud 和 Dubbo 服务,都适用于AP模式,AP模式为了服务的可能性而减弱了一致性,因此AP模式下只支持注册临时实例。
  • 如果需要在服务级别编辑或者存储配置信息,那么 CP 是必须,K8S服务和DNS服务则适用于CP模式。
    • CP模式下则支持注册持久化实例,此时则是以 Raft 协议为集群运行模式,该模式下注册实例之前必须先注册服务,如果服务不存在,则会返回错误。

1.3 服务配置中心

1.3.1 基础配置

POM

<!--nacos-config-->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
<!--nacos-discovery-->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>

bootstrap优先级高于application

bootstrap

# nacos配置
server:
  port: 3377

spring:
  application:
    name: nacos-config-client
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848 #Nacos服务注册中心地址
      config:
        server-addr: localhost:8848 #Nacos作为配置中心地址
        file-extension: yaml #指定yaml格式的配置
        group: DEV_GROUP
        namespace: 7d8f0f5a-6a53-4785-9686-dd460158e5d4

application

spring:
  profiles:
    active: dev # 表示开发环境

业务 ------- @RefreshScope //支持Nacos的动态刷新功能。

Nacos添加配置

设置DataId

${spring.application.name}-${spring.profiles.active}.${spring.cloud.nacos.config.file-extension}

  • prefix 默认为 spring.application.name 的值
  • spring.profile.active 即为当前环境对应的 profile,可以通过配置项 spring.profile.active 来配置
  • file-exetension 为配置内容的数据格式,可以通过配置项 spring.cloud.nacos.config.file-extension 来配置

Nacos会记录配置文件的历史版本默认保留30天,此外还有一键回滚功能,回滚操作将会触发配置更新

1.3.2 分类配置

默认情况:
Namespace=public,Group=DEFAULT_GROUP,默认Cluster是DEFAULT

  • Namespace主要用来实现隔离
  • Group可以把不同的微服务划分到同一个分组里面去
  • Service就是微服务;一个Service可以包含多个Cluster(集群),Nacos默认Cluster是DEFAULT,Cluster是对指定微服务的一个虚拟划分
  • Instance,就是微服务的实例

三种方案加载配置:

DataID方案

  • 默认空间+默认分组+DataID

  • 通过spring.profile.active属性就能进行多环境下配置文件的读取

  • spring:
      profiles:
        active: dev #DataID
    

Group方案

  • 通过Group实现环境区分

  • spring:
      cloud:
        nacos:
          config:
            group:DEV_GROUP
    
  • spring:
      profiles:
        active: info #DataID
    

Namespace方案

  • 新建 ----- 根据命名空间ID配置

  • spring:
      cloud:
        nacos:
          config:
            namespace: 7d8f0f5a-6a53-4785-9686-dd460158e5d4
    
  • spring:
      profiles:
        active: info #DataID
    

1.4 集群与持久化配置!

默认Nacos使用嵌入式数据库derby实现数据的存储。所以,如果启动多个默认配置下的Nacos节点,数据存储是存在一致性问题的。
为了解决这个问题,Nacos采用了集中式存储的方式来支持集群化部署,目前只支持MySQL的存储。

derby到mysql切换配置:

  • nacos-server-1.1.4\nacos\conf目录下找到sql脚本 ------ 建表
  • nacos-server-1.1.4\nacos\conf目录下找到application.properties
spring.datasource.platform=mysql
db.num=1
db.url.0=jdbc:mysql://127.0.0.1:3306/nacos_config?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true
db.user=root
db.password=123456

-------------------------Linux Nacos+MySQL生产环境配置-------------------------

Linux服务器上mysql数据库配置

  • nacos-mysql.sql

application.properties 配置

  • 同上

Linux服务器上nacos的集群配置cluster.conf

  • 不同服务端口号
  • 必须是 Linux命令hostname -i能够识别的IP

编辑Nacos的启动脚本startup.sh,使它能够接受不同的启动端口

  • /mynacos/nacos/bin 目录下有startup.sh
  • 传递不同的端口号启动不同的nacos实例-----命令:./startup.sh -p 3333

Nginx的配置,由它作为负载均衡器 nginx.conf

 upstream cluster{        
 	server 127.0.0.1:3333;        
 	server 127.0.0.1:4444;        
 	server 127.0.0.1:5555;    
 }
 server {        
 	listen 1111;        
 	server_name localhost;        
 	#charset koi8-r;        
 	#access_log  logs/host.access.log  main;        
 location / {            
 	#root   html;            
 	#index  index.html index.htm;            
 	proxy_pass http://cluster;        
 }

截止到此处,1个Nginx+3个nacos注册中心+1个mysql

2.Sentinel实现熔断与限流

2.1 Sentinel

分布式系统的流量防卫兵

Sentinel 以流量为切入点,从流量控制、熔断降级、系统负载保护等多个维度保护服务的稳定性。

Sentinel 分为两个部分:

  • 核心库(Java 客户端)不依赖任何框架/库,能够运行于所有 Java 运行时环境,同时对 Dubbo / Spring Cloud 等框架也有较好的支持。
  • 控制台(Dashboard)基于 Spring Boot 开发,打包后可以直接运行,不需要额外的 Tomcat 等应用容器。

2.2 安装使用

后台 :直接运行 java -jar sentinel-dashboard-1.7.0.jar

前台 :http://localhost:8080 登录账号密码均为sentinel

2.3 演示工程

nacos + sentinel

建模块 -------- POM

<!--SpringCloud ailibaba nacos -->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!--SpringCloud ailibaba sentinel-datasource-nacos 后续做持久化用到-->
<dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-datasource-nacos</artifactId>
</dependency>
<!--SpringCloud ailibaba sentinel -->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
<!--openfeign-->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>

YML

server:
  port: 8401
spring:
  application:
    name: cloudalibaba-sentinel-service
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848 #Nacos服务注册中心地址
    sentinel:
      transport:
        dashboard: localhost:8080 #配置Sentinel dashboard地址
        port: 8719
      datasource:
        ds1:
          nacos:
            server-addr: localhost:8848
            dataId: cloudalibaba-sentinel-service
            groupId: DEFAULT_GROUP
            data-type: json
            rule-type: flow
management:
  endpoints:
    web:
      exposure:
        include: '*'
feign:
  sentinel:
    enabled: true # 激活Sentinel对Feign的支持

主启动 @EnableDiscoveryClient

Sentinel采用的懒加载

2.4 流控规则

资源名:唯一名称,默认请求路径

针对来源:sentinel可以针对调用者进行限流,填写微服务名,默认default

阈值类型/单机阈值:

  • QPS:每秒的请求数量 当调用该api的QPS达到阈值时进行限流
  • 线程数:当调用该api的线程数达到阈值时进行限流

不需要集群

流控模式:

  • 直接:api达到条件直接限流
  • 关联:关联的资源达到阈值时限流
  • 链路:只记录指定链路上的流量(指定资源从入口进来的流量,达到阈值就限流)

流控效果:

  • 快速失败:直接失败抛异常
  • Warm Up:根据codeFactor冷加载因子(默认3)
  • 排队:阈值必须设置为QPS

2.4.1 流控模式

直接----------系统默认

  • QPS直接失败 ----- 请求拦截在外
  • 线程数直接失败 ----- 进入运行的线程数
  • 表示1秒钟内查询1次就是OK,若超过次数1,就直接-快速失败,报默认错误
  • Blocked by Sentinel (flow limiting) ------ 自定义处理

关联

  • 当关联的资源达到阈值时,就限流自己
  • Blocked by Sentinel (flow limiting)

链路

  • 多个请求调用了同一个微服务

2.4.2 流控效果

直接->快速失败(默认的流控处理)

预热

  • 默认coldFactor为3
  • 请求 QPS 从 threshold / 3 开始,经预热时长逐渐升至设定的 QPS 阈值

排队等待

  • 匀速排队,阈值必须设置为QPS

2.5 降级规则

Sentinel 熔断降级会在调用链路中某个资源出现不稳定状态时(例如调用超时或异常比例升高),限制对这个资源的调用,让请求快速失败,避免影响其它的资源而导致级联错误。

当资源被降级后,在接下来的降级时间窗口之内,对该资源的调用都自动熔断(默认行为是抛出 DegradeException)。

Sentinel的断路器是没有半开状态的

半开的状态系统自动去检测是否请求有异常:没有异常就关闭断路器恢复使用,有异常则继续打开断路器不可用。具体可以参考Hystrix

2.5.1 RT(秒级)

  • 平均响应时间
  • 超出阈值且在时间窗口内通过的请求>=5,两个条件同时满足后触发降级
  • 窗口期过后关闭断路器
  • RT最大4900(更大的需要通过-Dcsp.sentinel.statistic.max.rt=XXXX才能生效)

2.5.2 异常比列(秒级)

  • QPS >= 5 且异常比例(秒级统计)超过阈值时,触发降级
  • 时间窗口结束后,关闭降级

2.5.3 异常数(分钟级)

  • 异常数(分钟统计)超过阈值时,触发降级
  • 时间窗口结束后,关闭降级
  • 时间窗口一定要大于等于60秒

2.6 热点key限流

热点即经常访问的数据,很多时候我们希望统计或者限制某个热点数据中访问频次最高的TopN数据,并对其访问进行限流或者其它操作。

@SentinelResource(value = "testHotKey", blockHandler = "deal_testHotKey")
  • 限流模式只支持QPS模式
  • @SentinelResource注解的方法参数索引从0开始
  • 方法testHotKey里面第一个参数只要QPS超过每秒1次,马上降级处理

参数例外项

  • 我们期望p1参数当它是某个特殊值时,它的限流值和平时不一样
  • 热点参数的注意点,参数必须是基本类型或者String

2.7 系统规则

系统保护规则是从应用级别的入口流量进行控制,从单台机器的 load、CPU 使用率、平均 RT、入口 QPS 和并发线程数等几个维度监控应用指标,让系统尽可能跑在最大吞吐量的同时保证系统整体的稳定性。

系统保护规则是应用整体维度的,而不是资源维度的,并且仅对入口流量生效。入口流量指的是进入应用的流量(EntryType.IN),比如 Web 服务或 Dubbo 服务端接收的请求,都属于入口流量。

系统规则支持以下的模式:

  • 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 达到阈值即触发系统保护。

2.8 @SentinelResource

按资源名限流

  • 配置流控规则 ---- 自定义限流-----流控规则非持久

按URL地址限流

通过访问的URL来限流,会返回Sentinel自带默认的限流处理信息

  1. 系统默认的,没有体现我们自己的业务要求。
  2. 依照现有条件,我们自定义的处理方法又和业务代码耦合在一块,不直观。
  3. 每个业务方法都添加一个兜底的,那代码膨胀加剧。
  4. 全局统一的处理方法没有体现。

客户自定义限流处理逻辑

  • 创建CustomerBlockHandler类用于自定义限流处理逻辑

所有的代码都要用try-catch-finally方式进行处理

Sentinel主要有三个核心Api:

  • SphU定义资源
  • Tracer定义统计
  • ContextUtil定义了上下文

2.9 服务熔断

sentinel整合ribbon+openFeign+fallback

Ribbon

  • 提供者 + 消费者
  • blockHandler 断路器打开,断电跳闸,系统被保护
  • fallback
  • 业务类:
    • @SentinelResource(value = "fallback") 没有配置
    • @SentinelResource(value = "fallback",fallback = "handlerFallback") fallback只负责业务异常
    • @SentinelResource(value = "fallback",blockHandler = "blockHandler") blockHandler 只负责sentinel 控制台配置违规
    • @SentinelResource(value = "fallback", fallback = "handlerFallback", blockHandler = "blockHandler") 若 blockHandler 和 fallback 都进行了配置,则被限流降级而抛出 BlockException 时只会进入 blockHandler 处理逻辑。
    • 忽略属性exceptionsToIgnore = {IllegalArgumentException.class} 程序异常打到前台

Feign

  • 消费者调用提供者

  • # 激活Sentinel对Feign的支持
    feign:
      sentinel:
        enabled: true
    
  • 业务:

    • 带@FeignClient注解的业务接口
    • fallback = PaymentFallbackService.class
    • 添加@EnableFeignClients启动Feign的功能

2.10 规则持久化

一旦我们重启应用,sentinel规则将消失,生产环境需要将配置规则进行持久化

将限流配置规则持久化进Nacos保存,只要刷新8401某个rest地址,sentinel控制台的流控规则就能看到,只要Nacos里面的配置不删除,针对8401上sentinel上的流控规则持续有效

POM

<!--SpringCloud ailibaba sentinel-datasource-nacos 后续做持久化用到-->
<dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-datasource-nacos</artifactId>
</dependency>

添加Nacos数据源配置

spring:
  cloud:
    sentinel:
      datasource:
        ds1:
          nacos:
            server-addr: localhost:8848
            dataId: ${spring.application.name}
            groupId: DEFAULT_GROUP
            data-type: json
            rule-type: flow

添加Nacos业务规则配置

  • resource:资源名称;
  • limitApp:来源应用;
  • grade:阈值类型,0表示线程数,1表示QPS;
  • count:单机阈值;
  • strategy:流控模式,0表示直接,1表示关联,2表示链路;
  • controlBehavior:流控效果,0表示快速失败,1表示Warm Up,2表示排队等待;
  • clusterMode:是否集群

3.Seata处理分布式事务

单体应用被拆分成微服务应用,原来的三个模块被拆分成三个独立的应用,分别使用三个独立的数据源,业务操作需要调用三个服务来完成。此时每个服务内部的数据一致性由本地事务来保证,但是全局的数据一致性问题没法保证。

一次业务操作需要跨多个数据源或需要跨多个系统进行远程调用,就会产生分布式事务问题

3.1 Seata

Seata是一款开源的分布式事务解决方案,致力于在微服务架构下提供高性能和简单易用的分布式事务服务。

典型分布式事务

  • Transaction ID XID 全局唯一的事务ID
  • 3组件:
    • Transaction Coordinator (TC) 事务协调器,维护全局事务的运行状态,负责协调并驱动全局事务的提交或回滚;
    • Transaction Manager (TM) 控制全局事务的边界,负责开启一个全局事务,并最终发起全局提交或全局回滚的决议;
    • Resource Manager (RM) 控制分支事务,负责分支注册、状态汇报,并接收事务协调器的指令,驱动分支(本地)事务的提交和回滚

处理过程

  • TM 向 TC 申请开启一个全局事务,全局事务创建成功并生成一个全局唯一的 XID;
  • XID 在微服务调用链路的上下文中传播;
  • RM 向 TC 注册分支事务,将其纳入 XID 对应全局事务的管辖;
  • TM 向 TC 发起针对 XID 的全局提交或回滚决议;
  • TC 调度 XID 下管辖的全部分支事务完成提交或回滚请求。

使用

  • 本地@Transactional
  • 全局@GlobalTransactional

3.2 Seata-Server安装

下载解压

修改conf目录下的file.conf配置文件

  • 先备份原始file.conf文件
  • 主要修改:自定义事务组名称+事务日志存储模式为db+数据库连接信息

新建数据库seata -----db_store.sql

修改conf目录下的registry.conf配置文件:

  • 指明注册中心为nacos,及修改nacos连接信息

启动seata-server ------ softs\seata-server-0.9.0\seata\bin\seata-server.bat

3.3 订单、库存、账户案例

分布式事务业务说明:一个订单服务,一个库存服务,一个账户服务

  • 当用户下单时,会在订单服务中创建一个订单,
  • 然后通过远程调用库存服务来扣减下单商品的库存,
  • 再通过远程调用账户服务来扣减用户账户里面的余额,
  • 最后在订单服务中修改订单状态为已完成。

该操作跨越三个数据库,有两次远程调用,很明显会有分布式事务问题。

3.3.1 数据库

创建业务数据库:

  • seata_order:存储订单的数据库
  • seata_storage:存储库存的数据库
  • seata_account:存储账户信息的数据库

按照上述3库分别建对应的回滚日志表

3.3.2 微服务业务

下订单->减库存->扣余额->改(订单)状态

Order模块

  • seata-order-service2001 --- POM --- yml --- file.conf、registry.conf
  • domain --- Dao接口及实现 mapper ---Service接口及实现 --- Controller
  • Config配置 --- MyBatisConfig --- DataSourceProxyConfig使用Seata对数据源进行代理
  • 主启动 --- @SpringBootApplication(exclude = DataSourceAutoConfiguration.class) 取消数据源的自动创建

Storage模块

Account模块

3.3.3 测试

超时异常,没加@GlobalTransactional

  • 当库存和账户金额扣减后,订单状态并没有设置为已经完成,没有从零改为1
  • 而且由于feign的重试机制,账户余额还有可能被多次扣减

超时异常,添加@GlobalTransactional

@GlobalTransactional(name = "fsp-create-order",rollbackFor = Exception.class)

下单后数据库数据并没有任何改变

3.4 原理

分布式事务的执行流程

  • TM 开启分布式事务(TM 向 TC 注册全局事务记录);
  • 按业务场景,编排数据库、服务等事务内资源(RM 向 TC 汇报资源准备状态 )
  • TM 结束分布式事务,事务一阶段结束(TM 通知 TC 提交/回滚分布式事务)
  • TC 汇总事务信息,决定分布式事务是提交还是回滚
  • TC 通知所有 RM 提交/回滚 资源,事务二阶段结束

AT模式如何做到对业务的无侵入

  • 一阶段,Seata 会拦截“业务 SQL”
    • 解析 SQL 语义,找到“业务 SQL”要更新的业务数据,在业务数据被更新前,将其保存成“before image”
    • 执行“业务 SQL”更新业务数据,在业务数据更新之后,其保存成“after image”,最后生成行锁。
    • 以上操作全部在一个数据库事务内完成,这样保证了一阶段操作的原子性。
  • 二阶段如是顺利提交的话,
    • 因为“业务 SQL”在一阶段已经提交至数据库,所以Seata框架只需将一阶段保存的快照数据和行锁删掉,完成数据清理即可。
  • 二阶段回滚:
    • 二阶段如果是回滚的话,Seata 就需要回滚一阶段已经执行的“业务 SQL”,还原业务数据。
    • 回滚方式便是用“before image”还原业务数据;但在还原前要首先要校验脏写,对比“数据库当前业务数据”和 “after image”,
    • 如果两份数据完全一致就说明没有脏写,可以还原业务数据,如果不一致就说明有脏写,出现脏写就需要转人工处理。
posted @ 2021-07-08 22:15  FremontUltimate  阅读(136)  评论(0)    收藏  举报