随着微服务的流行,服务和服务之间的稳定性变得越来越重要。Sentinel 以流量为切入点,从流量控制、流量路由、熔断降级、系统自适应过载保护、热点流量防护等多个维度保护服务的稳定性。
Sentinel 具有以下特征:
丰富的应用场景:Sentinel 承接了阿里巴巴近 10 年的双十一大促流量的核心场景,例如秒杀(即突发流量控制在系统容量可以承受的范围)、消息削峰填谷、集群流量控制、实时熔断下游不可用应用等。
完备的实时监控:Sentinel 同时提供实时的监控功能。您可以在控制台中看到接入应用的单台机器秒级数据,甚至 500 台以下规模的集群的汇总运行情况。
广泛的开源生态:Sentinel 提供开箱即用的与其它开源框架/库的整合模块,例如与 Spring Cloud、Apache Dubbo、gRPC、Quarkus 的整合。您只需要引入相应的依赖并进行简单的配置即可快速地接入 Sentinel。同时 Sentinel 提供 Java/Go/C++ 等多语言的原生实现。
完善的 SPI 扩展机制:Sentinel 提供简单易用、完善的 SPI 扩展接口。您可以通过实现扩展接口来快速地定制逻辑。例如定制规则管理、适配动态数据源等。
Sentinel 分为两个部分:
核心库(Java 客户端)不依赖任何框架/库,能够运行于所有 Java 运行时环境,同时对 Dubbo / Spring Cloud 等框架也有较好的支持。
控制台(Dashboard)基于 Spring Boot 开发,打包后可以直接运行,不需要额外的 Tomcat 等应用容器。
Sentinel 社区正在将流量治理相关标准抽出到 OpenSergo 标准中,Sentinel 作为流量治理标准实现。有关 Sentinel 流控降级与容错 spec 的最新进展,请参考 opensergo-specification,也欢迎社区一起来完善标准与实现。
Sentinel 社区官方网站:https://sentinelguard.io/
入门步骤,首先引入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 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.7.8</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.example</groupId> <artifactId>Sentinel</artifactId> <version>0.0.1-SNAPSHOT</version> <name>Sentinel</name> <description>Sentinel</description> <properties> <java.version>1.8</java.version> </properties> <dependencies> <!-- ______________________________________________ Spring --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <!-- ______________________________________________ Ali sentinel --> <dependency> <groupId>com.alibaba.csp</groupId> <artifactId>sentinel-core</artifactId> <version>1.8.5</version> </dependency> <!-- 连接控制台,启动后手动触发一次才能在控制台看到 --> <dependency> <groupId>com.alibaba.csp</groupId> <artifactId>sentinel-transport-simple-http</artifactId> <version>1.8.5</version> </dependency> <!-- 使用注解配置时 --> <dependency> <groupId>com.alibaba.csp</groupId> <artifactId>sentinel-annotation-aspectj</artifactId> <version>1.8.5</version> </dependency> <dependency> <groupId>com.alibaba.csp</groupId> <artifactId>sentinel-parameter-flow-control</artifactId> <version>1.8.5</version> </dependency> <!-- ______________________________________________ tools --> <dependency> <groupId>cn.hutool</groupId> <artifactId>hutool-all</artifactId> <version>5.8.0.M1</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <configuration> <excludes> <exclude> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </exclude> </excludes> </configuration> </plugin> </plugins> </build> </project>
编码流控规则,这里采用手动编码,这里没有接入控制台
package com.example.sentinel.config; import com.alibaba.csp.sentinel.slots.block.RuleConstant; import com.alibaba.csp.sentinel.slots.block.flow.FlowRule; import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager; import org.springframework.context.annotation.Configuration; import javax.annotation.PostConstruct; import java.util.ArrayList; import java.util.List; @Configuration public class InitSRule { /** * Sentinel log base directory is: C:\Users\cuisu\logs\csp\ * p 代表通过的请求。 * block 代表被阻止的请求。 * s 代表成功执行完成的请求个数。 * e 代表用户自定义的异常。 * rt 代表平均响应时长。 */ @PostConstruct public void initFlowRule() { //1.创建存放限流规则的集合 List<FlowRule> rules = new ArrayList<>();//2.创建限流规则 FlowRule rule = new FlowRule(); //定义资源,表示sentinel会对哪个资源生效 rule.setResource("getHello"); //定义限流规则类型为QPS rule.setGrade(RuleConstant.FLOW_GRADE_QPS); //定义了QPS每秒能通过的请求个数 rule.setCount(1); //3.将限流规则存放到集合中 rules.add(rule); //4.加载限流规则 FlowRuleManager.loadRules(rules); } }
1) 抛出异常的方式
/** * SphU 包含了 try-catch 风格的 API。 * 用这种方式,当资源发生了限流之后会抛出 BlockException。这个时候可以捕捉异常,进行限流之后的逻辑处理。 */ @RequestMapping("/getHello1") public String getHello1() { Entry entry = null; try { entry = SphU.entry("getHello"); return "hello,now is :" + DateUtil.now(); } catch (BlockException blockException) { return "系统繁忙"; } catch (Exception e) { return "系统异常"; } finally { // 务必保证 exit,务必保证每个 entry 与 exit 配对 if (entry != null) { entry.exit(); } } }
2) 返回布尔值的方式
/** * 抛出异常的方式是当被限流时以抛出异常的形式感知,我们通过捕获异常进行限流的处理,这种方式跟上面不同的在于不抛出异常,而是返回一个布尔值 * 我们通过判断布尔值来进行限流逻辑的处理。这样我们就可以很容易写出if-else结构的代码。 */ @RequestMapping("/getHello2") public String getHello2() { if (SphO.entry("getHello")) { try { return "hello,now is :" + DateUtil.now(); } catch (Exception e) { e.printStackTrace(); return "系统异常"; } finally { SphO.exit(); //关闭资源 } } else { return "系统繁忙"; } }
3) 注解的方式
刚才的POM中已经引入了注解的配置,这里再编写代码使生效
package com.example.sentinel.config; import com.alibaba.csp.sentinel.annotation.aspectj.SentinelResourceAspect; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; /** * 用于配置Sentinel注解生效 */ @Configuration public class SAspectConfig { @Bean public SentinelResourceAspect sentinelResourceAspect() { return new SentinelResourceAspect(); } }
然后使用注解编写代码
/** * 采用注解方式 * value是资源名称,是必填项 * blockHandler填限流处理的方法名称 * fallback 接口出现异常时,Sentinel提供熔断降级的功能 */ @RequestMapping("/getHello3") @SentinelResource(value = "getHello", blockHandler = "helloBlock", fallback = "helloFallBack") public String getHello3() { return "hello,now is :" + DateUtil.now(); } public String helloBlock(BlockException e) { return "系统繁忙"; } /** * 注意是异常时才会熔断 */ public String helloFallBack(Throwable e) { return "系统异常,请稍后重试"; }
4) 熔断降级(看3注解中fallback)
5) 异步调用
在启动类增加注解启用异步支持
package com.example.sentinel; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.scheduling.annotation.EnableAsync; @SpringBootApplication @EnableAsync // 异步调用 public class SentinelApplication { public static void main(String[] args) { SpringApplication.run(SentinelApplication.class, args); } }
编写异步调用的实现代码
package com.example.sentinel.service; import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Service; /** * 异步调用接口 */ @Service public class AsyncService { @Async public void hello(){ System.out.println("异步开始"); try { Thread.sleep(5000); }catch (Exception e){ e.printStackTrace(); } System.out.println("异步结束"); } }
编写Controller代码
/** * 异步调用链路的统计 */ @Autowired private AsyncService asyncService; @RequestMapping("/getHello4") public String getHello4() { AsyncEntry asyncEntry = null; try { asyncEntry = SphU.asyncEntry("getHello"); asyncService.hello(); return "hello,now is :" + DateUtil.now(); } catch (BlockException blockException) { return "系统繁忙"; } catch (Exception e) { e.printStackTrace(); return "系统异常,请稍后重试"; } finally { if (asyncEntry != null) { asyncEntry.exit(); } } }
因此这里设置了QPS为1,浏览器打开地址刷新时,连续刷新,就会提示流控。
Java小强
未曾清贫难成人,不经打击老天真。
自古英雄出炼狱,从来富贵入凡尘。
发表评论: