spring cloud alibaba使用

文章目录

  • 架构图
  • 环境搭建
  • Nacos
    • 下载以及配置
    • 测试使用
    • 界面一些功能
    • 可配置项
    • nacos自带的ribbon负载均衡
  • OpenFegin
    • 日志配置
    • 设置超时时间
    • 自定义拦截器
  • Nacos-config
    • 根据nacos上的配置文件获取值
    • 切换环境
    • 配置文件的优先级
    • 命名空间的使用
    • 使用自定义的DataId配置
    • 给实体类赋值
  • sentinel
    • 所有降级方法都要加上参数BlockException ex
    • 初体验
    • @SentinelResource注解的使用
    • 注解实现服务熔断降级
    • 控制台部署
      • 非springboot springcloud环境
      • 整合spring-cloud-alibab
    • 控制台使用
      • 统一异常处理
      • 实时监控
      • 簇点链路
      • 流控规则
        • 关联
        • 链路
        • 流控效果
      • 熔断规则
      • 热点参数流控
      • 系统保护规则
      • 授权规则
      • 集群流控
    • openFeign整合sentinel
      • 控制台规则持久化
  • Seata分布式事务
    • 使用部署
  • geteWay
    • 初体验
    • 断言工厂
      • 内置断言工厂
      • 自定义断言工厂
    • 内置过滤器
      • 自定义过滤器
      • 全局过滤器

架构图

spring cloud alibaba使用_第1张图片

环境搭建

springboot和spring cloud alibaba版本有对应关系,具体:版本对应
这里使用
springboot:2.3.12.RELEASE
spring-cloud-alibaba:2.2.10-RC1
spring-cloud:Hoxton.SR12

父工程pom,子类模块继承即可


<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0modelVersion>
    <groupId>com.chenkegroupId>
    <artifactId>spring-cloud-alibaba-tulingartifactId>
    <version>0.0.1-SNAPSHOTversion>
    <name>spring-cloud-alibaba-tulingname>
    <description>spring-cloud-alibaba-tulingdescription>

    
    <packaging>pompackaging>
    <properties>
        <java.version>1.8java.version>
        <spring.cloud.alibaba.version>2.2.10-RC1spring.cloud.alibaba.version>
        <spring.cloud.version>Hoxton.SR12spring.cloud.version>
        <spring.boot.version>2.3.12.RELEASEspring.boot.version>
    properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starterartifactId>
        dependency>

        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-testartifactId>
            <scope>testscope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintagegroupId>
                    <artifactId>junit-vintage-engineartifactId>
                exclusion>
            exclusions>
        dependency>
    dependencies>
    
    <dependencyManagement>
         <dependencies>
             
             <dependency>
                 <groupId>com.alibaba.cloudgroupId>
                 <artifactId>spring-cloud-alibaba-dependenciesartifactId>
                 <version>${spring.cloud.alibaba.version}version>
                 <type>pomtype>
                 <scope>importscope>
             dependency>
             
             <dependency>
                 <groupId>org.springframework.bootgroupId>
                 <artifactId>spring-boot-starter-parentartifactId>
                 <version>${spring.boot.version}version>
                 <type>pomtype>
                 <scope>importscope>
             dependency>
             
             <dependency>
                 <groupId>org.springframework.cloudgroupId>
                 <artifactId>spring-cloud-dependenciesartifactId>
                 <version>${spring.cloud.version}version>
                 <type>pomtype>
                 <scope>importscope>
             dependency>
         dependencies>

    dependencyManagement>


    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.pluginsgroupId>
                <artifactId>maven-compiler-pluginartifactId>
                <version>3.8.1version>
                <configuration>
                    <source>1.8source>
                    <target>1.8target>
                    <encoding>UTF-8encoding>
                configuration>
            plugin>
            <plugin>
                <groupId>org.springframework.bootgroupId>
                <artifactId>spring-boot-maven-pluginartifactId>
            plugin>
        plugins>
    build>

project>

  • 打包方式修改为pom
  • 去掉paraent标签采用dependencyManagement标签管理。dependencyManagement管理的子类必须手动声明引用
  • properties 标签管理版本

Nacos

