第十七章 SpringCloud Alibaba入门简介 一、为什么使用Alibaba 1.因为:spring netflix进入维护模式
https://spring.io/blog/2018/12/12/spring-cloud-greenwich-rc1-available-now#spring-cloud-netflix-projects-entering-maintenance-modeopen in new window
什么是维护模式:spring cloud团队将不会再向模块添加新功能,我们将修复block级别的bug以及安全问题,我们也会考虑并审查社区的小型pull request。我们打算继续支持这些模块,知道Greenwich版本被普遍采用至少一年
SpringCloud Netflix将不再开发新的组件
以下spring cloud netflix模块和响应的starter将进入维护模式:
spring-cloud-netflix-archaius spring-cloud-netflix-hystrix-contract spring-cloud-netflix-hystrix-dashboard spring-cloud-netflix-hystrix-stream spring-cloud-netflix-hystrix spring-cloud-netflix-ribbon spring-cloud-netflix-turbine-stream spring-cloud-netflix-turbine spring-cloud-netflix-zuul 我们都知道SpringCloud版本迭代是比较快的,因而出现了很多重大ISSUE都还来不及Flix就又推另一个RELEASE了。进入维护模式意思就是目前以及以后一段时间SpingCloud Netflix提供的报务和功能就这么多了,不在开发新的组件和功能了。以后将以雏护和Merge分支Full Request为主。
2.所以:spring cloud alibaba来了
https://spring.io/projects/spring-cloud-alibabaopen in new window
2018.10.31,spring cloud Alibaba正式入驻了Spring Cloud官方孵化器,并在Maven中央库发布了第一个版本
主要功能:
服务限流降级 :默认支持 WebServlet、WebFlux, OpenFeign、RestTemplate、Spring Cloud Gateway, Zuul, Dubbo 和 RocketMQ 限流降级功能的接入,可以在运行时通过控制台实时修改限流降级规则,还支持查看限流降级 Metrics 监控。服务注册与发现 :适配 Spring Cloud 服务注册与发现标准,默认集成了 Ribbon 的支持。 分布式配置管理:支持分布式系统中的外部化配置,配置更改时自动刷新。消息驱动能力 :基于 Spring Cloud Stream 为微服务应用构建消息驱动能力。分布式事务 :使用 @GlobalTransactional 注解, 高效并且对业务零侵入地解决分布式事务问题。。阿里云对象存储 :阿里云提供的海量、安全、低成本、高可靠的云存储服务。支持在任何应用、任何时间、任何地点存储和访问任意类型的数据。分布式任务调度 :提供秒级、精准、高可靠、高可用的定时(基于 Cron 表达式)任务调度服务。同时提供分布式的任务执行模型,如网格任务。网格任务支持海量子任务均匀分配到所有 Worker(schedulerx-client)上执行。阿里云短信服务 :覆盖全球的短信服务,友好、高效、智能的互联化通讯能力,帮助企业迅速搭建客户触达通道。组件:
Sentinel :把流量作为切入点,从流量控制、熔断降级、系统负载保护等多个维度保护服务的稳定性。Nacos :一个更易于构建云原生应用的动态服务发现、配置管理和服务管理平台。RocketMQ :一款开源的分布式消息系统,基于高可用分布式集群技术,提供低延时的、高可靠的消息发布与订阅服务。Dubbo :Apache Dubbo™ 是一款高性能 Java RPC 框架。Seata :阿里巴巴开源产品,一个易于使用的高性能微服务分布式事务解决方案。Alibaba Cloud OSS : 阿里云对象存储服务(Object Storage Service,简称 OSS),是阿里云提供的海量、安全、低成本、高可靠的云存储服务。您可以在任何应用、任何时间、任何地点存储和访问任意类型的数据。Alibaba Cloud SchedulerX : 阿里中间件团队开发的一款分布式任务调度产品,提供秒级、精准、高可靠、高可用的定时(基于 Cron 表达式)任务调度服务。Alibaba Cloud SMS : 覆盖全球的短信服务,友好、高效、智能的互联化通讯能力,帮助企业迅速搭建客户触达通道。 二、如何使用? 按照官网一步一步来
https://spring.io/projects/spring-cloud-alibaba#learnopen in new window
新建父工程 ydl-cloud-alibaba
父工程引入
From: 元动力 1 2 3 4 5 6 7 8 9 10 11 < dependencyManagement> < dependencies> < dependency> < groupId> com.alibaba.cloud< /groupId> < artifactId> spring-cloud-alibaba-dependencies< /artifactId> < version> 2021.0.1.0< /version> < type> pom< /type> < scope> import< /scope> < /dependency> < /dependencies> < /dependencyManagement>
三、版本对应 我们按照最新的学习
https://github.com/alibaba/spring-cloud-alibaba/wiki/版本说明open in new window
Spring Cloud Alibaba Version Sentinel Version Nacos Version RocketMQ Version Dubbo Version Seata Version 2021.0.1.0* 1.8.3 1.4.2 4.9.2 2.7.15 1.4.2
Spring Cloud Alibaba Version Spring Cloud Version Spring Boot Version 2021.0.1.0 Spring Cloud 2021.0.1 2.6.3
第十九章 服务注册和配置中心 一、是什么 官方文档:https://nacos.io/zh-cn/docs/what-is-nacos.htmlopen in new window
1.nacos(NAming COnfiguration Service):服务注册和配置中心
From: 元动力 1 2 3 Nacos = Eureka + Config + Bus 替代Eureka做服务注册中心 替代Config做服务配置中心
From: 元动力 1 2 github地址: https://github.com/alibaba/Nacos Nacos地址: https://nacos.io/zh-cn/
2.Nacos 概念
地域 物理的数据中心,资源创建成功后不能更换。
可用区 同一地域内,电力和网络互相独立的物理区域。同一可用区内,实例的网络延迟较低。
接入点 地域的某个服务的入口域名。
命名空间 用于进行租户粒度的配置隔离。不同的命名空间下,可以存在相同的 Group 或 Data ID 的配置。Namespace 的常用场景之一是不同环境的配置的区分隔离,例如开发测试环境和生产环境的资源(如配置、服务)隔离等。
配置 在系统开发过程中,开发者通常会将一些需要变更的参数、变量等从代码中分离出来独立管理,以独立的配置文件的形式存在。目的是让静态的系统工件或者交付物(如 WAR,JAR 包等)更好地和实际的物理运行环境进行适配。配置管理一般包含在系统部署的过程中,由系统管理员或者运维人员完成。配置变更是调整系统运行时的行为的有效手段。
配置管理 系统配置的编辑、存储、分发、变更管理、历史版本管理、变更审计等所有与配置相关的活动。
配置项 一个具体的可配置的参数与其值域,通常以 param-key=param-value 的形式存在。例如我们常配置系统的日志输出级别(logLevel=INFO|WARN|ERROR) 就是一个配置项。
配置集 一组相关或者不相关的配置项的集合称为配置集。在系统中,一个配置文件通常就是一个配置集,包含了系统各个方面的配置。例如,一个配置集可能包含了数据源、线程池、日志级别等配置项。
配置集 ID Nacos 中的某个配置集的 ID。配置集 ID 是组织划分配置的维度之一。Data ID 通常用于组织划分系统的配置集。一个系统或者应用可以包含多个配置集,每个配置集都可以被一个有意义的名称标识。Data ID 通常采用类 Java 包(如 com.taobao.tc.refund.log.level)的命名规则保证全局唯一性。此命名规则非强制。
配置分组 Nacos 中的一组配置集,是组织配置的维度之一。通过一个有意义的字符串(如 Buy 或 Trade )对配置集进行分组,从而区分 Data ID 相同的配置集。当您在 Nacos 上创建一个配置时,如果未填写配置分组的名称,则配置分组的名称默认采用 DEFAULT_GROUP 。配置分组的常见场景:不同的应用或组件使用了相同的配置类型,如 database_url 配置和 MQ_topic 配置。
配置快照 Nacos 的客户端 SDK 会在本地生成配置的快照。当客户端无法连接到 Nacos Server 时,可以使用配置快照显示系统的整体容灾能力。配置快照类似于 Git 中的本地 commit,也类似于缓存,会在适当的时机更新,但是并没有缓存过期(expiration)的概念。
服务 通过预定义接口网络访问的提供给客户端的软件功能。
服务名 服务提供的标识,通过该标识可以唯一确定其指代的服务。
服务注册中心 存储服务实例和服务负载均衡策略的数据库。
服务发现 在计算机网络上,(通常使用服务名)对服务下的实例的地址和元数据进行探测,并以预先定义的接口提供给客户端进行查询。
元信息 Nacos数据(如配置和服务)描述信息,如服务版本、权重、容灾策略、负载均衡策略、鉴权配置、各种自定义标签 (label),从作用范围来看,分为服务级别的元信息、集群的元信息及实例的元信息。
应用 用于标识服务提供方的服务的属性。
服务分组 不同的服务可以归类到同一分组。
虚拟集群 同一个服务下的所有服务实例组成一个默认集群, 集群可以被进一步按需求划分,划分的单位可以是虚拟集群。
实例 提供一个或多个服务的具有可访问网络地址(IP:Port)的进程。
权重 实例级别的配置。权重为浮点数。权重越大,分配给该实例的流量越大。
健康检查 以指定方式检查服务下挂载的实例 (Instance) 的健康度,从而确认该实例 (Instance) 是否能提供服务。根据检查结果,实例 (Instance) 会被判断为健康或不健康。对服务发起解析请求时,不健康的实例 (Instance) 不会返回给客户端。
健康保护阈值 为了防止因过多实例 (Instance) 不健康导致流量全部流向健康实例 (Instance) ,继而造成流量压力把健康实例 (Instance) 压垮并形成雪崩效应,应将健康保护阈值定义为一个 0 到 1 之间的浮点数。当域名健康实例数 (Instance) 占总服务实例数 (Instance) 的比例小于该值时,无论实例 (Instance) 是否健康,都会将这个实例 (Instance) 返回给客户端。这样做虽然损失了一部分流量,但是保证了集群中剩余健康实例 (Instance) 能正常工作。
3.架构
image-20220419101248331 数据模型:
Nacos 数据模型 Key 由三元组唯一确定, Namespace默认是空串,公共命名空间(public),分组默认是 DEFAULT_GROUP。
image-20220419101435094 二、Nacos2.0 新增长连接功能
From: 元动力 1 Nacos2.0版本相比1.X新增了gRPC的通信方式,因此需要增加2个端口。新增端口是在配置的主端口(server.port)基础上,进行一定偏移量自动生成。
新增 鉴权插件
新增 配置加密
三、与其他注册中心对比 服务注册与服务框架 CAP模型 控制台管理 社区活跃度 Eureka AP高可用 支持 低(2.x版本闭源) Zookeeper CP一致 支持 中 Consul CP 支持 高 Nacos AP+CP 支持 高
四、切换 nacos可以切换 AP 和 CP ,可使用如下命令切换成CP模式
From: 元动力 1 curl -X PUT '$NACOS_SERVER:8848/nacos/v1/ns/operator/switches?entry=serverMode&value=CP'
五、下载 1.下载地址: https://github.com/alibaba/nacos/releasesopen in new window
直接下载网址:https://github.com/alibaba/nacos/releases/download/2.0.4/nacos-server-2.0.4.zipopen in new window
2.下载压缩包以后解压,进入bin目录,打开dos窗口,执行startup命令启动它。
From: 元动力 1 startup.cmd -m standalone
3.端口号8848
4.可访问 : http://localhost:8848/nacos/index.htmlopen in new window 地址,默认账号密码都是nacos
image-20220419165738557 六、注册中心功能 急速构建
1、服务提供者1 新建模块 nacos-provider8000
pom
From: 元动力 1 2 3 4 5 < !--SpringCloud Alibaba nacos--> < dependency> < groupId> com.alibaba.cloud< /groupId> < artifactId> spring-cloud-starter-alibaba-nacos-discovery< /artifactId> < /dependency>
yml
From: 元动力 1 2 3 4 5 6 7 8 9 10 11 server: port: 8000 spring: application: name: nacos-provider cloud: nacos: discovery: server-addr: localhost:8848
主启动类
controller 显示server.port
From: 元动力 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 package com.ydlclass.nacos.provider8000.controller;import org.springframework.beans.factory.annotation.Value;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.PathVariable;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController;@RestController @RequestMapping("/goods") public class GoodsController { @Value("${server.port}") Integer port; @GetMapping("/findById/{id}") public String findById (@PathVariable("id") Integer id) { return "nacos provider.port:" +port+"|id:" +id; } }
image-20220419234430142 2、服务提供者2 新建模块 nacos-provider8001
pom
From: 元动力 1 2 3 4 5 < !--SpringCloud Alibaba nacos--> < dependency> < groupId> com.alibaba.cloud< /groupId> < artifactId> spring-cloud-starter-alibaba-nacos-discovery< /artifactId> < /dependency>
yml
From: 元动力 1 2 3 4 5 6 7 8 9 10 server: port: 8001 spring: application: name: nacos-provider cloud: nacos: discovery: server-addr: localhost:8848
主启动类
controller 显示server.port
From: 元动力 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 package com.ydlclass.nacos.provider8000.controller;import org.springframework.beans.factory.annotation.Value;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.PathVariable;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController;@RestController @RequestMapping("/goods") public class GoodsController { @Value("${server.port}") Integer port; @GetMapping("/findById/{id}") public String findById (@PathVariable("id") Integer id) { return "nacos provider.port:" +port+"|id:" +id; } }
image-20220419234839829 3、服务消费者 新建模块 nacos-conusmer9000
pom
From: 元动力 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 < ?xml version="1.0" encoding="UTF-8"?> < 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.0< /modelVersion> < parent> < groupId> com.ydlclass< /groupId> < artifactId> ydl-cloud-alibaba< /artifactId> < version> 1.0.0< /version> < relativePath/> < !-- lookup parent from repository --> < /parent> < artifactId> nacos-conusmer9000< /artifactId> < name> nacos-conusmer9000< /name> < description> nacos-conusmer9000< /description> < properties> < java.version> 1.8< /java.version> < /properties> < dependencies> < dependency> < groupId> org.springframework.boot< /groupId> < artifactId> spring-boot-starter-web< /artifactId> < /dependency> < !--SpringCloud Alibaba nacos--> < dependency> < groupId> com.alibaba.cloud< /groupId> < artifactId> spring-cloud-starter-alibaba-nacos-discovery< /artifactId> < exclusions> < exclusion> < groupId> org.springframework.cloud< /groupId> < artifactId> spring-cloud-starter-netflix-ribbon< /artifactId> < /exclusion> < /exclusions> < /dependency> < dependency> < groupId> org.springframework.cloud< /groupId> < artifactId> spring-cloud-starter-loadbalancer< /artifactId> < version> 3.1.1< /version> < /dependency> < /dependencies> < build> < plugins> < plugin> < groupId> org.springframework.boot< /groupId> < artifactId> spring-boot-maven-plugin< /artifactId> < /plugin> < /plugins> < /build> < /project>
yml
From: 元动力 1 2 3 4 5 6 7 8 9 10 11 12 13 server: port: 9000 spring: application: name: nacos-consumer cloud: loadbalancer: ribbon: enabled: false nacos: discovery: server-addr: localhost:8848
主启动类
注册RestTemplate
From: 元动力 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 package com.ydlclass.nacos.conusmer9000.config;import org.springframework.boot.web.client.RestTemplateBuilder;import org.springframework.cloud.client.loadbalancer.LoadBalanced;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.web.client.RestTemplate;@Configuration public class RestTemplateConfig { @Bean @LoadBalanced public RestTemplate restTemplate () { return new RestTemplate (); } }
controller,调用
From: 元动力 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 package com.ydlclass.nacos.conusmer9000.controller;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.PathVariable;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController;import org.springframework.web.client.RestTemplate;@RestController @RequestMapping("/order") public class OrderController { @Autowired private RestTemplate restTemplate; @GetMapping("/add/{id}") public String add (@PathVariable("id") Integer id) { String url="http://nacos-provider/goods/findById/" +id; String result = restTemplate.getForObject(url, String.class); return result; } }
测试:http://localhost:9000/order/add/1open in new window
image-20220420101138103 image-20220420101147833 4、整合feign 1.在pom中导入
From: 元动力 1 2 3 4 5 < !-- openfeign --> < dependency> < groupId> org.springframework.cloud< /groupId> < artifactId> spring-cloud-starter-openfeign< /artifactId> < /dependency>
2.在主启动类上加上@EnableFeignClients,激活feign。
在主启动类上加上@EnableFeignClients,激活feign。
3.新建feign接口
From: 元动力 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 package com.ydlclass.nacos.conusmer9000.feign; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; /** * @Created by IT李老师 * 公主号 “元动力课堂” * 个人微 itlils */ @FeignClient("nacos-provider") public interface GoodsFeign { @GetMapping("/goods/findById/ {id }") public String findById(@PathVariable(" id")Integer id); }
4.controller新写方法
From: 元动力 1 2 3 4 5 6 7 8 9 10 @Autowired GoodsFeign goodsFeign;@GetMapping("/add2/{id}") public String add2 (@PathVariable("id") Integer id) { String str = goodsFeign.findById(id); return str; }
测试:http://localhost:9000/order/add2/1open in new window
image-20220420101855472 image-20220420101839462 七、服务注册中心对比 1.Nacos 生态图
image-20220420102428162 2.Nacos和CAP
image-20220420102902745 3.对比其他注册中心
image-20220420102925313 A:可用性 C:一致性 P:分区容错性
Nacos默认AP。
切换CP:
From: 元动力 1 curl -X PUT '$NACOS_SERVER:8848/nacos/v1/ns/operator/switches?entry=serverMode&value=CP'
八、配置中心功能 1、创建工程nacos-client7777 1.pom
From: 元动力 1 2 3 4 5 6 7 8 9 10 11 12 13 14 < dependency> < groupId> com.alibaba.cloud< /groupId> < artifactId> spring-cloud-starter-alibaba-nacos-config< /artifactId> < /dependency> < !--SpringCloud Alibaba 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-bootstrap< /artifactId> < /dependency>
2.application.yml
From: 元动力 1 2 3 spring: profiles: active: dev
bootstrap.yml
From: 元动力 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 server: port: 7777 spring: application: name: nacos-config-client cloud: nacos: discovery: server-addr: localhost:8848 config: server-addr: localhost:8848 file-extension: yaml
3.主启动类
4.controller
From: 元动力 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 package com.ydlclass.nacos.client7777.controller;import org.springframework.beans.factory.annotation.Value;import org.springframework.cloud.context.config.annotation.RefreshScope;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController;@RestController @RequestMapping("/config") @RefreshScope public class ConfigClientController { @Value("${name}") String name; @GetMapping("/name") public String name () { return name; } }
5.在Nacos中添加配置信息
https://nacos.io/zh-cn/docs/quick-start-spring-cloud.htmlopen in new window
image-20220420105534316 6.测试:http://localhost:7777/config/nameopen in new window
image-20220420111758851 更改配置
image-20220420111944907 再次访问接口。说明bus的功能也实现了。
image-20220420112019087 2、分类配置 image-20220420124712175 问题1:实际开发中,通常一个系统会准备dev/test/prod环境。如何保证环境启动时服务能正确读取nacos上相应环境的配置文件?
答案:namespace区分
问题2:一个大型分布式微服务系统有很多微服务子项目,每个微服务项目又都会有相应的开发环境、测试环境、预发环境、正式环境。那怎么对微服务配置进行管理呢?
答案:用group把不同的微服务划分到同一个分组里面去
Service就是微服务,一个service可以包含多个cluster集群,nacos默认cluster是DEFAULT,Cluster是对指定微服务的一个虚拟划分。
比方说为了容灾,将service微服务分别部署在了北京机房和上海机房,这是就可以给北京机房的service微服务起一个集群名称BJ,给上海的service微服务起一个集群名称SH,还可以尽量让同一个机房的微服务互相调用,以提升效率。
dgn方案 1.dataid方案(就是nacos的文件名)
指定spring.profile.active和配置文件的dataID来使不太环境下读取不同的配置 配置空间+配置分组+新建dev和test两个dataid:就是创建-后不同的两个文件名nacos-config-client-dev.yaml、nacos-config-client-test.yaml 通过IDEA里的spring.profile.active属性就能进行多环境下配置文件的读取。
2.Group方案(默认DEFAULT_GROUP)
在nacos创建配置文件时,给文件指定分组。 在IDEA中该group内容 实现的功能:当修改开发环境时,只会从同一group中进行切换。
From: 元动力 1 2 3 4 5 6 7 8 9 10 11 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: BJ_GROUP
image-20220420122132968 3.namespace方案(默认public)
这个是不允许删除的,可以创建一个新的命名空间,会自动给创建的命名空间一个流水号。 在nacos新建命名空间,自动出现e79f32ec-974a-4e90-9086-3ae5c64db7e3
image-20220420124924806 在IDEA的yml中指定命名空间namespace: e79f32ec-974a-4e90-9086-3ae5c64db7e3
From: 元动力 1 2 3 4 5 6 7 8 9 10 11 12 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: BJ_GROUP namespace: e79f32ec-974a-4e90-9086-3ae5c64db7e3
image-20220420122430460 最后,dataid、group、namespace 三者关系如下:(不同的dataid,是相互独立的,不同的group是相互隔离的,不同的namespace也是相互独立的)
image-20220420120816153 九、集群和持久化配置(重要) image-20220420151535043 https://nacos.io/zh-cn/docs/deployment.htmlopen in new window
1、Nacos部署环境 Nacos定义为一个IDC内部应用组件,并非面向公网环境的产品,建议在内部隔离网络环境中部署,强烈不建议部署在公共网络环境。
以下文档中提及的VIP,网卡等所有网络相关概念均处于内部网络环境。
2、Nacos支持三种部署模式 单机模式 - 用于测试和单机试用。 集群模式 - 用于生产环境,确保高可用。 多集群模式 - 用于多数据中心场景。 3、单机模式下运行Nacos Linux/Unix/Mac
From: 元动力 1 startup.sh -m standalone
Windows
From: 元动力 1 startup.cmd -m standalone
4、单机模式支持mysql 在0.7版本之前,在单机模式时nacos使用嵌入式数据库实现数据的存储,不方便观察数据存储的基本情况。0.7版本增加了支持mysql数据源能力,具体的操作步骤:
1.安装数据库,版本要求:5.6.5+ 2.初始化mysql数据库,数据库初始化文件:nacos-mysql.sql 3.修改conf/application.properties文件,增加支持mysql数据源配置(目前只支持mysql),添加mysql数据源的url、用户名和密码。
From: 元动力 1 2 3 4 5 6 spring.datasource.platform=mysql db.num=1 db.url.0=jdbc:mysql://11.162.196.16:3306/nacos_devtest?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true db.user=nacos_devtest db.password=youdontknow
再以单机模式启动nacos,nacos所有写嵌入式数据库的数据都写到了mysql
image-20220420153743075 5、集群部署说明 集群模式部署
这个快速开始手册是帮忙您快速在你的电脑上,下载安装并使用Nacos,部署生产使用的集群模式。
集群部署架构图
因此开源的时候推荐用户把所有服务列表放到一个vip下面,然后挂到一个域名下面
http://ip1open in new window :port/openAPI 直连ip模式,机器挂则需要修改ip才可以使用。
http://SLBopen in new window :port/openAPI 挂载SLB模式(内网SLB,不可暴露到公网,以免带来安全风险),直连SLB即可,下面挂server真实ip,可读性不好。
http://nacos.comopen in new window :port/openAPI 域名 + SLB模式(内网SLB,不可暴露到公网,以免带来安全风险),可读性好,而且换ip方便,推荐模式
deployDnsVipMode.jpg image-20220420182030318 a.预备环境准备 请确保是在环境中安装使用:
64 bit OS Linux/Unix/Mac,推荐使用Linux系统。 64 bit JDK 1.8+;下载open in new window .配置open in new window 。 Maven 3.2.x+;下载open in new window .配置open in new window 。 3个或3个以上Nacos节点才能构成集群。 b.下载源码或者安装包 下载编译后压缩包方式 下载地址
https://github.com/alibaba/nacos/releasesopen in new window
From: 元动力 1 2 tar -zxvf nacos-server-2.0.4.tar.gzcd nacos/bin
c.配置集群配置文件 在nacos的解压目录nacos/的conf目录下,有配置文件cluster.conf,请每行配置成ip:port。(请配置3个或3个以上节点)
From: 元动力 1 2 3 192.168.246.128:6666 192.168.246.128:7777 192.168.246.128:8888
d.确定数据源 虚拟机中,已经有mysql
root Ydlclass@666
image-20220420165604781 新建nacos_config库,导入数据
image-20220420165815259 application配置文件
From: 元动力 1 2 3 4 5 6 7 8 9 10 ### If use MySQL as datasource: spring.datasource.platform=mysql ### Count of DB: db.num=1 ### Connect URL of DB: db.url.0=jdbc:mysql://192.168.246.128:3306/nacos_config?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true&useUnicode=true&useSSL=false&serverTimezone=UTC db.user.0=root db.password.0=Ydlclass@666
nacos复制成3份,端口号改成 6666 7777 8888
e.启动服务器 三台nacos,修改启动参数,再启动
image-20220420171828918 f.结合nginx 1.上传nginx源码包
2.修改配置文件
From: 元动力 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 #user nobody; worker_processes 1; #error_log logs/error.log; #error_log logs/error.log notice; #error_log logs/error.log info; #pid logs/nginx.pid; events { worker_connections 1024; } http { include mime.types; default_type application/octet-stream; #log_format main '$remote_addr - $remote_user [$time_local] "$request" ' # '$status $body_bytes_sent "$http_referer" ' # '"$http_user_agent" "$http_x_forwarded_for"'; #access_log logs/access.log main; sendfile on; #tcp_nopush on; #keepalive_timeout 0; keepalive_timeout 65; #gzip on; upstream cluster{ server 192.168.246.128:6666; server 192.168.246.128:7777; server 192.168.246.128:8888; } server { listen 5555; 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; } #error_page 404 /404.html; # redirect server error pages to the static page /50x.html # error_page 500 502 503 504 /50x.html; location = /50x.html { root html; } # proxy the PHP scripts to Apache listening on 127.0.0.1:80 # #location ~ \.php$ { # proxy_pass http://127.0.0.1; #} # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000 # #location ~ \.php$ { # root html; # fastcgi_pass 127.0.0.1:9000; # fastcgi_index index.php; # fastcgi_param SCRIPT_FILENAME /scripts$fastcgi_script_name; # include fastcgi_params; #} # deny access to .htaccess files, if Apache's document root # concurs with nginx's one # #location ~ /\.ht { # deny all; #} } # another virtual host using mix of IP-, name-, and port-based configuration # #server { # listen 8000; # listen somename:8080; # server_name somename alias another.alias; # location / { # root html; # index index.html index.htm; # } #} # HTTPS server # #server { # listen 443 ssl; # server_name localhost; # ssl_certificate cert.pem; # ssl_certificate_key cert.key; # ssl_session_cache shared:SSL:1m; # ssl_session_timeout 5m; # ssl_ciphers HIGH:!aNULL:!MD5; # ssl_prefer_server_ciphers on; # location / { # root html; # index index.html index.htm; # } #} }
3.安装nginx
From: 元动力 1 2 ./configure make && make install
4.启动nginx
From: 元动力 1 2 cd /usr/local/nginx/sbin ./nginx
5.测试访问 http://192.168.246.128:5555/nacos/index.htmlopen in new window
image-20220420173847175 g.项目配置获取 1.新建配置文件
image-20220420173912640 2.项目修改获取配置文件地址
From: 元动力 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 # nacos配置 server: port: 7777 spring: application: name: nacos-config-client cloud: nacos: discovery: server-addr: 192.168.246.128:5555 #Nacos服务注册中心地址 config: server-addr: 192.168.246.128:5555 #Nacos作为配置中心地址 file-extension: yaml #指定yaml格式的配置 group: SX_GROUP namespace: d0492f75-fbe3-4bfc-8689-c4c200233bd9 #${spring.application.name}-${spring.profiles.active}.${file-extension} # nacos-config-client-dev.yaml
第二十章 SpringCloud Alibaba Sentinel实现熔断与限流 一、简介 一句话 :就是hystrix的替代!
官网:https://github.com/alibaba/sentinelopen in new window 中文版:https://github.com/alibaba/Sentinel/wiki/%E4%BB%8B%E7%BB%8Dopen in new window
随着微服务的流行,服务和服务之间的稳定性变得越来越重要。Sentinel 以流量为切入点,从流量控制、熔断降级、系统负载保护等多个维度保护服务的稳定性。
Sentinel 具有以下特征:
丰富的应用场景 :Sentinel 承接了阿里巴巴近 10 年的双十一大促流量的核心场景,例如秒杀(即突发流量控制在系统容量可以承受的范围)、消息削峰填谷、集群流量控制、实时熔断下游不可用应用等。完备的实时监控 :Sentinel 同时提供实时的监控功能。您可以在控制台中看到接入应用的单台机器秒级数据,甚至 500 台以下规模的集群的汇总运行情况。广泛的开源生态 :Sentinel 提供开箱即用的与其它开源框架/库的整合模块,例如与 Spring Cloud、Apache Dubbo、gRPC、Quarkus 的整合。您只需要引入相应的依赖并进行简单的配置即可快速地接入 Sentinel。同时 Sentinel 提供 Java/Go/C++ 等多语言的原生实现。完善的 SPI 扩展机制 :Sentinel 提供简单易用、完善的 SPI 扩展接口。您可以通过实现扩展接口来快速地定制逻辑。例如定制规则管理、适配动态数据源等。Sentinel 的主要特性:
image-20220424114427413 Sentinel 的开源生态:
image-20220424114442619 Sentinel 分为两个部分 :
二、下载 下载:https://github.com/alibaba/Sentinel/releasesopen in new window
image-20220424155816578 运行:
From: 元动力 1 java -jar sentinel-dashboard-1.8.4.jar
image-20220424155846911 http://localhost:8080open in new window
image-20220424121030210 账号和密码都是sentinel
image-20220424121050706 三、初始化演示工程 cloudalibaba-sentinel-service8000
a、 pom
From: 元动力 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 < !-- 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>
b、yml
From: 元动力 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 server: port: 8000 spring: application: name: cloudalibaba-sentinal-service cloud: nacos: discovery: server-addr: localhost:8848 sentinel: transport: dashboard: localhost:8080 port: 8719
c、 主启动类
@EnableDiscoveryClient
d、controller
From: 元动力 1 2 3 4 5 6 7 8 9 10 11 12 13 14 @RestController public class FlowLimitController { @GetMapping("/testA") public String testA () { return "----testA" ; } @GetMapping("/testB") public String testB () { return "----testB" ; } }
e、测试
启动8050,然后刷新sentinel后台页面(因为sentinel采用懒加载策略,所以需要调用服务后才在后台显示) 在浏览器分别输入,然后刷新sentinel后台页面: http://localhost:8000/demo/testAopen in new window http://localhost:8000/demo/testBopen in new window
image-20220424163655481 四、流量控制 Field 说明 默认值 resource 资源名,资源名是限流规则的作用对象 count 限流阈值 grade 限流阈值类型,QPS 或线程数模式 QPS 模式 limitApp 流控针对的调用来源 default
,代表不区分调用来源strategy 调用关系限流策略:直接、链路、关联 根据资源本身(直接) controlBehavior 流控效果(直接拒绝 / 排队等待 / 慢启动模式),不支持按调用关系限流 直接拒绝
(1)qps 直接 快速失败 image-20220424174126050 image-20220424174110634 (2)并发 直接 controller
From: 元动力 1 2 3 4 5 6 7 8 9 10 @GetMapping("/testA") public String testA(){ //业务逻辑比价复杂 try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } return "itlils testA"; }
image-20220424174723161 浏览器开两个tab分别快速访问
image-20220424174110634 (3)qps 关联失败 设置B A的失败策略
image-20220425162627537 image-20220425162638896 模拟postman 500ms 访问B,让B失败
image-20220425162746123 image-20220425162909533 image-20220425163419417 image-20220425163712061 (4)qps 链路 image-20220425170519379 我们可以对不同controller来的调用service方法限流,更加精细化。sentinel牛X!
a.新增GoodsService
From: 元动力 1 2 3 4 @SentinelResource("goods") public void queryGoods(){ System.err.println("查询商品"); }
b.配置文件
从1.6.3版本开始,Sentinel Web filter默认收敛所有URL的入口context,导致链路限流不生效。 从1.7.0版本开始,官方在CommonFilter引入了WEB_CONTEXT_UNIFY参数,用于控制是否收敛context,将其配置为false即可根据不同的URL进行链路限流。
From: 元动力 1 2 3 4 5 6 7 8 9 10 11 12 13 spring: application: name: cloudalibaba-sentinal-service cloud: nacos: discovery: server-addr: localhost:8848 sentinel: transport: dashboard: localhost:8080 # 默认8719端口,假如被占用了会自动从8719端口+1进行扫描,直到找到未被占用的端口 port: 8719 web-context-unify: false
有了资源了
image-20220425171005883 c.配置规则
image-20220425171040497 快速调用A
image-20220425171143687 快速调用B
image-20220425171157383 (5)warm up 它从开始阈值到最大QPS阈值会有一个缓冲阶段,一开始的阈值是最大QPS阈值的1/3,然后慢慢增长,直到最大阈值,适用于将突然增大的流量转换为缓步增长的场景。
Warm Up(RuleConstant.CONTROL_BEHAVIOR_WARM_UP
)方式,即预热/冷启动方式。当系统长期处于低水位的情况下,当流量突然增加时,直接把系统拉升到高水位可能瞬间把系统压垮。通过"冷启动",让通过的流量缓慢增加,在一定时间内逐渐增加到阈值上限,给冷系统一个预热的时间,避免冷系统被压垮。详细文档可以参考 流量控制 - Warm Up 文档open in new window ,具体的例子可以参见 WarmUpFlowDemoopen in new window 。
通常冷启动的过程系统允许通过的 QPS 曲线如下图所示:
image a.设置
image-20220425172716814 (6)匀速排队 匀速排队(RuleConstant.CONTROL_BEHAVIOR_RATE_LIMITER
)方式会严格控制请求通过的间隔时间,也即是让请求以均匀的速度通过,对应的是漏桶算法。详细文档可以参考 流量控制 - 匀速器模式open in new window ,具体的例子可以参见 PaceFlowDemoopen in new window 。
该方式的作用如下图所示:
image 这种方式主要用于处理间隔性突发的流量,例如消息队列。想象一下这样的场景,在某一秒有大量的请求到来,而接下来的几秒则处于空闲状态,我们希望系统能够在接下来的空闲期间逐渐处理这些请求,而不是在第一秒直接拒绝多余的请求。
注意:匀速排队模式暂时不支持 QPS > 1000 的场景。
a.配置
image-20220425174036373 b.测试,高并发访问b,后台控制台显示每秒2个处理
image-20220425174116975 五、熔断降级 https://github.com/alibaba/Sentinel/wiki/熔断降级open in new window
(1)概述 除了流量控制以外,对调用链路中不稳定的资源进行熔断降级也是保障高可用的重要措施之一。一个服务常常会调用别的模块,可能是另外的一个远程服务、数据库,或者第三方 API 等。例如,支付的时候,可能需要远程调用银联提供的 API;查询某个商品的价格,可能需要进行数据库查询。然而,这个被依赖服务的稳定性是不能保证的。如果依赖的服务出现了不稳定的情况,请求的响应时间变长,那么调用服务的方法的响应时间也会变长,线程会产生堆积,最终可能耗尽业务自身的线程池,服务本身也变得不可用。
chain 现代微服务架构都是分布式的,由非常多的服务组成。不同服务之间相互调用,组成复杂的调用链路。以上的问题在链路调用中会产生放大的效果。复杂链路上的某一环不稳定,就可能会层层级联,最终导致整个链路都不可用。因此我们需要对不稳定的弱依赖服务调用 进行熔断降级,暂时切断不稳定调用,避免局部不稳定因素导致整体的雪崩。熔断降级作为保护自身的手段,通常在客户端(调用端)进行配置。
注意 :本文档针对 Sentinel 1.8.0 及以上版本。1.8.0 版本对熔断降级特性进行了全新的改进升级,请使用最新版本以更好地利用熔断降级的能力。
(2)熔断策略 Sentinel 提供以下几种熔断策略:
慢调用比例 (SLOW_REQUEST_RATIO
):选择以慢调用比例作为阈值,需要设置允许的慢调用 RT(即最大的响应时间),请求的响应时间大于该值则统计为慢调用。当单位统计时长(statIntervalMs
)内请求数目大于设置的最小请求数目,并且慢调用的比例大于阈值,则接下来的熔断时长内请求会自动被熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求响应时间小于设置的慢调用 RT 则结束熔断,若大于设置的慢调用 RT 则会再次被熔断。
image-20220425190913439 测试
a.代码:
From: 元动力 1 2 3 4 5 6 7 8 9 10 11 12 13 @GetMapping("/testC/{id}") public String testC(@PathVariable("id")Integer id){ if(id==10){ //复杂的业务逻辑 try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } } return "itlils testC"; }
b.设置策略
image-20220425193024049 c.一直访问10
image-20220425192753272 d.过一会儿,访问10
image-20220425193646731 异常比例 (ERROR_RATIO
):当单位统计时长(statIntervalMs
)内请求数目大于设置的最小请求数目,并且异常的比例大于阈值,则接下来的熔断时长内请求会自动被熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求成功完成(没有错误)则结束熔断,否则会再次被熔断。异常比率的阈值范围是 [0.0, 1.0]
,代表 0% - 100%。
image-20220426115525060 a.代码:
From: 元动力 1 2 3 4 5 6 7 8 9 @GetMapping("/testD/{id}") public String testD(@PathVariable("id")Integer id){ if(id==10){ //异常调用 int a=1/0; } return "itlils testD"; }
b.设置策略
image-20220426115919630 c.测试
image-20220426115942858 d.疯狂访问
image-20220426120122888 e.过2秒钟后
image-20220426120218008 异常数 (ERROR_COUNT
):当单位统计时长内的异常数目超过阈值之后会自动进行熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求成功完成(没有错误)则结束熔断,否则会再次被熔断。 a.代码:
From: 元动力 1 2 3 4 5 6 7 8 @GetMapping("/testE/{id}") public String testE(@PathVariable("id")Integer id){ if(id==10){ //异常调用 int a=1/0; } return "itlils testE"; }
b.配置
image-20220426160955002 c.疯狂测试http://localhost:8000/demo/testE/10open in new window
image-20220426161032712 d.过几秒 测试http://localhost:8000/demo/testE/1open in new window
image-20220426161101190 六、热点参数限流 https://github.com/alibaba/Sentinel/wiki/热点参数限流open in new window
(1)是什么 何为热点?热点即经常访问的数据。很多时候我们希望统计某个热点数据中访问频次最高的 Top K 数据,并对其访问进行限制。比如:
商品 ID 为参数,统计一段时间内最常购买的商品 ID 并进行限制 用户 ID 为参数,针对一段时间内频繁访问的用户 ID 进行限制 热点参数限流会统计传入参数中的热点参数,并根据配置的限流阈值与模式,对包含热点参数的资源调用进行限流。热点参数限流可以看做是一种特殊的流量控制,仅对包含热点参数的资源调用生效。
image-20220426121224000 image-20220426162234051 Sentinel 利用 LRU 策略统计最近最常访问的热点参数,结合令牌桶算法来进行参数级别的流控。热点参数限流支持集群模式。
(2)基本使用 a.要使用热点参数限流功能,需要引入以下依赖:
From: 元动力 1 2 3 4 5 <dependency> <groupId>com.alibaba.csp</groupId> <artifactId>sentinel-parameter-flow-control</artifactId> <version>1.8.4</version> </dependency>
b.controller
From: 元动力 1 2 3 4 5 6 7 @GetMapping("/order") @SentinelResource(value = "hotKeys") public String order(@RequestParam("goodsId")String goodsId,@RequestParam("userId")String userId){ //业务逻辑 return "用户下单成功"; }
c.设置规则
image-20220426163343614 d.快速访问http://localhost:8000/demo/order?goodsId=1&userId=10open in new window
image-20220426163435037 e.快速访问http://localhost:8000/demo/order?userId=10open in new window
image-20220426163635673 说明设置限制源代码第一个参数,成功了。
f.为了给调用方一个正确的值,改造
From: 元动力 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 @GetMapping("/order") @SentinelResource(value = "hotKeys",blockHandler = "block_order") public String order(@RequestParam(value = "goodsId",required = false)String goodsId ,@RequestParam(value = "userId",required = false)String userId){ //业务逻辑 return "用户下单成功"; } public String block_order(@RequestParam(value = "goodsId",required = false)String goodsId ,@RequestParam(value = "userId",required = false)String userId, BlockException ex){ //记录错误日志 //logger.error(ex.getMessage())) return "用户下单失败,请稍后重试"; }
image-20220426164323925 (3)例外情况 秒杀情况下,某个参数goodsId=100,最新手机id,单独设置阈值1000.
a.配置
image-20220426165418745 b.疯狂访问http://localhost:8000/demo/order?goodsId=100&userId=10open in new window 不会降级
image-20220426165532893 注意:参数例外项,仅支持基本类型和字符串类型
注意:
加一个:int a=1/0;
业务上的错,sentinel不会管
image-20220426165926729 热点key限制,sentinel才会管,疯狂访问
image-20220426170005357 七、系统自适应限流 Sentinel 系统自适应限流从整体维度对应用入口流量进行控制,结合应用的 Load、CPU 使用率、总体平均 RT、入口 QPS 和并发线程数等几个维度的监控指标,通过自适应的流控策略,让系统的入口流量和系统的负载达到一个平衡,让系统尽可能跑在最大吞吐量的同时保证系统整体的稳定性。
image-20220426172901422 (1)背景 在开始之前,我们先了解一下系统保护的目的:
保证系统不被拖垮 在系统稳定的前提下,保持系统的吞吐量 长期以来,系统保护的思路是根据硬指标,即系统的负载 (load1) 来做系统过载保护。当系统负载高于某个阈值,就禁止或者减少流量的进入;当 load 开始好转,则恢复流量的进入。这个思路给我们带来了不可避免的两个问题:
load 是一个“结果”,如果根据 load 的情况来调节流量的通过率,那么就始终有延迟性。也就意味着通过率的任何调整,都会过一段时间才能看到效果。当前通过率是使 load 恶化的一个动作,那么也至少要过 1 秒之后才能观测到;同理,如果当前通过率调整是让 load 好转的一个动作,也需要 1 秒之后才能继续调整,这样就浪费了系统的处理能力。所以我们看到的曲线,总是会有抖动。 恢复慢。想象一下这样的一个场景(真实),出现了这样一个问题,下游应用不可靠,导致应用 RT 很高,从而 load 到了一个很高的点。过了一段时间之后下游应用恢复了,应用 RT 也相应减少。这个时候,其实应该大幅度增大流量的通过率;但是由于这个时候 load 仍然很高,通过率的恢复仍然不高。 TCP BBRopen in new window 的思想给了我们一个很大的启发。我们应该根据系统能够处理的请求,和允许进来的请求,来做平衡,而不是根据一个间接的指标(系统 load)来做限流。最终我们追求的目标是 在系统不被拖垮的情况下,提高系统的吞吐率,而不是 load 一定要到低于某个阈值 。如果我们还是按照固有的思维,超过特定的 load 就禁止流量进入,系统 load 恢复就放开流量,这样做的结果是无论我们怎么调参数,调比例,都是按照果来调节因,都无法取得良好的效果。
Sentinel 在系统自适应保护的做法是,用 load1 作为启动自适应保护的因子,而允许通过的流量由处理请求的能力,即请求的响应时间以及当前系统正在处理的请求速率来决定。
(2)系统规则 系统保护规则是从应用级别的入口流量进行控制,从单台机器的 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 达到阈值即触发系统保护。 (3)原理 先用经典图来镇楼:
TCP-BBR-pipe 我们把系统处理请求的过程想象为一个水管,到来的请求是往这个水管灌水,当系统处理顺畅的时候,请求不需要排队,直接从水管中穿过,这个请求的RT是最短的;反之,当请求堆积的时候,那么处理请求的时间则会变为:排队时间 + 最短处理时间。
推论一: 如果我们能够保证水管里的水量,能够让水顺畅的流动,则不会增加排队的请求;也就是说,这个时候的系统负载不会进一步恶化。 我们用 T 来表示(水管内部的水量),用RT来表示请求的处理时间,用P来表示进来的请求数,那么一个请求从进入水管道到从水管出来,这个水管会存在 P * RT
个请求。换一句话来说,当 T ≈ QPS * Avg(RT)
的时候,我们可以认为系统的处理能力和允许进入的请求个数达到了平衡,系统的负载不会进一步恶化。
接下来的问题是,水管的水位是可以达到了一个平衡点,但是这个平衡点只能保证水管的水位不再继续增高,但是还面临一个问题,就是在达到平衡点之前,这个水管里已经堆积了多少水。如果之前水管的水已经在一个量级了,那么这个时候系统允许通过的水量可能只能缓慢通过,RT会大,之前堆积在水管里的水会滞留;反之,如果之前的水管水位偏低,那么又会浪费了系统的处理能力。
推论二: 当保持入口的流量是水管出来的流量的最大的值的时候,可以最大利用水管的处理能力。 然而,和 TCP BBR 的不一样的地方在于,还需要用一个系统负载的值(load1)来激发这套机制启动。
注:这种系统自适应算法对于低 load 的请求,它的效果是一个“兜底”的角色。对于不是应用本身造成的 load 高的情况(如其它进程导致的不稳定的情况),效果不明显。
设置
image-20220426173149065 测试http://localhost:8000/demo/testAopen in new window
image-20220426173225393 八、@SentinelResource https://github.com/alibaba/Sentinel/wiki/注解支持open in new window
@SentinelResource
用于定义资源,并提供可选的异常处理和 fallback 配置项。 @SentinelResource
注解包含以下属性:
value
:资源名称,必需项(不能为空)
entryType
:entry 类型,可选项(默认为 EntryType.OUT
)
blockHandler
/ blockHandlerClass
: blockHandler
对应处理 BlockException
的函数名称,可选项。blockHandler 函数访问范围需要是 public
,返回类型需要与原方法相匹配,参数类型需要和原方法相匹配并且最后加一个额外的参数,类型为 BlockException
。blockHandler 函数默认需要和原方法在同一个类中。若希望使用其他类的函数,则可以指定 blockHandlerClass
为对应的类的 Class
对象,注意对应的函数必需为 static 函数,否则无法解析。
fallback
/fallbackClass
:fallback 函数名称,可选项,用于在抛出异常的时候提供 fallback 处理逻辑。fallback 函数可以针对所有类型的异常(除了
exceptionsToIgnore
里面排除掉的异常类型)进行处理。fallback 函数签名和位置要求:
返回值类型必须与原函数返回值类型一致; 方法参数列表需要和原函数一致,或者可以额外多一个 Throwable
类型的参数用于接收对应的异常。 fallback 函数默认需要和原方法在同一个类中。若希望使用其他类的函数,则可以指定 fallbackClass
为对应的类的 Class
对象,注意对应的函数必需为 static 函数,否则无法解析。 defaultFallback
(since 1.6.0):默认的 fallback 函数名称,可选项,通常用于通用的 fallback 逻辑(即可以用于很多服务或方法)。默认 fallback 函数可以针对所有类型的异常(除了exceptionsToIgnore
里面排除掉的异常类型)进行处理。若同时配置了 fallback 和 defaultFallback,则只有 fallback 会生效。defaultFallback 函数签名要求:
返回值类型必须与原函数返回值类型一致; 方法参数列表需要为空,或者可以额外多一个 Throwable
类型的参数用于接收对应的异常。 defaultFallback 函数默认需要和原方法在同一个类中。若希望使用其他类的函数,则可以指定 fallbackClass
为对应的类的 Class
对象,注意对应的函数必需为 static 函数,否则无法解析。 exceptionsToIgnore
(since 1.6.0):用于指定哪些异常被排除掉,不会计入异常统计中,也不会进入 fallback 逻辑中,而是会原样抛出。
1.8.0 版本开始,defaultFallback
支持在类级别进行配置。
注:1.6.0 之前的版本 fallback 函数只针对降级异常(DegradeException
)进行处理,不能针对业务异常进行处理 。
特别地,若 blockHandler 和 fallback 都进行了配置,则被限流降级而抛出 BlockException
时只会进入 blockHandler
处理逻辑。若未配置 blockHandler
、fallback
和 defaultFallback
,则被限流降级时会将 BlockException
直接抛出 (若方法本身未定义 throws BlockException 则会被 JVM 包装一层 UndeclaredThrowableException
)。
blockHandler熔断测试
a.controller
From: 元动力 1 2 3 4 5 6 7 8 9 10 //减库存接口 @GetMapping("/resume") @SentinelResource(value = "resume", blockHandlerClass = CommonBlockHandler.class, blockHandler = "handlerBlock2") public String resume(@RequestParam(value = "goodsId",required = false)String goodsId){ //业务逻辑 // int a=1/0; return "减库存成功"; }
b.CommonBlockHandler
From: 元动力 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 package com.ydlclass.cloudalibabasentinelservice8000.handler;import com.alibaba.csp.sentinel.slots.block.BlockException;import org.springframework.web.bind.annotation.RequestParam;public class CommonBlockHandler { public static String handlerBlock1 (BlockException ex) { return "系统出错,请稍后再试!" ; } public static String handlerBlock2 (@RequestParam(value = "goodsId",required = false) String goodsId, BlockException ex) { return "系统出错,请稍后再试!handlerBlock2" ; } }
c.测试 狂点http://localhost:8000/demo/resume?goodsId=100open in new window
image-20220429165149264 九、降级 (1)解释 a.熔断:微服务自己限流,不可用了。
b.降级:调用方,提供方错了,返回给客户一个友好对象。
image-20220429171002222 (2)新建3个项目 cloudalibaba-sentinal-provider9001
a.pom
From: 元动力 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 < dependency> < groupId> com.alibaba.cloud< /groupId> < artifactId> spring-cloud-starter-alibaba-nacos-discovery< /artifactId> < /dependency> < !-- SpringCloud ailibaba sentinel--> < dependency> < groupId> com.alibaba.cloud< /groupId> < artifactId> spring-cloud-starter-alibaba-sentinel< /artifactId> < /dependency> < dependency> < groupId> org.springframework.cloud< /groupId> < artifactId> spring-cloud-starter-openfeign< /artifactId> < /dependency> < dependency> < groupId> org.springframework.boot< /groupId> < artifactId> spring-boot-starter-web< /artifactId> < /dependency>
b.yml
From: 元动力 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 server: port: 9001 spring: application: name: cloudalibaba-sentinal-provider cloud: nacos: discovery: server-addr: localhost:8848 sentinel: transport: dashboard: localhost:8080 port: 8719 web-context-unify: false
c.启动类
@EnableDiscoveryClient
d.controller
From: 元动力 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 package com.ydlclass.provider.controller;import com.ydlclass.provider.domain.Goods;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.PathVariable;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController;@RestController @RequestMapping("/goods") public class GoodsController { @Value("${server.port}") Integer port; @GetMapping("/findById/{id}") public Goods findById (@PathVariable("id") Integer id) { Goods goods=new Goods (); goods.setGoodId(id); goods.setPrice(123 ); goods.setTitle("手机.port:" +port); goods.setStock(10 ); return goods; } }
e.doamin
From: 元动力 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 package com.ydlclass.provider.domain;import java.io.Serializable;public class Goods implements Serializable { private Integer goodId; private String title; private double price; private Integer stock; public Goods () { } public Goods (Integer goodId, String title, double price, Integer stock) { this .goodId = goodId; this .title = title; this .price = price; this .stock = stock; } @Override public String toString () { return "Goods{" + "goodId=" + goodId + ", title='" + title + '\'' + ", price=" + price + ", stock=" + stock + '}' ; } public Integer getGoodId () { return goodId; } public void setGoodId (Integer goodId) { this .goodId = goodId; } public String getTitle () { return title; } public void setTitle (String title) { this .title = title; } public double getPrice () { return price; } public void setPrice (double price) { this .price = price; } public Integer getStock () { return stock; } public void setStock (Integer stock) { this .stock = stock; } }
测试:http://localhost:9001/goods/findById/1open in new window
image-20220429172614387 cloudalibaba-sentinal-provider9002
同理。创建。
cloudalibaba-consumer-nacos-consumer8000
a.pom
From: 元动力 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 < dependencies> < dependency> < groupId> com.alibaba.cloud< /groupId> < artifactId> spring-cloud-starter-alibaba-nacos-discovery< /artifactId> < exclusions> < exclusion> < groupId> org.springframework.cloud< /groupId> < artifactId> spring-cloud-starter-netflix-ribbon< /artifactId> < /exclusion> < /exclusions> < /dependency> < dependency> < groupId> org.springframework.cloud< /groupId> < artifactId> spring-cloud-starter-loadbalancer< /artifactId> < version> 3.1.1< /version> < /dependency> < !-- SpringCloud ailibaba sentinel--> < dependency> < groupId> com.alibaba.cloud< /groupId> < artifactId> spring-cloud-starter-alibaba-sentinel< /artifactId> < /dependency> < dependency> < groupId> org.springframework.cloud< /groupId> < artifactId> spring-cloud-starter-openfeign< /artifactId> < /dependency> < dependency> < groupId> org.springframework.boot< /groupId> < artifactId> spring-boot-starter-web< /artifactId> < /dependency> < /dependencies>
b.yml
From: 元动力 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 server: port: 8000 spring: application: name: cloudalibaba-sentinal-consumer cloud: nacos: discovery: server-addr: localhost:8848 sentinel: transport: dashboard: localhost:8080 port: 8719 feign: sentinel: enabled: true
c.配置类
From: 元动力 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 package com.ydlclass.consumer.config;import org.springframework.cloud.client.loadbalancer.LoadBalanced;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.web.client.RestTemplate;@Configuration public class RestConfig { @Bean @LoadBalanced public RestTemplate restTemplate () { return new RestTemplate (); } }
d.controller
From: 元动力 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 package com.ydlclass.consumer.controller;import com.ydlclass.consumer.domain.Goods;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.PathVariable;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController;import org.springframework.web.client.RestTemplate;@RestController @RequestMapping("/order") public class OrderController { @Autowired RestTemplate restTemplate; @GetMapping("/add/{id}") public Goods add (@PathVariable("id") Integer id) { String url="http://cloudalibaba-sentinal-provider/goods/findById/" +id; Goods goods = restTemplate.getForObject(url, Goods.class); return goods; } }
e.goods 实体类 copy
From: 元动力 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 package com.ydlclass.consumer.domain;import java.io.Serializable;public class Goods implements Serializable { private Integer goodId; private String title; private double price; private Integer stock; public Goods () { } public Goods (Integer goodId, String title, double price, Integer stock) { this .goodId = goodId; this .title = title; this .price = price; this .stock = stock; } @Override public String toString () { return "Goods{" + "goodId=" + goodId + ", title='" + title + '\'' + ", price=" + price + ", stock=" + stock + '}' ; } public Integer getGoodId () { return goodId; } public void setGoodId (Integer goodId) { this .goodId = goodId; } public String getTitle () { return title; } public void setTitle (String title) { this .title = title; } public double getPrice () { return price; } public void setPrice (double price) { this .price = price; } public Integer getStock () { return stock; } public void setStock (Integer stock) { this .stock = stock; } }
f.测试
http://localhost:8000/order/add/1open in new window
发现负载均衡
image-20220429175240918 image-20220429175250362 (3)不同情况下 blockHandler与fallback情况 a.不配置
From: 元动力 1 2 3 4 5 if(id<0){ throw new IllegalArgumentException("非法参数"); }else if(id>100){ throw new NullPointerException("查无此商品"); }
image-20220429180414730 b.只配blockHandler
From: 元动力 1 2 3 4 5 6 7 8 9 public Goods fail_add (@PathVariable("id") Integer id, BlockException ex) { Goods goods = new Goods (); goods.setGoodId(-1 ); goods.setPrice(-1 ); goods.setStock(-1 ); goods.setTitle("限流之后的特殊对象" ); return goods; }
image-20220429180844762 image-20220429180818990 正常访问错id
image-20220429180927872 疯狂访问
image-20220429180940663 c.只配fallback
From: 元动力 1 2 3 4 5 6 7 8 9 10 11 12 @SentinelResource(value = "add",fallback = "fallback_add") public Goods fallback_add (@PathVariable("id") Integer id, Throwable ex) { Goods goods = new Goods (); goods.setGoodId(-2 ); goods.setPrice(-2 ); goods.setStock(-2 ); goods.setTitle("业务出错之后的特殊对象" ); return goods; }
image-20220429181347488 d.都配置
From: 元动力 1 @SentinelResource(value = "add",blockHandler = "fail_add",fallback = "fallback_add")
image-20220429181534288 -1 慢慢访问
image-20220429181559790 -1 疯狂访问
image-20220429181637087 e.忽略某些异常
From: 元动力 1 2 @SentinelResource(value = "add",blockHandler = "fail_add",fallback = "fallback_add", exceptionsToIgnore = {IllegalArgumentException.class})
image-20220429181851414 image-20220429181925759 十、feign调用 a.pom
From: 元动力 1 2 3 4 < dependency> < groupId> org.springframework.cloud< /groupId> < artifactId> spring-cloud-starter-openfeign< /artifactId> < /dependency>
b.yml
From: 元动力 1 2 3 4 5 #前面也已经添加了 #激活Sentinel对Feign的支持 feign: sentinel: enabled: true
c.主启动类 @EnableFeignClients
d.feign接口
From: 元动力 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 package com.ydlclass.consumer.feign;import com.ydlclass.consumer.domain.Goods;import org.springframework.cloud.openfeign.FeignClient;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.PathVariable;@FeignClient(value = "cloudalibaba-sentinal-provider",fallback = GoodsFeignImpl.class) public interface GoodsFeign { @GetMapping("/goods/findById/{id}") public Goods findById (@PathVariable("id") Integer id) ; }
e.实现类PaymentFallbackService
From: 元动力 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 package com.ydlclass.consumer.feign;import com.ydlclass.consumer.domain.Goods;import org.springframework.stereotype.Component;@Component public class GoodsFeignImpl implements GoodsFeign { @Override public Goods findById (Integer id) { Goods goods = new Goods (); goods.setGoodId(-3 ); goods.setPrice(-3 ); goods.setStock(-3 ); goods.setTitle("feign出错之后的特殊对象" ); return goods; } }
f.controller
From: 元动力 1 2 3 4 5 6 7 8 @Autowired GoodsFeign goodsFeign;@GetMapping("/add1/{id}") public Goods add1 (@PathVariable("id") Integer id) { Goods goods = goodsFeign.findById(id); return goods; }
g.测试出错,直接停了provider
image-20220429183335192 十一、配置持久化 sentinel的流控配置是临时的,所以我们可以把配置持久化到nacos
a.pom
From: 元动力 1 2 3 4 5 < !-- SpringCloud ailibaba sentinel-datasource-nacos 持久化需要用到--> < dependency> < groupId> com.alibaba.csp< /groupId> < artifactId> sentinel-datasource-nacos< /artifactId> < /dependency>
b.yml
From: 元动力 1 2 3 4 5 6 7 8 9 sentinel: datasource: ds1: nacos: server-addr: localhost:8848 dataId: ${spring.application.name} groupId: DEFAULT_GROUP data-type: json rule-type: flow
c.nacos添加配置
From: 元动力 1 2 3 4 5 6 7 8 9 10 11 [ { "resource": "/order/add1/{id}", "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表示Warm Up,2表示排队等待; clusterMode:是否集群 image-20220429185256498 d.测试
image-20220429185954032