okhttp support 307 redirect for palo

前言

雖然百度的Palo是個很強大的,基於MPP Search Engine的OLAP框架,但是由於處於開源的早期階段,各方面都不是很完善。其中,Palo集群的穩定性對於日漸依賴Palo的核心的業務來說顯得尤為重要。最近也一直在做Palo穩定性建設相關的工作。在對全鏈路監控這塊,自然而然地想到對業務中使用頻繁的http-mini-load介面進行SDK封裝,以實現對請求進行失敗重試以及失敗率的監控報警的功能。

遇到的問題

問題描述

  • 在實際的SDK封裝中,用到了流行的okhttp,發送請求如下:

PaloHttpUtil paloHttpUtil = PaloHttpUtil.builder().build();String bodyStr = "1 1 2018-04-12 20:13:00 4101628087476389 1
1 1 2018-04-12 20:13:00 4205819141030267 2";System.out.println(paloHttpUtil .put(String.format("http://xxxx:8030/api/feed/comment_trend/_load?" + "label=comment_trend_load_%s&columns=trend_type,user_type,timestamp,mid,count", prefix)) .auth("feed", "feed") .header("Expect", "100-continue") .body(bodyStr, ContentType.WILDCARD) .asyncSend(3, 10000, TimeUnit.MILLISECONDS) .string()

response: code 307 Temporary {「Status」:」Failed」} 也就是說發生了重定向。對於伺服器為何不在一個request中直接接收PUT的數據,這塊貼一下100-continue的定義。

100 (Continue/繼續) :如果伺服器收到頭信息中帶有100-continue的請求,這是指客戶端詢問是否可以在後續的請求中發送附件。

在這種情況下,伺服器用100(SC_CONTINUE)允許客戶端繼續或用417 (Expectation Failed)告訴客戶端不同意接受附件。這個狀態碼是 HTTP 1.1中新加入的。

為什麼palo 的miniload在請求的時候發生了重定向?這塊我們先研究一下為什麼okhttp不支持307重定向,解決問題之後我們再看看palo-fe裡面miniload的實現機制。

源碼追蹤

  • 我們通過debug深入源碼看看在哪一步處理的307重定向

由圖,我們可以看到對於request/response的處理,okhttp採取了插件的形式,類似於Spring AOP 源碼中切面invoke方法的處理方式。這種插件的方式意味著我們可以定製化請求處理邏輯。借官方原圖:

由於interceptor裡面是可以執行重試邏輯或直接返回response,所以,我們再深入看看在哪個Interceptor里直接返回了response。斷點打到RetryAndFollowUpInterceptor里如下的代碼塊:

Request followUp = this.followUpRequest(response, streamAllocation.route()); //這裡followUP返回null if (followUp == null) { if (!this.forWebSocket) { streamAllocation.release(); } return response; }

由於followUp返回了null,導致response直接返回。說明當前的redirect策略不支持307重定向,再深入具體的重定向策略followUpRequest

發現307,308如果request method不等於GET且不為HEAD時直接返回了null,由此對於307 的PUT重定向操作okhttp是不支持的

問題的解決

okhttp 添加自定義redirect interceptor

  • 前面提到,我們可以往okhttpClient裡面添加自定義的interceptor來達到對request/response靈活劫持的目的。於是考慮加一個支持palo put 307 重定向的redirect interceptor.

大致的策略還是跟RetryAndFollowUpInterceptor一樣,對followUpRequest的方法做了修改

case 300: case 301:case 302:case 303:case 307:

將307放到了300-303並列的位置,進入redirect邏輯。去掉將method統一替換成GET的邏輯:

if (HttpMethod.redirectsToGet(method)) { requestBuilder.method("GET", (RequestBody)null);} else { RequestBody requestBody = maintainBody ?userResponse.request().body() : null; requestBuilder.method(method, requestBody);}

改為:

boolean maintainBody = HttpMethod.requiresRequestBody(method); RequestBody requestBody = maintainBody ? userResponse.request().body() : null; requestBuilder.method(method, requestBody);

為了帶上用戶名密碼,去掉邏輯:

if (!this.sameConnection(userResponse, url)){ requestBuilder.removeHeader("Authorization");}

  • 至此,Palo http-mini-load put 307 Temporary 重定向問題得到了解決

使用apache httpcomponents

  • 在apache httpcomponents中,可以設置redirectStrategy,來達到重定向的策略,且不受http code的約束

可以看到本身的redirect機制還是比較強大的

  • 不過鑒於本人習慣用okhttp,且用自定義的interceptro也能解決問題,所以暫時沒有採用這種方法。

palo-fe miniload

回到問題的開始,為什麼在調palo-fe的minload介面會發生重定向呢?我們深入一下palo-fe的源代碼//入口 LoadAction.javapublic static void registerAction(ActionController controller) throws IllegalArgException { ExecuteEnv execEnv = ExecuteEnv.getInstance(); LoadAction action = new LoadAction(controller, execEnv); controller.registerHandler(HttpMethod.PUT, "/api/{" + DB_NAME_PARAM + "}/{" + TABLE_NAME_PARAM + "}/_load", action); } //在路由的execute方法中,看到如下邏輯 // Try to redirect to master //這裡還是由leader去負責寫請求,確保FE的高可用 if (redirectToMaster(request, response)) { return; } // Choose a backend sequentially. 順序的去選擇一個backend處理miniload的請求 List<Long> backendIds = Catalog.getCurrentSystemInfo().seqChooseBackendIds(1, true, false, clusterName); if (backendIds == null) { throw new DdlException("No live backend."); } Backend backend = Catalog.getCurrentSystemInfo().getBackend(backendIds.get(0)); if (backend == null) { throw new DdlException("No live backend."); } TNetworkAddress redirectAddr = new TNetworkAddress(backend.getHost(), backend.getHttpPort()); if (!Strings.isNullOrEmpty(subLabel)) { redirectAddr = execEnv.getMultiLoadMgr().redirectAddr(dbName, label, tableName, redirectAddr); }

  • 從上面的邏輯中我們可以得到以下幾個訊息:
    • 為了保證FE的高可用性,還是執行嚴格的Leader負責讀寫,Follower負責讀的請求策略
    • 之所以會發生307重定向的問題,是因為數據的導入實際上是轉發給BE來做的,FE只是做了一層中繼調度的工作

總結

  • okhttp 不支持307除get意外其他request method重定向的原因不得而知。不過對於開源的組件,也不必要滿足各種各樣奇怪的胃口,對於需求的定製化留好可擴展介面就行。
  • 個人博客地址:

CHAO LIs Blog?

jacobs.wanhb.cn圖標
推薦閱讀:

中芯國際最新財報解讀:梁孟松效應開始顯現
MacBook 電源適配器的飛翼繞線器的使用率有多高,是否多餘?
怎樣修改gif圖片中的文字?
看得清中美科技差距有多大,才明白中國科技發展有多快 | 袁方看科技
這10 個工具讓你高效製作PPT

TAG:編程 | 科技 | 大數據 |