服务使用中的各种问题
服务雪崩
服务降级
服务熔断
服务限流
安装Sentinel
sentinel和项目必须在同一个ip下
sentinel组件由2部分构成
后台
前台8080
运行命令
前提
java8环境OK
8080端口不能被占用
命令
java -jar sentinel-dashboard-1.7.1.jar
访问sentinel管理界面
http://localhost:8080
登录账户密码均为 sentinel
初始化演示工程
启动Nacos8848成功
http://localhost:8848/nacos/#/login
创建Module cloudalibaba-sentinel-service8401
POM<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd"><parent><artifactId>wms</artifactId><groupId>com.hk.wms</groupId><version>1.0-SNAPSHOT</version></parent><modelVersion>4.0.0</modelVersion><artifactId>wms-sentinel-service8401</artifactId> <dependencies><!-- 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><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><!--监控--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-actuator</artifactId></dependency><!--热部署--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-devtools</artifactId><scope>runtime</scope><optional>true</optional></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency> </dependencies> </project>
YMLserver:port: 8401spring:application:name: wms-sentinal-servicecloud:nacos:discovery:#Nacos服务注册中心地址server-addr: 192.168.1.131:8848sentinel:transport:#配置Sentin dashboard地址dashboard: localhost:8080# 默认8719端口,假如被占用了会自动从8719端口+1进行扫描,直到找到未被占用的 端口port: 8719 service-url:nacos-user-service: http://wms-provider config-url:config-service: http://wms-config-3377management:endpoints:web:exposure:include: '*'
主启动package com.hk.wms.wmssentinelservice8401;import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.discovery.EnableDiscoveryClient;@SpringBootApplication @EnableDiscoveryClient public class WmsSentinelService8401Application {public static void main(String[] args) {SpringApplication.run(WmsSentinelService8401Application.class, args);}}
业务类FlowLimitControllerpackage com.hk.wms.wmssentinelservice8401.controller;import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.client.RestTemplate; @RestController public class FlowLimitController {@Value("${service-url.nacos-user-service}")String serverUrl ;@Value("${config-url.config-service}")String configUrl;@AutowiredRestTemplate template;@GetMapping("/testA")public String testA() {return "----testA";}@GetMapping("/testB")public String testB() {return "----testB";}@GetMapping("/testC")public String testC() {return template.getForObject(serverUrl+"/provider/",String.class);} }
启动Sentinel8080
启动微服务8401
空空如也
Sentinel采用的懒加载说明
执行一次访问即可
http://localhost:8401/testA
http://localhost:8401/testB
sentinel8080正在监控微服务8401sentinel和项目必须在同一个ip下,sentinel才能监控微服务端口8401
流控规则
基本介绍
进一步解释说明
流控模式
直接(默认)
直接-> 快速失败
默认
配置及说明
测试
快速点击多次http://localhost:8401/testA
结果
思考
直接调用默认报错原因,技术方面OK,但是应该要有设置对应的报错信息和界面方便管理
关联
是什么
当关联的资源达到阈值时,就限流自己
当与A关联的资源B达到阈值后,就限流A自己
B惹事,A挂了
配置A
postman模拟并发密集访问testB
postman开启20个线程每隔0.3s请求/testB,导致A挂了
运行后发现testA挂了
流控效果
直接-> 快速失败(默认的流控处理)
直接失败,抛出异常
Blocked by Sentinel(flowing limit)
预热
说明
公式:阈值除以coldFactor(默认值为3),经过预热时长后才会阈值
官网
默认ColdFactor为3,即请求QPS从threashold/3开始,经预热时长逐渐升到设定的QPS阈值
限流冷启动
WarmUp配置
多次点击http://localhost:8401/testB
应用场景
排队等待
匀速排队,严格控制请求通过的间隔时间
官网
测试
postman设置请求发送速率即可
降级规则
基本介绍
进一步说明
Sentinel的断路器是没有半开状态的
半开的状态,系统会自动检测是否请求有异常,没有异常就关闭断路器恢复使用,有异常则继续打开断路器不可用,具体可以参考Hystrix
RT
是什么
测试
代码@GetMapping("/testD") public String testD() {try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}log.info("testD 测试RT");return "----testD"; }
配置
jmeter压测
结论
异常比例
是什么
配置
jmeter设置每秒10个请求即可
结论
异常数
是什么
异常数是按照分钟统计的
测试
代码@GetMapping("/testE") public String testE() {log.info("testE 测试异常数");int age = 10 / 0;return "----testE 测试异常数"; }
配置
jmeter
热点key限流
基本介绍
是什么
@SentinelResource
代码@GetMapping("/testHotKey") @SentinelResource(value = "testHotKey",blockHandler = "deal_testHotKey") public String testHotKey(@RequestParam(value = "p1",required = false)String p1,@RequestParam(value = "p2",required = false)String p2) {return "----testHotKey"; }public String deal_testHotKey(String p1, String p2, BlockException exception) {return "----deal_testHotKey, o(╥﹏╥)o"; // sentinel的默认提示都是: Blocked by Sentinel (flow limiting) }
com.alibaba.csp.sentinel.slots.block.BlockException
配置
1
@SentinelResource(value = "testHotKey")
异常会直接打印到前台用户界面,提示不太友好
2
@SentinelResource(value = "testHotKey",blockHandler = "deal_testHotKey")
方法testHotKey里面第一个参数只要QPS超过每秒1次,马上降级处理
用了我们自己定义的
参数例外项
上诉案例演示了第一个参数p1,当QPS超过1秒1次点击后马上被限流
特例情况
普通
超过1秒钟一个后,达到阈值1后马上被限流
我们期望p1参数当它是某一个特殊值时,它的限流值和平时不一样
特例
假如当p1的值等于5时,它的QPS可以为200
配置
测试
http://localhost:8401/testHotKey?p1=1&p2=2
http://localhost:8401/testHotKey?p1=5&p2=2
当p1为5的时候,阈值QPS变为了200
前提条件
热点参数的注意点,参数必须为基本类型或者String
其他
添加一个java运行异常试试
系统规则
是什么
@SentinelResource
配置@SentinelResource后如果想使用blockHandler配置流控规则的资源名就必须使用@SentinelResource的value值
按资源名称限流+后续处理
启动Nacos成功
启动Sentinel成功
Module
cloudalibaba-sentinel-service8401
业务类RateLimitController@GetMapping("/testHotKey")@SentinelResource(value = "testHotKey",blockHandler = "deal_testHotKey")public String testHotKey(@RequestParam(value = "p1",required = false)String p1,@RequestParam(value = "p2",required = false)String p2) {return "----testHotKey";}public String deal_testHotKey(String p1, String p2, BlockException exception) {return "----deal_testHotKey, o(╥﹏╥)o"; // sentinel的默认提示都是: Blocked by Sentinel (flow limiting)}
配置流控规则
一秒点击一次,ok,疯狂点击,返回自己定义的限流处理信息,限流发生
额外问题
此时关闭服务8401
Sentinel控制台,流控规则消失了??
所以说规则是跟着微服务的生命周期的
上面兜底方案面临的问题
客户自定义限流处理逻辑
创建CustomerBlockHandler类用于自定义限流处理逻辑
自定义限流处理类
CustomerBlockHandlerpublic class CustomerBlockHandler {public static String customBlockHandler(BlockException e){return "限流!!!!!";} }
注意:customBlockHandler方法必须是静态的,否则无法被调用
RateLimitController@GetMapping("/customBlockHandler")@SentinelResource(value="customBlockHandler",blockHandlerClass = CustomerBlockHandler.class,blockHandler = "customBlockHandler")public String customBlockHandler(){return "测试成功";}
启动微服务后先调用一次
Sentinel控制台配置
更多注解属性说明
多说一句
Sentinel主要有三个核心API
SPhU定义资源
Tracer定义统计
ContextUtil定义了上下文
服务熔断功能
Ribbon系列
启动nacos和sentinel
提供者9002/9001
POM ,YML,主启动,没有变化
业务类@GetMapping("/getStr/{id}")public String getStr(@PathVariable("id")int id){return id+":"+serverPort;}
消费者8401
ApplicationContextConfigpackage com.hk.wms.wmscustom83.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 ApplicationContextConfig {@Bean@LoadBalancedpublic RestTemplate getRestTemplate(){return new RestTemplate();} }
CircleBreakerController@GetMapping("/testF/{id}")@SentinelResource(value="testF",blockHandlerClass = CustomerBlockHandler.class,blockHandler = "customBlockHandler1",fallback = "handlerFallBack")public String CircleBreakerController(@PathVariable("id")int id){String str=template.getForObject(serverUrl+"/getStr/"+id,String.class);if(id<0){throw new IllegalArgumentException("IllegalArgument ,非法参数异常...");}if(str==null){throw new NullPointerException("NullPointerException,该ID没有对应记录,空指针异常");}return str;}
修改后请重启微服务
热部署对java代码级生效及时 ,对@SentinelResource注解内属性,有时效果不好
fallback管运行异常,public String handlerFallBack(@PathVariable int id,Throwable e){return "异常";}
blockHandler管配置违规public static String customBlockHandler1(@PathVariable int id, BlockException e){return "限流1!!!!!";}
注意:fallback方法和blockHandler方法必须有和@SentinelResource注解的方法具有同样的形参,否则无法生效
同时配置fallback和blockHandler
异常忽略
Feign系列
修改8401模块
8401消费者调用提供者9002
Feign组件一般是消费端
POM<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-openfeign</artifactId> </dependency>
YMLserver:port: 8401spring:application:name: wms-sentinal-servicecloud:nacos:discovery:#Nacos服务注册中心地址server-addr: 192.168.1.131:8848sentinel:transport:#配置Sentin dashboard地址dashboard: localhost:8080 #8080端口将会监控8401# 默认8719端口,假如被占用了会自动从8719端口+1进行扫描,直到找到未被占用的 端口port: 8719datasource:ds1:nacos:server-addr: 192.168.1.131:8848dataId: wms-sentinal-servicegroupId: DEFAULT_GROUPdata-type: jsonrule-type: flow service-url:nacos-user-service: http://wms-provider config-url:config-service: http://wms-config-3377management:endpoints:web:exposure:include: '*' #激活sentinel对feign的支持 feign:sentinel:enabled: true
业务类
带@FeignClient注解的业务接口
fallback = FeignFallbackService.classpackage com.hk.wms.wmssentinelservice8401.service;import org.springframework.cloud.openfeign.FeignClient; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable;@FeignClient(value="wms-provider",fallback = FeignFallbackService.class) public interface FeignService {@GetMapping(value="/getStr/{id}")public String getString(@PathVariable("id") int id); }
用于处理服务方的异常错误
package com.hk.wms.wmssentinelservice8401.service;import org.springframework.stereotype.Component;@Component public class FeignFallbackService implements FeignService{@Overridepublic String getString(int id) {return "服务降级返回";} }
Controller@GetMapping("/testE/{id}")@SentinelResource(value="testE",blockHandlerClass = CustomerBlockHandler.class,blockHandler = "customBlockHandler2",fallback = "handlerFallBack")public String getStrController(@PathVariable("id")int id){String str=feignService.getString(id);if(id<0){throw new IllegalArgumentException("IllegalArgument ,非法参数异常...");}if(str==null){throw new NullPointerException("NullPointerException,该ID没有对应记录,空指针异常");}return str;}
fallback管运行异常,
public String handlerFallBack(@PathVariable int id,Throwable e){return "异常";}
blockHandler管配置违规
public static String customBlockHandler2(@PathVariable int id, BlockException e){return id+"限流1!!!!!";}
注意:fallback方法和blockHandler方法必须有和@SentinelResource注解的方法具有同样的形参,否则无法生效
主启动
添加@EnableFeignClients启动Feign的功能package com.hk.wms.wmssentinelservice8401;import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; import org.springframework.cloud.openfeign.EnableFeignClients;@SpringBootApplication @EnableDiscoveryClient @EnableFeignClients public class WmsSentinelService8401Application {public static void main(String[] args) {SpringApplication.run(WmsSentinelService8401Application.class, args);}}
代码
package com.hk.wms.wmssentinelservice8401.controller;import com.alibaba.csp.sentinel.annotation.SentinelResource; import com.alibaba.csp.sentinel.slots.block.BlockException; import com.hk.wms.wmssentinelservice8401.myhandler.CustomerBlockHandler; import com.hk.wms.wmssentinelservice8401.service.FeignService; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; 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.RequestParam; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.client.RestTemplate;import javax.annotation.Resource;@RestController @Slf4j public class FlowLimitController {@ResourceFeignService feignService;@Value("${service-url.nacos-user-service}")String serverUrl ;@Value("${config-url.config-service}")String configUrl;@AutowiredRestTemplate template;@GetMapping("/testA")@SentinelResource(value="testA",blockHandler = "deal_testA")public String testA(@RequestParam(value="p1",required = false)String p1) {System.out.println(Thread.currentThread().getName()+".................testA");return "----testA";}@GetMapping("/testB")@SentinelResource(value="testB")public String testB() {try {Thread.sleep(800L);} catch (InterruptedException e) {e.printStackTrace();}finally {return "----testA";}}@GetMapping("/testC")public String testC() {return template.getForObject(serverUrl+"/provider/",String.class);}@GetMapping("/testHotKey")@SentinelResource(value = "testHotKey",blockHandler = "deal_testHotKey")public String testHotKey(@RequestParam(value = "p1",required = false)String p1,@RequestParam(value = "p2",required = false)String p2) {return "----testHotKey";}@GetMapping("/customBlockHandler")@SentinelResource(value="customBlockHandler",blockHandlerClass = CustomerBlockHandler.class,blockHandler = "customBlockHandler")public String customBlockHandler(){return "测试成功";}public String deal_testHotKey(String p1, String p2, BlockException exception) {return "----deal_testHotKey, o(╥﹏╥)o"; // sentinel的默认提示都是: Blocked by Sentinel (flow limiting)}public String deal_testA(String p1,BlockException e){return "----testA+";}@GetMapping("/testE/{id}")@SentinelResource(value="testE",blockHandlerClass = CustomerBlockHandler.class,blockHandler = "customBlockHandler2",fallback = "handlerFallBack")public String getStrController(@PathVariable("id")int id){String str=feignService.getString(id);if(id<0){throw new IllegalArgumentException("IllegalArgument ,非法参数异常...");}if(str==null){throw new NullPointerException("NullPointerException,该ID没有对应记录,空指针异常");}return str;}@GetMapping("/testF/{id}")@SentinelResource(value="testF",blockHandlerClass = CustomerBlockHandler.class,blockHandler = "customBlockHandler1",fallback = "handlerFallBack")public String CircleBreakerController(@PathVariable("id")int id){String str=template.getForObject(serverUrl+"/getStr/"+id,String.class);if(id<0){throw new IllegalArgumentException("IllegalArgument ,非法参数异常...");}if(str==null){throw new NullPointerException("NullPointerException,该ID没有对应记录,空指针异常");}return str;}public String handlerFallBack(@PathVariable int id,Throwable e){return "异常";}}
自定义 blockHandler管配置违规
package com.hk.wms.wmssentinelservice8401.myhandler;import com.alibaba.csp.sentinel.slots.block.BlockException; import org.springframework.web.bind.annotation.PathVariable;/****/ public class CustomerBlockHandler {public static String customBlockHandler(BlockException e){return "限流!!!!!";}public static String customBlockHandler1(@PathVariable int id, BlockException e){return "限流1!!!!!";}public static String customBlockHandler2(@PathVariable int id, BlockException e){return id+"限流1!!!!!";} }
熔断框架比较
Sentinel规则持久化
一旦我们重启应用,sentinel规则将消失,生产环境需要将配置规则进行持久化
将限流配置规则持久化进Nacos保存,只要刷新8401某个rest地址,sentinel控制台的流控规则就能看到,只要Nacos里面的配置不删除,针对8401上sentinel上的流控规则持续有效
步骤
修改cloudalibaba-sentinel-service8401
POM
<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-openfeign</artifactId></dependency>
YML
server:port: 8401spring:application:name: wms-sentinal-servicecloud:nacos:discovery:#Nacos服务注册中心地址server-addr: 192.168.1.131:8848sentinel:transport:#配置Sentin dashboard地址dashboard: localhost:8080 #8080端口将会监控8401# 默认8719端口,假如被占用了会自动从8719端口+1进行扫描,直到找到未被占用的 端口port: 8719 #datasource是sentinel的一个属性datasource:ds1:nacos:server-addr: 192.168.1.131:8848dataId: wms-sentinal-servicegroupId: DEFAULT_GROUPdata-type: jsonrule-type: flow service-url:nacos-user-service: http://wms-provider config-url:config-service: http://wms-config-3377 #暴露所有接口 management:endpoints:web:exposure:include: '*' #激活sentinel对feign的支持 feign:sentinel:enabled: true
#datasource是sentinel的一个属性datasource:ds1:nacos:server-addr: 192.168.1.131:8848dataId: wms-sentinal-servicegroupId: DEFAULT_GROUPdata-type: jsonrule-type: flow
注意:datasource是sentinel的一个属性,properties写法:spring.cloud.sentinel.datasource
添加Nacos业务规则配置
[{"resource": "testF","limitApp": "default","grade": 1,"count": 1,"strategy": 0,"controlBehavior": 0,"clusterMode": false} ]
内容解析
注意:resource的写法有两种,一种是按照路径的写法,就是@requestMapping的value,一种是写 @SentinelResource 的value这一步的作用是每次消费者微服务启动时在nacos中定义sentinel的流控规则,从而做到持久化的效果
启动8401后刷新sentinel发现业务规则有了
停止8401再看sentinel
发现流控规则没有了
重新启动8401再看sentinel
咋一看没有,但是你调用 /rateLimit/byUrl后它就会出现