前言git
掌門1對1精耕在線教育領域,近幾年業務獲得了快速發展,但同時也遭遇了「成長的煩惱」。隨着微服務數量不斷增長,流量進一步暴增,硬件資源有點不堪重負,那麼,如何實現更好的限流熔斷降級等流量防禦措施,這個課題就擺在了掌門人的面前。因爲 Spring Cloud 體系已經演進到第二代,第一代的 Hystrix 限流熔斷降級組件已經不大適合如今的業務邏輯和規模,同時它目前被 Spring Cloud 官方置於維護模式,將再也不向前發展。github
如何選擇一個更好的限流熔斷降級組件?通過對 Alibaba Sentinel、Resilience4j、Hystrix 等開源組件作了深刻的調研和比較,最終選定 Alibaba Sentinel 作微服務體系 Solar 中的限流熔斷降級必選組件。spring
Sentinel 簡介安全
阿里巴巴中間件部門開發的新一代以流量爲切入點,從流量控制、熔斷降級、系統負載保護等多個維度保護服務的穩定性的分佈式系統的流量防衛兵。它承接了阿里巴巴近10年的雙十一大促流量的核心場景,例如秒殺(即突發流量控制在系統容量能夠承受的範圍)、消息削峯填谷、集羣流量控制、實時熔斷下游不可用應用等。
它具備很是豐富的開源生態:服務器
它和 Hystrix 相比,有以下差別:架構
摘自官網 Sentinel Roadmapapp
關於 Sentinel 如何使用,它的技術實現原理怎樣等,官方文檔或者民間博客、公衆號文章等能夠提供很是詳盡且有價值的材料,這些不在本文的討論範圍內,就不一一贅述。筆者嘗試結合掌門1對1現有的技術棧以及中間件一體化的戰略,並着眼於強大的 Spring Cloud Alibaba 技術生態圈展開闡釋。負載均衡
Sentinel 深度集成 Apollo框架
Sentinel 官方在 sentinel-datasource-apollo 模塊中已經對 Apollo 作了一些擴展,主要實現了 Sentinel 規則的讀取和訂閱邏輯。這些並不夠,咱們須要對 Apollo 進行更深層次的集成。分佈式
摘自官網 在生產環境中使用 Sentinel
Solar SDK 環境初始化
定製 EnvironmentPostProcessor 類,實現以下:
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 指標參數,主要包括:
接下去是 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 的三個核心方法:
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
對於 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 的熔斷擴展。對於受權規則中涉及到的參數,簡要作以下說明:
基礎指標的熔斷
經過 Http Header 自動攜帶下游服務的基礎指標進行全鏈路傳遞的方式,對下游調用實施基礎指標的熔斷。支持以下指標:
① 服務名
當 A 服務發送請求到 B 服務,所攜帶的 A 服務名不知足條件,該請求就會被 B 服務熔斷。
spring.application.strategy.service.sentinel.request.origin.key=n-d-service-id
[
{ "resource": "sentinel-resource", "limitApp": "a-service-id", "strategy": 0 }
]
② 服務的 APPID
當 A 服務發送請求到 B 服務,所攜帶的 A 服務的 APPID 不知足條件,該請求就會被 B 服務熔斷。
spring.application.strategy.service.sentinel.request.origin.key=n-d-service-app-id
[
{ "resource": "sentinel-resource", "limitApp": "a-service-app-id", "strategy": 0 }
]
③ 服務實例所在的 IP 地址和 Port 端口
當 A 服務發送請求到 B 服務,所攜帶的 A 服務的 IP 地址和 Port 端口不知足條件,該請求就會被 B 服務熔斷。
spring.application.strategy.service.sentinel.request.origin.key=n-d-service-address
[
{ "resource": "sentinel-resource", "limitApp": "a-ip:a-port", "strategy": 0 }
]
灰度藍綠髮布指標的熔斷
經過 Http Header 自動攜帶下游服務的灰度藍綠髮布指標進行全鏈路傳遞的方式,對下游調用實施灰度藍綠髮布指標的熔斷。支持以下指標:
① 服務所在的組名
當 A 服務發送請求到 B 服務,所攜帶的 A 服務的組名和 B 服務的組名不一致,該請求就會被 B 服務熔斷。
spring.application.strategy.service.sentinel.request.origin.key=n-d-service-group
[
{ "resource": "sentinel-resource", "limitApp": "b-group", "strategy": 0 }
]
② 服務版本號
當 A 服務發送請求到 B 服務,所攜帶的 A 服務的版本號和 B 服務的版本號不一致,該請求就會被 B 服務熔斷。
spring.application.strategy.service.sentinel.request.origin.key=n-d-service-version
[
{ "resource": "sentinel-resource", "limitApp": "b-version", "strategy": 0 }
]
③ 服務所在的區域
當 A 服務發送請求到 B 服務,所攜帶的 A 服務的區域值和 B 服務的區域值不一致,該請求就會被 B 服務熔斷。
spring.application.strategy.service.sentinel.request.origin.key=n-d-service-region
[
{ "resource": "sentinel-resource", "limitApp": "b-region", "strategy": 0 }
]
④ 服務所在的子環境
當 A 服務發送請求到 B 服務,所攜帶的 A 服務的子環境值和 B 服務的子環境值不一致,該請求就會被 B 服務熔斷。
spring.application.strategy.service.sentinel.request.origin.key=n-d-service-env
[
{ "resource": "sentinel-resource", "limitApp": "b-env", "strategy": 0 }
]
業務自定義指標的熔斷
經過 Http Header 攜帶下游服務的業務自定義指標進行全鏈路傳遞的方式,對下游調用實施自定義指標的熔斷。
當 A 服務發送請求到 B 服務,所攜帶的 A 的自定義指標不知足條件,該請求就會被 B 服務熔斷。例如: A 服務把 userName 經過 Http Header 傳遞給 B 服務,而 B 服務只接受 userName 爲 zhangsan 的請求,那麼咱們能夠經過以下方式來解決:
public class MyServiceSentinelRequestOriginAdapter extends AbstractServiceSentinelRequestOriginAdapter { @Override public String parseOrigin(HttpServletRequest request) { return request.getHeader("userName"); } }
@Bean public ServiceSentinelRequestOriginAdapter ServiceSentinelRequestOriginAdapter() { return new MyServiceSentinelRequestOriginAdapter(); }
[
{ "resource": "sentinel-resource", "limitApp": "zhangsan", "strategy": 0 }
]
假如該方式仍未能知足業務場景,業務系統但願根據 userName 獲取 userType,根據用戶類型作統一熔斷,例如,用戶類型爲 AUTH_USER 的請求才能放行,其它都熔斷,那麼咱們能夠把上面的例子修改以下:
public class MyServiceSentinelRequestOriginAdapter extends AbstractServiceSentinelRequestOriginAdapter { @Override public String parseOrigin(HttpServletRequest request) { String userName = request.getHeader("userName"); String userType = getUserTypeByName(userName); return userType; } }
[
{ "resource": "sentinel-resource", "limitApp": "AUTH\_USER", "strategy": 0 }
]
組合指標的熔斷
經過 Http Header 攜帶下游服務的業務自定義指標、基礎指標或者灰度藍綠髮布指標進行全鏈路傳遞的方式,對下游調用實施組合指標的熔斷,例如,根據傳入的微服務版本號 + 用戶名,組合在一塊兒進行熔斷。下面示例表示爲下游服務版本爲 1.0 且 userName 爲 zhangsan,同時知足這兩個條件下,全部服務的請求容許被放行,不然被熔斷。
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; } }
[
{ "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 項目負責人宿何在落地過程當中的支持和幫助。