這兩周做的事情比較雜踪少,所以看代碼的點也稍微有點分散,不過我盡量用手頭的例子來把這些東西串起來糠涛。
做的事情還是上兩周那件事:我想攔截ES的Request 和Response援奢,統(tǒng)計我自己想要的指標并保存,那么需要完成以下3件事情:
- 怎么攔截
- 除了Request 和Response 外如何獲取container 里的其他Service
- 如何去跑我自己的攔截代碼的邏輯
怎么攔截
其實這個話題我在上一篇(Elasticsearch 5.x 源碼分析(8)用plugin來攔截Request脱羡、Response 的可行性調研)已經詳細介紹了萝究,大家有興趣可以往回翻一下看看就好了免都,只是這種辦法最大的缺點就是不是很美觀,埋的點也很多帆竹,甚至有時可能需要入侵ES的源代碼绕娘,這幾天搗鼓的是另外一個方法:AspectJ 攔截。
根據上一篇的思路栽连,RestSearchTemplateAction
調用的是
channel -> client.execute(SearchTemplateAction.INSTANCE, searchTemplateRequest, new ActionListener<SearchTemplateResponse>()
而RestSearchAction
調用的是
channel -> client.search(searchRequest, new ActionListener<SearchResponse>()
RestIndexAction
調用的是
client.index(indexRequest, new ActionListener<IndexResponse>()
剩下RestBulkAction
調用的是
channel -> client.bulk(bulkRequest, new ActionListener<BulkResponse>()
上面說到的最終會調用
public final <Request extends ActionRequest, Response extends ActionResponse, RequestBuilder extends ActionRequestBuilder<Request, Response, RequestBuilder>> void execute(
Action<Request, Response, RequestBuilder> action, Request request, ActionListener<Response> listener)
因此AspectJ的話只需要攔截這個方法即可
大致攔截語句如下
/**
* 攔截所有RestSearchAction , RestSearchTemplateAction , IndexAction 和 BulkAction 的請求
* @param pjp
* @throws Throwable
*/
@Around("execution(* org.elasticsearch.client.support.AbstractClient.execute(org.elasticsearch.action.Action, org.elasticsearch.action.ActionRequest, org.elasticsearch.action.ActionListener))")
public void doTrace(final ProceedingJoinPoint pjp) throws Throwable {
Object[] args = pjp.getArgs();
if(isWorking && args != null && args.length == 3) {
handleArg(args);
}
pjp.proceed(args);
}
如何獲得ES的IoC 注入Service 類
好了险领,加了攔截點那么其實你可以為所欲為了化撕,可是寺旺,當我僅僅只想拿到當前Elasticsearch 實例的clusterName的時候,卻遇到麻煩了洪燥。
大家都知道ES為了效率和輕便熔恢,是用的是Google的 Guice 來實現依賴注入脐湾,可是Guice 的bind 同時需要一個Injector 實例才能完成getInstance,也就是說如果我需要拿到ES 的clusterService.getClusterName()方法叙淌,那么我首先要先想辦法拿到clusterService所bind 的那個Injector 才行秤掌。
那首當其沖先看Node.java
,其中注冊Service的代碼塊在構造方法處
并且Node開放了一個API出來
OK ,那去找Node實例,往上找到Bootstrap.java 代碼
這里持有了一個node實例鹰霍,那就拿Bootstrap 實例好了闻鉴,可惜不開放,此路不通茂洒!
接著換一種思路孟岛,找傳進來的配置Settings不就可以了
那繼續(xù)往上找,找到environment 的初始化
再去找Settings類督勺,希望它內存保留一份副本渠羞,結果沒卵用,好吧玷氏,ES的安全措施做的還是不錯的堵未,這種配置的東西基本不讓外面調用或攔截有入侵的機會腋舌。那就是說如果我想攔截代碼盏触,想拿到ES的Service基本是不可能的。
如何去跑我的那段攔截代碼
如果就一兩行代碼的話那就完全沒必要這么復雜了块饺,但是我攔截的邏輯比較重赞辩,依賴的包都十幾個,這就有點尷尬了授艰,最好的機制還是自己弄個classLoader 去加載我自己的依賴辨嗽,這樣的話也不至于和ES本身的代碼會有沖突(logger 的API都好幾個...)。
這里先拋個銀子淮腾,話說上面那個問題糟需,有些人也會想到說:其實你自己隨便弄個plugin屉佳,然后把ES注入給我的Service我全部暴露出來不就行了? 因此它偏偏就不行洲押,原因就是武花,ES的plugin 和Modules 加載都是采用獨自的classLoader實現的。
因此這里我遇到了新的問題:
- 我的攔截邏輯要么用ES 的classLoader 來加載杈帐,要么用我自己的classLoader加載体箕,但是無論如何我都無法讀取到plugin 里面的類
- 像RestSearchTemplateAction,SearchTemplateRequest挑童,SearchTemplateResponse這些類都是打在 lang-mustashe.jar 里面的累铅,是屬于module plugin 存在,因此無論我是哪種classLoader加載站叼,我都是無法攔截并強轉成這些對象來做文章娃兽。
那么我想到的辦法只能是反射了,比如這樣
至于說是否需要用到自己的classLoader 尽楔,那就視乎你自己的邏輯復雜程度换薄,像我的話就是把所有的邏輯都封裝出去,然后用獨自的classLoader來加載翔试,避免重讀轻要,自己搞classLoader的話就切記注意一點,千萬要catch所有的throwable垦缅,避免JVM 掛了冲泥。