Spring Cloud

本文介绍了Nacos作为服务发现和配置管理平台的功能,包括启动配置、数据持久化以及Sentinel的流量控制、熔断降级策略。Sentinel提供了丰富的应用场景和规则,如QPS限流、线程数限流、降级策略等。此外,还探讨了Seata的分布式事务解决方案,包括AT模式的工作机制、系统构成以及客户端和服务端的配置。
摘要由CSDN通过智能技术生成

SpringCloud Alibaba

Nacos

简介

Naming Configuration Service

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

注册中心+配置中心的组合

本地安装启动

官网下载

启动命令

startup.cmd -m standalone
持久化

Nacos将config信息默认存储到内置的derby数据库,每次重启数据都会清空。因此可以将信息的数据持久化外部的Mysql数据库。

  • 创建DataBase

    Nacos安装目录\conf\nacos-mysql.sql

  • 修改Nacos配置文件

#mysql
spring.datasource.platform=mysql 
db.num=1
db.url.0=jdbc:mysql://localhost:3306/nacos_config?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true
db.user=root
db.password=root

Sentinal

简介

分布式系统的流量防卫兵

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

单独一个组件,可以独立出来

直接界面化的细粒度统一配置

特征
  • 丰富的应用场景

    双十一大促流量的核心场景,例如秒杀(即突发流量控制在系统容量可以承受的范围),消息削峰填谷、集群流量控制、实时熔断下游不可用应用等

  • 完备的实时监控

    可以在控制台中看到接入到应用的单台机器秒级数据

  • 广泛的开源生态

    开箱即用的与其他开源框架的整合模块

  • 完善的SPI扩展点

    提供简单易用、完善的SPI扩展接口。定制规则管理、适配动态数据源等

流控规则

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

针对来源:Sentinel可以针对调用者进行限流,填写微服务名,默认default(不区分来源)

阈值类型/单机阈值:

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

流控模式:

  • 直接(默认):api达到限流条件时,直接限流

  • 关联:当关联的资源达到阈值时,就限流自己(例如:支付资源达到阈值时,订单资源也限流)

  • 链路:只记录指定链路上的流量

流控效果:

  • 快速失败:直接抛出异常,出错
  • Warn up:预热,达到(阈值/冷却因子:3)时,刚开始报错,进过设定的预热时间后,最大处理能力达到阈值。适用于秒杀等场景,防止大流量一下子进入把系统打垮!
  • 排队等待:匀速排队,让请求以均匀的速度通过,阈值类型必须设置为QPS,否则无效。可以设置等待的超时时间。用于处理间隔性突发的流量,例如消息队列
降级规则(熔断降级)

降级策略

  • RT:平均响应时间,秒级

    平均响应时间 超出阈值 且 在时间窗口内通过的请求 >=5,两个条件同时满足后触发降级

    窗口期过后关闭断路器

    RT最大4900

  • 异常比例:秒级

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

  • 异常数:分钟级

    异常数(分钟统计)超过阈值时,触发降级;时间窗口结束后,关闭降级

Sentinel的断路器是没有半开状态的,Hystrix有,半开的状态系统自动检测是否有请求异常

热点Key限流 (*重要)

对经常被访问的热点数据,统计出访问频次最高的Top K数据,并对其访问进行限制!

根据具体的参数进行限流。

例如:

  • 商品ID作为参数,统计一段时间内最常被购买的商品ID并进行限流。
  • 用户ID作为参数,针对一段时间内频繁访问的用户ID进行限流。

LRU策略统计最近最常被访问的热点参数,结合令牌桶算法来进行参数级别的流控。热点参数限流支持集群模式。

参数例外项:

设定热点参数的值为某个特定值时,不进行限流

前提条件:热点参数的类型必须是基本数据类型或者是String

系统规则

