一、背景
小程序和第三方系统都要通过Http的方式调用苍穹的接口。所以我们使用苍穹提供的开放平台开发了【自定义服务】的Api接口,由于业务上需要判定是从第三方系统调用的,还是小程序调用的,所以我们在请求头部增加一个标识来区别(主要是想不要把这个标识放在请求body里面,把系统参数和业务参数分离)。
二、实现方法
刚好在分析过程中,在doCustomerService中可以通过全局变量ApiRequestContext.get()来获取请求的上下文,可以通过这个获取 所有的头部信息。
开发完毕,在开发环境运行,OK!!
三、发现问题
把系统部署到Linux的测试环境下,居然发现ApiRequestContext.get()获取到null值,原来以为是代码写错或者部署出错。经过认真检查,确认不是代码的问题。
四、分析排查&最终原因
1、ApiRequestContext的原理
经过反编译源码,确认ApiRequestContext是一个ThreadLocal的实现。初始化过程是通过initThreadLocal函数来初始化。
2、ApiRequestContext的初始化过程
调试启动的时候,在initThreadLocal进行断点。发现其调用堆栈:
从上图可以知道,ApiRequestContext的初始化是通过Jetty的Servlet来初始化的。
3、原因猜想
(事后诸葛亮,原来我也不懂Dubbo)云苍穹是采用微服务架构的,可以进行分应用部署,那么就存在一个问题,ThreadLocal只能在一个容器(服务内)生效,微服务调用一定是跨容器,而ThreadLocal是不可能跨容器的。所以就猜测是这个问题。当时还有这个疑问:就算是微服务调用,原则上也会初始化ApiRquestContext吧!!
当然,通过查找Dubbo的一些知识文章,知道 Dubbo的调用,不会走Jetty的Servlet的Filter了,因为Dubbo的调用协议是Dubbo协议,地址是Dubbo://xxxx的。
(此处省略一万字)反编译Dubbo的源码,发现本机启动的时候,调用的LocalDebugProxy来支持远程(其实是本地调用)调用,至于是采用远程调用还是本地调用,通过Dubbo的源码可以知道:
然后根据这个dubbo.service.lookup.local可以在DebugServer.java中查到这个参数定义:
4、验证
当然把这个参数改成false,让Dubbo不进行本地调用。
结果当然是我想到的结果,ApiRequestContext.get()返回是null。
五、结论
千万不要用ApiRequestContext.get(),
因为苍穹是基于微服务架构的,而这个不支持跨微服务使用。