sentinel-服务接入原理

news/2024/9/21 20:27:40/文章来源:https://www.cnblogs.com/LQBlog/p/18379000

通过sentinel前世今生介绍,我们知道了sentinel流控主要是依赖sentinel-core,但是我们生产环境往往需要动态更新流控规则所以需要集成nacos、zookeeper、redis、mysql、等中间存储。配置的复杂性和规则的复杂性我们需要可视化的方式对规则进行管理,我们需要集成dashboard。

这些sentinel都为我们提供了解决方案

 

集成这些组件我们需要复杂的配置,sentinel为了方便接入提供的spring-cloud-starter-alibaba-sentinel 自动装配进行快速服务整合

 

服务如何接入

版本选择

https://github.com/alibaba/spring-cloud-alibaba/wiki/版本说明

1.pom引入依赖

 

        <!--使用nacos持久化规则--><dependency><groupId>com.alibaba.csp</groupId><artifactId>sentinel-datasource-nacos</artifactId><version>1.8.0</version></dependency>

yml配置

server:port: 9000
spring:main:allow-circular-references: trueapplication:name: sentinel-nacos-starter-datasource-democloud:nacos:discovery.server-addr: 127.0.0.1:8848sentinel:web-context-unify: false # 关闭context整合 避免链路失效 默认会由context为根链路eager: true #启动立即加载规则,而不是懒加载transport:dashboard: 127.0.0.1:8080 #dashboard地址port: 8719 #dashboard通信端口,如果冲突则会自动+1 寻找可用端口heartbeat-interval-ms: 5000 #心跳秒数datasource:flow-rule:nacos:#流控规则的nacos配置文件 dashbard上报推送的是这个格式data-id: ${spring.application.name}-flow-rules#流控规则格式 XML OR JSON dashbard配置的json data-type: jsonnamespace: 04415d0c-9d8e-4e32-81a7-c0d737bb0063group-id: SENTINEL_GROUPserver-addr: 127.0.0.1:8848#规则类型控制规则更新的策略 rule-type: flowparam-flow-rule:nacos:data-id: ${spring.application.name}-param-rulesdata-type: jsonnamespace: 04415d0c-9d8e-4e32-81a7-c0d737bb0063group-id: SENTINEL_GROUPserver-addr: 127.0.0.1:8848rule-type: param-flowauthority-rule:nacos:data-id: ${spring.application.name}-authority-rulesdata-type: jsonnamespace: 04415d0c-9d8e-4e32-81a7-c0d737bb0063group-id: SENTINEL_GROUPserver-addr: 127.0.0.1:8848rule-type: authoritysystem-rule:nacos:data-id: ${spring.application.name}-system-rulesdata-type: jsonnamespace: 04415d0c-9d8e-4e32-81a7-c0d737bb0063group-id: SENTINEL_GROUPserver-addr: 127.0.0.1:8848rule-type: system

 

接入原理

规则是如何实现从nacos自动加载和监听的

com.alibaba.cloud.sentinel.custom.SentinelDataSourceHandler 源码,利用spring 生命周期提供的SmartInitializingSingleton 回调

