應用用的ES集羣版本是1.7,公司內部讓升級版本,說是這個集羣版本太老了,因爲種種緣由沒有升級,就這樣應用拖到了618,結果在這期間應用頻繁的發生502和rpc線程池被打滿的狀況,給咱們本應該順暢的618帶來了一片烏雲。java
獲得此報警以後,開始dump機器線程,發現有大量的es線程在阻塞,而且有不少的http線程也在阻塞,這也就不難排查發生502和RPC線程池被打滿的緣由,因爲es操做遲遲未返回,致使業務線程池被打滿,http鏈接一直等待結果返回,鏈接不釋放,致使了tomcat接收業務請求也被打滿,進而致使應用訪問頻繁出現502。node
上圖就是jstack下來線程,能夠看到有大量的http線程被阻塞,而後查看詳情,git
詳情中有大量的線程被阻塞在了ES的調用上,這樣問題就很明顯了,按理來講問題定位到,那解決就應該很好解決了。但是哪想到這纔是痛苦的開始。github
聯合es中間件組排查es集羣,結論沒有查到有任何慢日誌,說是es集羣正常。聯繫網絡組,網絡組排查以後網絡正常,聯繫運維組排查機器正常。把全部機器實例都刪除掉,從新換了一批機器以後問題依舊。不過經過調用監控,日誌,線程和堆棧種種跡象均指向了是es的問題。而且es集羣是低版本集羣,公司已經聲明再也不維護,以前沒有報這過這個問題,如今頻繁發生,猜測應該是es資源被縮減,無維護有部分緣由。爲了臨時解決這個問題,不能讓線程無限阻塞,應儘快釋放線程,即使沒有查到數據,也能快速返回。api
FilteredQueryBuilder fqb = QueryBuilders.filteredQuery(QueryBuilders.matchAllQuery(), boolFilterBuilder); SearchResponse searchResponse = client.prepareSearch(indexName) .setTimeout(TimeValue.timeValueMillis(500)) .setTypes(documentType) .setSearchType(SearchType.QUERY_THEN_FETCH) .setQuery(fqb) .addSort("created", SortOrder.DESC) .setFrom(0).setSize(30) .execute() .actionGet();
關鍵在於setTimeout(TimeValue.timeValueMillis(500))
這一行,查詢時間設置爲500毫秒,而後愉快的上線,本覺得問題會解決,結果比較打臉,問題依舊!tomcat
問題遲遲得不到解決,天天應用報警502,線程滿,碰到這種狀況只能重啓應用,煩不勝煩,而且很懷疑爲何明明設置了超時時間就是不生效。最後只能尋根!因爲以前對es瞭解的並非那麼透徹,認爲setTimeout就是查詢以後若是多長時間沒有返回,則斷開查詢鏈接直接返回。實際上並非這樣的,他的意思是在查詢es的時候,會查詢到多個分片上的數據,當到了設定的時間若是尚未查詢完,則把已經查詢到的數據返回。即使是這樣,這個timeout也是常常失效,在es官方的issue中有具體說到:網絡
Sadly, it is a best effort timeout, its not being checked on all places. Specifically, if you send a query that ends up being rewritten into many terms (fuzzy, or wildcard), that part (the rewrite part) does not check for a timeout.運維
傳送門:Timeout on search not respected異步
那到底怎麼配置才能知足若是在設定時間內查詢不到數據就超時,扔出超時異常的這種需求呢。通過查看能夠在api查詢的最後一步也就說 actionGet()或者get()中設置timeout超時時間 actionGet(timeout) T actionGet(long var1, TimeUnit var3) throws ElasticsearchException;
,這樣設置以後若是在設定的時間沒有查詢到數據,就會拋出timeout的異常,實際上這個超時並非鏈接超時,而是處理超時,它的超時邏輯是java異步future的超時。不過這也已經知足了咱們的需求。在設定時間內沒有處理完畢,會拋出超時的異常。elasticsearch
FilteredQueryBuilder fqb = QueryBuilders.filteredQuery(QueryBuilders.matchAllQuery(), boolFilterBuilder); SearchResponse searchResponse = client.prepareSearch(indexName) .setTimeout(TimeValue.timeValueMillis(500)) .setTypes(documentType) .setSearchType(SearchType.QUERY_THEN_FETCH) .setQuery(fqb) .addSort("created", SortOrder.DESC) .setFrom(0).setSize(30) .execute() .actionGet(1000);
改造以後,系統再也不拋502異常,線程數也趨於穩定,穩定在了400左右,以前經常是一千多。
因爲對es超時機制理解的不透特,因此又查詢了es的其餘超時相關設定。
Settings settings = Settings.builder().put("client.transport.sniff", true).build(); TransportClient client = new PreBuiltTransportClient(settings);
client.transport.ping_timeout ,The time to wait for a ping response from a node. Defaults to 5s. 默認5s,client ping命令響應的時間,若是無返回,則認爲此節點不可用。若是客戶端和集羣間網絡延遲較大或者鏈接不穩定,可能須要調大這個值。
SearchResponse scrollResp = client.prepareSearch(test) .addSort(FieldSortBuilder.DOC_FIELD_NAME, SortOrder.ASC) .setScroll(new TimeValue(60000)) .setQuery(qb) .setSize(100).get();
scroll裏面的時間,這個將啓用超時的scroll滾動,通過測試,這個參數應該又是一個薛定諤的參數,沒什麼做用,仍是少依賴它作一些事情吧