《掌门1对1微服务体系 Solar | 阿里巴巴 Sentinel 落地实践》

简介:

前言

掌门1对1精耕在线教育领域,近几年业务得到了快速发展,但同时也遭遇了“成长的烦恼”。随着微服务数量不断增加,流量进一步暴增,硬件资源有点不堪重负,那么,如何实现更好的限流熔断降级等流量防护措施,这个课题就摆在了掌门人的面前。由于 Spring Cloud 体系已经演进到第二代,第一代的 Hystrix 限流熔断降级组件已经不大适合现在的业务逻辑和规模,同时它目前被 Spring Cloud 官方置于维护模式,将不再向前发展。

如何选择一个更好的限流熔断降级组件?经过对 Alibaba SentinelResilience4jHystrix 等开源组件做了深入的调研和比较,最终选定 Alibaba Sentinel 做微服务体系 Solar 中的限流熔断降级必选组件。

Sentinel 简介

阿里巴巴中间件部门开发的新一代以流量为切入点,从流量控制、熔断降级、系统负载保护等多个维度保护服务的稳定性的分布式系统的流量防卫兵。它承接了阿里巴巴近10年的双十一大促流量的核心场景,例如秒杀(即突发流量控制在系统容量可以承受的范围)、消息削峰填谷、集群流量控制、实时熔断下游不可用应用等。

它具有非常丰富的开源生态:

它和 Hystrix 相比,有如下差异:

摘自官网 Sentinel Roadmap

关于 Sentinel 如何使用,它的技术实现原理怎样等,官方文档或者民间博客、公众号文章等可以提供非常详尽且有价值的材料,这些不在本文的讨论范围内,就不一一赘述。笔者尝试结合掌门1对1现有的技术栈以及中间件一体化的战略,并着眼于强大的 Spring Cloud Alibaba 技术生态圈展开阐释。

Sentinel 深度集成 Apollo

Sentinel 官方在 sentinel-datasource-apollo 模块中已经对 Apollo 做了一些扩展,主要实现了 Sentinel 规则的读取和订阅逻辑。这些并不够,我们需要对 Apollo 进行更深层次的集成。

摘自官网 在生产环境中使用 Sentinel

Solar SDK 环境初始化

定制 EnvironmentPostProcessor 类,实现如下:

  • Sentinel Dashboard 的项目名称从 Apollo AppId 的维度进行展示
  • 根据环境 env 值读取相应的配置文件,并访问对应环境的 Sentinel Dashboard 域名Sentinel Dashboard 在生产环境部署若干台 ECS 实例,阿里云 SLB 做负载均衡,实现对集群的水平扩展
public class SentinelClientEnvironmentPostProcessor implements EnvironmentPostProcessor {private final ResourceLoader resourceLoader = new DefaultResourceLoader();private static final String DEFAULT_CLASSPATH_LOCATION = "classpath:/META-INF/app.properties";private static final String DEFAULT_LOCATION = "/META-INF/app.properties";private static final String DEFAULT_LOG_LOCATION = "/opt/logs/";    @Overridepublic void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {try {Resource appResource = resourceLoader.getResource(DEFAULT_CLASSPATH_LOCATION);if (!appResource.exists()) {appResource = resourceLoader.getResource(DEFAULT_LOCATION);}Properties appProperties = new Properties();appProperties.load(new InputStreamReader(appResource.getInputStream()));String appId = appProperties.getProperty("app.id");System.setProperty("project.name", appId);System.setProperty("csp.sentinel.log.dir", DEFAULT_LOG_LOCATION + appId);Properties properties = new Properties();String path = isOSWindows() ? "C:/opt/settings/server.properties" : "/opt/settings/server.properties";File file = new File(path);if (file.exists() && file.canRead()) {FileInputStream fis = new FileInputStream(file);if (fis != null) {try {properties.load(new InputStreamReader(fis, Charset.defaultCharset()));} finally {fis.close();}}}String idc = properties.getProperty("idc");String location;String env = System.getProperty("env");if (StringUtils.isEmpty(idc)) {if (!isBlank(env)) {env = env.trim().toLowerCase();} else {env = System.getenv("ENV");if (!isBlank(env)) {env = env.trim().toLowerCase();} else {env = properties.getProperty("env");if (!isBlank(env)) {env = env.trim();} else {env = Env.FAT.getEnv();}}}location = "classpath:/META-INF/sentinel-" + env + ".properties";} else {location = "classpath:/META-INF/sentinel-" + idc + ".properties";}Resource serverResource = resourceLoader.getResource(location);properties.load(new InputStreamReader(serverResource.getInputStream()));for (String key : properties.stringPropertyNames()) {System.setProperty(key, properties.getProperty(key));}System.setProperty(CommonConstant.SENTINEL_VERSION_NAME, CommonConstant.SENTINEL_VERSION_VALUE);} catch (Exception e) {LOG.error(e.getMessage());}}private boolean isBlank(String str) {return Strings.nullToEmpty(str).trim().isEmpty();}private boolean isOSWindows() {String osName = System.getProperty("os.name");return !isBlank(osName) && osName.startsWith("Windows");}
}

