前言
最近在學(xué)習(xí)Kafka Connect,寫了個連接器的demo。在demo提交了幾個版本之后,突然發(fā)現(xiàn)Kafka Connect Rest API 無法正常響應(yīng)了,明明有正在運行的連接器,查詢status,居然返回{}
問題分析
對 Rest API 進(jìn)行debug后,確認(rèn)是有數(shù)據(jù)的,但是數(shù)據(jù)返回不到客戶端,很奇怪。因為我記得之前是好用的,所以我回滾了代碼版本,逐一排查之后發(fā)現(xiàn)當(dāng)引入Fastjson 依賴之后,會導(dǎo)致Connect Rest API 不可用
如果懶一點的話,到這里就已經(jīng)結(jié)束了,直接刪除Fastjson依賴,使用其他Json包。但是我很好奇,在我的理解里,F(xiàn)astjson 這種庫就是個工具包,如果我們程序沒有主動調(diào)用的時候,是不會對我們產(chǎn)生任何影響的。
百度谷歌一通之后,一籌莫展之際,點開了Fastjson的源碼包,在這里發(fā)現(xiàn)了Fastjson為JAXRS提供的SPI擴(kuò)展

JAXRS:Java API for RESTful Web Services,JavaEE提供的Web服務(wù)接口。Jersey 實現(xiàn)了JAXRS,而Kafka Connect 引用了Jersey 。
SPI:Service Provider Interface ,是JDK內(nèi)置的一種服務(wù)提供發(fā)現(xiàn)機(jī)制,可以參考我之前的博客 Java SPI 實戰(zhàn)
打開javax.ws.rs.ext.MessageBodyWriter 文件,可以看到提供的實現(xiàn)類是com.alibaba.fastjson.support.jaxrs.FastJsonProvider,定位到FastJsonProvider下writeTo方法,該方法會把object寫入到OutputStream中,看起來很靠譜,debug試一下

果然,說明Fastjson果然參與了Rest API的響應(yīng)。為什么使用Fastjson就響應(yīng)不了數(shù)據(jù)呢,看了下源碼,這里要求被序列化的Bean必須標(biāo)記Fastjson相關(guān)的注解,而實際的Bean使用的是Jackson的注解,所以Fastjson無法序列化數(shù)據(jù)。
接下來可以根據(jù)調(diào)用棧和全局搜索找一下,看看FastJsonProvider是在什么時機(jī)加載的,能否干掉他。
調(diào)用棧并沒有找到什么有用的信息,通過全局搜索MessageBodyWriter找到了FastJsonProvider的加載位置,MessageBodyFactory::initialize

上圖字面意思理解,使用 injectionManager (注入管理器),找到MessageBodyWriter的可用實現(xiàn)
這里的 customMbws size = 2,分別是FastJson和Jackson的實現(xiàn)。但是FastJson在前,而每次需要做JSON序列化的時候,會遍歷writers,如果找到支持application/json的MessageBodyWriter則直接返回,所以每次使用的都是FastJson的實現(xiàn)。
至此已經(jīng)明白了,為什么Fastjson 會影響Kafka Connect了,接下來就是想辦法解決了
這個時候還是沒有找到Fastjson是在哪加載的,在Fastjson的 wiki 中找到了些靈感,發(fā)現(xiàn)Fastjson 在Jersey 中并不是通過SPI的方式進(jìn)行的擴(kuò)展,而是通過FastJsonAutoDiscoverable,向Jersey 的 context中注冊FastJsonProvider

最后,我們在java 進(jìn)程啟動時指定參數(shù) -Dfastjson.auto.discoverable=false,禁用 FastJsonProvider
參考
https://github.com/alibaba/fastjson/wiki/Integrate-Fastjson-in-JAXRS