简介:
前言
掌门 1 对 1 精耕在线教育畛域,近几年业务失去了疾速倒退,但同时也遭逢了“成长的懊恼”。随着微服务数量一直减少,流量进一步暴增,硬件资源有点不堪重负,那么,如何实现更好的限流熔断降级等流量防护措施,这个课题就摆在了掌门人的背后。因为 Spring Cloud
体系曾经演进到第二代,第一代的 Hystrix
限流熔断降级组件曾经不大适宜当初的业务逻辑和规模,同时它目前被 Spring Cloud
官网置于保护模式,将不再向前倒退。
如何抉择一个更好的限流熔断降级组件?通过对 Alibaba Sentinel
、Resilience4j
、Hystrix
等开源组件做了深刻的调研和比拟,最终选定 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/";
@Override
public 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> {
@Override
public 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;}
}
@Override
protected String getDataId() {return ApolloConfigUtil.getFlowDataId();
}
}
③ 实现 Sentinel Apollo 规定订阅
@Component("apolloFlowRulePublisher")
public class ApolloFlowRulePublisher extends BaseApolloRulePublisher<List<FlowRuleEntity>> {
@Override
public 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);
}
@Override
protected 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> {
@Override
public void onPass(Context context, ResourceWrapper resourceWrapper, DefaultNode param, int count, Object... args) throws Exception { }
@Override
public 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();
@Override
protected Span buildSpan() {return tracer.buildSpan(SentinelStrategyConstant.SPAN_NAME).startManual();}
@Override
protected void outputSpan(Span span, String key, String value) {span.setTag(key, value);
}
@Override
protected void finishSpan(Span span) {span.finish();
}
}
实现 Sentinel InitFunc SPI 扩大
实现 SPI
的扩大切入类
public class SentinelSkywalkingTracerInitFunc implements InitFunc {
@Override
public 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;
@Bean
public 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> {
@Autowired
private InfluxDB influxDB;
@Override
public void save(MetricEntity metric) {
try {Point point = createPoint(metric);
influxDB.write(point);
} catch (Exception e) {LOG.error(e.getMessage());
}
}
@Override
public 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 {
@Override
public 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 {
@Override
public 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 {
@Override
public 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 {
@Override
public 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
我的项目负责人宿何在落地过程中的反对和帮忙。