珠海商城网站,杭州网页制作设计营销,乐华网络公司联系方式,新浦网站制作网站建设写在前面
源码 。 本文一起看下spring cloud的sentinel组件的使用。
1#xff1a;准备
1.1#xff1a;理论
对于一个系统来说#xff0c;最重要的就是高可用#xff0c;那么如何实现高可用呢#xff1f;你可能会说#xff0c;集群部署不就可以了#xff0c;但事实并…写在前面
源码 。 本文一起看下spring cloud的sentinel组件的使用。
1准备
1.1理论
对于一个系统来说最重要的就是高可用那么如何实现高可用呢你可能会说集群部署不就可以了但事实并非这么简单假定一个不那么靠谱的开发写了一个不那么靠谱的sql语句上线之后DB资源很快耗尽那么所有需要查询DB的服务都无法处理请求进而导致这些服务挂掉而上游的服务因为无法快速得到响应也会很快的挂掉进而整个系统都会无法对外提供服务这其实就是发生了非常可怕的服务雪崩这个过程可以简单参考下图 假定那个不那么靠谱的开发是在服务D开发了那个不那么靠谱的sql语句则上线后的后果就是D导致B和C不可用B和C的不可用又会导致A的不可用最终整个系统不可用。所以想要真正的实现高可用除了多节点的集群部署外我们还需要预防服务雪崩而本文要学习的sentinel正是这样的一个组件我们可以叫做容错组件。
基本上导致服务雪崩发生的原因在2个方面第一个方面是高并发导致的请求量增大第二方面就是应用内部的异常和错误就像那个不那么靠谱的sql。其实sentinel也正是从这两方面来预防服务雪崩的对于高并发sentinel可以从外部来限制并发量对于应用内部异常和错误sentinel可以进行降级和熔断。我们也会从这方面来展开sentinel的实战环节。
在java中万物皆对象在Linux中万物皆文件而在sentinel中万物皆资源而这里的资源我们可以认为就是接口地址而对这些资源作用的过程是通过其内部的一个责任链可以参考下图 比如有用来收集数据的StaticSlot构建调用链的NodeSelectorSlot形成一个树状的调用关系图如果业务有需要我们也可以增加自定义的额slot到这个调用链上。
1.2安装sentinel
首先在这里 下载可运行的jar包接着如下操作
$ java -Dserver.port8080 -Dcsp.sentinel.dashboard.serverlocalhost:8080 -Dproject.namesentinel-dashboard -jar sentinel-dashboard-1.8.2.jar
INFO: Sentinel log output type is: file
INFO: Sentinel log charset is: utf-8
INFO: Sentinel log base directory is: C:\Users\dell9020\logs\csp\
INFO: Sentinel log name use pid is: false. ____ _ __ _ _/\\ / ____ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | _ | _| | _ \/ _ | \ \ \ \\\/ ___)| |_)| | | | | || (_| | ) ) ) ) |____| .__|_| |_|_| |_\__, | / / / /|_||___//_/_/_/:: Spring Boot :: (v2.0.5.RELEASE)...成功后访问http://localhost:8080/#/login,进入登陆页面 账号sentinel/sentinel成功后进入如下页面
2限流实战
限流或者叫流量整形是sentinel从外部预防服务雪崩的重要手段。
2.1基础配置
这部分我们直接来使用sentiniel对我们项目做流量整形首先在custom和template模块引入依赖
dependencygroupIdcom.alibaba.cloud/groupIdartifactIdspring-cloud-starter-alibaba-sentinel/artifactId
/dependency然后在custom和template模块的application.yml中配置sentinel信息
spring:jpa:...cloud:sentinel:eager: true transport:port: 8719dashboard: localhost:8080接着使用SentinelResource注解标记要进行流量整形的接口如下
custom模块
dongshi.daddy.sentinel.controller.CouponCustomerController#requestCoupon
PostMapping(requestCoupon)
SentinelResource(value requestCoupon)
public Coupon requestCoupon(Valid RequestBody RequestCoupon request) {...
}
PostMapping(findCoupon)
SentinelResource(value customer-findCoupon)
public ListCouponInfo findCoupon(Valid RequestBody SearchCoupon request) {return customerService.findCoupon(request);
}template模块
// 读取优惠券
GetMapping(/getTemplate)
SentinelResource(value getTemplate)
public CouponTemplateInfo getTemplate(RequestParam(id) Long id){log.info(Load template, id{}, id);/*try {// 休眠二十秒模拟超时TimeUnit.SECONDS.sleep(20);} catch (InterruptedException e) {e.printStackTrace();}*/return couponTemplateService.loadTemplateInfo(id);
}// 批量获取
GetMapping(/getBatch)
// 降级要执行的方法
SentinelResource(value getTemplateInBatch, blockHandler getTemplateInBatch_block)
public MapLong, CouponTemplateInfo getTemplateInBatch(RequestParam(ids) CollectionLong ids) {log.info(getTemplateInBatch: {}, JSON.toJSONString(ids));return couponTemplateService.getTemplateInfoMap(ids);
}
// 降级要执行的方法
public MapLong, CouponTemplateInfo getTemplateInBatch_block(CollectionLong ids, BlockException exception) {log.info(接口被限流);return Maps.newHashMap();
}接着我们启动服务如果一切正常的话就可以看到custom和template这两个模块注册到sentinel上了
2.2通过流控规则进行限流
sentinel支持通过三种方式来进行流量整形
直接流控:针对特定接口sentinel称为资源限流
关联流控:当某接口sentinel称为资源达到限流条件时对关联的接口sentinel称为资源进行限流
链路流控:
2.2.1:直接流控
我们对资源getTemplateInBatch进行流控在sentinel选择template对应的微服务点击子菜单流控规则然后点击右上角 添加流控规则如下
为了测试我们使用jmeter来进行测试jmeter配置可以从这里 下载配置如下: 通过按钮 启动之后查看template模块日志输出如下
2024-01-08 15:34:44.411 INFO 26504 --- [io-20006-exec-7] d.d.s.c.CouponTemplateController : getTemplateInBatch: [2,3]
Hibernate: select coupontemp0_.id as id1_0_, coupontemp0_.available as availabl2_0_, coupontemp0_.type as type3_0_, coupontemp0_.created_time as created_4_0_, coupontemp0_.description as descript5_0_, coupontemp0_.name as name6_0_, coupontemp0_.rule as rule7_0_, coupontemp0_.shop_id as shop_id8_0_ from coupon_template coupontemp0_ where coupontemp0_.id in (? , ?)
2024-01-08 15:34:44.904 INFO 26504 --- [io-20006-exec-5] d.d.s.c.CouponTemplateController : 接口被限流可以看到第二次执行了熔断逻辑被限流了。
2.2.2:关联流控
jmeter配置下载 。
我们来设置当getTempalte接口qps超过1时限制getBatch接口配置如下 首先我们使用jmeter模拟qps 2不间断访问getTemplate接口如下 启动后这样肯定就会触发限流规则了这样我们先来启动jmeter 之后访问getBatch接口如下
http://localhost:20006/template/getBatch?ids2,3查看后台输出 然后我们来停止jmeter再来请求接口就会恢复正常了
2.2.3:链路流控
jmeter配置下载 。 对于同一个资源访问可以来自于不同的链路针对特定的链路进行流控就是链路流控了如下图上方链路就是被流控的 这里我们模拟从custom模块访问template模块的getBatch接口为了测试需要首先在custom模块中增加如下接口
// 批量获取
GetMapping(/getBatchFromCustomer)
public MapLong, CouponTemplateInfo getTemplateInBatch(RequestParam(ids) CollectionLong ids) {log.info(getTemplateInBatch111: {}, JSON.toJSONString(ids));return templateService.getTemplateInBatch(ids);
}因为需要进行限流的资源需要知道当前的调用者是谁才能知道是否需要触发所以首先需要在custom模块添加openfeign的拦截器在openfeign的请求中添加来源头
Configuration
public class OpenfeignSentinelInterceptor implements RequestInterceptor {Overridepublic void apply(RequestTemplate template) {template.header(SentinelSource, coupon-customer-serv);}
}接着在tempalte模块中获取这个来源的信息
Component
Slf4j
public class SentinelOriginParser implements RequestOriginParser {Overridepublic String parseOrigin(HttpServletRequest request) {log.info(request {}, header{}, request.getParameterMap(), request.getHeaderNames());return request.getHeader(SentinelSource);}
}接着我们来配置sentinel针对来源coupon-customer-serv进行限流如下 接着开启jmeter正常的话会看到被限流了 为了测试来源不匹配的情况不限流我们将sentinel的目标资源名称随便改为其他的如下 此时再开启jmeter测试就正常了
2.3三种流控效果
sentinel支持三种流控效果快速失败warm up,排队等待在上述的实战环节我们使用就是快速失败这也是sentinel默认的流控效果。
快速失败 直接失败。warm up 这是一种从低水位逐渐拉高的一种限流方式比如如下的配置 上图设置在5秒内拉高到10进行限流而非开始就是10开始的限流值需要除以冷却因子这里冷却因子是3所以就是10/33也就是当qps到达3时开始限流然后在5秒内将这个限流值拉高到最大值10
使用的场景如采用缓存DB读的场景如果接口一段时间内都处于很低的水位导致大量的缓存都失效了此时突然发生了突发流量某明星出轨了某漂亮country被原子弹攻击了此时缓存还没有完全构建起来,为了避免突发流量全部打到DB把DB打穿就可以考虑使用warm up在一段时间内从低水位逐渐拉到高水位同时在这段时间内完成缓存的构建工作。
排队等待 被限流的请求在一个任务队列中排队等待并按照超时等待时间从队列中删除任务如下500内还没有处理则移除任务的配置
2降级、熔断实战
jmeter配置 。 降级、熔断是sentinel从服务内部预防服务雪崩的重要手段。这部分我们一起来看下如何使用sentinel来实现降级和熔断。为了方便测试我们首先在template模块添加一个新的接口并设置为sentinel的资源
// 批量获取
GetMapping(/getBatchV1)
// 降级要执行的方法
SentinelResource(value getTemplateInBatchV1, fallback getTemplateInBatch_fallback)
public MapLong, CouponTemplateInfo getTemplateInBatchV1(RequestParam(ids) CollectionLong ids) {log.info(getTemplateInBatch: {}, JSON.toJSONString(ids));int i 1 / 0;return couponTemplateService.getTemplateInfoMap(ids);
}public MapLong, CouponTemplateInfo getTemplateInBatch_fallback(CollectionLong ids) {log.info(接口被fallback);return Maps.newHashMap();
}这里注意指定了fallback getTemplateInBatch_fallback则当抛出了RuntimeException时就会进入执行降级逻辑接着我们就需要在sentinel中配置熔断逻辑了如下: 降级熔断的判断过程可以参考下图(注意时示例) 接着执行jemter日志输出如下
2024-01-10 14:01:47.813 INFO 13112 --- [io-20006-exec-2] d.d.s.c.CouponTemplateController : getTemplateInBatch: [2,3] #
2024-01-10 14:01:47.813 INFO 13112 --- [io-20006-exec-2] d.d.s.c.CouponTemplateController : 接口被fallback
2024-01-10 14:01:47.813 INFO 13112 --- [o-20006-exec-10] d.daddy.sentinel.SentinelOriginParser : request org.apache.catalina.util.ParameterMap39d7b9e0, headerorg.apache.tomcat.util.http.NamesEnumerator7d50964c
2024-01-10 14:01:47.814 INFO 13112 --- [o-20006-exec-10] d.d.s.c.CouponTemplateController : 接口被fallback
2024-01-10 14:01:47.815 INFO 13112 --- [io-20006-exec-9] d.daddy.sentinel.SentinelOriginParser : request org.apache.catalina.util.ParameterMap39d7b9e0, headerorg.apache.tomcat.util.http.NamesEnumerator1f47105b
2024-01-10 14:01:47.816 INFO 13112 --- [io-20006-exec-9] d.d.s.c.CouponTemplateController : 接口被fallback
....
2024-01-10 14:01:47.813 INFO 13112 --- [io-20006-exec-2] d.d.s.c.CouponTemplateController : getTemplateInBatch: [2,3] # 1s后半开启后又重新进入熔断
2024-01-10 14:01:47.813 INFO 13112 --- [io-20006-exec-2] d.d.s.c.CouponTemplateController : 接口被fallback
2024-01-10 14:01:47.813 INFO 13112 --- [o-20006-exec-10] d.daddy.sentinel.SentinelOriginParser : request org.apache.catalina.util.ParameterMap39d7b9e0, headerorg.apache.tomcat.util.http.NamesEnumerator7d50964c
2024-01-10 14:01:47.814 INFO 13112 --- [o-20006-exec-10] d.d.s.c.CouponTemplateController : 接口被fallback
2024-01-10 14:01:47.815 INFO 13112 --- [io-20006-exec-9] d.daddy.sentinel.SentinelOriginParser : request org.apache.catalina.util.ParameterMap39d7b9e0, headerorg.apache.tomcat.util.http.NamesEnumerator1f47105b
2024-01-10 14:01:47.816 INFO 13112 --- [io-20006-exec-9] d.d.s.c.CouponTemplateController : 接口被fallback
...可以看到降级后满足条件就会进入熔断。一段时间后会尝试恢复这个状态叫做半熔断完整的状态转换参考下图
4:接入nacos实现持久化
二次开发后源码 。 在前面的实战中不知道你发现没有如果是应用重启则我们设置的规则就都会丢失这是因为这些规则信息默认是保存在内存中sentinel也支持将规则信息保存在nacos中本部分就一起来看下如何实现想要实现这个操作需要对sentinel的源码做一下简单的二次开发工作。我们开始。
4.1下载sentient源码
你可以在这里 下载源码如下图
下载后导入到idea中注意将idea中jdk相关的配置都修改为1.8否则可能会无法正常编译代码成功后如下图 接着就可以开始二次开发的工作了。
4.2二次开发
我们需要修改的模块是sentinel-dashboard首先需要修改nacos依赖范围默认是test的即只在test阶段生效我们需要修改为编译阶段的如下
dependencygroupIdcom.alibaba.csp/groupIdartifactIdsentinel-datasource-nacos/artifactId!-- 将scope注释掉改为编译期打包 --!--scopetest/scope--
/dependency接着我们需要将test包下com.alibaba.csp.sentinel.dashboard.rule.nacos的四个nacos操作的相关文件如下
NacosConfig初始化 Nacos Config 的连接(该类我们需要修改设置自己环境的nacos地址)
NacosConfigUtil约定了 Nacos 配置文件所属的 Group 和文件命名后缀等常量字段
FlowRuleNacosProvider从 Nacos Config 上获取限流规则(从nacos获取配置数据)
FlowRuleNacosPublisher将限流规则发布到 Nacos Config。(将配置写到nacos)我们需要在main目录中创建一样的包路径并将这四个类拷贝过去如下图 首先我们修改NacosConfig类nacosConfigService方法该方法负责创建操作nacos的ConfigService的bean如下
/*
负责更新以及读取nacos的类
*/
Bean
public ConfigService nacosConfigService() throws Exception {// 将Nacos的注册地址引入进来Properties properties new Properties();properties.setProperty(serverAddr, 192.168.10.77:8868);properties.setProperty(namespace, dev);
// return ConfigFactory.createConfigService(localhost);return ConfigFactory.createConfigService(properties);
}然后修改FlowControllerV2中写入sentinel配置的pulisher和读取配置的provider为nacos的如下
Autowired
// Qualifier(flowRuleDefaultProvider)
// 修改为nacos的数据provider提供者这样就能从nacos中获取数据
Qualifier(flowRuleNacosProvider)
private DynamicRuleProviderListFlowRuleEntity ruleProvider;
Autowired
// Qualifier(flowRuleDefaultPublisher)
// 修改为nacos的发布者这样就能将规则配置数据写到nacos中
Qualifier(flowRuleNacosPublisher)
private DynamicRulePublisherListFlowRuleEntity rulePublisher;我们来看下publisher向nacos写数据的源码
// com.alibaba.csp.sentinel.dashboard.rule.nacos.FlowRuleNacosPublisher#publish
Override
public void publish(String app, ListFlowRuleEntity rules) throws Exception {AssertUtil.notEmpty(app, app name cannot be empty);if (rules null) {return;}// app 应用名称// public static final String FLOW_DATA_ID_POSTFIX -flow-rules;// public static final String GROUP_ID SENTINEL_GROUP;// converter.convert(rules) 转成字符串configService.publishConfig(app NacosConfigUtil.FLOW_DATA_ID_POSTFIX,NacosConfigUtil.GROUP_ID, converter.convert(rules));
}源码中就可以看到nacos作为配置中心时的组是SENTINEL_GROUP,dataId是${spring.application.name}-flow-rules。最后我们修改页面sidebar.html增加如下内容
li ui-sref-activeactivea ui-srefdashboard.flow({app: entry.app})i classglyphicon glyphicon-filter/inbsp;nbsp;流控规则 极客时间改造/a
/li最后需要改造我们的微服务以cusotm为例首先增加依赖
dependencygroupIdcom.alibaba.csp/groupIdartifactIdsentinel-datasource-nacos/artifactId
/dependency然后增加配置:
spring:cloud:sentinel:datasource:# 数据源的key可以自由命名geekbang-flow:# 指定当前数据源是nacosnacos:# 设置Nacos的连接地址、命名空间和Group IDserver-addr: localhost:8848namespace: devgroupId: SENTINEL_GROUP# 设置Nacos中配置文件的命名规则dataId: ${spring.application.name}-flow-rules# 必填的重要字段指定当前规则类型是限流rule-type: flow最后打jar包,成功后会在target目录看到jar包 最后启动项目
java -Dserver.port8080 -Dcsp.sentinel.dashboard.serverlocalhost:8080 -Dproject.namesentinel-dashboard -jar sentinel-dashboard.jar启动custom模块登录后就可以看到新加的菜单 如果是该菜单增加配置则会同步到nacos中并且配置dataID是coupon-customer-serv-sentinel-flow-rules: 配置内容是以文本的格式存储的但存储的也是json
写在后面
参考文章列表
jmeter之简单使用 。