乐趣区

关于互联网:掌门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/";    

    @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 InitFuncSPI 机制实现埋点输入:

SentinelProcessorSlot 作为 SPI 接口进行扩大(1.7.2 版本以前 SlotChainBuilder 作为 SPI),使得 Slot Chain 具备了扩大的能力。您能够自行退出自定义的 slot 并编排 slot 间的程序,从而能够给 Sentinel 增加自定义的性能。

摘自官网 Sentinel 工作主流程

形象 Sentinel ProcessorSlot 埋点输入

SentinelProcessorSlotEntryCallback 提供 onPassonBlocked 两个办法,毕竟限流熔断降级并不是惯例的性能,不会产生在大流量下面,所以 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 如果有多个,能够通过 , 分隔。特地留神,下文为了形容简略,只以单个为例
  • strategy0 示意白名单,符合条件就放行流量;strategy1 示意黑名单,符合条件就限度流量。特地留神,下文为了形容简略,只以白名单为例

根底指标的熔断

通过 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 服务只承受 userNamezhangsan 的申请,那么咱们能够通过如下形式来解决:

  • 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 服务减少受权规定,limitAppzhangsan
[
    {
        "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.0userNamezhangsan,同时满足这两个条件下,所有服务的申请容许被放行,否则被熔断。

  • 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 我的项目负责人宿何在落地过程中的反对和帮忙。

退出移动版