Sentinel系统自适应限流从整体维度对应用入口流量进行控制,结合应用的Load、CPU使用率、总体水平RT、入口QPS和并发线程数等几个维度的监控指标,通过自适应的流控策略,让系统的入口流量和系统的负载达到一个均衡,让系统尽可能跑在最大吞吐量的同时,保证系统的稳定性。

支持模式:

  • Load自适应(仅对于Linux或者Unix系统有效)
  • Cpu Usage
  • 平均RT:单位为毫秒
  • 并发线程数
  • 入口QPS
@SentinelResource注释

用法

@SentinelResource(value=“资源名byresourse”, blockhandler=“兜底方法名”)

处理的是Sentinel控制台配置的违规情况,有blockHandler方法配置的兜底处理。

  1. 按资源名称限流及后续处理

    服务停止后,Sentinel控制台也消失

  2. 按URL限流

服务熔断

客户自定义限流处理逻辑

解决兜底方法的痛点问题(与Hystrix相同)

  • 与业务解耦
  • 代码膨胀
  • 全局统一的处理方法

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

@SentinelResource(value=“资源名byresourse”, blockhandlerclass=自定义类名,blockhandler=“兜底方法名”)

注解方式的埋点不支持private方法

blockHandler处理的是配置违规(Sentinel配置)

fallback处理的是运行异常(业务代码)

例外异常:exceptionsToIgnore

如果发生此异常,不进行fallback处理

基于Ribbon时使用@SentinelResource注解

基于OpenFeign时使用@FeignClient

@FeignClient(value="", fallback=“service类的实现类”)

application.xml中要打开Sentinel对feign的监控设置feign.sentinel.enable=true

持久化规则

业务痛点:

一旦重启业务应用,Sentinel规则将会消失,生产环境需要将配置规则进行持久化

解决方法:

将限流配置规则持久化进Nacos保存(保存到数据库,文件都可以)

pom引入sentinel-datasource-nacos依赖

application添加datasource相关属性配置

Seata(分布式事务)

分布式事务

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

AT 模式
前提
  • 基于支持本地 ACID 事务的关系型数据库
  • Java 应用,通过 JDBC 访问数据库。
整体机制

两阶段提交协议的演变:

  • 一阶段:业务数据和回滚日志记录在同一个本地事务中提交,释放本地锁和连接资源
  • 二阶段:
    • 提交异步化,非常快速的完成
    • 回滚通过第一阶段的回滚日志进行反向补偿。(回复时会检查备份镜像,防止覆盖别人的更新内容)
构成

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

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

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

UNDO LOG 记录:回滚日志

XID :全局唯一事务ID

Branch ID :各服务分支ID

本地环境配置
服务端

配置

Seata1.4.0

Nacos1.4.1

MySQL 8.0.13

1.官网下载1.4.0版

http://seata.io/zh-cn/blog/download.html

解压即可

2.配置文件更改

file.conf中的保存mode改为db (注:这个地方如果配置在nocas的话,file.conf可以不用修改)