com.alibaba.cloud.sentinel.custom.SentinelDataSourceHandler#parseBeanDefinition

	public void afterSingletonsInstantiated() {//遍历配置文件规则sentinelProperties.getDatasource().forEach((dataSourceName, dataSourceProperties) -> {try {List<String> validFields = dataSourceProperties.getValidField();if (validFields.size() != 1) {log.error("[Sentinel Starter] DataSource " + dataSourceName+ " multi datasource active and won't loaded: "+ dataSourceProperties.getValidField());return;}AbstractDataSourceProperties abstractDataSourceProperties = dataSourceProperties.getValidDataSourceProperties();abstractDataSourceProperties.setEnv(env);abstractDataSourceProperties.preCheck(dataSourceName);//注册registerBean(abstractDataSourceProperties, dataSourceName+ "-sentinel-" + validFields.get(0) + "-datasource");}catch (Exception e) {log.error("[Sentinel Starter] DataSource " + dataSourceName+ " build error: " + e.getMessage(), e);}});}

 

	private void registerBean(final AbstractDataSourceProperties dataSourceProperties,String dataSourceName) {//根据配置构建 DataSource 的BeanDefinitionBeanDefinitionBuilder builder = parseBeanDefinition(dataSourceProperties, dataSourceName);//进行初始化this.beanFactory.registerBeanDefinition(dataSourceName,builder.getBeanDefinition());// 获取对应的dataSourceAbstractDataSource newDataSource = (AbstractDataSource) this.beanFactory.getBean(dataSourceName);// 执行注入dataSourceProperties.postRegister(newDataSource);}

 

	public void postRegister(AbstractDataSource dataSource) {switch (this.getRuleType()) {case FLOW:FlowRuleManager.register2Property(dataSource.getProperty());break;case DEGRADE:DegradeRuleManager.register2Property(dataSource.getProperty());break;case PARAM_FLOW:ParamFlowRuleManager.register2Property(dataSource.getProperty());break;case SYSTEM:SystemRuleManager.register2Property(dataSource.getProperty());break;case AUTHORITY:AuthorityRuleManager.register2Property(dataSource.getProperty());break;case GW_FLOW:GatewayRuleManager.register2Property(dataSource.getProperty());break;case GW_API_GROUP:GatewayApiDefinitionManager.register2Property(dataSource.getProperty());break;default:break;}}

spring mvc 资源是如何实现自动注册的

 

com.alibaba.cloud.sentinel.SentinelWebAutoConfiguration 会初始化一个spring 拦截器 并注入容器

	@Autowiredprivate SentinelProperties properties;@Autowiredprivate Optional<UrlCleaner> urlCleanerOptional;@Autowiredprivate Optional<BlockExceptionHandler> blockExceptionHandlerOptional;@Autowiredprivate Optional<RequestOriginParser> requestOriginParserOptional; 
/*** 基于下面配置 初始化一个spring mvc拦截器* @param sentinelWebMvcConfig* @return*/@Bean@ConditionalOnProperty(name = "spring.cloud.sentinel.filter.enabled",matchIfMissing = true)public SentinelWebInterceptor sentinelWebInterceptor(SentinelWebMvcConfig sentinelWebMvcConfig) {return new SentinelWebInterceptor(sentinelWebMvcConfig);}/*** 拦截器的一些自定义配置,如 自定义sentinel流控处理器* @return*/@Bean@ConditionalOnProperty(name = "spring.cloud.sentinel.filter.enabled",matchIfMissing = true)public SentinelWebMvcConfig sentinelWebMvcConfig() {//......}

 

 

com.alibaba.csp.sentinel.adapter.spring.webmvc.AbstractSentinelInterceptor#preHandle

 public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {try {//根据url获取资源名字String resourceName = this.getResourceName(request);if (StringUtil.isEmpty(resourceName)) {return true;} else if (this.increaseReferece(request, this.baseWebMvcConfig.getRequestRefName(), 1) != 1) {return true;} else {String origin = this.parseOrigin(request);String contextName = this.getContextName(request);ContextUtil.enter(contextName, origin);Entry entry = SphU.entry(resourceName, 1, EntryType.IN);request.setAttribute(this.baseWebMvcConfig.getRequestAttributeName(), entry);return true;}} catch (BlockException var12) {BlockException e = var12;try {//执行自定义的 sentinel流控处理器this.handleBlockException(request, response, e);} finally {ContextUtil.exit();}return false;}}

如何自定义spring MVC 流控处理器

从容器获取,我们可以根据这个钩子方法进行统一扩展,比如权限的,指定url的流控降级规则

@Autowired
private Optional<BlockExceptionHandler> blockExceptionHandlerOptional;

 

实现BlockExceptionHandler自定义流控处理器

@Component
public class CustomBlockExceptionHandler implements BlockExceptionHandler {@Overridepublic void handle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, BlockException e)throws Exception {Map<String, Object> resData=new HashMap<>();if (e instanceof FlowException) {resData.put("status",100);resData.put("msg","触发限流规则|"+Thread.currentThread().getId()+"|"+ DateFormatUtils.format(System.currentTimeMillis(), "yyyy-MM-dd HH:mm:ss:SSS"));} else if (e instanceof DegradeException) {resData.put("status",101);resData.put("msg","降级了|" +Thread.currentThread().getId()+"|"+ DateFormatUtils.format(System.currentTimeMillis(), "yyyy-MM-dd HH:mm:ss:SSS")
);} else if (e instanceof ParamFlowException) {resData.put("status",102);resData.put("msg","热点参数限流"+Thread.currentThread().getId()+"|"+ DateFormatUtils.format(System.currentTimeMillis(), "yyyy-MM-dd HH:mm:ss:SSS")
);} else if (e instanceof SystemBlockException) {resData.put("status",103);resData.put("msg","系统规则(负载/...不满足要求"+Thread.currentThread().getId()+"|"+ DateFormatUtils.format(System.currentTimeMillis(), "yyyy-MM-dd HH:mm:ss:SSS")
);} else if (e instanceof AuthorityException) {resData.put("status",104);resData.put("msg","授权规则不通过"+Thread.currentThread().getId());}// http状态码httpServletResponse.setStatus(500);httpServletResponse.setCharacterEncoding("utf-8");httpServletResponse.setHeader("Content-Type", "application/json;charset=utf-8");httpServletResponse.setContentType("application/json;charset=utf-8");new ObjectMapper().writeValue(httpServletResponse.getWriter(),resData);}
}

如何整合Fegin

yml配置

feign.sentinel.enabled=true

代码使用

文档:https://github.com/alibaba/spring-cloud-alibaba/wiki/Sentinel

 

源码处

通过扩展 Feign.Builder实现

com.alibaba.cloud.sentinel.feign.SentinelFeignAutoConfiguration

@Configuration(proxyBeanMethods = false)
@ConditionalOnClass({ SphU.class, Feign.class })
public class SentinelFeignAutoConfiguration {@Bean@Scope("prototype")@ConditionalOnMissingBean@ConditionalOnProperty(name = "feign.sentinel.enabled")public Feign.Builder feignSentinelBuilder() {return SentinelFeign.builder();}}

 

 

 

注解支持

参考文档

https://github.com/alibaba/Sentinel/wiki/注解支持

 

自动装配源码处

com.alibaba.cloud.sentinel.custom.SentinelAutoConfiguration#sentinelResourceAspect

 

控制台各个规则使用

流控规则

qps

 

我们最常用的限流规则,限制我们的资源能够接收的每秒最大请求数。防止超过资源接收到自身最大的请求流量。

比如我们抢购功能,压测接口最大峰值qps是1000,我们应该设置每秒超过1000的快速失败,避免大量请求引起的阻塞排队。

 

 

并发线程数

与qps不同的是,是每秒基于线程维度统计,线程数并不等于qps,比如设置5,接口性能是500毫秒。那么在不排队情况,1个线程每秒能执行2个请求,5个线程则是10的qps

下面例子20个线程并发抢占最终成功10个

 

 

 

高级设置流控模式

 

  • 直接:统计当前资源的请求,触发阈值时对当前资源直接限流,也是默认的模式

 

  • 关联:统计与当前资源相关的另一个资源,触发阈值时。限流自己

      比如商品详情本身支持2000qps。但是当商品有促销活动时会调用促销资源。促销资源只支持100qps。那么这种时候我们接口也就支持100qp,超过100则会触发等待。则可以通过关联来限制。(关联资源单独设置了流控,则取最小那个)

      或者有下单接口,下单完之后会调用支付接口。当支付接口出现限流,我们可以触发限流下单。

 

  • 链路:统计从指定链路访问到本资源的请求,触发阈值时,对指定链路限流

     自下而上精细化控制,我们的资源,比如我们查询订单接口。支持200qps,在支付的时候会查询订单。订单详情也会查询订单。这个时候我们的支付优先级肯定更高,则我们设置订单资源关联非支付的则只允许50qps

 

高级设置流控效果

 

  • 快速失败:默认效果,达到阈值则快速失败
  • Warm Up:预热模式,我们接口能够支持2000qps,但是刚启动的时候很多缓存没加载。需要预热一段时间,则缓慢的放流量最终到2000qps,比如一心助手门店维度缓存数据

 

 

热点规则

何为热点?热点即经常访问的数据。很多时候我们希望统计某个热点数据中访问频次最高的 Top K 数据,并对其访问进行限制。比如:

  • 商品 ID 为参数,统计一段时间内最常购买的商品 ID 并进行限制
  • 用户 ID 为参数,针对一段时间内频繁访问的用户 ID 进行限制

热点参数限流会统计传入参数中的热点参数,并根据配置的限流阈值与模式,对包含热点参数的资源调用进行限流。热点参数限流可以看做是一种特殊的流量控制,仅对包含热点参数的资源调用生效。

 

Sentinel 利用 LRU 策略统计最近最常访问的热点参数,结合令牌桶算法来进行参数级别的流控。

场景

抢购活动,针对茅台系列商品 0005,0006.可以通过首页活动广告页进入活动,也可以通过商品详情进入活动页。商品详情这个系列商品会匹配到活动,会走抢购活动的分支,所以根据0005,0006 进行单独的qps设置

 

    @Overridepublic void run(ApplicationArguments args) throws Exception {initParamFlowRules();}/*** com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowSlot#checkFlow* 默认是string.valueof* @throws IOException*/public void initParamFlowRules() throws IOException {ParamFlowRule rule = new ParamFlowRule("flowRuleParamsString").setParamIdx(0).setCount(20);//总阀20 按各个参数分别统计// 针对 String 类型的参数,参数值为李强 单独设置限流 QPS 阈值为 1,而不是全局的阈值 20.ParamFlowItem item = new ParamFlowItem().setObject(String.valueOf("李强")).setClassType(String.class.getName()).setCount(1);rule.setParamFlowItemList(Collections.singletonList(item));ParamFlowRuleManager.loadRules(Collections.singletonList(rule));}

 

 

授权规则

很多时候,我们需要根据调用方来限制资源是否通过,这时候可以使用 Sentinel 的黑白名单控制的功能。黑白名单根据资源的请求来源(origin)限制资源是否通过,

若配置白名单则只有请求来源位于白名单内时才可通过;若配置黑名单则请求来源位于黑名单时不通过,其余的请求通过。

 

 

源码

 

  /*** 资源应用仅允许appA,appB访问* 调用方信息通过 ContextUtil.enter(resourceName, origin) 方法中的 origin 参数传入。* ContextUtil.enter(resourceName, origin)*/public void intAuthorityRule(){AuthorityRule rule = new AuthorityRule();rule.setResource("test");rule.setStrategy(RuleConstant.AUTHORITY_WHITE);rule.setLimitApp("appA,appB");AuthorityRuleManager.loadRules(Collections.singletonList(rule));}

 

在starter的拦截器中提供RequestOriginParser 由我们扩展

com.alibaba.cloud.sentinel.SentinelWebAutoConfiguration

public class SentinelWebAutoConfiguration implements WebMvcConfigurer {......@Autowiredprivate Optional<RequestOriginParser> requestOriginParserOptional;......}

com.alibaba.csp.sentinel.adapter.spring.webmvc.AbstractSentinelInterceptor#preHandle

   public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {try {String resourceName = this.getResourceName(request);if (StringUtil.isEmpty(resourceName)) {return true;} else if (this.increaseReferece(request, this.baseWebMvcConfig.getRequestRefName(), 1) != 1) {return true;} else {//获取originString origin = this.parseOrigin(request);String contextName = this.getContextName(request);ContextUtil.enter(contextName, origin);Entry entry = SphU.entry(resourceName, 1, EntryType.IN);request.setAttribute(this.baseWebMvcConfig.getRequestAttributeName(), entry);return true;}} catch (BlockException var12) {BlockException e = var12;try {this.handleBlockException(request, response, e);} finally {ContextUtil.exit();}return false;}}protected String parseOrigin(HttpServletRequest request) {String origin = "";//RequestOriginParserif (this.baseWebMvcConfig.getOriginParser() != null) {origin = this.baseWebMvcConfig.getOriginParser().parseOrigin(request);if (StringUtil.isEmpty(origin)) {return "";}}return origin;}

系统规则

系统保护规则是从应用级别的入口流量进行控制,从单台机器的总体 Load、RT、入口 QPS 和线程数四个维度监控应用数据,让系统尽可能跑在最大吞吐量的同时保证系统整体的稳定性。

系统保护规则是应用整体维度的,而不是资源维度的,并且仅对入口流量生效。入口流量指的是进入应用的流量(EntryType.IN),比如 Web 服务或 Dubbo 服务端接收的请求,都属于入口流量。

系统保护规则我的理解是一个兜底策略。比如我们压测都是针对单一接口压测,如果我们压测接口本身是cpu密集型的高并发接口,我们把他压到极致,但是一个服务不仅仅是这一个接口提供能力。当时高并发接口和其他接口同时提供能力。往往还没到我们限流阀值系统负载已经上来了。

所以通过系统规则做一次兜底

 

LOAD:linxu的load指标

RT:平均响应时长

线程数:入口流量的并发线程数

CPU使用率

 

sentinel怎么区分入口流量

通过 

Entry entry = SphU.entry(resourceName, 1, EntryType.IN);控制

com.alibaba.csp.sentinel.EntryType

public enum EntryType {IN,OUT;private EntryType() {}
}

sentinle的spring adapter模块在拦截器埋点的就是IN

com.alibaba.csp.sentinel.adapter.spring.webmvc.AbstractSentinelInterceptor#preHandle

 

在服务fegin埋点就是OUT

com.alibaba.cloud.sentinel.feign.SentinelInvocationHandler#invoke

 

 

运维相关

规则是否加载成功

参考文档:https://sentinelguard.io/zh-cn/docs/metrics.html

我们如果使用nacos推的方式,容易推失败,比如我遇到过推失败后拉取的是nacos缓存

dashbard源码处:

com.alibaba.csp.sentinel.dashboard.client.SentinelApiClient#executeCommand

URL说明
http://10.4.1.125:8719/getRules?type=degrade 获取熔断规则
http://10.4.1.125:8719/getRules?type=flow
获取限流规则
http://10.4.1.125:8719/getRules?type=system 系统规则
http://10.4.1.125:8719/getRules?type=authority
获取授权规则
http://10.4.1.125:8719/getRules?getParamFlowRules
系统规则
http://10.4.1.125:8721/getParamFlowRules 热点规则

更多端点?

 

 

sentinel日志查看

参考文档:https://sentinelguard.io/zh-cn/docs/logs.html

默认在${user_home}/logs/csp目录下,可通过sp.sentinel.log.dir进行修改

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/787083.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

*2024.8.25 鲜花

没啥文采,写的不好。NTERNET OVERDOSE この混沌とした 令和のインターネットを照らす 一筋の光 電子の海を漂うオタクに笑顔を 未来の平和をお約束 躁鬱だけどまかせとけ インターネット・エンジェル ただいま降臨 社会をやめろ 家族をやめろ 人間関係をやめろ 今すぐ薄暗い部…

5分钟说透chatgpt

5分钟说清楚 ——到底它为啥能这么火? ——到底牛逼在哪? ——到底我能用来干嘛?把“他”想象成一个博览群书的人 想象一下,现在有一个知识非常渊博的一个人,博览群书,掌握了绝大多数的人类文本知识。(没错,chatgpt确实就是掌握了这么多,而且随着模型的增长,他会看更…

sentinel-前世今生

方便理解sentinel,假如我们自己要实现一套sentinel sentinel前世今生 方便理解sentinel,假如我们自己要实现一套sentinel 第一阶段 一心助手业务服务出现异常,通过监控大盘,发现超过自身服务能够承载的流量,导致请求出现大量排队,服务阻塞,进而导致其他依赖服务出现雪崩效应…

2024.8.25 鲜花

没啥文采,写的不好。NTERNET OVERDOSE この混沌とした 令和のインターネットを照らす 一筋の光 電子の海を漂うオタクに笑顔を 未来の平和をお約束 躁鬱だけどまかせとけ インターネット・エンジェル ただいま降臨 社会をやめろ 家族をやめろ 人間関係をやめろ 今すぐ薄暗い部…

zabbix-grafana配置

一、grafana 安装配置 安装grafana # yum install -y https://dl.grafana.com/oss/release/grafana-11.1.4-1.x86_64.rpm启动grafana # systemctl start grafana-servergrafana 在线安装 zabbix 插件,重启grafana服务 # grafana-cli plugins list-remote | grep -i zabbix id:…

Neo-GNNs: Neighborhood Overlap-aware Graph Neural Networks for Link Prediction

目录概符号说明MotivationNeo-GNN代码Neo-GNNs: Neighborhood overlap-aware graph neural networks for link prediction. NeurIPS, 2021.概 一种计算上相对高效的, 同时利用结构信息和特征信息的链接预测模型. 符号说明\(\mathcal{G} = (\mathcal{V}, \mathcal{E})\), graph;…

Thanos HA

ThanosHA组件HA组件Sidercar Receiver其它组件Querier Store Compactor RulerSidecar & Receiver工作方式Sidecar 为实现高可用,Sidecar组件与Prometheus运行在一个Pod中,双副本的Prometheus独立运行采集数据(scrape metrics),默认情况部署在Kubernetesk的Prometheus使…

063、Vue3+TypeScript基础,作用域插槽的使用

01、main.js代码如下:// 引入createApp用于创建Vue实例 import {createApp} from vue // 引入App.vue根组件 import App from ./App.vue// 引入emitter用于全局事件总线 // import emitter from @/utils/emitterconst app = createApp(App);// App.vue的根元素id为app app.mou…

使用AI识别语音和B站视频并通过GPT生成思维导图

AI脑图除了对文本、网页链接和文件生成思维导图外,现在也支持了对语音和B站视频的内容识别,并自动生成思维导图。AI脑图除了对文本、网页链接和文件生成思维导图外,现在也支持了对语音和B站视频的内容识别,并自动生成思维导图。 语音生成思维导图 直接发送语音:对AI脑图公…

C++函数调用栈从何而来

竹杖芒鞋轻胜马,谁怕?一蓑烟雨任平生~个人主页:rainInSunny | 个人专栏:C++那些事儿、Qt那些事儿目录写在前面原理综述x86架构函数调用栈分析如何获取rbp寄存器的值总结 写在前面程序员对函数调用栈是再熟悉不过了,无论是使用IDE调试还是GDB等工具进行调试,都离不开函数…

[COCI2017-2018#5] Planinarenje

这道题目是二分图博弈的板子 介绍一下二分图博弈:设两部的节点分别为\(x_1,x_2,...,x_n\)和\(y_1,y_2,...,y_m\),先手选择了\(x_i\)这个节点,则先手必胜当且仅当\(x_i\)是最大匹配的必须点(也就是说少了\(x_i\)的话最大匹配数会减少) 证明: 任选一个最大匹配,则\(x_i\)为…

C# StartsWith 字符串的实例方法解析

原文链接:https://www.jb51.net/program/317525q14.htm 在 C# 中,StartsWith 是一个字符串的实例方法,用于检查字符串是否以指定的前缀开头。它返回一个布尔值,指示字符串是否以指定的前缀开始。