目录
-
sentinel 下载安装:
- 访问管理页面 localhost:8080
-
初始化监控:
- 建module
- 导入POM
<dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-config</artifactId> </dependency> <dependency> <groupId>com.alibaba.csp</groupId> <artifactId>sentinel-datasource-nacos</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-stream-rabbit</artifactId> </dependency> <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>com.ljw.springcloudstudy</groupId> <artifactId>cloud-api-commons</artifactId> <version>${project.version}</version> </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.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> <version>0.2.1.RELEASE</version> </dependency> </dependencies>
-
YML
server: port: 8401 spring: application: name: cloudalibaba-sentinel-service cloud: nacos: discovery: server-addr: localhost:8848 sentinel: transport: dashboard: localhost:8080 # 默认8719端口,假如被占用会自动从8719开始依次+1扫描,直至找到未被占用的端口 port: 8719 management: endpoints: web: exposure: include: "*"
-
主启动
@SpringBootApplication @EnableDiscoveryClient public class MainApp8401 { public static void main(String[] args) { SpringApplication.run(MainApp8401.class,args); } }
-
controller
@RestController public class FlowLimitController { @GetMapping("/testA") public String testA() { return "-------testA"; } @GetMapping("/testB") public String testB() { return "-------testB"; } }
-
执行一次访问后访问控制台(懒加载),可以看到该服务
-
流量控制:
- 流控模式为直接,流控效果为快速失败时:
- 流控模式为关联时,当关联资源达到阈值时,限流自己;
当testB访问达到阈值后,testA:
- 流控效果为warm up(预热)时:一开始阈值为设定阈值/cold Factor,经过预热时长后,变为设定的阈值
- 流控效果为排队等待时:使得请求匀速通过
-
服务降级:
- 首先是慢调用比例:
-
@GetMapping("/testD") public String testD() { try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } return "-------testD"; } }
-
配置:
-
测试后发现熔断
-
异常比例:
-
@GetMapping("/testE") public String testE() { //异常 int age=10/0; return "-------testE"; }
-
-
-
异常数:
-
/** * 异常数 * @return */ @GetMapping("/testF") public String testF() { int age=10/0; return "-------testF"; }
-
-
-
热点规则:
-
针对热点参数进行限流;
/** * 热点参数限流 * * @param p1 * @param p2 * @return */ @GetMapping("/testHotKey") // 作为唯一标识,一般和url统一,也就是资源名称 @SentinelResource(value = "testHotKey", blockHandler = "dealTestHotKey") public String testHotKey(@RequestParam(value = "p1", required = false) String p1, @RequestParam(value = "p2", required = false) String p2) { return "-----testHotKey"; } //兜底方法,如果发现了热点参数,就会走这里 public String dealTestHotKey(String p1, String p2, BlockException exception) { return "------dealTestHotKey"; } }
- 制定热点规则:
- 第一个参数在一秒钟内QPS超过了阈值,则立刻进行降级处理,携带p1也会按照这个规则进行;
- 参数例外项:当热点参数为特殊值的时候,会有不同于其他的限流阈值;
- 如果是运行异常,那么@SentinelResource不负责,不走该兜底方法,@SentinelResource只负责违背控制台配置的异常。
/** * 热点参数限流 * * @param p1 * @param p2 * @return */ @GetMapping("/testHotKey") // 作为唯一标识,一般和url统一,也就是资源名称 @SentinelResource(value = "testHotKey", blockHandler = "dealTestHotKey") public String testHotKey(@RequestParam(value = "p1", required = false) String p1, @RequestParam(value = "p2", required = false) String p2) { // 模拟运行时异常 int age =10/0; return "-----testHotKey"; } //兜底方法,如果发现了热点参数,就会走这里 public String dealTestHotKey(String p1, String p2, BlockException exception) { return "------dealTestHotKey/(ㄒoㄒ)/~~"; }
-
sentinel系统规则(系统自适应限流):
- 从整体维度对应用入口流量进行控制;
- 以入口QPS举例:
、
-
@SentinelResource的配置:
- 按资源名称进行限流+后续处理;
- 首先在8401中引入自定义jar包:
<!--用于支付Entity--> <dependency> <groupId>com.ljw.springcloudstudy</groupId> <artifactId>cloud-api-commons</artifactId> <version>${project.version}</version> </dependency>
-
新加一个业务类:
@RestController public class ReTeLimitController { @GetMapping("/byResource") @SentinelResource(value = "byResource",blockHandler = "handleException") public CommonResult byResource(){ return new CommonResult(200,"按资源名称进行限流测试",new Payment(2020L,"serial001")); } public CommonResult handleException(BlockException exception){ return new CommonResult(444,exception.getClass().getCanonicalName()+"\t","服务不可用"); } }
-
添加限流条件:
-
测试:
-
关闭服务后,流控规则会消失(非持久化);
-
如果没有自定义blockHandler方法,则会用系统自带的。
-
按资源url进行限流+后续处理;
-
@GetMapping("/byUrl") // 没有自定义blockHandler方法,将会使用默认的 @SentinelResource(value = "byUrl") public CommonResult byUrl(){ return new CommonResult(200,"按资源url进行限流测试",new Payment(2020L,"serial002")); } }
-
-
测试:
- 自定义限流处理+解耦:
- 创建自定义限流处理类:
public class CustomerBlockHandler { public static CommonResult handlerException(BlockException exception){ return new CommonResult(444,"自定义限流处理,global",new Payment(2020L,"serial003")); } public static CommonResult handlerException2(BlockException exception){ return new CommonResult(444,"自定义限流处理,global",new Payment(2020L,"serial003")); } }
@GetMapping("/customerBlockHandler") //调用限流处理的方法2 @SentinelResource(value = "customerBlockHandler", blockHandlerClass = CustomerBlockHandler.class, blockHandler = "handlerException2") public CommonResult customerBlockHandler() { return new CommonResult(200, "自定义限流处理", new Payment(2020L, "serial003")); } }
需要使用资源名进行限流;
- 测试:
-
Sentinel服务熔断:
- 新建payment服务9003,9004,消费者84
- 服务端YML:
server: port: 9003 spring: application: name: nacos-payment-provider cloud: nacos: discovery: server-addr: localhost:8848 management: endpoints: web: exposure: include: "*"
- 服务端controller:
@RestController public class PaymentController { @Value("${server.port}") public String serverPort; public static HashMap<Long, Payment> hashMap=new HashMap<>(); static { hashMap.put(1L,new Payment(1L, UUID.randomUUID().toString())); hashMap.put(2L,new Payment(2L, UUID.randomUUID().toString())); hashMap.put(3L,new Payment(3L, UUID.randomUUID().toString())); } @GetMapping("/paymentSQL/{id}") public CommonResult<Payment> paymentSQL(@PathVariable("id") Long id){ Payment payment = hashMap.get(id); CommonResult<Payment> result = new CommonResult<>(200, "from sql,serverPort" + serverPort, payment); return result; } }
-
消费端主启动:
@SpringBootApplication @EnableFeignClients @EnableDiscoveryClient public class OrderMain84 { public static void main(String[] args) { SpringApplication.run(OrderMain84.class,args); } }
-
消费端service:
@FeignClient(value ="nacos-payment-provider") public interface PaymentFeignService { @GetMapping("/paymentSQL/{id}") public CommonResult<Payment> paymentSQL(@PathVariable("id") Integer id); }
-
消费端controller
@RestController public class OrderController { private static final String SERVICE_NAME="nacos-payment-provider"; @Resource private PaymentFeignService paymentFeignService; @GetMapping("/consumer/fallback/{id}") @SentinelResource(value = "fallback") public CommonResult<Payment> paymentSQL(@PathVariable("id") Long id){ CommonResult<Payment> result = paymentFeignService.paymentSQL(id); if (id == 4){ throw new IllegalArgumentException("IllegalArgumentException,非法参数异常"); }else if (result.getData()==null){ throw new NullPointerException("NullPointerException,该ID没有对应记录,空指针异常"); } return result; } }
-
fallback负责处理运行时异常,blockhandler负责配置违规;
-
如果没有配fallback,那么将直接报错;
-
只配置fallback时:
@GetMapping("/consumer/fallback/{id}") // 配置fallback方法 @SentinelResource(value = "fallback",fallback = "handlerFallBack") public CommonResult<Payment> paymentSQL(@PathVariable("id") Long id){ CommonResult<Payment> result = paymentFeignService.paymentSQL(id); if (id == 4){ throw new IllegalArgumentException("IllegalArgumentException,非法参数异常"); }else if (result.getData()==null){ throw new NullPointerException("NullPointerException,该ID没有对应记录,空指针异常"); } return result; } public CommonResult<Payment> handlerFallBack(@PathVariable("id") Long id,Throwable e){ Payment payment = new Payment(id, "null"); return new CommonResult<Payment>(444,"兜底异常handlerFallBack,exception内容"+e.getMessage(),payment); }
-
只配置blockhandler,不处理运行时异常,只处理配置违规,但是可以在配置中设置异常比例和异常数来处理运行时异常;
-
同时配置fallback和blockHandler:
@GetMapping("/consumer/fallback/{id}") // 配置fallback方法 @SentinelResource(value = "fallback",fallback = "handlerFallBack",blockHandler = "blockHandler") public CommonResult<Payment> paymentSQL(@PathVariable("id") Long id){ CommonResult<Payment> result = paymentFeignService.paymentSQL(id); if (id == 4){ throw new IllegalArgumentException("IllegalArgumentException,非法参数异常"); }else if (result.getData()==null){ throw new NullPointerException("NullPointerException,该ID没有对应记录,空指针异常"); } return result; } public CommonResult<Payment> handlerFallBack(@PathVariable("id") Long id,Throwable e){ Payment payment = new Payment(id, "null"); return new CommonResult<Payment>(444,"兜底异常handlerFallBack,exception内容"+e.getMessage(),payment); } public CommonResult<Payment> blockHandler(@PathVariable("id") Long id, BlockException blockException){ Payment payment = new Payment(id, null); return new CommonResult<>(445,"blockHandler限流,blockException"+blockException.getMessage(),payment); } }
-
-
-
同时违反配置和运行报错:
走blockHandler;
-
exceptionsToIgnore:
- 也就是发生该异常的时候,不再走fallback方法,没有降级效果;
-
@GetMapping("/consumer/fallback/{id}") // 配置fallback方法 @SentinelResource(value = "fallback", fallback = "handlerFallBack", blockHandler = "blockHandler", // 也就是发生该异常的时候,不再走fallback方法,没有降级效果 exceptionsToIgnore = {IllegalArgumentException.class}) public CommonResult<Payment> paymentSQL(@PathVariable("id") Long id){ CommonResult<Payment> result = paymentFeignService.paymentSQL(id); if (id == 4){ throw new IllegalArgumentException("IllegalArgumentException,非法参数异常"); }else if (result.getData()==null){ throw new NullPointerException("NullPointerException,该ID没有对应记录,空指针异常"); } return result; } public CommonResult<Payment> handlerFallBack(@PathVariable("id") Long id,Throwable e){ Payment payment = new Payment(id, "null"); return new CommonResult<Payment>(444,"兜底异常handlerFallBack,exception内容"+e.getMessage(),payment); } public CommonResult<Payment> blockHandler(@PathVariable("id") Long id, BlockException blockException){ Payment payment = new Payment(id, null); return new CommonResult<>(445,"blockHandler限流,blockException"+blockException.getMessage(),payment); }
-
-
-
Sentinel服务熔断OpenFeign:
- 消费者端YML
server: port: 84 spring: application: name: nacos-payment-order cloud: nacos: discovery: server-addr: localhost:8848 sentinel: transport: dashboard: localhost:8080 # 默认8719端口,假如被占用会自动从8719开始依次+1扫描,直至找到未被占用的端口 port: 8719 management: endpoints: web: exposure: include: "*" #开启sentinel对feign的支持 feign: sentinel: enabled: true
-
主启动:
@SpringBootApplication @EnableFeignClients @EnableDiscoveryClient public class OrderMain84 { public static void main(String[] args) { SpringApplication.run(OrderMain84.class,args); } }
-
service:
@FeignClient(value ="nacos-payment-provider",fallback = PaymentFeignServiceImpl.class) public interface PaymentFeignService { @GetMapping("/paymentSQL/{id}") public CommonResult<Payment> paymentSQL(@PathVariable("id") Long id); }
-
通配服务降级,处理服务端宕机,服务调用失败的情况;
@Component public class PaymentFeignServiceImpl implements PaymentFeignService { @Override public CommonResult<Payment> paymentSQL(Long id) { return new CommonResult<Payment>(444, "服务降级访问,------PaymentFeignServiceImpl", new Payment(id, "errorSerial")); } }
-
sentinel持久化规则:
- 需要导入POM
<dependency> <groupId>com.alibaba.csp</groupId> <artifactId>sentinel-datasource-nacos</artifactId> </dependency>
-
YML
server: port: 8401 spring: application: name: cloudalibaba-sentinel-service cloud: nacos: discovery: server-addr: localhost:8848 sentinel: transport: dashboard: localhost:8080 # 默认8719端口,假如被占用会自动从8719开始依次+1扫描,直至找到未被占用的端口 port: 8719 # 持久化sentinel规则的配置 datasource: ds1: nacos: server-addr: localhost:8848 dataId: cloudalibaba-sentinel-service groupId: DEFAULT_GROUP data-type: json rule-type: flow management: endpoints: web: exposure: include: "*"
-
在nacos中新建配置:
其中,resource:资源名称;limitApp:来源应用; grade:阀值类型,0表示线程数,1表示QPS,count:单机阈值,strategy:流控模式,0表示直接,1表示关联,2表示链路;controlBehavior:流控效果,0表示快速失败,1表示Warm Up,2表示排队等待;clusterMode:是否集群;
-
创建成功,测试:
服务重启后,该规则依旧存在。