基於OkHttp自定義攔截器實現外部服務多地址配置且請求失敗自動切換地址

最近在業務中接入了一個第三方的接口,第三方爲了提高服務的可用性提供了多個地址供外部服務調用,所以需要實現在請求某一個地址不可用時自動切換到另一個地址並重試的功能。由於業務中使用 OkHttp,所以直接用 OkHttp 的自定義攔截器實現。

1.在 application.properties 中配置外部服務地址,多個地址用英文半角逗號隔開

xxx.api.addr=http://www.baidu.com,http://www.google.com

2.自定義 Intercept

import okhttp3.Interceptor;
import okhttp3.Request;
import okhttp3.Response;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.util.List;

/**
 * okHttp攔截器,主要用於攔截失敗請求並重試
 * @author hellohuan
 * @date 2020/1/11 9:38
 */
public class RetryAndChangeUrlInterceptor implements Interceptor {

    private static final Logger LOG = LoggerFactory.getLogger(RetryAndChangeUrlInterceptor.class);
    /**
     * 默認重試3次
     */
    private int retryCount;
    /**
     * 默認url
     */
    private String firstAddr;
    /**
     * url地址池
     */
    private List<String> address;

    public RetryAndChangeUrlInterceptor(String firstUrl, List<String> urls) {
        this.firstAddr = firstUrl;
        this.address = urls;
        this.retryCount = 3;
    }

    public RetryAndChangeUrlInterceptor(String firstUrl, List<String> urls, int tryCount) {
        this.firstAddr = firstUrl;
        this.address = urls;
        this.retryCount = tryCount;
    }

    @Override
    public Response intercept(Chain chain) throws IOException {
        Request request = chain.request();
        // try the request
        Response response = doRequest(chain, request);
        int tryCount = 0;
        String url = request.url().toString();
        while (response == null && tryCount < retryCount) {
            String old = url;
//            url = switchServer(url);
            url = switchServer(url, tryCount % address.size(), (tryCount + 1) % address.size());
            Request newRequest = request.newBuilder().url(url).build();
            LOG.warn("Http Request is failed , Request index - {} , Old url - {} , New url = {}", tryCount, old, url);
            tryCount++;
            // retry the request
            response = doRequest(chain, newRequest);
        }
        if (response == null) {
            throw new IOException();
        }
        return response;
    }


    private Response doRequest(Chain chain, Request request) {
        Response response = null;
        try {
            response = chain.proceed(request);
        } catch (Exception e) {
        }
        return response;
    }

    /**
     * 單向取址
     * @param url
     * @return
     */
    private String switchServer(String url) {
        String newUrlString = url;
        if (url.contains(firstAddr)) {
            for (String server : address) {
                if (!firstAddr.equals(server)) {
                    newUrlString = url.replace(firstAddr, server);
                    break;
                }
            }
        } else {
            for (String server : address) {
                if (url.contains(server)) {
                    newUrlString = url.replace(server, firstAddr);
                    break;
                }
            }
        }
        return newUrlString;
    }

    /**
     *  循環取址
     * @param url
     * @param oldIndex
     * @param newIndex
     * @return
     */
    private String switchServer(String url, int oldIndex, int newIndex) {
        String newUrlString = url;
        if (newIndex < address.size() || oldIndex < address.size()){
            String oldAddr = address.get(oldIndex);
            String newAddr = address.get(newIndex);
            newUrlString = url.replace(oldAddr, newAddr);
        }
        return newUrlString;
    }
}

3.在創建 HttpClient 時添加 intercept

public XXXHttpClient(@Value("#{'${xxx.api.addr}'.split(',')}")List<String> urls) {
		if (urls.isEmpty()){
			LOG.error("xxx api 地址爲空,請檢查配置文件");
			System.exit(0);
		}
		String firstUrl = urls.get(0);
		OkHttpClient client = new OkHttpClient.Builder()
				.addInterceptor(new RetryAndChangeUrlInterceptor(firstUrl, urls, urls.size()-1))
				.retryOnConnectionFailure(true)
				.connectTimeout(5, TimeUnit.SECONDS).writeTimeout(10, TimeUnit.SECONDS)
				.readTimeout(10, TimeUnit.SECONDS).build();
		this.client = client;
	}

這樣,就實現了第三方接口的多地址配置和請求失敗自動切換地址重試。

#全文畢
歡迎關注微信公衆號:Javall咖啡屋
每天更新各種互聯網技術(前後端、數據庫、中間件、設計模式、數據結構、算法)學習心得體會

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章