把 SentinelClientEnvironmentPostProcessor 类放置 \resources\META-INF\spring.factories 文件中,内容为

org.springframework.boot.env.EnvironmentPostProcessor=\
com.zhangmen.solar.component.sentinel.common.context.SentinelClientEnvironmentPostProcessor

在 \resources\META-INF 目录下,定制环境配置文件,文件名格式为 sentinel-{环境号}.properties 。下文以 dev 环境和 flow 流控配置(其它规则配置,请自行参考 Spring Cloud Alibaba Sentinel 的相关资料)为样例。

sentinel-dev.properties

spring.cloud.sentinel.transport.dashboard=127.0.0.1:8080
spring.cloud.sentinel.datasource.ds.apollo.namespaceName=application
spring.cloud.sentinel.datasource.ds.apollo.flowRulesKey=sentinel.flowRules
spring.cloud.sentinel.datasource.ds.apollo.ruleType=flow
...

Sentinel Dashboard 持久化改造

原生的 Sentinel Dashboard 在创建完规则后,规则内容保存在服务的内存中,当服务重启后所有的规则内容都会消失。因此,在生产部署时需要考虑配置持久化,并且使用 Apollo 动态规则的感知能力。

① 向外暴露 Sentinel 规则的 Restful 接口

@RestController
@RequestMapping(value = "/v2/flow")
public class FlowControllerV2 {@Autowired@Qualifier("apolloFlowRuleProvider")private DynamicRuleProvider<List<FlowRuleEntity>> ruleProvider;@Autowired@Qualifier("apolloFlowRulePublisher")private DynamicRulePublisher<List<FlowRuleEntity>> rulePublisher;....
}

② 实现 Sentinel Apollo 规则提供

@Component("apolloFlowRuleProvider")
public class ApolloFlowRuleProvider extends BaseApolloRuleProvider<FlowRuleEntity> {@Overridepublic List<FlowRuleEntity> getRules(String appName) throws Exception {List<FlowRuleEntity> flowRuleEntityList = super.getRules(appName);if (!CollectionUtils.isEmpty(flowRuleEntityList)) {List<FlowRuleEntity> flowRuleEntities = JSONArray.parseArray(flowRuleEntityList.toString(), FlowRuleEntity.class);long id = 1;for (FlowRuleEntity entity : flowRuleEntities) {entity.setId(id++);entity.getClusterConfig().setFlowId(entity.getId());}return flowRuleEntities;} else {return null;}}@Overrideprotected String getDataId() {return ApolloConfigUtil.getFlowDataId();}
}

③ 实现 Sentinel Apollo 规则订阅

@Component("apolloFlowRulePublisher")
public class ApolloFlowRulePublisher extends BaseApolloRulePublisher<List<FlowRuleEntity>> {@Overridepublic void publish(String app, String operator, List<FlowRuleEntity> rules) throws Exception {if (!CollectionUtils.isEmpty(rules)) {for (int i = 0; i < rules.size(); i++) {rules.get(i).setId((long) (i + 1));rules.get(i).setApp(null);rules.get(i).setGmtModified(null);rules.get(i).setGmtCreate(null);rules.get(i).setIp(null);rules.get(i).setPort(null);rules.get(i).getClusterConfig().setFlowId((long) (i + 1));}} else {rules = null;}super.publish(app, operator, rules);}@Overrideprotected String getDataId() {return ApolloConfigUtil.getFlowDataId();}
}

上述代码实现了对 Apollo 配置读写操作。熟悉 Apollo 的同学应该知道,这些操作需要基于 Apollo OpenApi 来操作;动态感知能力的逻辑已经由 sentinel-datasource-apollo 模块实现。

Sentinel 集成 Skywalking

由于掌门1对1微服务技术栈落地的比较早,鉴于历史的局限性(当时没有更先进的技术可供选择),除了 Hystrix 比较古老以外,另一个技术栈的痛点是全链路监控中间件的改造也提上议事日程,CAT 作为开源界老牌作品,为公司底层全链路监控提供强有力的保障,但随着技术的演进,它逐渐已经不适合公司的未来发展方向,经过对比,最终选择 Skywalking 将作为它的替代者(关于 Skywalking 的技术选型,将在后面掌门1对1微服务体系 Solar 的公众号系列文章中会一一阐述)。

