2.3.1 Context
Context即调用链上下文,贯穿整条调用链。在响应式编程项目中,Context隐藏在订阅过程中传递,而在非响应式编程项目中,Context可通过ThreadLocal在调用链上传递。
我们经常会在一些框架中见到Context的身影,Context可用于减少参数的深层传递,减少方法的参数个数,具体做法是将一些调用链上被多个方法使用的参数提取到Context中,使调用链上方法的执行强依赖Context,即Context作为这些方法执行所依赖的环境。
Context还可用于弥补前期框架设计上的不足。试想一下,如果调用链上的某个方法需要增加一个参数,那么可能从调用链入口方法开始,每个方法都会增加一个参数,并且由入口方法将参数层层向下传递,尽管参数途经的很多方法都不需要用到它,但是都必须接收它并将它向下传递。只要增加一个参数,就需要改动很多代码,这显然是不合理的,而使用Context就可以绕过层层传递。在调用链入口处将新增参数写入Context,使调用链上的任何方法都可以从Context中获取该参数。
在Sentinel中,Context维持着入口节点(entranceNode)、调用链上当前资源的Entry实例(curEntry)及调用来源(origin)等信息。
Context类定义了如下字段。
• name:调用链的入口名称。
• entranceNode:调用链的入口节点,类型为EntranceNode。
• curEntry:调用链上当前资源的Entry实例,类型为CtEntry。
• origin:调用来源,即服务消费者的名称或服务消费者的IP,由服务消费者传递过来。
提示:如果服务提供者和服务消费者都是WebMvc应用,且都使用Sentinel的WebMvc适配模块,那么Sentinel会尝试从请求头中获取S-user参数的值作为调用来源的名称,只要服务消费者在请求头传递了这个参数,服务提供者就能够获取。
调用链上Context实例的创建时机为,调用链上ContextUtil类的enter方法被首次调用时。
假设服务B提供一个查询天气预报的接口给服务A调用,且服务B实现天气预报查询的接口需要调用第三方服务C的接口实现,同时服务B是一个WebMvc应用,在调用服务C接口时使用OpenFeign框架,那么服务B既使用了Sentinel的WebMvc适配模块,也使用了Sentinel的OpenFeign适配模块。
在此例中,当服务B收到服务A的请求时,会创建一个入口名称为sentinel_spring_web_ context的Context实例,当服务B在向服务C发起接口调用时,由于调用链上已经存在一个Context实例,因此不会创建新的Context实例。