Sentinel源码解析之初始化过程

原创不易,转载请注明出处


前言

我们这篇文章要介绍下sentinel初始化的这么一个过程,在上一篇《Sentinel源码解析之一次请求走进Sentinel》我们也介绍过,你项目启动sentinel是不会初始化的,只有你发起请求经过sentinel(就是需要sentinel进行流控的时候,才会触发它的初始化工作),本文准备以sentinel与servlet适配的那个CommonFilter 为切入点,看看sentinel初始化都干些什么事情,当然你也可以使用任何是熟悉的sentinel与其他项目适配子项目里面的类为切入点,比如说以sentinel与dubbo 适配项目的SentinelDubboConsumerFilter。

1.关于spi技术

对于spi这个技术,这里不做基础介绍, 基础详细的介绍可以看下《java SPI机制详解》这篇文章。
这里需要重点说下的是,spi 扩展技术,几乎在所有的java开源项目中都会用到,可能实现不太一样,但是思想都是一样的,一个开源框架要想有良好的扩展性,除了好的设计模式使用,就是spi技术的使用了,你可以定义一套接口,实现几个默认的实现,然后如果其他人想自己加功能可以基于接口做自己的实现,使用spi配置一下就可以了。举个例子,我个人感觉dubbo spi 技术是dubbo框架实现微内核的核心,是dubbo的基石,你只有理解了dubbo spi 的一些功能原理后,你才能真正把握整个dubbo 项目的设计思路,才能深入理解dubbo运行流程。
在sentinel中也是使用了spi 技术来扩展的,sentinel 在java spi技术基础上加了排序的功能。

2.初始化过程介绍

sentinel初始化是在Env这个类中的静态代码块中做的,然后它会通过spi技术加载InitFunc 实现类,然后对这些实现类进行排序,最后调用这些实现类的init方法进行初始化,如果是按照《Sentinel源码解析之一次请求走进Sentinel》这篇文章介绍那个案例的配置,就会有两个实现类被加载,毕竟我们就引入了2个依赖,一个是servlet的适配,一个是simpleHttp通信的包,这里我们只介绍最基础的,其他的初始化组件其实也是一个道理,只不过实现的功能不大一样

  1. CommandCenterInitFunc : 这个其实就是与sentinel dashboard 进行通信的组件,咱们使用 sentinel-transport-simple-http 的话,就是创建serverSocket,处理dashboard 发送过来的请求。
  2. HeartbeatSenderInitFunc : 这个从名字上就能看出来,就是个发送心跳的组件,就是向sentinel 注册然后发送心跳的
    接下来我们就从源码角度看下初始化

3.源码解析

3.1 入口

我们还是以《Sentinel源码解析之一次请求走进Sentinel》这个例子,在这篇文章中最后我们介绍了ContextUtil.enter(target, origin);这行,其实就是创建context,然后为这个资源创建node。
接着我们看下entry = SphU.entry(target, EntryType.IN);,这行代码
在这里插入图片描述
重点就在这个Env上面,进入Env看看
在这里插入图片描述
有个静态代码块,里面就是初始化入口。进入InitExecutor.doInit(); 看看
在这里插入图片描述
主要是三部分,首先是使用spi技术加载InitFunc 接口的实现类们,获取到实现类们,根据实现类上面的@InitOrder注解来排序,@InitOrder上面的数越大越靠后,也就是靠后初始化,可以看下是怎样排的
在这里插入图片描述
其实就是@InitOrder上面数越大的越靠后,没有这个@InitOrder注解的,直接就是int最大值,也就是最后面
回到这个doInit 方法来,最后一部分就是循环初始化了。
CommandCenterInitFunc 与HeartbeatSenderInitFunc 的@InitOrder 都是-1 ,但是从spi中拿出来的时候CommandCenterInitFunc 是在前面,然后排完序还是在前面,其实它俩谁在前面都没关系,他们没有前后依赖关系

3.2 CommandCenterInitFunc 初始化

在这里插入图片描述
我这里也把CommandCenterInitFunc 的初始化分成了3步,首先是获取CommandCenter实现类,然后进行启动之前的准备工作,最后就是启动