业务系统要求对限流熔断降级实现全链路实时埋点,并希望在 Skywalking 界面上提供限流熔断降级埋点的多维度统计。由于 Skywalking 实现了 OpenTracing 标准化协议,那么以 OpenTracing 为桥梁,通过 Solar SDK 输出 Sentinel 埋点到 Skywalking Server 不失为一个好的技术选择。下面简单扼要介绍一下基于 Sentinel InitFunc 的 SPI 机制实现埋点输出:

Sentinel 将 ProcessorSlot 作为 SPI 接口进行扩展(1.7.2 版本以前 SlotChainBuilder 作为 SPI ),使得 Slot Chain 具备了扩展的能力。您可以自行加入自定义的 slot 并编排 slot 间的顺序,从而可以给 Sentinel 添加自定义的功能。

摘自官网 Sentinel 工作主流程

抽象 Sentinel ProcessorSlot 埋点输出

Sentinel 的 ProcessorSlotEntryCallback 提供 onPass 和 onBlocked 两个方法,毕竟限流熔断降级并不是常规的功能,不会发生在大流量上面,所以 onPass 上我们不做任何处理,否则正常的调用去实现拦截,将为产生大量的埋点数据,会让 Skywalking Server 承受很大的性能压力,所以 onBlocked 将是我们关注的重点,它除了输出 Sentinel 本身的上下文参数之外,也会输出微服务 Solar 指标参数,主要包括:

  • 埋点 Span名称,这里为 SENTINEL ,在 Skywalking 全链路监控界面中,用户可以非常容易的找到这个埋点
  • 服务所在的名,指服务的逻辑分组
  • 服务类型,包括服务和网关(网关也是一种特殊的服务), Sentinel 埋点可以支持在服务和网关上的输出
  • 服务的 APPID,它为 Apollo 组件的范畴概念
  • 服务名,它对应为 spring.application.name 的配置值
  • 服务实例所在的 IP 地址和 Port 端口
  • 服务版本号
  • 服务所在的区域
  • 服务所在的子环境

接下去是 Sentinel 层面的参数,请自行参考 Sentinel 官方文档和源码,了解其含义,这里不做具体讲解。

public abstract class SentinelTracerProcessorSlotEntryCallback<S> implements ProcessorSlotEntryCallback<DefaultNode> {@Overridepublic void onPass(Context context, ResourceWrapper resourceWrapper, DefaultNode param, int count, Object... args) throws Exception {}@Overridepublic void onBlocked(BlockException e, Context context, ResourceWrapper resourceWrapper, DefaultNode param, int count, Object... args) {S span = buildSpan();PluginAdapter pluginAdapter = PluginContextAware.getStaticApplicationContext().getBean(PluginAdapter.class);outputSpan(span, DiscoveryConstant.SPAN_TAG_PLUGIN_NAME, context.getName());outputSpan(span, DiscoveryConstant.N_D_SERVICE_GROUP, pluginAdapter.getGroup());outputSpan(span, DiscoveryConstant.N_D_SERVICE_TYPE, pluginAdapter.getServiceType());String serviceAppId = pluginAdapter.getServiceAppId();if (StringUtils.isNotEmpty(serviceAppId)) {outputSpan(span, DiscoveryConstant.N_D_SERVICE_APP_ID, serviceAppId);}outputSpan(span, DiscoveryConstant.N_D_SERVICE_ID, pluginAdapter.getServiceId());outputSpan(span, DiscoveryConstant.N_D_SERVICE_ADDRESS, pluginAdapter.getHost() + ":" + pluginAdapter.getPort());outputSpan(span, DiscoveryConstant.N_D_SERVICE_VERSION, pluginAdapter.getVersion());outputSpan(span, DiscoveryConstant.N_D_SERVICE_REGION, pluginAdapter.getRegion());outputSpan(span, DiscoveryConstant.N_D_SERVICE_ENVIRONMENT, pluginAdapter.getEnvironment());outputSpan(span, SentinelStrategyConstant.ORIGIN, context.getOrigin());outputSpan(span, SentinelStrategyConstant.ASYNC, String.valueOf(context.isAsync()));outputSpan(span, SentinelStrategyConstant.RESOURCE_NAME, resourceWrapper.getName());outputSpan(span, SentinelStrategyConstant.RESOURCE_SHOW_NAME, resourceWrapper.getShowName());outputSpan(span, SentinelStrategyConstant.RESOURCE_TYPE, String.valueOf(resourceWrapper.getResourceType()));outputSpan(span, SentinelStrategyConstant.ENTRY_TYPE, resourceWrapper.getEntryType().toString());outputSpan(span, SentinelStrategyConstant.RULE_LIMIT_APP, e.getRuleLimitApp());if (tracerSentinelRuleOutputEnabled) {outputSpan(span, SentinelStrategyConstant.RULE, e.getRule().toString());}outputSpan(span, SentinelStrategyConstant.CAUSE, e.getClass().getName());outputSpan(span, SentinelStrategyConstant.BLOCK_EXCEPTION, e.getMessage());outputSpan(span, SentinelStrategyConstant.COUNT, String.valueOf(count));if (tracerSentinelArgsOutputEnabled) {outputSpan(span, SentinelStrategyConstant.ARGS, JSON.toJSONString(args));}finishSpan(span);}protected abstract S buildSpan();protected abstract void outputSpan(S span, String key, String value);protected abstract void finishSpan(S span);
}

