001/** 002 * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). 003 * <p> 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * <p> 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * <p> 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package io.jboot.support.sentinel; 017 018import com.alibaba.csp.sentinel.datasource.FileWritableDataSource; 019import com.alibaba.csp.sentinel.datasource.ReadableDataSource; 020import com.alibaba.csp.sentinel.datasource.WritableDataSource; 021import com.alibaba.csp.sentinel.slots.block.flow.FlowRule; 022import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager; 023import com.alibaba.csp.sentinel.transport.util.WritableDataSourceRegistry; 024import com.alibaba.fastjson.JSON; 025import com.alibaba.fastjson.TypeReference; 026import com.jfinal.kit.PathKit; 027import io.jboot.core.spi.JbootSpiLoader; 028import io.jboot.support.sentinel.datasource.*; 029import io.jboot.utils.StrUtil; 030 031import java.io.File; 032import java.util.List; 033 034public class JbootSentinelBuilder { 035 036 public void init() { 037 SentinelConfig config = SentinelConfig.get(); 038 039 // 初始化 sentinel 数据源, 040 // 当配置数据源的时候,sentinel 控制面板的配置将会更新的时候,无法写入到数据源的,需要去实现主动写入,这是 Sentinel 的一个坑 041 // todo 晚点实现主动 Sentinel 控制台写入到数据源 042 if (StrUtil.isNotBlank(config.getDatasource())) { 043 SentinelDatasourceFactory factory = getDatasourceFactory(config); 044 ReadableDataSource rds = factory.createDataSource(); 045 FlowRuleManager.register2Property(rds.getProperty()); 046 } 047 048 // 当未配置数据源的情况下,使用文件数据源 049 // 将可写数据源注册至 transport 模块的 WritableDataSourceRegistry 中. 050 // 这样收到控制台推送的规则时,Sentinel 会先更新到内存,然后将规则写入到文件中. 051 // 文档:https://github.com/alibaba/Sentinel/wiki/%E5%9C%A8%E7%94%9F%E4%BA%A7%E7%8E%AF%E5%A2%83%E4%B8%AD%E4%BD%BF%E7%94%A8-Sentinel 052 else { 053 054 String rulePath = config.getRuleFile(); 055 File ruleFile = rulePath.startsWith("/") ? new File(rulePath) : new File(PathKit.getWebRootPath(), rulePath); 056 057 ReadableDataSource rds = new FileDataSource<>(ruleFile, this::decodeJson); 058 FlowRuleManager.register2Property(rds.getProperty()); 059 060 WritableDataSource<List<FlowRule>> wds = new FileWritableDataSource<>(ruleFile, this::encodeJson); 061 WritableDataSourceRegistry.registerFlowDataSource(wds); 062 } 063 064 } 065 066 private <T> String encodeJson(T t) { 067 return JSON.toJSONString(t); 068 } 069 070 private List<FlowRule> decodeJson(String source) { 071 return JSON.parseObject(source, new TypeReference<List<FlowRule>>() { 072 }); 073 } 074 075 076 private SentinelDatasourceFactory getDatasourceFactory(SentinelConfig config) { 077 String datasource = config.getDatasource(); 078 switch (datasource) { 079 case SentinelConfig.DATASOURCE_APOLLO: 080 return new ApolloDatasourceFactory(); 081 case SentinelConfig.DATASOURCE_NACOS: 082 return new NacosDatasourceFactory(); 083 case SentinelConfig.DATASOURCE_ZOOKEEPER: 084 return new ZookeeperDatasourceFactory(); 085 case SentinelConfig.DATASOURCE_REDIS: 086 return new RedisDatasourceFactory(); 087 default: 088 SentinelDatasourceFactory dataSourceFactory = JbootSpiLoader.load(SentinelDatasourceFactory.class, datasource); 089 if (dataSourceFactory == null) { 090 throw new NullPointerException("Can not load SentinelDatasourceFactory spi for name: " + datasource); 091 } 092 return dataSourceFactory; 093 } 094 } 095 096 097}