千万不要用ApiRequestContext原创
金蝶云社区-189XXXX2123
189XXXX2123
6人赞赏了该文章 881次浏览 未经作者许可,禁止转载编辑于2021年11月19日 17:27:17

一、背景


    小程序和第三方系统都要通过Http的方式调用苍穹的接口。所以我们使用苍穹提供的开放平台开发了【自定义服务】的Api接口,由于业务上需要判定是从第三方系统调用的,还是小程序调用的,所以我们在请求头部增加一个标识来区别(主要是想不要把这个标识放在请求body里面,把系统参数和业务参数分离)。

二、实现方法


    刚好在分析过程中,在doCustomerService中可以通过全局变量ApiRequestContext.get()来获取请求的上下文,可以通过这个获取 所有的头部信息。

image.png

    开发完毕,在开发环境运行,OK!!

三、发现问题


    把系统部署到Linux的测试环境下,居然发现ApiRequestContext.get()获取到null值,原来以为是代码写错或者部署出错。经过认真检查,确认不是代码的问题。

四、分析排查&最终原因

    1、ApiRequestContext的原理


        经过反编译源码,确认ApiRequestContext是一个ThreadLocal的实现。初始化过程是通过initThreadLocal函数来初始化。

image.png


    2、ApiRequestContext的初始化过程

        调试启动的时候,在initThreadLocal进行断点。发现其调用堆栈:

        image.png

        从上图可以知道,ApiRequestContext的初始化是通过Jetty的Servlet来初始化的。

         3、原因猜想

        (事后诸葛亮,原来我也不懂Dubbo)云苍穹是采用微服务架构的,可以进行分应用部署,那么就存在一个问题,ThreadLocal只能在一个容器(服务内)生效,微服务调用一定是跨容器,而ThreadLocal是不可能跨容器的。所以就猜测是这个问题。当时还有这个疑问:就算是微服务调用,原则上也会初始化ApiRquestContext吧!!

            当然,通过查找Dubbo的一些知识文章,知道 Dubbo的调用,不会走Jetty的Servlet的Filter了,因为Dubbo的调用协议是Dubbo协议,地址是Dubbo://xxxx的。

        (此处省略一万字)反编译Dubbo的源码,发现本机启动的时候,调用的LocalDebugProxy来支持远程(其实是本地调用)调用,至于是采用远程调用还是本地调用,通过Dubbo的源码可以知道:

image.png

        然后根据这个dubbo.service.lookup.local可以在DebugServer.java中查到这个参数定义:

            image.png

            4、验证

                当然把这个参数改成false,让Dubbo不进行本地调用。

                结果当然是我想到的结果,ApiRequestContext.get()返回是null。

五、结论

                 千万不要用ApiRequestContext.get(),

                因为苍穹是基于微服务架构的,而这个不支持跨微服务使用。


 


        




             

                           

赞 6