Entry entry = null;
try {
entry = SphU.entry(resource);
logger.info(“业务操作…{}”,count.get());
Thread.sleep(15);
} catch (BlockException e) {
if (e instanceof DegradeException){
logger.error(“触发熔断机制…{}”,count.get());
Thread.sleep(500);
}
} finally {
if (entry != null) {
entry.exit();
}
if (count.get()>=20){
stop = true;
}
}
}
logger.info(“----------------------------”);
}
在上面的代码中,我们一共有20个请求。我们让线程停顿15ms使平均RT超过阈值,也就是超过10ms。
我们定义的规则里面是1秒内连续5个请求的平均RT超出阈值,就可以触发熔断
,所以当第6个请求到达时,就会触发熔断。
熔断多久呢?就在3秒的时间窗口。
上面的测试代码中,在触发熔断之后,我们又手动让线程停顿了 1000ms ,所以每次熔断的请求会有3个。
是不是这样,我们运行代码,看下结果:
10:56:20.022 [main] INFO orderService - 业务操作…1
10:56:20.040 [main] INFO orderService - 业务操作…2
10:56:20.056 [main] INFO orderService - 业务操作…3
10:56:20.072 [main] INFO orderService - 业务操作…4
10:56:20.088 [main] INFO orderService - 业务操作…5
10:56:20.127 [main] ERROR orderService - 触发熔断机制…6
10:56:21.128 [main] ERROR orderService - 触发熔断机制…7
10:56:22.128 [main] ERROR orderService - 触发熔断机制…8
10:56:23.129 [main] INFO orderService - 业务操作…9
10:56:23.145 [main] INFO orderService - 业务操作…10
10:56:23.160 [main] INFO orderService - 业务操作…11
10:56:23.176 [main] INFO orderService - 业务操作…12
10:56:23.192 [main] INFO orderService - 业务操作…13
10:56:23.207 [main] ERROR orderService - 触发熔断机制…14
10:56:24.208 [main] ERROR orderService - 触发熔断机制…15
10:56:25.208 [main] ERROR orderService - 触发熔断机制…16
10:56:26.209 [main] INFO orderService - 业务操作…17
10:56:26.224 [main] INFO orderService - 业务操作…18
10:56:26.240 [main] INFO orderService - 业务操作…19
10:56:26.255 [main] INFO orderService - 业务操作…20
10:56:26.271 [main] INFO orderService - ----------------------------
至此,我们就可以说,Sentinel 能够正常工作了。
二、系统集成
上面只是一个很简单的Demo示例,如果我们希望在我们的SpringBoot项目中使用Sentinel,还需要一些工作。
1、Sentinel 控制台
Sentinel 提供一个轻量级的开源控制台,它是使用SpringBoot开发的。
它提供机器发现以及健康情况管理、监控(单机和集群),规则管理和推送的功能。
所以,我们先把这个控制台运行起来。
第一步,需要在https://github.com/alibaba/Sentinel/releases
这个地址,下载最新版本的控制台 jar 包。
第二步,使用命令启动控制台程序,其中 -Dserver.port=9080
用于指定 Sentinel 控制台端口。
java -Dserver.port=9080 -Dcsp.sentinel.dashboard.server=localhost:9080 -Dproject.name=sentinel-dashboard -jar sentinel-dashboard.jar
第三步,我们的业务系统引入 Transport 模块来与 Sentinel 控制台进行通信。
com.alibaba.csp sentinel-transport-simple-http 1.7.2第四步,在我们的业务系统中,设置JVM启动参数,用来指明Sentinel控制台的地址。
-Dcsp.sentinel.dashboard.server=127.0.0.1:9080
最后,启动我们的业务系统,然后打开Sentinel控制台,如果可以看到机器列表就可以了。
2、定义规则
在定义规则之前,我们需要规划好资源范围。
什么意思呢?比如我们拿一个订单业务来说,是不是所有的订单操作都算一个资源?还是拆分开来看,创建订单算一个资源,订单查询算另外一个资源。
所以,我们可以先把希望流控的资源名称定义出来。
public final class ResourceConstants {
public static final String ORDER_SERVICE = OrderService.class.getName();
public static final String ORDER_SERVICE_ORDERS = ORDER_SERVICE+“.orders”;
public static final String ORDER_SERVICE_CREATE = ORDER_SERVICE+“.create”;
}
由于是一个SpringBoot项目,我们可以在系统启动的时候,来加载流控规则。
@Component
public class ApplicationStartup implements ApplicationListener {
@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
initFlowRule(ResourceConstants.ORDER_SERVICE,5);
initFlowRule(ResourceConstants.ORDER_SERVICE_ORDERS,5);
}
public void initFlowRule(String resourceName,int count) {
FlowRule flowRule = new FlowRule(resourceName)
.setCount(count)
.setGrade(RuleConstant.FLOW_GRADE_QPS);
List list = new ArrayList<>();
list.add(flowRule);
FlowRuleManager.loadRules(list);
}
}
然后,我们在Controller加入Sentinel的代码,来达到流控的效果。
@RequestMapping(“/getOrders”)
public ResponseEntity getOrders(){
Entry entry = null;
try {
entry = SphU.entry(ResourceConstants.ORDER_SERVICE_ORDERS);
return ResponseEntity.ok(orderService.orders());
} catch (BlockException e) {
logger.error(“请求被限流…{}”,e.getRule().getResource());
return ResponseEntity.badRequest().body(e.getRule());
} finally {
if (entry != null) {
entry.exit();
}
}
}
现在,我们拿JMeter来测试一下,启动10个线程来请求这个接口。只会通过5个请求,拒绝5个请求。
至此,我们已经可以在SpringBoot项目中简单使用Sentinel了,不过此时还有两个很明显的问题。
- 在每个需要流控的地方,通过API硬编码,侵入性太强而且也不方便;
- 流控规则只保留在内存中,系统重启就没了,没有持久化规则数据。
接下来,我们来解决上述的两个问题。
三、框架适配
得益于广泛的开源生态,Sentinel 提供开箱即用的与其它开源框架/库的整合模块。我们只需要引入相应的依赖并进行简单的配置即可快速地接入 Sentinel。
我们希望可以对 Web 请求进行流量控制,那么需要引入Sentinel 提供与 Servlet 的整合。
com.alibaba.csp sentinel-web-servlet 1.7.21、Filter配置
因为是SpringBoot应用,我们通过Configuration进行配置。
@Configuration
public class SentinelFilterConfig {
@Bean
public FilterRegistrationBean sentinelFilterRegistration() {
FilterRegistrationBean registration = new FilterRegistrationBean<>();
registration.setFilter(new CommonFilter());
registration.addUrlPatterns(“/*”);
registration.setName(“sentinelFilter”);
registration.setOrder(1);
return registration;
}
}
在我们自己的业务代码中,就可以免去Sentinel API部分了。
@RequestMapping(“/getOrders”)
public ResponseEntity getOrders(){
return ResponseEntity.ok(orderService.orders());
}
在流控规则不变的情况下,我们拿JMeter启动10个线程来请求这个接口。同样的只会通过5个请求,拒绝5个请求。
2、UrlBlockHandler
默认情况下,当请求被限流时会返回默认的提示页面。
我们可以在代码中调用 WebServletConfig.setBlockPage(blockPage)
方法设定自定义的跳转 URL,当请求被限流时会自动跳转至设定好的 URL。
如果不打算让它跳转页面,我们也可以实现 UrlBlockHandler 接口并编写定制化的限流处理逻辑。
比如像下面这样,限流或熔断之后,会向客户端返回一个异常的HTTP状态码和提示信息。
public class SentinelUrlBlockHandler implements UrlBlockHandler {
public static final String flowMsg = “触发流控机制~”;
public static final String degradeMsg = “触发熔断机制~”;
Logger logger = LoggerFactory.getLogger(this.getClass());
@Override
public void blocked(HttpServletRequest request, HttpServletResponse response, BlockException ex){
logger.error(“熔断限流…{}”,ex.getRule());
response.setCharacterEncoding(“UTF-8”);
response.setStatus(HttpStatus.BAD_REQUEST.value());
PrintWriter out = response.getWriter();
if (ex instanceof FlowException){
out.print(flowMsg);
}else if (ex instanceof DegradeException){
out.print(degradeMsg);
}
out.flush();
out.close();
}
}
然后将其注册至 WebCallbackManager 中。
WebCallbackManager.setUrlBlockHandler(new SentinelUrlBlockHandler());
3、UrlCleaner
Sentinel Web Filter 会将每个到来的不同的 URL 都作为不同的资源处理。
比如订单业务中的,创建订单、订单查询、订单删除等等,因为URL的不同,都会被当作不同的资源。
如果我们希望将这些操作都归到订单资源下/order/*
,就需要实现 UrlCleaner 接口清洗一下资源。
比如像下面这样,将资源归类。比如/order/getOrders和/order/createOrder
,都会变成/order/*
。
public class SentinelUrlClean implements UrlCleaner {
@Override
public String clean(String originUrl) {
if (originUrl == null || originUrl.isEmpty()) {
return originUrl;
}
int lastSlashIndex = originUrl.lastIndexOf(“/”);
if (lastSlashIndex >= 0) {
originUrl = originUrl.substring(0, lastSlashIndex) + “/*”;
}
return originUrl;
}
}
然后将其注册至 WebCallbackManager 中。
WebCallbackManager.setUrlCleaner(new SentinelUrlClean());
当时,更绝对一些,如果整个系统都采用一个资源,那么这里只返回一个固定的url也可以。
四、最佳实践
上面我们说到,现在的Sentinel规则数据都只保留在内存中,没办法做到集中管理和推送规则,不具备生产环境可用性。
规则管理及推送,一般有三种方式。
- 原始模式
将规则推送至客户端并直接更新到内存中。重启即消失,不建议在生产环境中使用。
- Pull 模式
客户端主动向某个规则管理中心定期轮询拉取规则,这个规则中心可以是 RDBMS、文件 等。不保证实时性,拉取过于频繁可能会导致性能问题。
- Push 模式
规则中心统一推送,客户端通过注册监听器的方式时刻监听变化,比如使用 Nacos、Zookeeper 等配置中心,有更好的实时性和一致性。生产环境下一般采用 push 模式的数据源。
生产环境下一般更常用的是 push 模式的数据源。对于 push 模式的数据源,如远程配置中心(ZooKeeper, Nacos, Apollo等等),推送的操作不应由 Sentinel 客户端进行,而应该经控制台统一进行管理,直接进行推送,数据源仅负责获取配置中心推送的配置并更新到本地。因此推送规则正确做法应该是 ** 配置中心控制台/Sentinel 控制台 → 配置中心 → Sentinel 数据源 → Sentinel **,而不是经 Sentinel 数据源推送至配置中心。
接下来我们来实现由Nacos配置中心统一管理数据。
1、启动Nacos
关于Nacos本文不再多说,下载一个启动就好了。
2、引入依赖
NacosDataSource,官方已经提供了,我们引入相关依赖即可。
com.alibaba.csp sentinel-datasource-extension 1.7.2 com.alibaba.csp sentinel-datasource-nacos 1.7.23、从数据源中读取规则数据
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
如果你觉得这些内容对你有帮助,可以扫码获取!!(备注Java获取)
最后
针对最近很多人都在面试,我这边也整理了相当多的面试专题资料,也有其他大厂的面经。希望可以帮助到大家。
下面的面试题答案都整理成文档笔记。也还整理了一些面试资料&最新2021收集的一些大厂的面试真题(都整理成文档,小部分截图)
最新整理电子书
《一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码》,点击传送门即可获取!
36.jpg" alt=“img” style=“zoom: 33%;” />
最后
针对最近很多人都在面试,我这边也整理了相当多的面试专题资料,也有其他大厂的面经。希望可以帮助到大家。
下面的面试题答案都整理成文档笔记。也还整理了一些面试资料&最新2021收集的一些大厂的面试真题(都整理成文档,小部分截图)
[外链图片转存中…(img-tSBhVDad-1712510028196)]
最新整理电子书
[外链图片转存中…(img-2VDzESYM-1712510028196)]
《一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码》,点击传送门即可获取!