当前位置: 首页 > ai >正文

微服务雪崩防护最佳实践之sentinel

思考

1、当服务访问量达到一定程度,流量扛不住的时候,该如何处理?

2、服务之间相互依赖,当服务A出现响应时间过长,影响到服务B的响应,进而产生连锁反应,直至影响整个依赖链上的所有服务,该如何处理?

这是分布式、微服务开发不可避免的问题。

我们系统假如是这样的调用图。当G服务出现程序Bug,大流量请求,硬件故障,缓存击穿时,导致服务不可用。进而导致D服务不可用,导致A服务不可用。

B、C、D三个服务共享A服务的线程池,当D服务出现故障时,则导致A服务中所有线程池都在等待D服务响应而被阻塞。进而导致A服务不可用

 解决方案

超时机制

在不做任何处理的情况下,服务提供者不可用会导致消费者请求线程强制等待,而造成系统资源耗尽。加入超时机制,一旦超时,就释放资源。由于释放资源速度较快,一定程度上可以抑制资源耗尽的问题。

服务限流(资源隔离)

限制请求核心服务提供者的流量,使大流量拦截在核心服务之外,这样可以更好的保证核心服务提供者不出问题,对于一些出问题的服务可以限制流量访问,只分配固定线程资源访问,这样能使整体的资源不至于被出问题的服务耗尽,进而整个系统雪崩。那么服务之间怎么限流,怎么资源隔离?例如可以通过线程池+队列的方式,通过信号量的方式。

当D服务出问题时,A调用C服务和B服务不受影响。

服务熔断

远程服务不稳定或网络抖动时暂时关闭,就叫服务熔断。

服务降级

有服务熔断,必然要有服务降级。

sentinel开源功能不足之点

 相信用过开源版本sentinel的小伙伴,会发现当服务重新启动是,sentinel控制台规则数据则失效;接下里引入sentinel-datasource-nacos 规则持久化,来解决sentinel规则失效问题。但是发现在sentinel的dashboard的客户端更改规则生效,但是nacos中的配置未发生变更,导致dashboard的和nacos的数据不一致现象。

 

红色的箭头未实现,就会导致dashboard配置的规则和nacos配置中的数据不一致。

为了解决这个问题,官方的建议是在dashboard源码中进行改造,直接写入数据到nacos中。标红的部分

所以实现dashboard和nacos中的数据规则一致,则有两个方案:

 方案一 需要在应用服务端进行改造

