admin 管理员组

文章数量: 887021

入门篇

前言

本文会从简单的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 (!initializedpareAndSet(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_VALUEif (!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源码交流群,欢迎喜欢阅读源码的朋友加群,添加下面的微信, 备注”加群“ 。

本文标签: 入门篇