## transaction log store, only used in seata-server
store {
  ## store mode: file、db、redis
  mode = "db"
### 并对应修改DB连接信息

注册中心和配置中心都选择Nacos

type修改为Nacos,并配置Nacos连接信息

registry.conf

registry {
  # file 、nacos 、eureka、redis、zk、consul、etcd3、sofa
  type = "nacos"
  loadBalance = "RandomLoadBalance"
  loadBalanceVirtualNodes = 10

  nacos {
    application = "seata-server"
    serverAddr = "127.0.0.1:8848"
    group = "SEATA_GROUP"
    namespace = ""
    cluster = "default"
    username = "nacos"
    password = "nacos"
  }
}

config {
  # file、nacos 、apollo、zk、consul、etcd3
  type = "nacos"

  nacos {
    serverAddr = "127.0.0.1:8848"
    namespace = ""
    group = "SEATA_GROUP"
    username = "nacos"
    password = "nacos"
  }
}

如果选择使用文件时,需要配置…/conf/下的file.conf文件内容

具体配置内容可参照下面的config.txt

https://github.com/seata/seata/tree/develop/script/config-center

config.txt

3.将配置信息注册到nacos的配置中心

修改config.txt内容

# 保存模式为数据库
store.mode=db
# 数据库连接信息
store.db.driverClassName=com.mysql.jdbc.Driver
store.db.url=jdbc:mysql://127.0.0.1:3306/seata?useUnicode=true&serverTimezone=UTC
store.db.user=root
store.db.password=123456
# seata的事务分组信息
# 这个地方一定注意,这里的【my_test_tx_group】要和seata客户端的各个服务的spring.cloud.alibaba.seata.tx-service-group的值一致
# 此处的【default】与registry.conf中cluster必须保持一致
service.vgroupMapping.my_test_tx_group=default

注册到nacos的配置中心

sh脚本地址

https://github.com/seata/seata/tree/develop/script/config-center/nacos

使用.gitbash工具执行

sh nacos-config.sh -h 127.0.0.1 -p 8848 -g SEATA_GROUP -t seata -u nacos -w nacos

命令解析:-h -p 指定nacos的端口地址;-g 指定配置的分组,注意,是配置的分组;-t 指定命名空间id; -u -w指定nacos的用户名和密码,同样,这里开启了nacos注册和配置认证的才需要指定。

导入成功后效果

在这里插入图片描述
在这里插入图片描述

4.数据库操作

创建seata服务端用的seata数据库和数据表

目前最新版本(1.4.0)的目录下,是没有创建seata数据库的相关SQL脚本的,需要从官方的github下载

https://github.com/seata/seata/tree/develop/script/server/db

[mysql.sql](

在这里插入图片描述

由于本地用的Mysql8.0.13,启动seata时报只读权限的错误

java.sql.SQLException: Could not retrieve transation read-only status server

需要更改seata的数据库驱动jar。默认为mysql-connector-java-5.x.x.jar

将mysql-connector-java-8.0.19.jar从【seata\lib\jdbc】拷贝到【seata\lib】目录下

接着会报时区错误

java.sql.SQLException: The server time zone value '锟斤拷锟斤拷锟斤拷准时锟斤拷' is unrecognized or represents more than one time zone. You must configure either the server or JDBC driver (via the 'serverTimezone' configuration property) to use a more specifc time zone value if you want to utilize time zone support.

解决方法是在file.conf的文件中添加时区的参数serverTimezone=UTC

jdbc:mysql://127.0.0.1:3306/seata?useUnicode=true&characterEncoding=utf-8&serverTimezone=UTC

5.Seata服务启动

直接执行bin目录下的seata-server.bat

在这里插入图片描述

在这里插入图片描述

客户端

客户端请求创建订单,并调用库存服务,减去库存数量。此处的服务调用采用openFeign

SpringBoot: 2.2.2.RELEASE

SpringCloud:Hoxton.SR1

SpringCloud Alibaba:2.1.0.RELEASE

seata-all:1.4.0

1.Maven配置pom.xml

 <!--seata-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-seata</artifactId>
            <exclusions>
                <exclusion>
                    <artifactId>seata-all</artifactId>
                    <groupId>io.seata</groupId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>io.seata</groupId>
            <artifactId>seata-all</artifactId>
            <version>1.4.0</version>
        </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-openfeign</artifactId>
        </dependency>
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>1.1.10</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>

2.Yaml文件配置

在这里插入图片描述

将registy.conf拷贝到工程的resources目录下(如果再yaml中配置的话,这个文件的拷贝应该可以省略)

文件内容和服务端seata/conf下的文件内容相同

在这里插入图片描述

3.配置seata数据源DataSourceProxy(各个服务工程都需要配置,可以提出为共用模块)

seata的处理机制主要是用这个DataSourceProxy如果不配置,全局事务无法生效

/**
 * Description:配置seata数据源
 * Date: 2021-04-04
 */
@Configuration
public class DataSourceConfiguration {

    @Value("${mybatis.mapper-locations}")
    private String mapperLocations;

    @Bean
    @ConfigurationProperties(prefix = "spring.datasource")
    public DataSource druidDataSource() {
        return new DruidDataSource();
    }

    @Bean
    public DataSourceProxy dataSourceProxy(DataSource dataSource) {
        return new DataSourceProxy(dataSource);
    }

    @Bean
    public SqlSessionFactory sqlSessionFactoryBean(DataSourceProxy dataSourceProxy) throws Exception {
        SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
        sqlSessionFactoryBean.setDataSource(dataSourceProxy);
        sqlSessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources(mapperLocations));
        sqlSessionFactoryBean.setTransactionFactory(new SpringManagedTransactionFactory());

        return sqlSessionFactoryBean.getObject();
    }
}

4.编写业务代码

Conroller,Service,Dao,Mapper.xml等业务代码(此处省略,具体可看git代码)

参照下面两个Module

cloudalibaba-consumer-order83
cloud-alibaba-seata-9004

5.创建业务数据库

在每个业务数据库中,创建undo_log数据表用于数据回滚

CREATE TABLE `undo_log` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `branch_id` bigint(20) NOT NULL,
  `xid` varchar(100) NOT NULL,
  `context` varchar(128) NOT NULL,
  `rollback_info` longblob NOT NULL,
  `log_status` int(11) NOT NULL,
  `log_created` datetime NOT NULL,
  `log_modified` datetime NOT NULL,
  `ext` varchar(100) DEFAULT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `ux_undo_log` (`xid`,`branch_id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8

在这里插入图片描述

6.启动个服务,进行测试

测试URL

http://localhost:83/business/buy

正常参数:

{
	"userId" : "user6",
	"commodityCode" : "cd123",
	"name" : "name6",
	"count" : 100,
	"amount" : 100
}

异常参数:

{
	"userId" : "user6",
	"commodityCode" : "cd123",
	"name" : "mainex",
	"count" : 100,
	"amount" : 100
}
执行流程
  1. TM开启分布式事务(TM向TC注册全局事务记录)。加注解GlobalTransactional即开启
  2. 换业务场景,编排数据库,服务等事务内资源(RM向TC汇报资源准备状态)
  3. TM结束分布式事务,事务一阶段结束(TM通知TC提交/回滚分布式事务)
  4. TC汇总事务信息,决定分布式事务是提交还是回滚
  5. TC通知所有RM提交/回滚资源,事务二阶段结束。
原理浅析
一阶段加载

在一阶段,seata会拦截"业务sql":

  • 解析sql语义,找到业务sql要更新的业务数据,在业务数据被更新前,将其保存成"before image"
  • 执行业务sql,更新业务数据
  • 在业务sql数据更新之后,将其保存成"after image",最后生成行锁

以上操作全部在一个数据库事务(这个事务是本地事务会被RM注册为分支事务)内完成,这样保证了一阶段的原子性

在这里插入图片描述

二阶段提交(异步)

根据第一阶段的情况决定是进行全局提交还是全局回滚操作。对服务端来说,等到一阶段完成未抛异常,全局事务的发起方TM会向服务端申请提交这个全局事务。

如果一切顺利,各个RM执行分支事务都正常,又因为各个RM的业务sql已经在一阶段提交至数据库了,所以seata框架只需要将一阶段保存的快照数据和行锁删掉,完成数据清理即可。

在这里插入图片描述

二阶段回滚

如果一阶段执行某个业务sql出现了异常,二阶段就要进行全局回滚,回滚一阶段已经执行的业务sql,还原业务数据。
回滚方式便是通过"before image“还原业务数据,但在还原前要首先校验脏写,对比数据库当前业务数据和”after image",如果两份数据完全一致就说明没有脏写,可以还原业务数据,如果不一致就说明有脏写,出现脏写就需要转人工处理。

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值