方案二 需要在dashboard的源码中进行改造(官方推荐

设计模式的核心理念逻辑之一:开放封闭原则。选择方案一比较好点。

我对方案一进行的实现。

代码实现

1.SpringContextHolder 工具类(为了能获取到配置中变量)

@Component
public class SpringContextHolder implements ApplicationContextAware {private static ApplicationContext context;public static ApplicationContext getContext() {return context;}@Overridepublic void setApplicationContext(ApplicationContext applicationContext) {context = applicationContext;}
}

2. SentinelNacosProperties 实体类

public class SentinelNacosProperties {public String serverAddr;public String groupId;public String dataId;public String username;public String password;public String namespace;public String appName;
//自己添加get/set方法
}

 3.利用sentinel的扩展点InitFunc(NacosDataSourceInitFunc)

@Slf4j
public class NacosDataSourceInitFunc implements InitFunc {@Overridepublic void init() throws Exception {log.info("NacosDataSourceInitFunc init...");Environment env = SpringContextHolder.getContext().getEnvironment();String appName = env.getProperty("spring.application.name", "demo-project");String groupId = "SENTINEL_GROUP";String serverAddr = env.getProperty("spring.cloud.nacos.config.server-addr", "localhost:8848");String namespace = env.getProperty("spring.cloud.nacos.config.namespace", "public");String username = env.getProperty("spring.cloud.nacos.config.username", "nacos");String password = env.getProperty("spring.cloud.nacos.config.password", "xx#pj70Qdd1");log.info("NacosDataSourceInitFunc appName:{}, group:{}, serverAddr:{}", appName, groupId, serverAddr);SentinelNacosProperties sentinelNacosProperties = new SentinelNacosProperties();sentinelNacosProperties.setAppName(appName);sentinelNacosProperties.setGroupId(groupId);sentinelNacosProperties.setServerAddr(serverAddr);sentinelNacosProperties.setNamespace(namespace);sentinelNacosProperties.setUsername(username);sentinelNacosProperties.setPassword(password);// =============== FlowRule 流控规则 ==================String flowDataId = appName + "-flow-rules";sentinelNacosProperties.setDataId(flowDataId);WritableDataSource<List<FlowRule>> flowRuleWritableDataSource = new NacosWritableDataSource<>(sentinelNacosProperties, JSON::toJSONString);WritableDataSourceRegistry.registerFlowDataSource(flowRuleWritableDataSource);// =============== DegradeRule 降级规则 ==================String degradeDataId = appName + "-degrade-rules";sentinelNacosProperties.setDataId(degradeDataId);WritableDataSource<List<DegradeRule>> degradeRuleWritableDataSource = new NacosWritableDataSource<>(sentinelNacosProperties, JSON::toJSONString);WritableDataSourceRegistry.registerDegradeDataSource(degradeRuleWritableDataSource);// =============== AuthorityRule 授权规则 ==================String authorityDataId = appName + "-authority-rules";sentinelNacosProperties.setDataId(authorityDataId);WritableDataSource<List<AuthorityRule>> authorityRuleWritableDataSource = new NacosWritableDataSource<>(sentinelNacosProperties, JSON::toJSONString);WritableDataSourceRegistry.registerAuthorityDataSource(authorityRuleWritableDataSource);// =============== SystemRule 系统规则 ==================String systemDataId = appName + "-system-rules";sentinelNacosProperties.setDataId(systemDataId);WritableDataSource<List<SystemRule>> systemRuleWritableDataSource = new NacosWritableDataSource<>(sentinelNacosProperties, JSON::toJSONString);WritableDataSourceRegistry.registerSystemDataSource(systemRuleWritableDataSource);log.info("NacosDataSourceInitFunc init finished");}
}

由于sentinel的各个规则都可以在不同的nacos服务上,根据自己的修改此配置。

4. 写数据源的实现

import com.alibaba.csp.sentinel.datasource.Converter;
import com.alibaba.csp.sentinel.datasource.WritableDataSource;
import com.alibaba.nacos.api.NacosFactory;
import com.alibaba.nacos.api.PropertyKeyConst;
import com.alibaba.nacos.api.config.ConfigService;
import com.alibaba.nacos.api.config.ConfigType;
import com.alibaba.nacos.api.exception.NacosException;
import org.springframework.beans.factory.annotation.Value;import java.util.Properties;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;/*** Sentinel nacos写数据源实现**/
public class NacosWritableDataSource<T> implements WritableDataSource<T> {private final String serverAddr;private final String groupId;private final String dataId;private final String username;private final String password;private final String namespace;private final Properties properties;private ConfigService configService;private final Converter<T, String> configEncoder;private final Lock lock = new ReentrantLock(true);public NacosWritableDataSource(SentinelNacosProperties nacosProperties, Converter<T, String> configEncoder) {this.serverAddr = nacosProperties.getServerAddr();this.namespace = nacosProperties.getNamespace();this.groupId = nacosProperties.getGroupId();this.dataId = nacosProperties.getDataId();this.username = nacosProperties.getUsername();this.password = nacosProperties.getPassword();this.properties = NacosWritableDataSource.buildProperties( nacosProperties);this.configEncoder = configEncoder;initConfigService();}private void initConfigService() {try {this.configService = NacosFactory.createConfigService(properties);} catch (NacosException e) {e.printStackTrace();}}private static Properties buildProperties(SentinelNacosProperties nacosProperties) {Properties properties = new Properties();properties.setProperty(PropertyKeyConst.SERVER_ADDR, nacosProperties.getServerAddr());properties.setProperty(PropertyKeyConst.NAMESPACE, nacosProperties.getNamespace());properties.setProperty(PropertyKeyConst.USERNAME, nacosProperties.getUsername());properties.setProperty(PropertyKeyConst.PASSWORD, nacosProperties.getPassword());return properties;}@Overridepublic void write(T t) throws Exception {lock.lock();try {configService.publishConfig(dataId, groupId, this.configEncoder.convert(t));} finally {lock.unlock();}}@Overridepublic void close() throws Exception {}}

5. application.yml 规则定义

spring:cloud:sentinel:transport:dashboard: localhost:8080 # Sentinel 控制台地址port: 8719 # 本地启动的端口,用于与 Sentinel 控制台通信eager: true # 是否立即初始化web-context-unify: false # 是否合并 Web 上下文datasource:flow-rules:nacos:server-addr: 127.0.0.1:8848dataId: ${spring.application.name}-flow-rulesgroupId: SENTINEL_GROUPdata-type: jsonrule-type: flowdegrade-rules:nacos:server-addr: 127.0.0.1:8848dataId: ${spring.application.name}-degrade-rulesgroupId: SENTINEL_GROUPdata-type: jsonrule-type: degradeparam-flow-rules:nacos:server-addr: 127.0.0.1:8848dataId: ${spring.application.name}-param-flow-rulesgroupId: SENTINEL_GROUPdata-type: jsonrule-type: param-flowauthority-rules:nacos:server-addr: 127.0.0.1:8848dataId: ${spring.application.name}-authority-rulesgroupId: SENTINEL_GROUPdata-type: jsonrule-type: authoritysystem-rules:nacos:server-addr: 127.0.0.1:8848dataId: ${spring.application.name}-system-rulesgroupId: SENTINEL_GROUPdata-type: jsonrule-type: system

nacos的配置自己添加一下即可。

5.由于我们利用的sentinel的SPI机制

需要以下的文件夹META-INF/services/

com.alibaba.csp.sentinel.init.InitExecutor的类

 将你的实现类按照图中存放即可。

 测试

nacos中流控规则中的阈值是33.

dashboard的界面显示也是33.则代表nacos->服务内存->dashboard 这条链路已经走通。 

 现在我们在dashboard上进行修改阈值为:55

查看naocs是否同步修改:

 

nacos中已经修改,则代表方案一已实现。

http://www.xdnf.cn/news/15796.html

相关文章:

  • Django ORM系统
  • SearchService 该类只运行在数据节点
  • 【文件IO】认识文件描述符和内核缓冲区
  • SSH开启Socks5服务
  • C++ STL容器
  • 金融大前端中的 AI 应用:智能投资顾问与风险评估
  • 【Nature Communications】GaN外延层中位错辅助的电子和空穴输运
  • 0401聚类-机器学习-人工智能
  • nvm、npm、pnpm、cnpm、yarn
  • 《深入C++多态机制:从虚函数表到运行时类型识别》​
  • 数据并表技术全面指南:从基础JOIN到分布式数据融合
  • Spring Boot 自动装配用法
  • Materials Studio学习笔记(二十九)——尿素的几何优化
  • 树同构(Tree Isomorphism)
  • [特殊字符] 小程序 vs 智能体:下一代应用开发,谁主沉浮?
  • 【Java项目安全基石】登录认证实战:Session/Token/JWT用户校验机制深度解析
  • 基于自定义数据集微调SigLIP2-分类任务
  • PDF 编辑器:多文件合并 拆分 旋转 顺序随便调 加水印 密码锁 页码背景
  • [学习] 深入理解傅里叶变换:从时域到频域的桥梁
  • vscode环境下c++的常用快捷键和插件
  • 嵌入式通信DQ单总线协议及UART(一)
  • Linux练习二
  • 鸿蒙蓝牙通信
  • [AI风堇]基于ChatGPT3.5+科大讯飞录音转文字API+GPT-SOVITS的模拟情感实时语音对话项目
  • 字节跳动开源Seed-X 7B多语言翻译模型:28语种全覆盖,性能超越GPT-4、Gemini-2.5与Claude-3.5
  • 关于Vuex
  • GeoPandas 城市规划:Python 空间数据初学者指南
  • 零基础 “入坑” Java--- 十二、抽象类和接口
  • ndexedDB 与 LocalStorage:全面对比分析
  • aosp15实现SurfaceFlinger的dump输出带上Layer详细信息踩坑笔记