入门篇-sentinel初始化之InitFunc(一)

前言

本文会从简单的demo使用,来跟踪代码,本文的内容主要介绍一个demo, 然后跟踪一下这个demo运行之前,sentinel做了哪些准备工作。

注意: 笔者使用的sentinel版本都是基于官方1.8.0版本

demo

引入pom文件

 <dependency>
     <groupId>com.alibaba.csp</groupId>
     <artifactId>sentinel-core</artifactId>
     <version>1.8.0</version>
</dependency>

test类

package com.test.xx;

import java.util.ArrayList;
import java.util.List;

import com.alibaba.csp.sentinel.Entry;
import com.alibaba.csp.sentinel.SphU;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.alibaba.csp.sentinel.slots.block.RuleConstant;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRule;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager;

/**
 * @author yunhe.zhang
 * @version Test.java, v 0.1 2021-03-19 10:00 yunhe.zhang Exp $$
 */
public class Test {
    public static void main(String[] args) {
        // 配置规则.
        initFlowRules();

        while (true) {
            // 1.5.0 版本开始可以直接利用 try-with-resources 特性
            try (Entry entry = SphU.entry("HelloWorld")) {
                // 被保护的逻辑
                System.out.println("hello world");
            } catch (BlockException ex) {
                // 处理被流控的逻辑 , 如果发生流控,那么就会走到这里
                System.out.println("blocked!");
            }
        }
    }
    private static void initFlowRules(){
        List<FlowRule> rules = new ArrayList<>();
        FlowRule rule = new FlowRule();
        rule.setResource("HelloWorld");
        rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
        // Set limit QPS to 20.
        rule.setCount(20);
        rules.add(rule);
        FlowRuleManager.loadRules(rules);
    }
}

引入上面的这个pom, 然后写一段代码,这个demo就可以运行了,其实已经可以看到一些效果了。

这篇文章的重点不在于讲解sentinel怎么工作的,而是讲一下,他在工作之前做了哪些初始化工作。

Env

上面的代码中,在while循环里面执行的是这个方法Entry entry = SphU.entry("HelloWorld") , 我们就从这个为入口,

public static Entry entry(String name, EntryType trafficType, int batchCount, Object... args)
        throws BlockException {
        return Env.sph.entry(name, trafficType, batchCount, args);
}

Env.sph 这个对象很重要,限流的所有核心逻辑都在这个里面, 下面我们看一下Env类com.alibaba.csp.sentinel.Env

public class Env {

    public static final Sph sph = new CtSph();

    static {
        // If init fails, the process will exit. (意思是如果初始化失败,那么进程会退出)
        InitExecutor.doInit();
    }

}

代码非常的简单,sph的初始化了一个实例 new CtSph() ,

我们看一下InitExecutor.doInit() 这个里面做了哪些初始化工作

/**
 * 根据SPI机制加载已经注册的功能模块,并按照注册的顺序执行
 */
public final class InitExecutor {

    private static AtomicBoolean initialized = new AtomicBoolean(false);

    /**
     * If one {@link InitFunc} throws an exception, the init process
     * will immediately be interrupted and the application will exit.
     *
     * The initialization will be executed only once.
     */
    public static void doInit() {
        // 使用cas机制,保证初始化动作仅执行一次
        if (!initialized.compareAndSet(false, true)) {
            return;
        }
        try {
            // 通过SPI机制,获取META-INF文件夹下面,services文件夹下面的`com.alibaba.csp.sentinel.init.InitFunc`
            ServiceLoader<InitFunc> loader = ServiceLoaderUtil.getServiceLoader(InitFunc.class);
            // 循环获取到的功能模块,进行初始化,同时进行排序
            List<OrderWrapper> initList = new ArrayList<OrderWrapper>();
            for (InitFunc initFunc : loader) {
                RecordLog.info("[InitExecutor] Found init func: " + initFunc.getClass().getCanonicalName());
                // 进行排序,插入到一个list里面去保存起来
                insertSorted(initList, initFunc);
            }
            for (OrderWrapper w : initList) {
                // 执行初始化动作,各个实现类,各做各的
                w.func.init();
                RecordLog.info(String.format("[InitExecutor] Executing %s with order %d",
                    w.func.getClass().getCanonicalName(), w.order));
            }
        } catch (Exception ex) {
            RecordLog.warn("[InitExecutor] WARN: Initialization failed", ex);
            ex.printStackTrace();
        } catch (Error error) {
            RecordLog.warn("[InitExecutor] ERROR: Initialization failed with fatal error", error);
            error.printStackTrace();
        }
    }

    private static void insertSorted(List<OrderWrapper> list, InitFunc func) {
        // 获取排序值
        int order = resolveOrder(func);
        int idx = 0;
        // 这里进行比较排序值,从小到大依次排列
        for (; idx < list.size(); idx++) {
            if (list.get(idx).getOrder() > order) {
                break;
            }
        }
        list.add(idx, new OrderWrapper(order, func));
    }

    private static int resolveOrder(InitFunc func) {
        // 类中是否存在排序注解,不存在则取一个默认值 Integer.MAX_VALUE
        if (!func.getClass().isAnnotationPresent(InitOrder.class)) {
            return InitOrder.LOWEST_PRECEDENCE;
        } else {
            // 存在就取自己设置的
            return func.getClass().getAnnotation(InitOrder.class).value();
        }
    }

    private InitExecutor() {}

    // 排序对象
    private static class OrderWrapper {
        private final int order;
        private final InitFunc func;

        OrderWrapper(int order, InitFunc func) {
            this.order = order;
            this.func = func;
        }

        int getOrder() {
            return order;
        }

        InitFunc getFunc() {
            return func;
        }
    }
}

步骤大致说明:

  1. 通过SPI机制,获取META-INF文件夹下面,services文件夹下面的com.alibaba.csp.sentinel.init.InitFunc , 目前在源码里面找到的,com.alibaba.csp.sentinel.init.InitFunc文件里, 默认仅提供了一个类com.alibaba.csp.sentinel.metric.extension.MetricCallbackInit , 这个是限流成功,失败的时候做数据统计的。 通过多少,拒绝多少,都有统计。
  2. 对获取到的功能模块进行排序处理,从小到大的一次排列好。

sentinel-core里面只有一个类初始化,但是如果引入了其他模块的话,就会有很多了,下面列举一下


引入sentinel-transport , 开启客户端和控制台的连接

  1. com.alibaba.csp.sentinel.transport.init.CommandCenterInitFunc

    初始化所有客户端上报控制台的命令连接,控制台上收集到的信息,都是通过这个来进行触发的

  2. com.alibaba.csp.sentinel.transport.init.HeartbeatSenderInitFunc
    设置心跳线程,对dashboard控制台发起心跳,如果不设置的话,是5秒一次


引入sentinel-cluster , 使用sentinel的集权限流

  1. com.alibaba.csp.sentinel.cluster.client.init.DefaultClusterClientInitFunc

​ 用与初始化集群限流客户端的所需资源;

  1. com.alibaba.csp.sentinel.cluster.server.init.DefaultClusterServerInitFunc

    用与初始化集群限流服务端的所需资源;


引入sentinel-extension > sentinel-parameter-flow-control

  1. com.alibaba.csp.sentinel.init.ParamFlowStatisticSlotCallbackInit

    用于初始化参数流控回调.

其实看了这么多源码,很多开源的项目都喜欢用SPI机制,这是一个让人很爽的机制,有兴趣的朋友可以研究研究。

sharedCode源码交流群,欢迎喜欢阅读源码的朋友加群,添加下面的微信, 备注”加群“ 。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值