目录
Spring Cloud Alibaba 参考文档
Nacos:
例:
基于nocos服务的提供者:
基于nocos服务的消费者:
Nocos作为配置中心配置:
Namespace-Group-DataId:
Sentinel:
作用:
下载运行:
流控模式:
直接:
关联:
编辑
链路:
流控效果:
预热warm up:
排队等待:
熔断:
慢调用比例:
异常比例:
异常数:
@SentinelResource
资源名称限流+自定义限流返回:
热点:
普通正常限流:
参数例外项:
授权:
持久化:
依赖:
yml:
添加Nacos业务配置规则:
GateWay和Sentinel集成实现服务限流
1. 类和注解
2. 成员变量和构造函数
3. SentinelGatewayBlockExceptionHandler Bean
4. SentinelGatewayFilter Bean
5. doInit() 方法
6. initBlockHandler() 方法
7. 核心流程概述
8. 作用与总结
Nacos:
Nacos = Eureka+Config +Bus
Nacos = Spring Cloud Consul
官网-下载:点击跳转
启动Nacos服务器:
startup.cmd -m standalone
例:
基于nocos服务的提供者:
依赖:
<!--nacos-discovery-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
yml:
server:port: 9001spring:application:name: nacos-payment-providercloud:nacos:discovery:server-addr: localhost:8848 #配置Nacos地址
配置controller:
package com.atguigu.cloud.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.RestController;@RestController public class PayAlibabaController {@Value("${server.port}")private String serverPort;@GetMapping(value = "/pay/nacos/{id}")public String getPayInfo(@PathVariable("id") Integer id){return "nacos registry, serverPort: "+ serverPort+"\t id"+id;} }
nocos控制台:http://localhost:8848/nacos
可以查看到服务已经被注册进来。
基于nocos服务的消费者:
依赖:
<!--nacos-discovery-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!--loadbalancer-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>
yml:
server:
port: 83
spring:
application:
name: nacos-order-consumer
cloud:
nacos:
discovery:
server-addr: localhost:8848
#消费者将要去访问的微服务名称(nacos微服务提供者叫什么你写什么)
service-url:
nacos-user-service: http://nacos-payment-provider
配置config:
package com.atguigu.cloud.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 RestTemplateConfig {@Bean@LoadBalanced//赋予RestTemplate负载均衡的能力public RestTemplate restTemplate(){return new RestTemplate();}
}
运行结果:
Nocos作为配置中心配置:
依赖:
<!--bootstrap-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bootstrap</artifactId>
</dependency>
<!--nacos-config-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
application.yml:
server:
port: 3377
spring:
profiles:
active: dev # 表示开发环境
#active: prod # 表示生产环境
#active: test # 表示测试环境
bootstrap.yml:
# nacos 配置
spring:
application:
name: nacos-config-client
cloud:
nacos:
discovery:
server-addr: localhost:8848 #Nacos 服务注册 中心地址
config:
server-addr: localhost:8848 #Nacos 作为 配置中心 地址
file-extension: yaml # 指定 yaml 格式的配置
# nacos 端配置文件 DataId 的命名规则是:
# ${spring.application.name}-${spring.profile.active}.${spring.cloud.nacos.config.file-extension}
# 本案例的 DataID 是 :nacos-config-client-dev.yaml
为什么需要写2个yml:
Nacos同Consul一样,在项目初始化时,要保证先从配置中心进行配置拉取,
拉取配置之后,才能保证项目的正常启动,为了满足动态刷新和全局广播通知
springboot中配置文件的加载是存在优先级顺序的,bootstrap优先级高于application
nocos的dataid配置公式:
${spring.application.name}-${spring.profiles.active}.${spring.cloud.nacos.config.file-extension}
Namespace-Group-DataId:
Namespace:
Group:
DataId:
prod对应上面的
nacos-config-client-prod.yaml
的prod。
修改config,添加上指定的namespace喝group,如果不指定会使用默认的配置:
Sentinel:
官网:home | Sentinel
下载地址:https://github.com/alibaba/Sentinel/releaseshttps://github.com/alibaba/Sentinel/releaseshttps://github.com/alibaba/Sentinel/releases
作用:
从流量路由、流量控制、流量整形、熔断降级、系统自适应过载保护、热点流量防护等多个维度来帮助开发者保障微服务的稳定性
下载运行:
下载好jar包后,通过 java -jar 包名 运行
通过yml将服务注册进nacos和sentinel:
server:port: 8401spring:application:name: cloudalibaba-sentinel-servicecloud:nacos:discovery:server-addr: localhost:8848 #Nacos服务注册中心地址sentinel:transport:dashboard: localhost:8080 #配置Sentinel dashboard控制台服务地址port: 8719 #默认8719端口,假如被占用会自动从8719开始依次+1扫描,直至找到未被占用的端口
sentinel采用的是懒加载,即没有流量就不加载,有流量才加载:
流控模式:
直接:
关联:
链路:
yml:
web-context-unify: false # controller层的方法对service层调用不认为是同一个根链路
通过C,D 2个链路去调用service
配置sentinel:
通过C去调用service,会受到限流。
流控效果:
预热warm up:
预热5秒钟,最高阈值为6,预热时为 最高阈值的1/3 --->2
排队等待:
注:
每秒钟允许处理一个请求,当请求发出5秒后没被处理就会被拒绝。
比如第一秒发出20个请求,现在的设置5秒内处理的请求数量是6个,其他的15个就会被拒绝掉。
熔断:
慢调用比例:
进入熔断状态判断依据:在统计时长内,实际请求数目>设定的最小请求数 且 实际慢调用比例>比例阈值 ,进入熔断状态。
例 :
/*** 新增熔断规则-慢调用比例* @return*/
@GetMapping("/testF")
public String testF()
{//暂停几秒钟线程try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); }System.out.println("----测试:新增熔断规则-慢调用比例 ");return "------testF 新增熔断规则-慢调用比例";
}
每次调用设置为1s
但是在配置时我们配置的时间是200毫秒,所有会被熔断
使用jmeter发送10个请求:
再次访问该地址,会发现进入熔断状态:
异常比例:
异常比例(ERROR_RATIO ):当单位统计时长(statInterva1Ms)内请求数目大于设置的最小请求数目,并且异常的比例大于阈值,则接下来的熔断时长内请求会自动被熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求成功完成(没有错误)则结束熔断,否则会再次被熔断。异常比率的阈值范围是[0.0,1.0],代表 0%-100%。
异常数:
异常数(ERROR COUNT ):当单位统计时长内的异常数目超过阈值之后会自动进行熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求成功完成(没有错误)则结束熔断,否则会再次被熔断。
@SentinelResource
SentinelResource是一个流量防卫防护组件注解,
用于指定防护资源,对配置的资源进行流量控制、熔断降级等功能。
资源名称限流+自定义限流返回:
@GetMapping("/rateLimit/byResource")@SentinelResource(value = "byResourceSentinelResource",blockHandler = "handleException")public String byResource(){return "按资源名称SentinelResource限流测试OK";}public String handleException(BlockException exception){return "服务不可用@SentinelResource启动"+"\t"+"o(╥﹏╥)o";}
@SentinelResource(value = "byResourceSentinelResource", blockHandler = "handleException")
- 这是 Sentinel 提供的注解,主要用于配置资源限流、降级、熔断等策略。
value = "byResourceSentinelResource"
:资源名称,用于标识当前方法的限流资源。Sentinel 使用这个资源名称来监控和控制该方法的流量。blockHandler = "handleException"
:指定一个方法来处理限流(或熔断)触发时的异常。具体来说,当资源超出流量限制时,Sentinel 会调用这个blockHandler
方法来处理业务逻辑。
看到这,我们很容易联想到服务降级,那blockHandler和fallback有什么区别呢?
blockHandler,主要针对sentinel配置后出现的违规情况处理
fallback,程序异常了JVM抛出的异常服务降级
热点:
何为热点
热点即经常访问的数据,很多时候我们希望统计或者限制某个热点数据中访问频次最高的TopN数据,并对其访问进行限流或者其它操作
普通正常限流:
现在设置的是对第一个参数进行限流,没有设置对第二个参数进行限流。
http://localhost:8401/testHotKey?p1=abc 每秒频次超过1会限流
http://localhost:8401/testHotKey?p1=abc&p2=33含有参数P1,当每秒访问的频率超过1次时,会触发Sentinel的限流操作
http://localhost:8401/testHotKey?p2=abc 没有热点参数P1,不断访问则不会触发限流操作
参数例外项:
我们期望p1参数当它是某个特殊值时,到达某个约定值后【普通正常限流】规则突然例外、失效了,它的限流值和平时不一样。
假如当p1的值等于5时,它的阈值可以达到200或其它值
授权:
在某些场景下,需要根据调用接口的来源判断是否允许执行本次请求。此时就可以使用Sentinel提供的授权规则来实现,Sentinel的授权规则能够根据请求的来源判断是否允许本次请求通过。
在Sentinel的授权规则中,提供了 白名单与黑名单 两种授权类型。白放行、黑禁止
例:
@Component
public class MyRequestOriginParser implements RequestOriginParser
{@Overridepublic String parseOrigin(HttpServletRequest httpServletRequest) {return httpServletRequest.getParameter("serverName");}
}
配置:
表示serverName带有test,test2的参数 会被限流
持久化:
将限流配置规则持久化进Nacos保存,只要刷新8401某个rest地址,sentinel控制台的流控规则就能看到,只要Nacos里面的配置不删除,针对8401上sentinel上的流控规则持续有效
依赖:
<!--SpringCloud ailibaba sentinel-datasource-nacos -->
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-nacos</artifactId>
</dependency>
yml:
server:port: 8401spring:application:name: cloudalibaba-sentinel-servicecloud:nacos:discovery:server-addr: localhost:8848 #Nacos服务注册中心地址sentinel:transport:dashboard: localhost:8080 #配置Sentinel dashboard控制台服务地址port: 8719 #默认8719端口,假如被占用会自动从8719开始依次+1扫描,直至找到未被占用的端口web-context-unify: false # controller层的方法对service层调用不认为是同一个根链路datasource:ds1:nacos:server-addr: localhost:8848dataId: ${spring.application.name}groupId: DEFAULT_GROUPdata-type: jsonrule-type: flow # com.alibaba.cloud.sentinel.datasource.RuleType
查看源码可知,rule-type对应的就是:
流量控制规则 FlowRule
熔断降级规则 DegradeRule
访问控制规则 AuthorityRule
系统保护规则 SystemRule
热点规则 ParamFlowRule。
所有我们需要使用什么就配置什么,这里我配置的是flow 流量控制规则
添加Nacos业务配置规则:
[
{
"resource": "/rateLimit/byUrl",
"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:是否集群。
GateWay和Sentinel集成实现服务限流
@Configuration
public class GatewayConfiguration {private final List<ViewResolver> viewResolvers;private final ServerCodecConfigurer serverCodecConfigurer;public GatewayConfiguration(ObjectProvider<List<ViewResolver>> viewResolversProvider, ServerCodecConfigurer serverCodecConfigurer){this.viewResolvers = viewResolversProvider.getIfAvailable(Collections::emptyList);this.serverCodecConfigurer = serverCodecConfigurer;}@Bean@Order(Ordered.HIGHEST_PRECEDENCE)public SentinelGatewayBlockExceptionHandler sentinelGatewayBlockExceptionHandler() {// Register the block exception handler for Spring Cloud Gateway.return new SentinelGatewayBlockExceptionHandler(viewResolvers, serverCodecConfigurer);}@Bean@Order(-1)public GlobalFilter sentinelGatewayFilter() {return new SentinelGatewayFilter();}@PostConstruct //javax.annotation.PostConstructpublic void doInit() {initBlockHandler();}//处理/自定义返回的例外信息private void initBlockHandler() {Set<GatewayFlowRule> rules = new HashSet<>();rules.add(new GatewayFlowRule("pay_routh1").setCount(2).setIntervalSec(1));GatewayRuleManager.loadRules(rules);BlockRequestHandler handler = new BlockRequestHandler() {@Overridepublic Mono<ServerResponse> handleRequest(ServerWebExchange exchange, Throwable t) {Map<String,String> map = new HashMap<>();map.put("errorCode", HttpStatus.TOO_MANY_REQUESTS.getReasonPhrase());map.put("errorMessage", "请求太过频繁,系统忙不过来,触发限流(sentinel+gataway整合Case)");return ServerResponse.status(HttpStatus.TOO_MANY_REQUESTS).contentType(MediaType.APPLICATION_JSON).body(BodyInserters.fromValue(map));}};GatewayCallbackManager.setBlockHandler(handler);}}
解析:
这段代码配置了 Spring Cloud Gateway 与 Sentinel 的整合,目的是在网关层面使用 Sentinel 进行流量控制,并自定义限流触发后的响应。下面是逐行解析:
1. 类和注解
@Configuration public class GatewayConfiguration {
- @Configuration:表示该类是一个 Spring 配置类,Spring 会扫描这个类,并将其中的
@Bean
方法注册到 Spring 容器中。2. 成员变量和构造函数
private final List<ViewResolver> viewResolvers; private final ServerCodecConfigurer serverCodecConfigurer;public GatewayConfiguration(ObjectProvider<List<ViewResolver>> viewResolversProvider, ServerCodecConfigurer serverCodecConfigurer) {this.viewResolvers = viewResolversProvider.getIfAvailable(Collections::emptyList);this.serverCodecConfigurer = serverCodecConfigurer; }
- viewResolvers 和 serverCodecConfigurer:这两个成员变量分别用于 Spring WebFlux 的视图解析器和编解码器配置。
- 构造函数注入:
viewResolversProvider
是一个ObjectProvider
,它可以提供 Spring 配置的视图解析器列表(如果有的话),如果没有则返回一个空列表。serverCodecConfigurer
用于配置 WebFlux 的编解码器。3.
SentinelGatewayBlockExceptionHandler
Bean@Bean @Order(Ordered.HIGHEST_PRECEDENCE) public SentinelGatewayBlockExceptionHandler sentinelGatewayBlockExceptionHandler() {return new SentinelGatewayBlockExceptionHandler(viewResolvers, serverCodecConfigurer); }
- @Bean:将这个方法返回的对象注册为 Spring 的 Bean。
- @Order(Ordered.HIGHEST_PRECEDENCE):该注解设置了
SentinelGatewayBlockExceptionHandler
的优先级为最高,意味着它会优先于其他的GlobalFilter
处理请求。SentinelGatewayBlockExceptionHandler
是一个处理 Sentinel 限流或熔断异常的处理器,在请求被限流或阻止时,会触发该处理器。4.
SentinelGatewayFilter
Bean@Bean @Order(-1) public GlobalFilter sentinelGatewayFilter() {return new SentinelGatewayFilter(); }
- @Bean:这段代码注册了一个
SentinelGatewayFilter
实例,它是一个GlobalFilter
,用于在 Spring Cloud Gateway 中进行全局的限流控制。- @Order(-1):设置该过滤器的优先级为负值,确保它在其他过滤器之前执行。
5.
doInit()
方法@PostConstruct public void doInit() {initBlockHandler(); }
- @PostConstruct:表示
doInit()
方法会在 Spring 完成依赖注入之后自动执行。这个方法调用了initBlockHandler()
,用于初始化限流规则和限流后的处理逻辑。6.
initBlockHandler()
方法private void initBlockHandler() {Set<GatewayFlowRule> rules = new HashSet<>();rules.add(new GatewayFlowRule("pay_routh1").setCount(2).setIntervalSec(1));GatewayRuleManager.loadRules(rules);BlockRequestHandler handler = new BlockRequestHandler() {@Overridepublic Mono<ServerResponse> handleRequest(ServerWebExchange exchange, Throwable t) {Map<String, String> map = new HashMap<>();map.put("errorCode", HttpStatus.TOO_MANY_REQUESTS.getReasonPhrase());map.put("errorMessage", "请求太过频繁,系统忙不过来,触发限流(sentinel+gataway整合Case)");return ServerResponse.status(HttpStatus.TOO_MANY_REQUESTS).contentType(MediaType.APPLICATION_JSON).body(BodyInserters.fromValue(map));}};GatewayCallbackManager.setBlockHandler(handler); }
设置限流规则:
GatewayFlowRule("pay_routh1")
: 创建一个名为"pay_routh1"
的限流规则,限定该路由每秒最多允许 2 个请求。setCount(2).setIntervalSec(1)
: 设置请求频率为每秒最多 2 次请求。GatewayRuleManager.loadRules(rules)
: 将规则加载到 Sentinel 中。设置限流后的处理逻辑:
- BlockRequestHandler:这是 Sentinel 在流控规则触发时执行的回调方法。当请求触发限流时,
handleRequest
方法会被调用。handleRequest()
:如果流量被控制(例如请求频率过高),返回一个HttpStatus.TOO_MANY_REQUESTS
(HTTP 429)错误,并包含一个 JSON 格式的错误消息,通知客户端请求频繁。7. 核心流程概述
- 请求到达 Gateway:客户端发送请求到 Spring Cloud Gateway。
- SentinelGatewayFilter 处理请求:请求进入
SentinelGatewayFilter
,Sentinel 会根据定义的流量控制规则判断请求是否需要限流。- 流量超限:如果请求超过了流量限制(例如每秒 2 次),则触发限流。
- 触发 BlockRequestHandler:当流控规则触发时,
BlockRequestHandler
被调用,返回 429 错误和错误消息,告知客户端请求过于频繁。8. 作用与总结
- 流量控制:这段代码利用 Sentinel 和 Spring Cloud Gateway 实现了对请求的流量控制,防止系统被过高的请求负载压垮。
- 限流响应:当请求被限流时,定义了自定义的错误响应,确保客户端能够收到清晰的错误信息。
- 全局配置:通过
GlobalFilter
和SentinelGatewayBlockExceptionHandler
,全局管理了 Sentinel 的限流规则和错误处理逻辑。总体来说,这段代码配置了 Spring Cloud Gateway 与 Sentinel 的结合,处理请求的限流和熔断,并定义了当流控规则被触发时返回的错误响应。
注:文章可能有不完整,或有误的地方,如有问题还请评论区斧正。