核心功能

  • 服务注册:Nacos Client会通过发送REST请求的方式向Nacos Server注册自己的服务,提供自身的元数据,比如ip地
    址、端口等信息。Nacos Server接收到注册请求后,就会把这些元数据信息存储在一个双层的内存Map中。
  • 服务心跳:在服务注册后,Nacos Client会维护一个定时心跳来持续通知Nacos Server,说明服务一直处于可用状态,防
    止被剔除。默认5s发送一次心跳。
  • 服务同步:Nacos Server集群之间会互相同步服务实例,用来保证服务信息的一致性。 leader raft
  • 服务发现:服务消费者(Nacos Client)在调用服务提供者的服务时,会发送一个REST请求给Nacos Server,获取上面
    注册的服务清单,并且缓存在Nacos Client本地,同时会在Nacos Client本地开启一个定时任务定时拉取服务端最新的注
    册表信息更新到本地缓存
  • 服务健康检查:Nacos Server会开启一个定时任务用来检查注册服务实例的健康情况,对于超过15s没有收到客户端心跳
    的实例会将它的healthy属性置为false(客户端服务发现时不会发现),如果某个实例超过30秒没有收到心跳,直接剔除该
    实例(被剔除的实例如果恢复发送心跳则会重新注册

下载以及配置

注册中心+配置中心+服务管理

nacos下载:nacos下载,这里用2.2.0 win版本

下载后解压,修改nacos\bin目录下startup.cmd中 set MODE=“standalone”
(默认为cluster集群,这里改为单机)
nacos\conf\application.properties 可以修改nacos的启动端口等信息,也可以设置数据源和mysql整合

双击startup.cmd启动,访问http://localhost:8848/nacos/#/login,账号密码都为nacos
spring cloud alibaba使用_第2张图片

测试使用

新建两个子模块,引入依赖order-nacos 和 stock-nacos,利用nacos实现order调用stock服务的接口
spring cloud alibaba使用_第3张图片
order-nacos 和 stock-nacos的pom

  <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-webartifactId>
        dependency>
		
        <dependency>
            <groupId>com.alibaba.cloudgroupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discoveryartifactId>
        dependency>

order-nacos 主要代码
要使用restTemplate来进行请求
必须加上@LoadBalanced,nacos自身并不具备服务名转地址的功能,需要这个注解。自带轮询的负载均衡算法

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

配置类

server:
  port: 8010
spring:
  application:
    name: order-service
  cloud:
    nacos:
      #nacos地址
      server-addr: 127.0.0.1:8848
      #哪个用户的
      discovery:
        #哪个用户的
        username: nacos
        password: nacos
        #命名空间 默认public
        namespace: public

controller

@RestController
@RequestMapping("/order")
public class OrderController {

    @Autowired
    private RestTemplate restTemplate;

    @RequestMapping("/getOrder")
    public String get()
    {
    	//这里使用服务名来调用,如果没有nacos就要写具体的ip地址,难以维护
        return restTemplate.getForObject("http://stock-service/stock/reduct",String.class);
    }
}

stock-nacos 主要代码

配置类除了服务名和端口同上一直,提供接口

@RestController
@RequestMapping("/stock")
public class StockController {

    @Value("${server.port}")
    String port;

    @RequestMapping("/reduct")
    public String reduct()
    {
        return "hello,world" + port;
    }
}

spring cloud alibaba使用_第4张图片
启动两个stock,在order模块请求时会在客户端自动使用负载均衡算法,在两个stock中切换

界面一些功能

spring cloud alibaba使用_第5张图片
上面为大的分组,对应配置文件的namespace: public,适合不同项目时的管理,下面的分组名称适合项目内部的分类。

spring cloud alibaba使用_第6张图片
雪崩保护(nacos的不常用,一般用sentinel):
保护阈值: 设置0-1之间的值 0.6
临时实例: spring.cloud.nacos.discovery.ephemeral =false, 当服务宕机了也不会从服务列表中剔除

如果达到阈值,请求依然会发送到不健康状态的服务上,避免同时太多的请求到达健康的实例导致支撑不住

spring cloud alibaba使用_第7张图片
权重配合负载均衡机制

可配置项

spring cloud alibaba使用_第8张图片
spring cloud alibaba使用_第9张图片
spring cloud alibaba使用_第10张图片

nacos自带的ribbon负载均衡

  • Ribbon客户端组件提供一系列的完善的配置,如超时,重试等。通过Load Balancer获取到服务提供的所有机器实例,Ribbon会自动基于某种规则(轮询,随机)去调用这些服务。Ribbon也
    可以实现我们自己的负载均衡算法(从注册中心拉取列表,不用额外配置)
  • 服务端的负载均衡就类似于nginx,先发送请求,然后通过负载均衡算法,在多个服务器之间选择一个进行访问;即在服务器端再进行负载均衡算法分配(要额外再nginx中配置服务的地址)

常用的轮询方法

  • 随机,通过随机选择服务进行执行,一般这种方式使用较少;
  • 轮训,负载均衡默认实现方式,请求来之后排队处理;
  • 加权轮训,通过对服务器性能的分型,给高配置,低负载的服务器分配更高的权重,均衡各个服务器的压力;
  • 地址Hash,通过客户端请求的地址的HASH值取模映射进行服务器调度。 ip —>hash
  • 最小链接数,即使请求均衡了,压力不一定会均衡,最小连接数法就是根据服务器的情况,比如请求积压数等参数,将请求分
    配到当前压力最小的服务器上。 最小活跃数

使用

	@Bean
	//这个就是ribbon的注解
    @LoadBalanced
    public RestTemplate restTemplate(RestTemplateBuilder restTemplateBuilder)
    {
        return restTemplateBuilder.build();
    }

就可以用服务名替代ip地址进行访问了,而且有默认的负载均衡算法(轮询)

	@RequestMapping("/getOrder")
    public String get()
    {
        return restTemplate.getForObject("http://stock-service/stock/reduct",String.class);
    }

ribbon的负载均衡
spring cloud alibaba使用_第11张图片

  • RandomRule 随机
  • RoundRobinRule 轮询负载均衡策略
  • RetryRule 在轮询的基础上进行重试(在连接可用时间未超时中)
  • WeightedResponseTimeRule 权重 如果一个服务的平均响应时间越短则权重越大,那么该服务实例被选中执行任务的概率也就越大
  • BestAvailableRule 过滤掉失效的服务实例的功能,然后顺便找出并发请求最小的服务实例来使用(没有采用轮询)
  • ZoneAvoidanceRule 默认规则,复合判断server所在区域的性能和server的可用性选择服务器(没有采用轮询)
  • AvailabilityFilteringRule 先过滤掉故障实例,再选择并发较小的实例
  • NacosRule 基于nacos上的权重进行的负载均衡

配置负载均衡
1.java配置方法
spring cloud alibaba使用_第12张图片
config要放在springboot项目扫描不到的包中,不如不同的负载均衡算法会失效
RandomRuleConfig

@Bean
    public IRule getRule()
    {
    	
        return new RandomRule();
    }

WeightedResponseTimeRuleConfig

@Bean
    public IRule getRule()
    {
        return new WeightedResponseTimeRule();
    }

使用

@SpringBootApplication
//name是服务名(nacos上),多个@RibbonClient就是针对不同的服务用不同的算法
@RibbonClients(value = {
        @RibbonClient(name = "stock-service",configuration = RandomRuleConfig.class)
        //,@RibbonClient(name = "stock-xxx",configuration = WeightedResponseTimeRuleConfig.class)
})
public class OrderApplication {
    public static void main(String[] args) {
        SpringApplication.run(OrderApplication.class,args);
    }

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

2.配置文件方式
采用nacos的权重,首先在nacos上配置服务的权重,或者在项目中配置。
spring.cloud.nacos.discovery.weight

客户端配置
spring cloud alibaba使用_第13张图片

stock-service:
  ribbon:
    NFLoadBalancerRuleClassName: com.alibaba.cloud.nacos.ribbon.NacosRule

3.自定义负载均衡算法
实现抽象类AbstractLoadBalancerRule
Server choose(Object var1) 代表选择哪个服务
ILoadBalancer 表示从nacos上获取对应服务名的服务
spring cloud alibaba使用_第14张图片
默认都是懒加载,第一次调用才会加载可能会慢,可以修改配置

 ribbon:
   eager‐load:
     # 开启ribbon饥饿加载
     enabled: true
     # 配置mall‐user使用ribbon饥饿加载,多个使用逗号分隔
     clients: mall‐order

OpenFegin

Feign可以做到使用 HTTP 请求远程服务时就像调用本地方法一样的体验,开发者完全感知不
到这是远程方法,更感知不到这是个 HTTP 请求。它像 Dubbo 一样,consumer 直接调用接
口方法调用 provider,而不需要通过常规的 Http Client 构造请求再解析返回数据。它解决了
让开发者调用远程接口就跟调用本地方法一样,无需关注与远程的交互细节,更无需关注分布
式环境开发。

引入依赖(客户端)

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

创建接口feign(客户端)

//服务名,path是类上的@RequestMapping,没有可不写
@FeignClient(value = "stock-service",path = "/stock")
public interface StockControllerFeign {

    @RequestMapping("/reduct")
    String reduct();
}

对应的服务上的实现类

@RestController
@RequestMapping("/stock")
public class StockController {
    @RequestMapping("/reduct")
    public String reduct()
    {
        return "hello,world" + port;
    }
}

springboot主方法上加上注解起开(创建的接口要可以被扫描到)

@EnableFeignClients

客户端调用服务端接口

@RestController
@RequestMapping("/order")
public class OrderController {
    @Autowired
    private StockControllerFeign stockControllerFeign;
    @RequestMapping("/getOrder")
    public String get()
    {
        return stockControllerFeign.reduct();
    }
}

日志配置

springboot的日志等级默认为info,先修改为debug

//该包名下
logging:
    level:
      com.chenke.order.feign: debug

配置文件形式(全局配置)

@Configuration
public class FeignLog {

    @Bean
    public Logger.Level feignLoggerLevel() {
         //设置日志等级
         return Logger.Level.FULL;
         }
}

配置文件形式(部分feign才有日志)

feign:
  client:
    config:
      stock-service: #对应微服务
        loggerLevel: FULL

配置类形式(部分feign才有日志)
去掉@Configuration

public class FeignLog {

    @Bean
    public Logger.Level feignLoggerLevel() {
         //设置日志等级
         return Logger.Level.FULL;
         }

}

在对应的feign的注解上添加configuration

@FeignClient(value = "stock-service",path = "/stock",configuration = FeignLog.class)

设置超时时间

feign:
  client:
    config:
      stock-service: #对应微服务
        loggerLevel: FULL
        connectTimeout: 5000 #连接超时时间,默认2s,A连接到B的时间
        readTimeout: 10000 #请求处理超时时间,默认5s,连接之后B处理的时间

配置文件,使用和上面日志的配置差不多

@Configuration
 public class FeignConfig {
 	 @Bean
 	public Request.Options options() {
 		return new Request.Options(5000, 10000);
 	}
 }

自定义拦截器

在fegin调用远程服务之前进行拦截处理(写在客户端)

public class MyInterceptor implements RequestInterceptor {
    @Override
    public void apply(RequestTemplate requestTemplate) {
        //业务逻辑
        System.out.println("自定义拦截器启动");
    }
}

注入方式(代码)

@Configuration
public class FeignLog {
	@Bean
     public MyInterceptor getMyInterceptor()
     {
         return new MyInterceptor();
     }
}

注入方式(配置文件)

feign:
  client:
    config:
      stock-service: #对应微服务
        loggerLevel: FULL
        connectTimeout: 5000 #连接超时时间,默认2s,A连接到B的时间
        readTimeout: 10000 #请求处理超时时间,默认5s,连接之后B处理的时间
        requestInterceptors[0]: com.chenke.order.config.MyInterceptor
        requestInterceptors[1]: com.chenke.order.config.MyInterceptor

注解模式和日志差不多

Nacos-config

官方文档
原理是在项目中,会有一个定时器,大概10ms拉取nacos上最新的MD5和本地的MD5做比较,如果不同就会进行跟新

如果要做权限管理,要开启nacos的配置文件中的

nacos.core.auth.caching.enabled=true

根据nacos上的配置文件获取值

引入依赖

 <dependency>
            <groupId>com.alibaba.cloudgroupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-configartifactId>
 dependency>

在项目中必须引入一个bootstrap.properties,可以把nacos的配置都移动到这

# 必须和服务名相同
spring.application.name=order-service
spring.cloud.nacos.server-addr=127.0.0.1:8848
# 如果开启了权限要添加账号密码
spring.cloud.nacos.config.username=nacos
spring.cloud.nacos.config.password=nacos

在nacos上配置文件(默认data id要是服务名.properties)
在这里插入图片描述
spring cloud alibaba使用_第15张图片
使用

        String userName = applicationContext.getEnvironment().getProperty("user.name");
        String userAge = applicationContext.getEnvironment().getProperty("user.age");
        System.err.println("user name :"+userName+"; age: "+userAge);

使用yaml文件
这个配置的文件后缀只针对默认的配置文件(和服务同名)
bootstrap.properties 加上

# 默认是properties 
spring.cloud.nacos.config.file-extension=yaml

再把nacos上的data id改成 服务名+yaml

切换环境

只有默认的配置文件(data id相同的)才能用
在这里插入图片描述
使用这种格式,然后在bootstrap.properties添加

spring.profiles.active=dev

也可以在idea上直接配置启动
spring cloud alibaba使用_第16张图片

配置文件的优先级

spring.profiles.active=dev 如果dev不存在,还会使用默认的配置文件。如果dev中某个属性不存在,也会使用默认的配置文件中的值
dev

chen.name=ck

默认文件

chen.age=15

在spring.profiles.active=dev情况下 name和age都能获取到值

命名空间的使用

spring cloud alibaba使用_第17张图片

可以修改去那个空间获取配置文件(默认public)

spring.cloud.nacos.config.namespace=cc5458d0-77e1-4cd1-950a-291c402bac57

spring cloud alibaba使用_第18张图片
可以修改去哪个组获取配置文件(默认DEFAULT_GROUP)

spring.cloud.nacos.config.group=chenkeGroup

spring cloud alibaba使用_第19张图片

使用自定义的DataId配置

spring.cloud.nacos.config.extension-configs[n].data-id 的值必须带文件扩展名,文件扩展名既可支持 properties,又可以支持 yaml/yml。文件名字可以乱写,但是扩展名必须properties或者yaml/yml
spring cloud alibaba使用_第20张图片

spring.cloud.nacos.config.extension-configs[0].data-id=chenke.properties
spring.cloud.nacos.config.extension-configs[0].group=DEFAULT_GROUP
spring.cloud.nacos.config.extension-configs[0].refresh=true

等价于

spring.cloud.nacos.config.shared-configs[0].data-id=chenke.properties
spring.cloud.nacos.config.shared-configs[0].group=DEFAULT_GROUP
spring.cloud.nacos.config.shared-configs[0].refresh=true

但是在文件中如果存在相同的key优先级有所不同,会根据优先级覆盖

profiles>默认>extension-configs(下标越大越优先)>shared-configs(下标越大越优先)

给实体类赋值

@Component
@RefreshScope
public class User {
    @Value("${user.name}")
    private String name;

    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
}

@Value可以直接读取远程配置文件,加上@RefreshScope可以实时刷新远程的修改

sentinel

官网

所有降级方法都要加上参数BlockException ex

初体验

限流,每秒只能访问一次
导入依赖

<dependency>
            <groupId>com.alibaba.cspgroupId>
            <artifactId>sentinel-coreartifactId>
            <version>1.8.6version>
dependency>

代码形式

@RestController
@RequestMapping("/sentinel")
public class SentinelController {

    //等于一个方案名
    private static final String RESOURCE_NAME = "hello";
    @RequestMapping(value = "/hello")
    public String hello() {
        Entry entry = null;
        try {
            // 资源名可使用任意有业务语义的字符串,比如方法名、接口名或其它可唯一标识的字符串。
            entry = SphU.entry(RESOURCE_NAME);
            // 被保护的业务逻辑
            return "hello";
        } catch (BlockException e) {
            // 资源访问阻止,被限流或被降级
             //进行相应的处理操作
            System.out.println("block");
        } catch (Exception ex) {
            // 若需要配置降级规则,需要通过这种方式记录业务异常
            Tracer.traceEntry(ex, entry);
        }
        finally {
            if (entry != null) {
                entry.exit();
            }
        }
        return "g";
    }
    /**
      * 定义流控规则 初始化bean的
      */
    @PostConstruct
    private static void initFlowRules() {
         List<FlowRule> rules = new ArrayList<>();
         FlowRule rule = new FlowRule();
         //设置受保护的资源
         rule.setResource(RESOURCE_NAME);
        // 设置流控规则 QPS
        rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
        // 设置受保护的资源阈值
        // 一秒一次
        rule.setCount(1);
        rules.add(rule);
        // 加载配置好的规则
        FlowRuleManager.loadRules(rules);
    }
}

@SentinelResource注解的使用

详细使用官方
不仅仅可以针对controller,在业务方法上也可以设置
依赖

		<dependency>
            <groupId>com.alibaba.cspgroupId>
            <artifactId>sentinel-coreartifactId>
            <version>1.8.6version>
        dependency>

        <dependency>
            <groupId>com.alibaba.cspgroupId>
            <artifactId>sentinel-annotation-aspectjartifactId>
            <version>1.8.6version>
        dependency>

注入切面

@Configuration
public class SentinelConfig {
    @Bean
    public SentinelResourceAspect sentinelResourceAspect() {
         return new SentinelResourceAspect();
    }
}

使用

@RestController
@RequestMapping("/sentinelAnn")
public class SentinelAnnController {

    //等于一个方案名
    private static final String SENTINEL_RESOURCE = "SentinelResource";
    @RequestMapping(value = "/hello")
    @SentinelResource(
            value = SENTINEL_RESOURCE,
            blockHandler  = "qpsHello",
            defaultFallback = "throwableHello1",
            fallback = "throwableHello"
             )
    public String hello() {
            int i = 1/0;
            return "hello";
    }
    public  String qpsHello(BlockException ex)
    {
        ex.printStackTrace();
        return "我被限流了呃";
    }

    public  String throwableHello(Throwable ex)
    {
        ex.printStackTrace();
        return "我异常了,贼炸";
    }
    public  String throwableHello1(Throwable ex)
    {
        ex.printStackTrace();
        return "我异常了,贼炸1";
    }
    /**
      * 定义流控规则 初始化bean的
      */
    @PostConstruct
    private static void initFlowRules() {
         List<FlowRule> rules = new ArrayList<>();
         FlowRule rule = new FlowRule();
         //设置受保护的资源
         rule.setResource(SENTINEL_RESOURCE);

        // 设置流控规则 QPS
        rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
        // 设置受保护的资源阈值
        // 一秒一次
        rule.setCount(1);
        rules.add(rule);
        // 加载配置好的规则
        FlowRuleManager.loadRules(rules);
    }
    }

spring cloud alibaba使用_第21张图片
spring cloud alibaba使用_第22张图片
都属于BlockException

blockHandler 优先级大于fallback,比如限制了qps为1,但是代码会有算术异常。在多次访问的情况下走blockHandler 。 所有方法都必须为public

注解实现服务熔断降级

官方使用
现代微服务架构都是分布式的,由非常多的服务组成。不同服务之间相互调用,组成复杂的调用链路。以上的问题在链路调用中会产生放大的效果。复杂链路上的某一环不稳定,就可能会层层级联,最终导致整个链路都不可用。因此我们需要对不稳定的弱依赖服务调用进行熔断降级,暂时切断不稳定调用,避免局部不稳定因素导致整体的雪崩。熔断降级作为保护自身的手段,通常在客户端(调用端)进行配置。

例如:调用服务订单–>用户积分–>库存模块。这里的用户积分就属于弱模块,当积分模块出现问题,可以暂时降级为服务订单–>库存模块,积分模块在后期再进行补偿。

@RestController
@RequestMapping("/degrade")
public class DegradeController {
    //等于一个方案名
    private static final String SENTINEL_RESOURCE = "degrade";
    @RequestMapping(value = "/hello")
    @SentinelResource(
            value = SENTINEL_RESOURCE,
            blockHandler  = "deHello"
    )
    public String hello() {
        //模拟积分(弱依赖)模块出问题情况
        System.out.println("订单创建成功");
        System.out.println("积分调用成功"+1/0);
        System.out.println("库存调用成功");
        return "成功";
    }

    //降级后方法
    public String deHello(BlockException ex)
    {
        //舍弃积分(弱依赖)的调用
        System.out.println("订单创建成功");
        System.out.println("库存调用成功");
        return "成功";
    }

        @PostConstruct
        private static void initFlowRules() {
        List<DegradeRule> rules = new ArrayList<>();
        //再30秒内 最少有两次请求,且异常两次后走降级方法,10秒后恢复
            // 熔断恢复后 如果马上又异常,不会进行上面的判断,而是直接继续降级
            // count数会多一次,源码用的不是大于等于
        DegradeRule rule = new DegradeRule(SENTINEL_RESOURCE)
                .setGrade(RuleConstant.DEGRADE_GRADE_EXCEPTION_COUNT)
                .setCount(2) //两次(实际三次)
                .setMinRequestAmount(2) //最少两次请求
                .setStatIntervalMs(30*1000) //30秒内
                .setTimeWindow(10);//熔断降级10秒
        rules.add(rule);
        DegradeRuleManager.loadRules(rules);
		//在每次状态变化的时候回调
		EventObserverRegistry.getInstance().addStateChangeObserver("logging",
                    (prevState, newState, rule1, snapshotValue) -> {
                        if (newState == CircuitBreaker.State.OPEN) {
                            // 变换至 OPEN state 时会携带触发时的值
                            System.err.println(String.format("%s -> OPEN at %d, snapshotValue=%.2f", prevState.name(),
                                    TimeUtil.currentTimeMillis(), snapshotValue));
                        } else {
                            System.err.println(String.format("%s -> %s at %d", prevState.name(), newState.name(),
                                    TimeUtil.currentTimeMillis()));
                        }
                    });
    }
}

控制台部署

非springboot springcloud环境

导入依赖

<dependency>
			
         <groupId>com.alibaba.cspgroupId>
            <artifactId>sentinel-coreartifactId>
            <version>1.8.6version>
        dependency>

        <dependency>
            <groupId>com.alibaba.cspgroupId>
            <artifactId>sentinel-annotation-aspectjartifactId>
            <version>1.8.6version>
        dependency>

        <dependency>
            <groupId>com.alibaba.cspgroupId>
            <artifactId>sentinel-transport-simple-httpartifactId>
            <version>1.8.6version>
        dependency>

下载对应的控制台jar包
https://github.com/alibaba/Sentinel/releases

启动jar包,可以编辑成win批处理

java -Dserver.port=8858 -Dsentinel.dashboard.auth.username=chenke -Dsentinel.dashboard.auth.password=123456 -jar D:\huangjing\sentinel-dashboard-1.8.6.jar
pause

启动后在项目的jvm参数中添加

端口和上方客户端端口对应
-Dcsp.sentinel.dashboard.server=127.0.0.1:8858 

启动项目后访问接口,即可看到接口访问情况,以及用代码配置的策略
spring cloud alibaba使用_第23张图片
spring cloud alibaba使用_第24张图片

整合spring-cloud-alibab

官方文档

依赖

<dependency>
            <groupId>com.alibaba.cloudgroupId>
            <artifactId>spring-cloud-starter-alibaba-sentinelartifactId>
        dependency>

项目配置文件添加,客户端的端口信息

spring:
  application:
    name: order-service
  cloud:
    nacos:
      #nacos地址
      server-addr: 127.0.0.1:8848
      username: nacos
      password: nacos
    sentinel:
      transport:
        dashboard: 127.0.0.1:8858

控制台使用

统一异常处理

如果抛出的异常不需要做比较特殊的处理。可以设置统一的异常处理,在接口上就不需要写@SentinelResource注解了,但是这样就没有资源名了,直接根据接口路径配置即可
spring cloud alibaba使用_第25张图片
e.getRule 可以返回详细的控制规则

实时监控

spring cloud alibaba使用_第26张图片

簇点链路

spring cloud alibaba使用_第27张图片
可以看到所有的接口和配置的资源名 (配置规则要在资源名的那一行配置,这里是自定义的degrade)

流控规则

spring cloud alibaba使用_第28张图片
QPS:一秒内可以请求多少次(单机阈值),超过的请求抛出异常
并发线程数:针对慢sql等情况。同时允许多少个线程(单机阈值)同时执行。例如一个服务需要10秒才能进行响应,设置了单机阈值为1,只有在一个线程执行完10秒后返回,新的请求才能进入。

spring cloud alibaba使用_第29张图片
直接:统计当前资源的请求,触发阈值时对当前资源直接限流,也是默认的模式
关联:统计与当前资源相关的另一个资源,触发阈值时,对当前资源限流
链路:统计从指定链路访问到本资源的请求,触发阈值时,对指定链路限流

例子(限制了每秒1次):
直接:该资源超过限制即触发
关联:例如资源名A为查询的功能,可以配置一个关联的路径B为关联资源(修改),用B来判断规则,如果达到限流A。因为修改和查询同时占用资源,但是修改优先度更高。
链路:可以配置路径,针对这些路径进行限流(没有配置的路径可以随便访问该资源)

关联

设置两个接口,和一个业务类。模拟查询和修改的情况

    @RequestMapping(value = "/get")
    public String hello() {
        return orderService.getName("查询");
    }
    @RequestMapping(value = "/update")
    public String hello1() {
        return orderService.getName("修改");
    }

配置
spring cloud alibaba使用_第30张图片
如果update达到了规则,则限制get的功能,update不受影响

限流的是get接口,而不是业务实现

链路

配置文件加上

spring.cloud.sentinel.web‐context‐unify: false

如果@SentinelResource写再业务方法上 ,无法通过BlockException异常的统一处理,只能自己编写方法

设置两个接口,和一个业务类。模拟查询和修改的情况

    @RequestMapping(value = "/get")
    public String hello() {
        return orderService.getName("查询");
    }
    @RequestMapping(value = "/update")
    public String hello1() {
        return orderService.getName("修改");
    }
@Service
//模拟业务调用数据库
public class orderService {
    @SentinelResource(value = "getName",blockHandler = "getNameDe")
    public String getName(String name)
    {
        System.out.println("调用数据库。。。。");
        return name;
    }

    public String getNameDe(String name, BlockException ex)
    {
        return "限流"+name;
    }
}

请求一次接口后查看控制台,会出现调用的链路
spring cloud alibaba使用_第31张图片
在簇点链路界面随便点击一个getName进行配置
spring cloud alibaba使用_第32张图片
点击新增后添加规则,然后在流控规则的页面就能看到配置。可以重复上面步骤配置多个链路。
spring cloud alibaba使用_第33张图片
把规则update的删除,就会发现,只有查询会被流控。而修改可以随便访问,限流的业务实现,而不是get或者update接口

流控效果
  • 快速失败 超过洪峰流量后直接拒绝
  • Warm up(激增流量:某一些时间点流量暴增,平缓流量->突然增加->平缓流量) 设置预热时间,在这时间内,慢慢的达到阈值。
  • 排队等候(脉冲流量:一段时间内流量暴增,平缓流量->突然增加->平缓流量->突然增加) 超过洪峰流量后在设置的时间内,如果前面的请求响应完,还在时间范围,就继续处理

例子

  • Warm up:qps设置成10000,预热时间为5秒。第一秒qps为(10000/3,默认因子为3)3333,慢慢的增加到10000,可以给系统增加缓存的时间。以免瞬间所有请求打到数据库(缓存击穿)
  • 排队等候:qps设置10000,超时等待时间设置成五秒。如果第一秒打进了20000,先处理其中的10000,保留10000,如果在5秒内,系统不繁忙了。再处理这10000。

熔断规则

  • 慢调用比例
    spring cloud alibaba使用_第34张图片

  • 异常比例
    spring cloud alibaba使用_第35张图片

  • 异常数
    spring cloud alibaba使用_第36张图片
    多作用于客户端

热点参数流控

  • 一定要使用@SentinelResource,统一异常处理没有办法针对热点key进行判断
  • 参数必须是long,int,double,float,char,byte,String
@RestController
@RequestMapping("/degrade")
public class DegradeController {

    @RequestMapping(value = "/get")
    @SentinelResource(value = "hot",blockHandler = "hotHello")
    public String hello(Integer id) {
        return "成功"+id;
    }
    
    public String hotHello(Integer id,BlockException ex) {
        return "限流了"+id;
    }
}

流控id为1的参数
spring cloud alibaba使用_第37张图片
测试结果:访问id为1的4次后进入了限流方法,而反问id为2需要11次。

单机阈值: 针对所有参数的值进行设置的一个公共的阈值

  1. 假设当前 参数 大部分的值都是热点流量, 单机阈值就是针对热点流量进行设置, 额外针对普通流量进行参数值流控
  2. 假设当前 参数 大部分的值都是普通流量, 单机阈值就是针对普通流量进行设置, 额外针对热点流量进行参数值流控

系统保护规则

属于兜底的保护规则,在不清楚什么原因,或者流量毫无规律的情况下设置全局的保护
spring cloud alibaba使用_第38张图片

spring cloud alibaba使用_第39张图片
是针对单台机器所有的平均值进行计算的

授权规则

集群流控

openFeign整合sentinel

spring cloud alibaba使用_第40张图片
在调用方也实现远程接口的实现,在@FeignClient的注解fallback后面添加

控制台规则持久化

默认控制台的配置没有持久化,重启就消失,这里使用nacos的配置中心来使用

导入依赖

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

在nacos上编写配置文件
spring cloud alibaba使用_第41张图片
都对应规则的内容

[
    {
        "resource":"hot",
        "controlBehavior":0,
        "count":3.0,
        "grade":1,
        "limitApp":"default",
        "strategy":0
    }
]

在项目中添加配置文件

server:
  port: 8010
spring:
  application:
    name: order-service
  cloud:
    nacos:
      #nacos地址
      server-addr: 127.0.0.1:8848
      username: nacos
      password: nacos
    #sentinel配置~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    sentinel:
      transport:
        dashboard: 127.0.0.1:8858
      web-context-unify: false
      datasource:
        flow-rule: # 可以自定义
          nacos:
           server-addr: 127.0.0.1:8848
           username: nacos
           password: nacos
           dataId: sentinel.rule
           rule‐type: flow

重启项目即可。但是如果在sentinel上修改了配置并不会同步到nacos,需要手动同步(或者源码扩展)

Seata分布式事务

2PC
AT(Seata解决方案)
TCC
可靠消息最终一致性的方案 MQ

使用部署

官网,下载地址

1.修改持久化配置,默认是file模式,存在本地。但是在集群模式下无法统一,改成db模式
conf/application.yml 下修改,并配置连接。再新建表(seata\script\server\db 文件地址)
spring cloud alibaba使用_第42张图片
spring cloud alibaba使用_第43张图片

2.seata也要注册到注册中心,要通过注册中心和服务来进行交互。配置文件
spring cloud alibaba使用_第44张图片
3.seata配置注册中心配置中心。(扩展配置)
spring cloud alibaba使用_第45张图片
seata\script\config-center\config.txt 这个文件需要部署到nacos的配置中心上,修改mode为db,并修改数据库相关配置

store.mode=db
store.lock.mode=db
store.session.mode=db
# 关于db的配置也要修改

4.文件中的service.vgroupMapping.default_tx_group=default 代表异地容灾
spring cloud alibaba使用_第46张图片
5.将这个txt配置文件同步到nacos
seata\script\config-center\nacos目录下的nacos-config.sh,无法运行安装一个git

sh nacos-config.sh -u nacos -w nacos

-h  ip地址 默认localhost
-p 端口 默认8848
-g 配置分组 默认SEATA_GROUP
-t 租户信息,默认为空,对应nacos命名空间id
-u 账号 如果nacos开启了权限
-p 密码 如果nacos 开启了权限

6.配置 @GlobalTransactional

两个模块,订单和库存

都添加依赖

<dependency>
 <groupId>com.alibaba.cloud</groupId>
 <artifactId>spring‐cloud‐starter‐alibaba‐seata</artifactId>
</dependency>

7.各微服务对应数据库中添加undo_log表(seata失败时的逆向sql)

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,
 PRIMARY KEY (`id`),
 UNIQUE KEY `ux_undo_log` (`xid`,`branch_id`)
 ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;

8.修改application.yml配置
spring cloud alibaba使用_第47张图片

spring cloud alibaba使用_第48张图片
即可使用@GlobalTransactional

geteWay

初体验

1.新建模块gateway,并加入依赖(和mvc的依赖冲突)

<dependency>
            <groupId>org.springframework.cloudgroupId>
            <artifactId>spring-cloud-starter-gatewayartifactId>
dependency>

2.修改application.yml配置

server:
  port: 8088
spring:
  application:
    name: gateway
  cloud:
    gateway:
      routes:
        - id: order_cyz
          uri: http://127.0.0.1:8011 #需要路由的路径
          predicates:
            - Path=/cyz/**  #断言,匹配路径
          filters:
            - StripPrefix=1 #过滤器,去掉第一层的请求

3.访问

http://127.0.0.1:8088/cyz/stock/reduct 等于访问  http://127.0.0.1:8011/stock/reduct

断言工厂

内置断言工厂

  • 基于Datetime类型的断言工厂
    AfterRoutePredicateFactory: 接收一个日期参数,判断请求日期是否晚于指定日期
    BeforeRoutePredicateFactory: 接收一个日期参数,判断请求日期是否早于指定日期
    BetweenRoutePredicateFactory: 接收两个日期参数,判断请求日期是否在指定时间段内
    时间格式:ZonedDateTime.now()
routes:
        - id: order_cyz
          uri: http://127.0.0.1:8011 #需要路由的路径
          predicates:
            ‐ After=2019‐12‐31T23:59:59.789+08:00[Asia/Shanghai] #在这个事件之前才能请求
            ‐ Before=2019‐12‐31T23:59:59.789+08:00[Asia/Shanghai] #在这个事件之后才能请求
            ‐ Between=2019‐12‐31T23:59:59.789+08:00[Asia/Shanghai],2019‐12‐31T23:59:59.789+08:00[Asia/Shanghai] #在这个事件之前才能请求
            
  • 基于远程地址的断言工厂
    RemoteAddrRoutePredicateFactory:接收一个IP地址段,判断请求主机地址是否在地址段中
‐ RemoteAddr=192.168.1.1/24
  • 基于Cookie的断言工厂
    CookieRoutePredicateFactory:接收两个参数,cookie 名字和一个正则表达式。 判断请求
    cookie是否具有给定名称且值与正则表达式匹配。
 ‐ Cookie=chocolate,ch
  • 基于Header的断言工厂
    HeaderRoutePredicateFactory:接收两个参数,标题名称和正则表达式。 判断请求Header是否具有给定名称且值与正则表达式匹配。
 ‐ Header=X‐Request‐Id, \d+
  • 基于Host的断言工厂
    HostRoutePredicateFactory:接收一个参数,主机名模式。判断请求的Host是否满足匹配规则。
‐ Host=**.testhost.org
  • 基于Method请求方法的断言工厂
    MethodRoutePredicateFactory:接收一个参数,判断请求类型是否跟指定的类型匹配。
‐ Method=GET
  • 基于Path请求路径的断言工厂
    PathRoutePredicateFactory:接收一个参数,判断请求的URI部分是否满足路径规则。
 ‐ Path=/foo/{segment} #{}占位符 可以匹配reful风格
  • 基于Query请求参数的断言工厂
    QueryRoutePredicateFactory :接收两个参数,请求param和正则表达式, 判断请求参数是否具有给定名称且值与正则表达式匹配。
‐ Query=baz, ba.    #xxx:8080?baz=1
  • 基于路由权重的断言工厂
    WeightRoutePredicateFactory:接收一个[组名,权重], 然后对于同一个组内的路由按照权重转发
 routes:
   ‐id: weight_route1
  	 uri: host1
   	 predicates:
  		‐Path=/product/**
  		‐Weight=group3, 1
  ‐id: weight_route2
     uri: host2
     predicates:
  		‐Path=/product/**
  		‐Weight= group3, 9

自定义断言工厂

自定义路由断言工厂需要继承 AbstractRoutePredicateFactory 类,重写 apply 方法的逻辑。在 apply 方法中可以通过
exchange.getRequest() 拿到 ServerHttpRequest 对象,从而可以获取到请求的参数、请求方式、请求头等信息。
1、 必须spring组件 bean
2. 类必须加上RoutePredicateFactory作为结尾
3. 必须继承AbstractRoutePredicateFactory
4. 必须声明静态内部类 声明属性来接收 配置文件中对应的断言的信息
5. 需要结合shortcutFieldOrder进行绑定
6.通过apply进行逻辑判断 true就是匹配成功 false匹配失败

内置过滤器

第六点中全部都是
例如添加请求头

filters:
  ‐ AddRequestHeader=X‐Request‐color, red #添加请求头

自定义过滤器

继承AbstractNameValueGatewayFilterFactory且我们的自定义名称必须要以GatewayFilterFactory结尾并交给spring管理。

全局过滤器

spring cloud alibaba使用_第49张图片

你可能感兴趣的:(spring,cloud,alibab,spring,boot,java,spring,cloud)