3.2.1 获取CommandCenter 实现类

在这里插入图片描述
先是调用getCommandCenter ,在调用之前进行了静态代码块的执行,走了resolveInstance 方法,在这个方法通过这个SpiLoader 加载了CommandCenter 的实现类对象
在这里插入图片描述
这个方法其实就是使用spi技术加载CommandCenter 的实现类对象,因为我们这里
在这里插入图片描述
我们这里没有引用netty的那个传输依赖,而是使用的sentinel-transport-simple-http 这个依赖,所以会返回SimpleHttpCommandCenter对象

3.2.2 beforeStart

接着就要看下SimpleHttpCommandCenter 这个对象的初始化之前的准备工作了,看看都干些啥

在这里插入图片描述
这里先是加载 handler ,这个handler是什么呢?其实就是传输过来的命令处理器,比如说数据传输的时候使用用code区分什么操作,然后服务端收到这个codo之后执行对应的handler,其实就是这个意思
看下加载namedHandlers方法的执行
在这里插入图片描述
在这里插入图片描述
其实还是使用spi技术来加载CommandHandler 的实现类们,然后遍历这些实现类,parseCommandName 就是将实现类上面的@CommandMapping注解name取出来 当作map的key。
再来看下注册handler的方法
在这里插入图片描述
这里就是遍历,然后调用了registerCommand方法
在这里插入图片描述
其实就是塞到SimpleHttpCommandCenter 的类成员这个map中了,到时候来请求的时候,就会从这个map中取到对应的handler来执行对应的业务逻辑。

3.2.3 start

在这里插入图片描述
这个start方法虽然长,但是很好理解,我发现国人写的代码还是很好理解的,简洁明了,外国人写的代码就比较绕,
第一步就是创建一个cpu核心数大小的线程,接着创建一个serverInitTask,可以看到这个task里面有个获取serverSocket的代码,然后将serverSocket 放入ServerThread中放入线程池, 创建一个线程,将这个serverInitTask扔进去执行。
先看看这个ServerSocket 的代码
在这里插入图片描述
就是创建了一个普通的serverSocket,端口的话先去你这个系统参数中获取csp.sentinel.api.port,没有的话使用默认的8719
接着就是创建一个ServerThread 的任务扔到线程池中运行,我们看下ServerThread 任务都干啥
在这里插入图片描述
这里就是监听连接,接受请求,封装请求,然后交给线程处理,到这我们SimpleHttpCommandCenter 的start就完事了

3.3 HeartbeatSenderInitFunc 初始化

在这里插入图片描述
首先是获取心跳发送器,然后初始化任务调度器使用的东西,最后就是启动定时发送心跳的任务了。我们先来看下获取心跳发送器的流程
在这里插入图片描述
跟上一个获取CommandCenter 实现类差不多,也是使用spi来获取HeartbeatSender 的实现类,这里是SimpleHttpHeartbeatSender这个实现类,但是在初始化的时候,它构造里面有段逻辑
在这里插入图片描述
这个其实就是获取配置的sentinel dashboard的地址,就是csp.sentinel.dashboard.server这个参数配置的,这里就不详细看了。
接着就是initSchedulerIfNeeded 初始化 调度器需要的东西了
在这里插入图片描述
这里其实就是创建了2个线程的调度线程池
接着就是获取一个心跳间隔,它会从这个参数中获取 csp.sentinel.heartbeat.interval.ms,没有的话就去发送器里面获取,默认是10s,然后将心跳间隔设置到系统参数中去。
最后就是调用scheduleHeartbeatTask 初始化任务调度了
在这里插入图片描述
其实就是创建发送心跳的定时任务。好了,到这我们的HeartbeatSenderInitFunc 初始化就完成了。

总结

本文先是介绍了一下spi技术,然后又介绍了下sentinel初始化过程,介绍了sentinel初始化都干些啥, 最后以sentinel与servlet适配项目的CommonFilter 为切入点,通过源码深入剖析了sentinel两个基础组件初始化过程。

已标记关键词 清除标记
©️2020 CSDN 皮肤主题: 编程工作室 设计师:CSDN官方博客 返回首页