整合 OpenTracing & Skywalking

实现 SentinelTracerProcessorSlotEntryCallback 的三个核心方法:

  • buildSpan - 创建 Skywalking 的埋点 Span 对象
  • outputSpan - 输出相关埋点数据的键值对到 Skywalking 的埋点 Span 对象中
  • finishSpan - 提交 Skywalking 的埋点 Span 对象到 Skywalking Server
public class SentinelSkywalkingTracerProcessorSlotEntryCallback extends SentinelTracerProcessorSlotEntryCallback<Span> {private Tracer tracer = new SkywalkingTracer();@Overrideprotected Span buildSpan() {return tracer.buildSpan(SentinelStrategyConstant.SPAN_NAME).startManual();}@Overrideprotected void outputSpan(Span span, String key, String value) {span.setTag(key, value);}@Overrideprotected void finishSpan(Span span) {span.finish();}
}

实现 Sentinel InitFunc SPI 扩展

实现 SPI 的扩展切入类

public class SentinelSkywalkingTracerInitFunc implements InitFunc {@Overridepublic void init() throws Exception {StatisticSlotCallbackRegistry.addEntryCallback(SentinelSkywalkingTracerProcessorSlotEntryCallback.class.getName(), new SentinelSkywalkingTracerProcessorSlotEntryCallback());}
}

把 SPI 的扩展切入类放置 \resources\META-INF\services\com.alibaba.csp.sentinel.init.InitFunc 文件中,内容为

com.nepxion.discovery.plugin.strategy.sentinel.skywalking.monitor.SentinelSkywalkingTracerInitFunc

摘自 Nepxion Discovery 开源社区

对于 Sentinel 跟 Opentracing, Skywalking, Jaeger 的集成可参考 https://github.com/Nepxion/Discovery 中的 discovery-plugin-strategy-sentinel-starter-opentracing, discovery-plugin-strategy-sentinel-starter-skywalking 等模块。

最终在 Skywalking 全链路界面上输出如下:

全链路调用链中,我们可以看到 solar-service-a 服务的链路上输出了 SENTINEL 埋点,表示 solar-service-a 上发生了 Sentinel 限流熔断降级事件之一。

点击 SENTINEL 埋点,在呼出的内容看板上,我们可以看到 solar-service-a 服务发生了限流事件,上面显示限流的规则和异常信息以及微服务 Solar 指标等一系列参数。

我们可以点击界面上边的【熔断查询】进行 Sentinel 相关数据的分析和统计

Sentinel 集成 InfluxDB & Grafana

监控数据持久化到 InfluxDB

① Sentinel MetricFetcher 拉取数据

实现 Dashboard 服务端拉取 Sentinel 客户端(即 Solar 微服务)的监控数据

@Component
public class MetricFetcher {@Autowired@Qualifier("influxDBMetricRepository")private MetricsRepository<MetricEntity> metricStore;...
}

② InfluxDB 实例初始化

@Configuration
public class InfluxDBAutoConfiguration {@Value("${spring.influx.url}")private String influxDBUrl;@Value("${spring.influx.user}")private String userName;@Value("${spring.influx.password}")private String password;@Value("${spring.influx.database}")private String database;@Beanpublic InfluxDB influxDB() {InfluxDB influxDB = null;try {influxDB = InfluxDBFactory.connect(influxDBUrl, userName, password);influxDB.setDatabase(database).enableBatch(100, 1000, TimeUnit.MILLISECONDS);influxDB.setLogLevel(InfluxDB.LogLevel.NONE);} catch (Exception e) {LOG.error(e.getMessage());}return influxDB;}
}

③ Sentinel 数据写入到 InfluxDB

@Component("influxDBMetricRepository")
public class InfluxDBMetricRepository implements MetricsRepository<MetricEntity> {@Autowiredprivate InfluxDB influxDB;@Overridepublic void save(MetricEntity metric) {try {Point point = createPoint(metric);influxDB.write(point);} catch (Exception e) {LOG.error(e.getMessage());}}@Overridepublic void saveAll(Iterable<MetricEntity> metrics) {if (metrics == null) {return;}try {BatchPoints batchPoints = BatchPoints.builder().build();metrics.forEach(metric -> {Point point = createPoint(metric);batchPoints.point(point);});influxDB.write(batchPoints);} catch (Exception e) {LOG.error(e.getMessage());}}
}

