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}