• SpringCloud Alibaba Nacos详解


    1. 现有项目注册中心替换alibaba组件Nacos

    QQ图片20201109164856

    1.springcloud 、springboot、springcloud alibaba版本对应

    spring cloud alibaba文档:https://github.com/alibaba/spring-cloud-alibaba/wiki

    版本对应:https://github.com/alibaba/spring-cloud-alibaba/wiki/版本说明

    注意:

    • 0.9版本之后,毕业版本的groudId 为 com.alibaba.com
    • 0.9版本之前,化器版本groudId 为org.springframework.cloud(不推荐使用)

    image-20201029100642198

    image-20201029100835653

    image-20201029101405686

    2. 依赖修改

    目前mango系统各组件版本

    • springboot版本为1.5
    • springcloud alibaba使用毕业版版本1.5.0RELEASE ,不要使用孵化器版本
    • nacos service 阿里文档中推荐1.1.1版本,实际上使用最新的稳定版本也可以使用,测试验证使用1.2版本可以正常使用
    <!--pom文件依赖新增一下内容,删除其他注册中心的相关的依赖(例如:Eureka) 和其他配置中心的依赖(例如:Springcloud config)-->
    <dependencyManagement>
        <!--只是对版本进行管理,不会实际引入jar -->
        <dependencies>
            <dependency>
           <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>Edgware.SR6</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <dependency>
                <groupId>com.alibaba.cloud</groupId>
                <artifactId>spring-cloud-alibaba-dependencies</artifactId>
                <version>1.5.1.RELEASE</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>
    
    <dependencies>
      <!--mango系统替换后需要指定springCloud-common包版本号-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-commons</artifactId>
            <version>1.3.5.RELEASE</version>
        </dependency>
        <!--因为nacos config是通过web获取配置,需要导入springboot-web依赖-->
         <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!--nacos 服务发现-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
            <exclusions>
                <exclusion>
                    <artifactId>spring-cloud-commons</artifactId>
                    <groupId>org.springframework.cloud</groupId>
                </exclusion>
                <exclusion>
                    <artifactId>fastjson</artifactId>
                    <groupId>com.alibaba</groupId>
                </exclusion>
            </exclusions>
        </dependency>
           <!--nacos 配置管理-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
            <exclusions>
                <exclusion>
                    <artifactId>spring-cloud-commons</artifactId>
                    <groupId>org.springframework.cloud</groupId>
                </exclusion>
            </exclusions>
        </dependency>
          <!--sentinel依赖,注意网关根据不同的实现方式对应着不同的sentinel依赖-->
        <dependency>
              <groupId>com.alibaba.csp</groupId>
              <artifactId>spring-boot-starter-ahas-sentinel-client</artifactId>
              <version>1.7.2</version>
    	</dependency>
        
        <dependency>      
              <groupId>org.springframework.cloud</groupId>       
              <artifactId>spring‐cloud‐starter‐openfeign</artifactId>   
        </dependency>
    </dependencies>
    

    网关不同实现方式sentinle对应的依赖:

    <!--zuul 1.x网关接入, Mango即是zuul 1.X-->
    <dependency>
         <groupId>com.alibaba.csp</groupId>
         <artifactId>spring-cloud-zuul-starter-ahas-sentinel</artifactId>
         <version>1.1.8</version>
    </dependency>
    <!--springcloudGateway网关接入-->
    <dependency>
          <groupId>com.alibaba.csp</groupId>
          <artifactId>spring-cloud-gateway-starter-ahas-sentinel</artifactId>
          <version>1.1.8</version>
    </dependency>
    

    3. 配置文件修改

    其他详细的配置可参考nacos-配置管理中的介绍

    #bootstrap
    spring:
        application:
            name: gateway
        cloud:
            sentinel:
                transport:
                	#sentinel 控制面板
                    dashboard: 127.0.0.1:8083
            nacos:
                config:
                	# nacos service地址
                    server-addr: 127.0.0.1:8848
                    namespace: public 
                    group: DEFAULT_GROUP
                    data-id: gateway.yml
                    file-extension: yaml
                    refresh:
                        enables: true # 默认为fasle ,设为true,开启动态刷新
    
    #application
    server:
    	port: 56020 #启动端口 命令行注入  
    spring:   
    	application:     
    		name: gateway
         cloud:     
         	nacos:      
            	discovery:       
            		# nacos service地址
                	server‐addr: 127.0.0.1:8848
    

    4. 代码修改

    1. 启动类新增服务发现注解 @EnableDiscoveryClient @EnableFeignClients
    2. 生产者声明@FeignClient(name = "uaa") ,mango项目中,生产者远程代理在common中,注意修改common的依赖

    2. nacos介绍

    Nacos是阿里的一个开源产品,它是针对微服务架构中的服务发现、配置管理、服务治理的综合型解决方案。
    nacos文档: https://nacos.io/en-us/docs/what-is-nacos.html

    2.1 nacos-配置管理

    nacos-config文档:https://github.com/alibaba/spring-cloud-alibaba/wiki/Nacos-config

    2.1.1 配置中心的使用场景

    微服务架构,系统拆分为一个个服务节点,配置文件也被分割,分散中就包含了很多相同的配置,造成冗余。

    配置中心就是将配置从应用里剥离出来, 进行统一管理。

    配置中心的服务流程

    1. 用户在配置中心更新配置信息。
    2. 服务A和服务B及时得到配置更新通知,从配置中心获取配置
    

    2.1.2 常见的配置中心

    nacos 读写性能最高,且和springcloud condig相比,nacos带图形化界面配置。

    img

    2.1.3 nacos 快速部署

    1. 下载地址:https://github.com/alibaba/nacos/releases,下载源码自己编译/下载编译好的安装包

    2. 安装包启动方式:bin目录下执行 sh startup.sh -m standalone

      standalone 代表单机模式运行,非集群模式

      sh startup ‐m cluster 标识集群启动

    3. 启动成功, http://127.0.0.1:8848/nacos可打开nacos控制台,默认账号密码都为 nacos

    4. OPEN API 配置管理测试 :https://nacos.io/en-us/docs/open-api.html

      1. 发布配置: curl -X POST "http://127.0.0.1:8848/nacos/v1/cs/configs dataId=nacos.cfg.dataId&group=test&content=HelloWorld"
      2. 获取配置: curl -X GET "http://127.0.0.1:8848/nacos/v1/cs/configs?dataId=nacos.cfg.dataId&group=test"

    2.1.4 nacos 外部mysql数据库支持

    单机模式nacos使用的是嵌入式数据库实现数据储存,可以配置mysql作为外部存储(目前只支持使用mysql)。

    1. 新建数据库nacos_config

    2. 执行nacos下conf/nacos-mysql.sql 文件

    3. 修改nacos下conf/application.properties,增加支持mysql数据源配置,增加MySQL数据源账号密码

    4. 增加后记得重启一下nacos

      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=
      db.password=
      

    2.1.5 nacos 配置管理应用

    对于nacos配置管理,通过namespace、group、Data ID 可以定位到一个配置集(也可以理解为一个配置文件)。

    • namespace:设计是 nacos 基于此做多环境以及多租户(多个用户共同使用nacos)数据(配置和服务)隔离的。
      一个租户情况下:有多套不同环境,可以使用同一套nacos集群,通过建多个不同的nacespace来做配置/服务的隔离
      多个租户情况下:不同租户分配不同的账号密码,创建自己的namespace,不同租户下namespace互不可见(nacos还没有实现多租户功能)

    nacos 服务隔离,应该是同一namespace下才可以调用其他服务,注册中心虽然是一个,但是注册表不同

    服务获取配置集需要配置的内容:

    1. nacos server 地址 ,必须配置
    2. namespace:可以不指定,默认是public,若指定填写的是namespace的id而不是名称,id在nacos管理页面查看
    3. group:可以不指定,默认是DEFAUT_GROUP
    4. dataId:必须指定

    nacos配置管理提供了配置clone,历史版本、回滚、订阅者查询等核心管理能力,通过图形化界面很好操作。

    监听查询
    Nacos提供配置订阅者即监听者查询能力,同时提供客户端当前配置的MD5校验值,以便帮助用户更好的检查配 置变更是否推送到 Client 端。

    
    

    2.1.6. nacos登录管理:

    除了使用默认的nacos账号密码,可以通过直接在数据库中增加账号密码,密码为BCrypt加密方法
    也可以在nacos service中管理登录功能,

    spring.security.enabled=false management.security=false security.basic.enabled=false nacos.security.ignore.urls=/** 
    

    2.1.7 nacos集成分布式系统

    通过 Spring cloud原生方式快捷的获取配置内容:

    1. 在bootstrap.yml基础配置

    Spring:	
    	cloud:
            nacos:
                config:
                	enabled: true # 可不指定,默认true,false时关闭了nacos-config配置
                    server-addr: 127.0.0.1:8848 #必须指定
                    namespace: c67e4a97‐a698‐4d6d‐9bb1‐cfac5f5b51c4 # 可不指定,默认为public
                    ext-configs[0]: #配置单个配置集的时候,可省略
                      	group: DEFAULT_GROUP  # 可不指定,默认为DEFAULT_GROUP
                     	file-extension: yaml  # 默认是properties
                    	data-id: service.yaml # 可不指定,默认是${spring.application.name}和文件拓展名组合
                    						# 指定data-id时,需要带上文件拓展名,yaml和yml都可以,和file-extension两者不干扰,也可以配置多个data-id,优先级根据ex-config[n]中n的数字大小决定,数字越大,优先级越高
                    	refresh:
                        	enables: true # 默认为false,设为fasle 关闭动态刷新
                     ext‐config[1]:       
                      # Data Id 不在默认的组,不支持动态刷新 
                     	data‐id: ext‐config‐common02.properties  # 可以配置公用的配置               
                     	group: GLOBALE_GROUP                      
    

    在加载配置文件时,不仅会加载${spring.application.name}.${file-extension:properties}为前缀的基础配置

    还会加载${spring.application}0 - ${profile}.${file-extension:properties}的基础信息

    ${spring.profiles.active} 当通过配置文件来指定时必须放在 bootstrap.properties 文件中。但是实际项目中,时通过启动参数-Dspring.profiles.active=<profile>来切换不同的配置

    通过修改namespace、group、dataId可以获取指定的配置集,还可以修改profile.active来指定配置集

    2. 自定义共享DataId配置

    spring:   
     	cloud:   
      		nacos:   
         		config:      
         			# 配置分享dataId必须带上文件后缀名,refreable-dataids也是
         			# shared-dataids 中group为默认的DEFAULT_GROUP ,若所配的data-id不在默认组中获取不到配置集
         			# 支持多个DataId配置,逗号隔开,优先级按照配置出现的先后顺序,即后面的优先级要高于前面。
        			shared‐dataids: ext‐config‐common01.properties,ext‐config‐common02.properties 
        		 		# 指定动态刷新的 配置集
        		 		refreshable‐dataids: ext‐config‐common01.properties 
    

    3. 配置的优先级

    Spring Cloud Alibaba Nacos Config 目前提供了三种配置能力从 Nacos 拉取相关的配置。

    • A: 通过 spring.cloud.nacos.config.shared-configs[n].data-id 支持多个共享 Data Id 的配置
    • B: 通过 spring.cloud.nacos.config.extension-configs[n].data-id 的方式支持多个扩展 Data Id 的配置
    • C: 通过内部相关规则(应用名、应用名+ Profile )自动生成相关的 Data Id 配置

    当三种方式共同使用时,他们的一个优先级关系是:A < B < C

    4. 实时获取最新配置

    // 注入配置文件上下文
    @Autowired 
    private ConfigurableApplicationContext applicationContext;  
    
    @GetMapping(value = "/configs") 
    public String getConfigs(){   
        // 实时获取配置
    	return applicationContext.getEnvironment().getProperty("common.name"); 
    }
    

    2.1.8 nacos集群部署

    1. 安装三个以上nacos

    2. 在所有nacos conf目录下将cluster.conf.example文件改为cluster.conf,将每行配置成 ip:port。(请配置3个或3个以上节点)

      # ip:port  
      127.0.0.1:8848 
      127.0.0.1:8849 
      127.0.0.1:8850 
      
      
    3. 单机的话还需修改conf下application.properties中server.port ,防止端口冲突,如果服务器有多个ip也要指定具体的ip地址,如:nacos.inetutils.ip-address=127.0.0.1

    4. 客户端配置,spring.cloud.nacos.config.server-addr: 127.0.0.1:8848,127.0.0.1:8849,127.0.0.1:8850

      关闭其中的注册中心,其他注册注册中心会被成功选举。

    2.2服务发现

    2.2.1 常见服务发现中心:

    从长远来看Nacos在以后的版本会 支持SpringCLoud+Kubernetes的组合,填补 2 者的鸿沟,在两套体系下可以采用同一套服务发现和配置管理的解 决方案,这将大大的简化使用和维护的成本。另外,Nacos 计划实现 Service Mesh,也是未来微服务发展的趋 势。

    image-20201030093733986

    2.2.2 代码修改

    增加依赖,增加服务发现注解, 如1-4,省略

    2.2.3 服务管理

    1. 可以通过nacos service 可视化界面对服务进行管理, 可以看到已经注册的服务,且可以进行服务上线下线管理,通过配置实例的权重,修改实例接收的流量,权重为0时,不接受流量,和下线效果一样
    2. 也可以在服务详情页面,进行元数据的修改,key-value的结构

    2.2.4 nacos disvocery 配置项信息详情

    配置项 Key 默认值 说明
    服务端地址 spring.cloud.nacos.discovery.server-addr Nacos Server 启动监听的ip地址和端口
    服务名 spring.cloud.nacos.discovery.service ${spring.application.name} 给当前的服务命名
    服务分组 spring.cloud.nacos.discovery.group DEFAULT_GROUP 设置服务所处的分组
    权重 spring.cloud.nacos.discovery.weight 1 取值范围 1 到 100,数值越大,权重越大
    网卡名 spring.cloud.nacos.discovery.network-interface 当IP未配置时,注册的IP为此网卡所对应的IP地址,如果此项也未配置,则默认取第一块网卡的地址
    注册的IP地址 spring.cloud.nacos.discovery.ip 优先级最高
    注册的端口 spring.cloud.nacos.discovery.port -1 默认情况下不用配置,会自动探测
    命名空间 spring.cloud.nacos.discovery.namespace 常用场景之一是不同环境的注册的区分隔离,例如开发测试环境和生产环境的资源(如配置、服务)隔离等。
    AccessKey spring.cloud.nacos.discovery.access-key 当要上阿里云时,阿里云上面的一个云账号名
    SecretKey spring.cloud.nacos.discovery.secret-key 当要上阿里云时,阿里云上面的一个云账号密码
    Metadata spring.cloud.nacos.discovery.metadata 使用Map格式配置,用户可以根据自己的需要自定义一些和服务相关的元数据信息
    日志文件名 spring.cloud.nacos.discovery.log-name
    集群 spring.cloud.nacos.discovery.cluster-name DEFAULT 配置成Nacos集群名称
    接入点 spring.cloud.nacos.discovery.enpoint UTF-8 地域的某个服务的入口域名,通过此域名可以动态地拿到服务端地址
    是否集成Ribbon ribbon.nacos.enabled true 一般都设置成true即可
    是否开启Nacos Watch spring.cloud.nacos.discovery.watch.enabled true 可以设置成false来关闭 watch

    3. sentinel

    image-20201116143639964

    流控制组件,包括流控制,并发限制,电路中断和自适应系统保护,以确保微服务的可靠性。

    sentinel包括两个部分:

    1. 核心库:不依赖任何框架
    2. 控制台:springboot应用,可直接运行,负责管理推送规则、监控、集群限流分配,机器发现等.

    sentinel功能:

    1. 流控
    2. 断路和并发
      1. 最大并发限制
        过限制并发线程的数量(即信号隔离)来减少不稳定资源的影响,而不是使用线程池。(hystrix使用线程池来实现隔离,可做对比//TODO
        当资源的响应时间变长时,线程将开始被占用。当线程数累积到一定数量时,新的传入请求将被拒绝。反之亦然,当资源恢复并变得稳定时,占用的线程也将被释放,新请求将被接受。
      2. 断路
        根据不稳定资源的响应时间降级不稳定资源保证可靠性。当资源的响应时间太大时,将在指定的时间窗口中拒绝所有对该资源的访问。

    3.1 定义资源

    能通过sentinel Api定义的diamond,就是资源

    可以是:服务,方法,某一段代码

    3.1.1 Api方式定义资源

    文档:https://github.com/alibaba/Sentinel/wiki/如何使用

    // 举例
    @GetMapping(value="/echo")
    public String echo(){
    	// 定义资源
    	try(Entry entry = Sphu.entry("echo")){
    	 //定义被保护的逻辑
    	 	return "i am from port"+port;
    	}catch(BlockException e){
    		retrun "当前访问被控制了";
    	}
    }
    

    3.1.2 使用注解定义资源

    sentinle提供了@SentinelResource注解的方式定义资源,更推荐使用。

    主要属性 作用
    value 资源名称,必需项(不能为空)
    blockHandler 在原方法被限流/降级/系统保护的时候调用,可选项必须与原方法必须处于同一个类中、访问类型必须为public、返回类型需要与原方法相匹配、参数类型需要和原方法相匹配。若希望使用其他类的函数,则可以指定 blockHandlerClass 为对应的类的 Class 对象,注意对应的函数必需为 static 函数,否则无法解析。
    1. 定义配置类

      @Configuration
      public class SentinelAspectConfiguration {
      
          @Bean
          public SentinelResourceAspect sentinelResourceAspect() {
              return new SentinelResourceAspect();
          }
      }
      
    2. 使用注解定义资源

      @GetMapping(value = "/echo")
      @SentinelResource(value = "echo",blockHandler = "echoBlockHandler")
      public String echo() {
      
          //调用规则
          this.initFlowRules();
          //定义被保护的逻辑
          return "i am from port " + port;
      }
      // 1. 必须处于同一个类,2. 访问类型必须为public,3. 返回值类型必须与原方法一致,4. 参数与类型必须与原方法相同,BlockException是特例
      public String echoBlockHandler(BlockException e){
          //处理被控制的逻辑
          return "当前访问被控制了";
      }
      
      private void initFlowRules() {
          List<FlowRule> rules = new ArrayList<>();
          FlowRule rule = new FlowRule();
          //关联域名
          rule.setResource("echo");
          //设定限流阈值类型
          rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
          //设置限流阈值
          rule.setCount(2);
          //设置流控针对的调用来源,default为不区分来源
          rule.setLimitApp("default");
          rules.add(rule);
          FlowRuleManager.loadRules(rules);
      }
      

    3.2 定义规则

    规则就是保护服务稳定的准则,可以是流控规则、服务熔断降级规则、系统保护规则、来源访问控制规则、热点参数规则

    sentinle的所有规则都在内存中动态查询修改,规则可以实时进行调整。

    定义规则有两种途径:1.sentinel Api 2. sentinel控制台

    3.2.1 Api规则定义实现

    ![1584606721445](file://E:/Sentinel%E4%BD%BF%E7%94%A8%E5%AE%9E%E8%B7%B5/%E8%AE%B2%E4%B9%89/assets/1584606721445.png?lastModify=1604835718)

    // 举例
    private void initFolwRules(){
    	// flowRule 流控规则,可以定义多个流控规则
    	List<FlowRule> rules = new ArrayList<>();
    	FlowRule rule = new FlowRule();
    	//关联域名、资源名称
    	rule.setResource("echo");
    	//设定限流阀值类型
      rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
        //设置限流阈值
        rule.setCount(2);
        //设置流控针对的调用来源,default为不区分来源
        rule.setLimitApp("default");
        rules.add(rule);
       
        FlowRuleManager.loadRules(rules);
    }
    

    3.2.2 资源和规则结合

            return "当前访问被控制了";
    @GetMapping(value = "/echo")
    public String echo() {
    
        //调用规则
        this.initFlowRules();
    
        //定义资源
        try (Entry entry = SphU.entry("echo")){
            //定义被保护的逻辑
            return "i am from port " + port;
        } catch (BlockException e) {
            //处理被控制的逻辑
            return "当前访问被控制了";
        }
    }
    
    private void initFlowRules() {
        List<FlowRule> rules = new ArrayList<>();
        FlowRule rule = new FlowRule();
        //关联域名
        rule.setResource("echo");
        //设定限流阈值类型
        rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
        //设置限流阈值
        rule.setCount(2);
        //设置流控针对的调用来源,default为不区分来源
        rule.setLimitApp("default");
        rules.add(rule);
        FlowRuleManager.loadRules(rules);
    }
    

    3.3 sentinle控制台使用

    访问:https://github.com/alibaba/Sentinel/releases。进行对应版本下载,可下载jar包,也可下载exe文件

    3.3.1 客户端接入控制台

    客户端添加控制台配置后重启服务,若未开启自动心跳, 则服务资源后调用后,才能在控制台看到具体服务。sentinel是延迟加载的。

    project:
    	name: 服务名
    // application.yml中添加配置
    sentinel:
    	dashboard:
    		server: sentinel控制台地址
    		
    

    3.4 sentinel各规则详解

    3.4.1流控- QPS(每秒查询率)流量控制

    sentinel实现流控的原理是监控应用流量的 QPS并发线程数等指标,当达到指定的阈值时对流量进行控制,以避免被瞬时的流量高峰冲垮,从而保障应用的高可用性。

    3.4.1.1 不针对来源进行流控

    针对来源选项值当为default,代表不针对来源。即对所有微服务都生效。

    3.4.1.2 针对特定来源流控

    对于一个系统而言,不同的请求来源,很有可能会设置不同的规则。

    例如对于商品搜索结果页面,针对PC端指定一个规则,针对小程序端指定一个规则。

    1. 添加依赖

    2. 声明配置bean

      @Component
      public class MyRequestOriginParser implements RequestOriginParser {
          @Override
          public String parseOrigin(HttpServletRequest httpServletRequest) {
              // 从请求路径或者请求头中获取请求来源信息
              // 此处以请求头为例
              String origin = httpServletRequest.getParameter("origin");
              if (StringUtils.isBlank(origin)) {
                  throw new IllegalArgumentException("origin must be exist");
              }
              return origin;
          }
      }
      
    3. 添加配置类

      @Configuration
      @ConditionalOnWebApplication(
          type = ConditionalOnWebApplication.Type.SERVLET
      )
      @ConditionalOnClass({CommonFilter.class})
      @ConditionalOnProperty(
          name = {"spring.cloud.sentinel.enabled"},
          matchIfMissing = true
      )
      //@EnableConfigurationProperties({SentinelProperties.class})
      // 从sentinel源码中copy出来
      public class SentinelWebAutoConfiguration {
          //private static final Logger log = LoggerFactory.getLogger(com.alibaba.cloud.sentinel.SentinelWebAutoConfiguration.class);
          //    @Autowired
          //    private SentinelProperties properties;
          @Autowired
          private Optional<UrlCleaner> urlCleanerOptional;
          @Autowired
          private Optional<UrlBlockHandler> urlBlockHandlerOptional;
          @Autowired
          private Optional<RequestOriginParser> requestOriginParserOptional;
      
          public SentinelWebAutoConfiguration() {
          }
      
          @PostConstruct
          public void init() {
              this.urlBlockHandlerOptional.ifPresent(WebCallbackManager::setUrlBlockHandler);
              this.urlCleanerOptional.ifPresent(WebCallbackManager::setUrlCleaner);
              this.requestOriginParserOptional.ifPresent(WebCallbackManager::setRequestOriginParser);
          }
      
          @Bean
          @ConditionalOnProperty(
              name = {"spring.cloud.sentinel.filter.enabled"},
              matchIfMissing = true
          )
          public FilterRegistrationBean sentinelFilter() {
              FilterRegistrationBean<Filter> registration = new FilterRegistrationBean();
              //        com.alibaba.cloud.sentinel.SentinelProperties.Filter filterConfig = this.properties.getFilter();
              //        if (filterConfig.getUrlPatterns() == null || filterConfig.getUrlPatterns().isEmpty()) {
              //            List<String> defaultPatterns = new ArrayList();
              //            defaultPatterns.add("/*");
              //            filterConfig.setUrlPatterns(defaultPatterns);
              //        }
      
              //        registration.addUrlPatterns((String[])filterConfig.getUrlPatterns().toArray(new String[0]));
              Filter filter = new CommonFilter();
              registration.setFilter(filter);
              registration.setOrder(-2147483648);
              //registration.setOrder(filterConfig.getOrder());
              registration.addInitParameter("HTTP_METHOD_SPECIFY", String.valueOf(false));
              //log.info("[Sentinel Starter] register Sentinel CommonFilter with urlPatterns: {}.", filterConfig.getUrlPatterns());
              return registration;
          }
      }
      
      
      1. 在控制台增加流控规则,指定来源

        ![1585061229263](file://E:/Sentinel%E4%BD%BF%E7%94%A8%E5%AE%9E%E8%B7%B5/%E8%AE%B2%E4%B9%89/assets/1585061229263.png?lastModify=1604838986)

      2. 访问资源增加origin=pc,才会被限流,不是pc时可以任意访问。例如:http://localhost:8083/info?origin=pc

    3.4.2 流控-并发线程控制

    并发线程数限流用于保护业务线程数不被耗尽。

    例如,当下游服务出现响应时间延长,则会导致上游服务吞吐量下降和更多的线程数占用,极端情况下甚至导致线程池耗尽。

    为了避免这种情况,业内比较常见的解决方案是线程隔离(hystrix)。给不同的业务逻辑分配不同的线程池。但是这种方案会造成大量的线程上下文切换,严重影响效率。

    sentinle实现原理:统计请求上下文线程数、超出阈值,新的请求被拒绝,效果当前与信号隔离。

    // 测试,在控制台增加并发线程控制规则,可以发现有一部分请求会被隔离掉,这是因为超过了线程的阈值。
    public class MyThread implements Runnable {
        @Override
        public void run() {
            RestTemplate restTemplate = new RestTemplate();
            String forObject = restTemplate.getForObject("http://localhost:8083/info", String.class);
            System.out.println(forObject);
        }
    }
    
    public class ThreadTest {
        public static void main(String[] args) throws InterruptedException {
            //线程池
            ExecutorService pool = Executors.newCachedThreadPool();
            for(int i=0;i<100;i++){
                MyThread myThread = new MyThread();
                pool.execute(myThread);
            }
        }
    }
    

    3.4.3 流控-流控模式、流控效果

    1. QPS关联模式-快速失败:场景为两个方法会产生性能上相互影响,如读方法和写方法。若写方法优先级高,在定义读方法流控规则时,将关联资源定为写方法,当写方法操作超过阈值,会限制读方法。
    2. QPS链路模式快速失败:链路即从指定入口访问的资源达到阈值则限流

    流控效果:

    1. 快速失败:达到限流规则,不允许进行访问

    2. warmUp:冷启动/预热,对流量的增加是逐步增加的,适用于处理突发性流量。threshold (阈值)/coldFactor(冷加载因子,默认为3)=最初的阈值,接着最初的阈值*预热时长来最终实现限流。

    3. 排队等待/匀速器:让请求匀速通过,适用处理间隔性突发的流量。

      原理:如果当前请求距离上个通过的请求通过的时间间隔不小于预设值,则让当前请求通过;

      否则,计算当前请求的预期通过时间,如果该请求的预期通过时间小于规则预设的 timeout 时间,则该请求会等待直到预设时间到来通过(排队等待处理);

      若预期的通过时间超出最大排队时长,则直接拒接这个请求。

      ![1585067276895](file://E:/Sentinel%E4%BD%BF%E7%94%A8%E5%AE%9E%E8%B7%B5/%E8%AE%B2%E4%B9%89/assets/1585067276895.png?lastModify=1604840075)

    3.4.4 熔断降级

    避免某个接口异常,造成请求堆积,导致系统雪崩。

    1. 熔断降级策略

    3.4.4.1 熔断策略-平均响应时间:rt

    1s 内持续进入 5 个请求,对应时刻的平均响应时间超过阈值count,以 ms 为单位),那么会在下一个时间窗口DegradeRule 中的 timeWindow,以 s 为单位)之内,对这个方法的调用都会自动地熔断

    3.4.4.2 熔断策略-异常比例

    当资源的每秒请求量 >= 5,并且每秒异常总数通过量的比值超过阈值每秒异常总数/通过量 > 阈值)之后,资源进入降级状态,即在下一个时间窗口,对这个方法的调用自动熔断。异常比率的阈值范围是 [0.0, 1.0],代表 0% - 100%

    3.4.4.3 熔断策略-异常数

    当资源近 1 分钟的异常数目超过阈值之后会进行熔断。注意由于统计是分钟级别的,若 timeWindow 小于 60s,则结束熔断状态后仍可能再进入熔断状态。

    3.4.5 热点参数限流

    热点就是经常被访问的数据,也叫热数据。

    热点参数限流会统计访问时所带的参数,根据定义的规则进行参数级别的限流。也是一种流量控制。

    =sentinel热点参数限流实现原理:Sentinel 利用 LRU 策略统计最近最常访问的热点参数,结合令牌桶算法来进行参数级别的流控。

    举例:对id为1的参数进行限流,那么在访问时,生效的级别只会在id为1时,当id为其他值时不会进行限流控制。

    ![1584713089706](file://E:/Sentinel%E4%BD%BF%E7%94%A8%E5%AE%9E%E8%B7%B5/%E8%AE%B2%E4%B9%89/assets/1584713089706.png?lastModify=1604846149)

    3.4.5.1 热点参数位限流

    ![1584714823721](file://E:/Sentinel%E4%BD%BF%E7%94%A8%E5%AE%9E%E8%B7%B5/%E8%AE%B2%E4%B9%89/assets/1584714823721.png?lastModify=1604846213)

    上述配置声明:对于hot资源通过QPS限流模式对访问路径上第一个参数,每秒只能访问一次。

    3.4.5.1 热点参数值限流

    ![1584715354860](file://E:/Sentinel%E4%BD%BF%E7%94%A8%E5%AE%9E%E8%B7%B5/%E8%AE%B2%E4%B9%89/assets/1584715354860.png?lastModify=1604846338)

    上述内容声明:对于hot资源通过QPS限流模式对访问路径上第一个参数,每秒只能访问一次。但 当参数值2,每秒能访问两次。当参数值3,每秒能访问10000次。

    3.4.6 系统自适应限流

    文档:https://github.com/alibaba/Sentinel/wiki/系统自适应限流

    3.4.6.1 系统保护简介

    sentinel从 应用的load、cpu利用率、总体平均rt、入口QPS和并发线程几个维度的监控指标,通过自适应的流控策略,让系统的入口流量和系统的负载达到一个平衡。

    系统保护目的:1. 保证系统不会拖垮 2.系统稳定的前提下,保证系统的吞吐量

    以往的系统保护思路:根据硬指标,即load来做系统过载的保护,当系统负载高于阈值,就会禁止或减少流量的进入,load好转即恢复流量的进入。

    以往设计思路的弊端

    1. load是一个结果,根据load 的情况来调节流量的通过率会有延迟性
    2. 恢复慢:当下游应用异常导致RT过高,从而load达到了一个很高的点,当下游应用恢复,这是load应然很高,通过率依然还在受限制

    sentinel系统保护的思路

    1. 根据系统能处理的请求允许进来的请求做平衡,而不是通过一个间接的指标(load)做限流。
    2. 在sentinel系统保护的做法中,load是作用启动自适应保护因子,而允许通过的流量由处理请求的能力,即请求的响应时间以及当前系统正在处理的请求速率来决定。

    3.4.6.2 系统保护-规则

    系统保护规则是从应用级别的入口流量做控制的,并且仅对入口流量生效(进入应用的流量EntryType.IN,如web服务或Dubbo服务端接收的请求)。

    从单台机器的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 达到阈值即触发系统保护

    3.4.6.3 系统保护原理

    3.5 动态规则、规则持久化

    ​ Sentinel默认会把规则信息保存到内存中,当服务重启之后,规则就会丢失。可以将规存储在文件、数据库或者配置中心当中。

    3.5.1 规则推送模式

    • pull(拉)模式:客户端定期轮询拉取规则,规则持久化,但实时性不能保证,拉取过于频繁还有性能问题。
    • push(推)模式:规则中心统计推送,客户端通过注册监听器的方式监听变化,可以使用Nacos、zookeeper等配置中心。这种方式有更好的实时性和以执行的保证。推荐使用

    4. 负载

    文档:https://juejin.im/post/6844903608371118094

    在衡量服务器的性能时,经常会涉及到几个指标,load(机器负载)、cpu、mem、qps、rt等。每个指标都有其独特的意义,很多时候在线上出现问题时,往往会伴随着某些指标的异常。

    负载(load)是linux机器的一个重要指标,直观了反应了机器当前的状态。

    在UNIX系统中,系统负载是对当前CPU工作量的度量,被定义为特定时间间隔内运行队列中的平均线程数。load average 表示机器一段时间内的平均load。这个值越低越好。负载过高会导致机器无法处理其他请求及操作,甚至导致死机。Linux的负载高,主要是由于CPU使用、内存使用、IO消耗三部分构成。任意一项使用过多,都将导致服务器负载的急剧攀升。

    load值代表的是对应时间内的jobs的平均数量,比如load1就表示过去1分钟内的jobs数量的平均值。

    4.1 查看机器负载

    在Linux机器上,有多个命令都可以查看机器的负载信息。其中包括uptimetopw等。

    • uptime:能够打印系统总共运行了多长时间和系统的平均负载。
    ➜  ~ uptime
    13:29  up 23:41, 3 users, load averages: 1.74 1.87 1.97
    现在时间、系统已经运行了多长时间、目前有多少登陆用户、系统在过去的1分钟、5分钟和15分钟内的平均负载。
    
    • w:能够打印当前时间,系统启动到现在的时间,登录用户的数目,系统在最近1分钟、5分钟和15分钟的平均负载。然后是每个用户的各项数据,项目显示顺序如下:登录帐号、终端名称、远 程主机名、登录时间、空闲时间、JCPU、PCPU、当前正在运行进程的命令行]。

    • top:是Linux下常用的性能分析工具,能够实时显示系统中各个进程的资源占用状况,类似于Windows的任务管理器。

    4.2 机器正常负载范围

    没有一个明确的指标。

    可以根据自己机器的实际情况,建立一个指标的基线(如近一个月的平均值),只要日常的load在基线上下范围内不太大都可以接收,如果差距太多可能就要人为介入检查了。

    阮一峰对此的建议为:
    当系统负荷持续大于0.7,你必须开始调查了,问题出在哪里,防止情况恶化。
    当系统负荷持续大于1.0,你必须动手寻找解决办法,把这个值降下来。
    当系统负荷达到5.0,就表明你的系统有很严重的问题,长时间没有响应,或者接近死机了。你不应该让系统达到这个值。

    注:这里的指标是但cpu的,如果是多喝系统,需乘上cpu的数量

    4.3 如何降低负荷

    CPU使用、内存使用、IO消耗都可能导致负载高。如果是软件问题,有可能由于Java中的某些线程被长时间占用、大量内存持续占用等导致。建议从以下几个方面排查代码问题:

    1. 是否有内存泄露导致频繁GC
    2. 是否有死锁发生
    3. 是否有大字段的读写
    4. 会不会是数据库操作导致的,排查SQL语句问题
    5. 死循环

    这里还有个建议,如果发现线上机器Load飙高,可以考虑先把堆栈内存dump下来后,进行重启,暂时解决问题,然后再考虑回滚和排查问题。

    4.4 排查思路

    1、使用uptime查看当前load,发现load飙高。

    ➜  ~ uptime
    13:29  up 23:41, 3 users, load averages: 10 10 10
    复制代码
    

    2、使用top命令,查看占用CPU较高的进程ID。

    ➜  ~ top
    
    PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND
    1893 admin     20   0 7127m 2.6g  38m S 181.7 32.6  10:20.26 java
    复制代码
    

    发现PID为1893的进程占用CPU 181%。而且是一个Java进程,基本断定是软件问题。

    3、使用 top命令,查看具体是哪个线程占用率较高

    ➜  ~ top -Hp 1893
    PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND
    4519 admin     20   0 7127m 2.6g  38m R 18.6 32.6   0:40.11 java
    复制代码
    

    4、使用printf命令查看这个线程的16进制

    ➜  ~ printf %x 4519
    11a7
    复制代码
    

    5、使用jstack命令查看当前线程正在执行的方法。(Java命令学习系列(二)——Jstack)

    ➜  ~ jstack 1893 |grep -A 200 11a7
    "thread-5" #500 daemon prio=10 os_prio=0 tid=0x00007f632314a800 nid=0x11a2 runnable [0x000000005442a000]
    java.lang.Thread.State: RUNNABLE
    at sun.misc.URLClassPath$Loader.findResource(URLClassPath.java:684)
    at sun.misc.URLClassPath.findResource(URLClassPath.java:188)
    at java.net.URLClassLoader$2.run(URLClassLoader.java:569)
    at java.net.URLClassLoader$2.run(URLClassLoader.java:567)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.net.URLClassLoader.findResource(URLClassLoader.java:566)
    at org.hibernate.validator.internal.xml.ValidationXmlParser.getInputStreamForPath(ValidationXmlParser.java:248)
    at com.hollis.test.util.BeanValidator.validate(BeanValidator.java:30)
    复制代码
    

    从上面的线程的栈日志中,可以发现,当前占用CPU较高的线程正在执行我代码的com.hollis.test.util.BeanValidator.validate(BeanValidator.java:30)类。那么就可以去排查这个类是否用法有问题了。

    6、还可以使用jstat(Java命令学习系列(四)——jstat)来查看GC情况,看看是否有频繁FGC,然后再使用jmap(Java命令学习系列(三)——Jmap)来dump内存,查看是否存在内存泄露。

  • 相关阅读:
    usb3.0 3.1 3.2的区别和联系
    在SQL Server 数据表中显示“说明列”步骤
    在platformIO点亮ESP32S3板载RGB灯
    conda指令合集
    dockercompose安装canal
    dockercompose安装elasticsearch及kibana
    dcokercompose安装nacos并连接外部MySQL
    线程基础知识14 ReentrantLock和ReentrantReadWriteLock
    线程基础知识11原子类
    线程基础知识13死锁
  • 原文地址:https://www.cnblogs.com/zhaoyuan72/p/14582399.html
Copyright © 2020-2023  润新知