Grafana 界面展现监控数据

Sentinel Limit-App 熔断扩展

掌门1对1已经实现通过灰度蓝绿发布方式,实现对流量的精确制导和调拨,但为了进一步实施更安全的流量保障,引入了基础指标和灰度蓝绿发布指标的熔断,同时也支持业务自定义指标和组合指标的熔断。

通过对 Sentinel Limit-App机制的扩展并定制授权规则,实现微服务 Solar 的熔断扩展。对于授权规则中涉及到的参数,简要做如下说明:

  • resource 为 @SentinelResource 注解的 value ,也可以是调用的 URL 路径值
  • limitApp 如果有多个,可以通过 , 分隔。特别注意,下文为了描述简单,只以单个为例
  • strategy 为 0 表示白名单,符合条件就放行流量; strategy 为 1 表示黑名单,符合条件就限制流量。特别注意,下文为了描述简单,只以白名单为例

基础指标的熔断

通过 Http Header 自动携带下游服务的基础指标进行全链路传递的方式,对下游调用实施基础指标的熔断。支持如下指标:

① 服务名

当 A 服务发送请求到 B 服务,所携带的 A 服务名不满足条件,该请求就会被 B 服务熔断。

  • B 服务增加配置项
spring.application.strategy.service.sentinel.request.origin.key=n-d-service-id
  • B 服务增加授权规则, limitApp 为 A 服务名
[{"resource": "sentinel-resource","limitApp": "a-service-id","strategy": 0}
]

② 服务的 APPID

当 A 服务发送请求到 B 服务,所携带的 A 服务的 APPID 不满足条件,该请求就会被 B 服务熔断。

  • B 服务增加配置项
spring.application.strategy.service.sentinel.request.origin.key=n-d-service-app-id
  • B 服务增加授权规则, limitApp 为 A 服务的 APPID
[{"resource": "sentinel-resource","limitApp": "a-service-app-id","strategy": 0}
]

③ 服务实例所在的 IP 地址和 Port 端口

当 A 服务发送请求到 B 服务,所携带的 A 服务的 IP 地址和 Port 端口不满足条件,该请求就会被 B 服务熔断。

  • B 服务增加配置项
spring.application.strategy.service.sentinel.request.origin.key=n-d-service-address
  • B 服务增加授权规则, limitApp 为 A 服务实例所在的 IP 地址和 Port 端口
[{"resource": "sentinel-resource","limitApp": "a-ip:a-port","strategy": 0}
]

灰度蓝绿发布指标的熔断

通过 Http Header 自动携带下游服务的灰度蓝绿发布指标进行全链路传递的方式,对下游调用实施灰度蓝绿发布指标的熔断。支持如下指标:

① 服务所在的

当 A 服务发送请求到 B 服务,所携带的 A 服务的名和 B 服务的名不一致,该请求就会被 B 服务熔断。

  • B 服务增加配置项
spring.application.strategy.service.sentinel.request.origin.key=n-d-service-group
  • B 服务增加授权规则, limitApp 为 B 服务的组名
[{"resource": "sentinel-resource","limitApp": "b-group","strategy": 0}
]

② 服务版本号

当 A 服务发送请求到 B 服务,所携带的 A 服务的版本号和 B 服务的版本号不一致,该请求就会被 B 服务熔断。

  • B 服务增加配置项
spring.application.strategy.service.sentinel.request.origin.key=n-d-service-version
  • B 服务增加授权规则, limitApp 为 B 服务的版本号
[{"resource": "sentinel-resource","limitApp": "b-version","strategy": 0}
]

③ 服务所在的区域

当 A 服务发送请求到 B 服务,所携带的 A 服务的区域值和 B 服务的区域值不一致,该请求就会被 B 服务熔断。

  • B 服务增加配置项
spring.application.strategy.service.sentinel.request.origin.key=n-d-service-region
  • B 服务增加授权规则, limitApp 为 B 服务的区域值
[{"resource": "sentinel-resource","limitApp": "b-region","strategy": 0}
]

④ 服务所在的子环境

当 A 服务发送请求到 B 服务,所携带的 A 服务的子环境值和 B 服务的子环境值不一致,该请求就会被 B 服务熔断。

  • B 服务增加配置项
spring.application.strategy.service.sentinel.request.origin.key=n-d-service-env
  • B 服务增加授权规则, limitApp 为 B 服务的子环境值
[{"resource": "sentinel-resource","limitApp": "b-env","strategy": 0}
]

