这里简单记录一下Sentinel在接收第一次请求来临是做的初始化准备工作.此工作是利用JDK的ServiceLoader(A simple service-provider loading facility)加载实现的.Sentinel的所有准备工作都是从加载InitFunc开始,然后InitFunc的实现类又使用ServiceLoader去加载其它需要加载资源.
第一次请求来临时,也就是调用entry();
方法的时候
/**
* Checking all {@link Rule}s about the resource.
*
* @param name the unique name of the protected resource
* @throws BlockException if the block criteria is met, eg. when any rule's threshold is exceeded.
*/
public static Entry entry(String name) throws BlockException {
return Env.sph.entry(name, EntryType.OUT, 1, OBJECTS0);
}
进入这个方法的时候就Env.class
中的静态代码块开始了初始化工作.
public class Env {
public static final NodeBuilder nodeBuilder = new DefaultNodeBuilder();
public static final Sph sph = new CtSph();
static {
// 如果初始化失败,进程将会退出
InitExecutor.doInit();
}
}
进入InitExecutor
阅读:
/**
* 加载已经注册的功能模块,并按照注册的顺序执行
*/
public final class InitExecutor {
private static AtomicBoolean initialized = new AtomicBoolean(false);
/**
*如果在加载某一个{@link InitFunc}出现异常,初始化进程将立即中断,应用将退出.
* 初始化动作只执行一次.
*/
public static void doInit() {
//判断是否是第一次初始化,不是则直接返回
if (!initialized.compareAndSet(false, true)) {
return;
}
try {
//此处去加载"META-INF/services/"目录下所配置的所有实现了InitFunc接口的类
ServiceLoader<InitFunc> loader = ServiceLoader.load(InitFunc.class);
List<OrderWrapper> initList = new ArrayList<OrderWrapper>();
for (InitFunc initFunc : loader) {
RecordLog.info("[InitExecutor] Found init func: " + initFunc.getClass().getCanonicalName());
//将加载完的所有实现类排序
insertSorted(initList, initFunc);
}
for (OrderWrapper w : initList) {
//挨个执行每个InitFunc实现类的init()方法,init()方法又会去加载其它所需资源
w.func.init();
RecordLog.info(String.format("[InitExecutor] Initialized: %s with order %d",
w.func.getClass().getCanonicalName(), w.order));
}
} catch (Exception ex) {
RecordLog.warn("[InitExecutor] Init failed", ex);
ex.printStackTrace();
} catch (Error error) {
RecordLog.warn("[InitExecutor] Init failed with fatal error", error);
error.printStackTrace();
throw error;
}
}
private static void insertSorted(List<OrderWrapper> list, InitFunc func) {
//获取func的执行顺序
int order = resolveOrder(func);
int idx = 0;
for (; idx < list.size(); idx++) {
if (list.get(idx).getOrder() > order) {
break;
}
}
//按照执行顺序的先后加到list中
list.add(idx, new OrderWrapper(order, func));
}
private static int resolveOrder(InitFunc func) {
//获取实现类的执行顺序,如果没有指定InitOrder则是最大执行顺序Integer.MAX_VALUE
if (!func.getClass().isAnnotationPresent(InitOrder.class)) {
return InitOrder.LOWEST_PRECEDENCE;
} else {
return func.getClass().getAnnotation(InitOrder.class).value();
}
}
private InitExecutor() {}
//将InitFunc 实现类和自己的执行顺序包装在OrderWrapper 中
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;
}
}
}
InitFunc.class
public interface InitFunc {
void init() throws Exception;
}
InitFunc的实现类大约有如下几个(其中也包括一些测试用例中的):
CommandCenterInitFunc
:用与初始化所有CommandCenter
;DefaultClusterClientInitFunc
:用与初始化集群限流客户端的所需资源;DefaultClusterServerInitFunc
:用与初始化集群限流服务端的所需资源;FileDataSourceInit
:用于初始化文件数据源,在实际生产中我们可以用MySQL或Nacos等作为数据源;HeartbeatSenderInitFunc
:用于初始化心跳发送者,此处有两个选择HttpHeartbeatSender
,SimpleHttpHeartbeatSender
;ParamFlowStatisticSlotCallbackInit
:用于初始化参数流控回调.
此处InitFunc就加载完了,至于init()方法去加载其它的资源我将会在其它文章中分享.