业务自定义指标的熔断

通过 Http Header 携带下游服务的业务自定义指标进行全链路传递的方式,对下游调用实施自定义指标的熔断。

当 A 服务发送请求到 B 服务,所携带的 A 的自定义指标不满足条件,该请求就会被 B 服务熔断。例如: A 服务把 userName 通过 Http Header 传递给 B 服务,而 B 服务只接受 userName 为 zhangsan 的请求,那么我们可以通过如下方式来解决:

  • B 服务通过适配类实现 Sentinel Origin 值的解析
public class MyServiceSentinelRequestOriginAdapter extends AbstractServiceSentinelRequestOriginAdapter {@Overridepublic String parseOrigin(HttpServletRequest request) {return request.getHeader("userName");}
}
  • B 服务的配置类里通过 @Bean 方式进行适配类创建
@Bean
public ServiceSentinelRequestOriginAdapter ServiceSentinelRequestOriginAdapter() {return new MyServiceSentinelRequestOriginAdapter();
}
  • B 服务增加授权规则, limitApp 为 zhangsan
[{"resource": "sentinel-resource","limitApp": "zhangsan","strategy": 0}
]

假如该方式仍未能满足业务场景,业务系统希望根据 userName 获取 userType,根据用户类型做统一熔断,例如,用户类型为 AUTH_USER 的请求才能放行,其它都熔断,那么我们可以把上面的例子修改如下:

  • B 服务的适配类更改如下:
public class MyServiceSentinelRequestOriginAdapter extends AbstractServiceSentinelRequestOriginAdapter {@Overridepublic String parseOrigin(HttpServletRequest request) {String userName = request.getHeader("userName");String userType = getUserTypeByName(userName);        return userType;}
}
  • B 服务的授权规则更改如下:
[{"resource": "sentinel-resource","limitApp": "AUTH_USER","strategy": 0}
]

组合指标的熔断

通过 Http Header 携带下游服务的业务自定义指标、基础指标或者灰度蓝绿发布指标进行全链路传递的方式,对下游调用实施组合指标的熔断,例如,根据传入的微服务版本号 + 用户名,组合在一起进行熔断。下面示例表示为下游服务版本为 1.0 且 userName 为 zhangsan,同时满足这两个条件下,所有服务的请求允许被放行,否则被熔断。

  • B 服务的适配类更改如下:
public class MyServiceSentinelRequestOriginAdapter extends AbstractServiceSentinelRequestOriginAdapter {@Overridepublic String parseOrigin(HttpServletRequest request) {String version = request.getHeader(DiscoveryConstant.N_D_SERVICE_VERSION);String userName = request.getHeader("userName");return version + "&" + userName;}
}
  • B 服务的授权规则更改如下:
[{"resource": "sentinel-resource","limitApp": "1.0&zhangsan","strategy": 0}
]

Sentinel 网关流控实践

阐述网关流控实践的时候,我们使用精确匹配的方式对某个服务的请求做限流控制为例;对网关代理的 solar-service-a 服务的接口 /inspector/inspect 做限流控制为例。

API 分组管理

API 管理页面里添加 solar-service-a, 并精确匹配串 /inspector/inspect

网关流控规则

在流控规则界面里配置相关的规则

最终在 Skywalking 全链路界面上输出如下(跟 Solar 服务侧 Sentinel 埋点相似,不一一阐述了):



Sentinel 集群限流实践

我们采用 Sentinel 官方提供的嵌入式 Token Server 解决方案,即服务集群中选择一个节点做为 Token Server ,同时该节点也作为 Token Client 响应外部的请求的服务器。具体实现方式通过 Sentinel 实现预留的 SPI InitFunc 接口,可以参考官方 sentinel-demo 模块下面的 sentinel-demo-cluster-embedded 。

public class SentinelApolloTokenClusterInitFunc implements InitFunc {@Overridepublic void init() throws Exception {// Register client dynamic rule data source.initDynamicFlowRuleProperty();initDynamicParamRuleProperty();// Register token client related data source.// Token client common config:ClusterClientConfigInitializer.doInit();// Token client assign config (e.g. target token server) retrieved from assign map:ClusterClientAssignConfigInitializer.doInit();// Register token server related data source.// Register dynamic rule data source supplier for token server:ClusterRuleSupplierInitializer.doInit();// Token server transport config extracted from assign map:ServerTransportConfigInitializer.doInit();// Init cluster state property for extracting mode from cluster map data source.ClusterStateInitializer.doInit();// ServerFlowConfig 配置ServerFlowConfigInitializer.doInit();}
}

把 SPI 的扩展切入类放置 \resources\META-INF\services\com.alibaba.csp.sentinel.init.InitFunc 文件中,内容为

com.zhangmen.solar.sentinel.SentinelApolloTokenClusterInitFunc

作者介绍

任浩军,掌门基础架构部研发经理。曾就职于平安银行、万达、惠普,曾负责平安银行平台架构部 PaaS 平台基础服务框架研发。10 多年开源经历,Github ID:@HaojunRen,Nepxion 开源社区创始人,Nacos Group Member,Spring Cloud Alibaba & Nacos & Sentinel & OpenTracing Committer。

张彬彬,掌门基础架构部架构师。主要负责公司微服务架构以及开源项目的开发和实践,开源项目爱好者,多年互联网开发经验。

非常感谢阿里巴巴 Sentinel 项目负责人宿何在落地过程中的支持和帮助。

监控 Java 中间件 API Nacos 开发工具 时序数据库 Sentinel 微服务 Spring

版权声明:本文首发在云栖社区,遵循云栖社区版权声明:本文内容由互联网用户自发贡献,版权归用户作者所有,云栖社区不为本文内容承担相关法律责任。云栖社区已升级为阿里云开发者社区。如果您发现本文中有涉嫌抄袭的内容,欢迎发送邮件至:developer2020@service.aliyun.com 进行举报,并提供相关证据,一经查实,阿里云开发者社区将协助删除涉嫌侵权内容。

原文链接

本文为云栖社区原创内容,未经允许不得转载。

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

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

相关文章

腾讯云~Docker安装Redis6.2.6

文章目录1. 测试2. 安装策略组3. 远程连接1. 测试 无密码 docker run \ -d \ --name redis-my \ -v /app/redis/data:/data \ -p 16379:6379 \ redis:6.2.6有密码&#xff08;推荐&#xff09; docker run \ -d \ --name redis-my \ -v /app/redis/data:/data \ -p 16379:63…

中台,很多人理解的都不对

简介&#xff1a; 最近中台比较热&#xff0c;但业界内并没有对中台有统一认可清晰的定义&#xff0c;很多人会把中台与数据仓库、数据湖混为一谈。但需要提醒读者注意的是&#xff0c;数据中台并不是一个系统&#xff0c;它首先是一种组织架构。前言&#xff1a; -更多关于数智…

被“钱”困住的开源开发者们!

「Given enough eyeballs&#xff0c;all bugs are shallow.」&#xff08;只要有足够多的眼睛&#xff0c;就可以让所有 Bug 浮现&#xff09;1997 年&#xff0c;随着《大教堂与集市》的到来&#xff0c;开源新时代的号角正式吹响&#xff0c;也将 Linus 法则深深地烙印在开源…

盘点技术史:流量运营(PC 时代)

简介&#xff1a; 流量分析只比互联网诞生晚几年&#xff0c;作为一个生态&#xff0c;互联网需要有人提供服务&#xff0c;同时也需要有人消费服务&#xff0c;而在互联网上经营服务跟在线下经营五金店一样&#xff0c;如果想要成功&#xff0c;需要时刻关注来访问的顾客的情况…

腾讯云~Docker安装Mariadb

文章目录1. 拉取mariadb镜像2. 创建数据目录3. 创建容器4. 安全策略组5. 远程连接1. 拉取mariadb镜像 docker pull mariadb2. 创建数据目录 创建mariadb容器跟物理机上目录的映射路径&#xff0c;根据实际情况自定义即可 mkdir -p /app/mariadb/data3. 创建容器 docker run…

IDEA打包war包并发布到服务器上

IDEA打包war包并发布到服务器上 1、首先修改pom.xml 1、添加打包方式&#xff0c;打包成war包&#xff0c;否则打包成jar包 <packaging>war</packaging>2、在spring-boot-starter-web添加tomcat <dependency><groupId>org.springframework.boot<…

阿里云荣获可信云容器安全能力先进级认证, ACK/ACR为企业级安全护航

阿里云关注企业级用户的Kubernetes生产落地痛点&#xff0c;结合企业生产环境的大量实践&#xff0c;全面帮助企业真正落地云原生架构。安全侧问题&#xff0c;是众多大中型或金融领域企业的核心关注点。 端到端云原生安全架构 早在2018年&#xff0c;阿里云容器服务团队率先…

Kubernetes 稳定性保障手册(极简版)

作者 | 悟鹏来源 | 阿里巴巴云原生头图 | 下载于视觉中国Kubernetes 在生产环境中的采用率越来越高&#xff0c;复杂度越来越高&#xff0c;由此带来的稳定性保障的挑战越来越大。对于基于 Kubernetes 的云产品&#xff0c;稳定性保障已成为基本诉求&#xff0c;稳定性缺陷会给…

腾讯云~Docker安装Nginx

文章目录一、入门试炼1. 创建挂载目录2. 赋予目录权限3. 临时容器4. 拷贝文件5. 删除临时容器6. 自定义配置启动7. 安全策略组二、 企业内部使用2.1. nacos 集群2.2. 多个域名公用80端口是实现反向代理和负载均衡一、入门试炼 1. 创建挂载目录 mkdir /app/nginx/conf/ /app/n…

开放、普惠、高性能-SLS时序存储助力打造企业级全方位监控方案

无所不在的时序数据 时间带走一切&#xff0c;长年累月会把你的名字、外貌、性格、命运都改变。 ---柏拉图 随着时间的推移&#xff0c;万事万物都在不停的变化&#xff0c;而我们也会用各种数字去衡量这些变化信息&#xff0c;比如年龄、重量、速度、温度、金钱...在数字化时代…

一招上手!这样设计扛住亿级流量活动系统

作者 | 刘艳杰责编 | 伍杏玲出品 | CSDN云计算&#xff08;CSDNcloud)在企业里&#xff0c;做活动是一种十分常见的需求&#xff0c;有面向C端用户开展的活动&#xff0c;也有面向公司内部员工的活动。随着互联网技术的不断发展和疫情等方面的原因&#xff0c;线上开展的活动也…

域名解析到服务器

这本章主要讲购买的域名和服务器怎样进行域名解析。简单来说就是别人访问你的网站只需要在浏览器上输入你购买的域名&#xff0c;就可以访问你搭建的网站。 文章目录1. 解析2. 设置记录类型3. 绑定公网ip1. 解析 2. 设置记录类型 选择A类型 3. 绑定公网ip 效果图 备案 http…

基因行业容器存储解决方案

1、基因行业背景 1.1. 中国基因市场分析 基因组学是未来精准医疗的“基石”。70%的基因企业选择使用云计算来处理基因组相关业务。过去一年内&#xff0c;阿里云为基因医学影像数据提供了安全可靠的存储&#xff0c;500%数据增长&#xff0c;增长迅猛。强大无限制的计算能力&…

位置转经纬度~此key未开启WebserviceAPI服务功能

文章目录1. 现象2. 腾讯位置服务3. 登录开发者平台4. 开启WebserviceAPI5. 测试地址转经纬度api1. 现象 此key未开启WebserviceAPI服务功能&#xff0c;您可登陆https://lbs.qq.com/进行控制台key管理页面&#xff0c;找到此key并设置启用webserviceAPI 2. 腾讯位置服务 ht…

程序员今年在相亲市场上这么受欢迎?

前段时间有一个很火的网络段子&#xff1a;杭州的工程师在某婚恋交友网注册了一个账号&#xff0c;经过一个多月的“无人无津”后&#xff0c;他在公司职位栏加上了“JAVA开发工程师”短短几天就收到238封交友私信&#xff0c;最后还因为私信太多回复不了&#xff0c;他选择暂时…

云原生时代,谁是容器的最终归宿?

前言 “云原生技术有利于各组织在公有云、私有云和混合云等新型动态环境中&#xff0c;构建和运行可弹性扩展的应用。云原生的代表技术包括容器、服务网格、微服务、不可变基础设施和声明式 API。这些技术能够构建容错性好、易于管理和便于观察的松耦合系统。结合可靠的自动化…

腾讯云~安装MongoDB

文章目录一、 安装建议二、 安装步骤2.1. 下载2.2. 解压缩2.3. 重命名2.4. 配置环境变量2.5. 使其生效2.6. 配置MongoDB2.7. 启动MongoDB2.8. 网络安全组一、 安装建议 MongoDB 可以在mac/win/linux上安装&#xff0c;我个人建议在linux上安装会更好&#xff0c;这样测试起来更…

全部满分!阿里云函数计算通过可信云21项测试

今日&#xff0c;“2020 可信云线上峰会”正式召开。会上&#xff0c;中国信通院公布了混合云安全、云组网、函数即服务、消息队列、云计算安全运营中心等首次评估结果。阿里云函数计算通过了基础能力要求、平台可观测能力、服务性能、服务安全和服务计量准确性等 21 项测试&am…

Springboot集成Elasticsearch

在springboot中集成Elasticsearch&#xff0c;以我开发的博客系统项目为例,这是一篇文章内容。 准备 本次Elasticsearch使用的是单机版&#xff0c;版本为6.7.8&#xff0c;并且装好中文分词器。 Springboot集成Elasticsearch 首先在pom文件中引入Elasticsearch依赖。 <!-…

CentOS 替代品

我推荐替代产品&#xff1a;AlmaLinux 免费&#xff0c;和centos相似度都比较高&#xff0c;社区活跃度高 官网&#xff1a;https://almalinux.org/