跨平臺調(diào)用 - Retrofit

為什么不用Spring RestTemplate 與 Feign

Feign

Feign是局域網(wǎng)調(diào)用袱蜡,而這里需求則是跨平臺的廣域網(wǎng)調(diào)用楷扬。

RestTemplate - 先提出兩個需求:

  1. 我們需要像Feign那樣配置一個,聲明式的接口 ,在易用性敬锐、可讀性等方面能有較好的表現(xiàn)闰非。
  2. 請求時蒲祈,BaseUrl也就是域名地址是動態(tài)變化的。
    =>回答: 對RestTemplate來說萝嘁,每次請求前我們需要提前配置好網(wǎng)關(guān)地址梆掸,然后再加上請求的Path。無法實(shí)現(xiàn)動態(tài)的BaseUrl改變與請求接口的統(tǒng)一管理牙言。

Retrofit能解決這些痛點(diǎn)嗎酸钦?

  1. Retrofit支持BaseUrl的動態(tài)改變。
  2. 它能將請求接口規(guī)劃在每個Retrofit Client中進(jìn)行統(tǒng)一管理咱枉。

大致背景

需要完成的是B端類似應(yīng)用市場的功能卑硫,存在Pass與SaaS端,Pass端主要是各個用戶對平臺自己進(jìn)行部署安裝蚕断。SaaS則是我們維護(hù)在公網(wǎng)上的Pass欢伏,用戶可以通過它們自己的Pass平臺訪問到SaaS從而拿到應(yīng)用數(shù)據(jù)實(shí)現(xiàn)下載,上傳亿乳,查看等硝拧,所以這里的需求就是需要在Pass端對遠(yuǎn)程SaaS進(jìn)行跨平臺的調(diào)用径筏。

在項(xiàng)目中使用retrofit的演進(jìn)過程

  1. 通常如果存在一個baseurl,只需要配置并build一個retrofit障陶,然后createClient滋恬,創(chuàng)建出訪問接口的Client。
  2. 但在這里我們的baseUrl會時刻改變抱究,意味著SaaS的地址會發(fā)送變化恢氯。
  3. 而且有不同Client訪問不同平臺的微服務(wù),比如Pass端的訪問接口包含了對多個微服務(wù)的請求鼓寺,那么它們就應(yīng)該劃分為不同的Client勋拟,統(tǒng)一管理。
存在多個baseurl
  1. 創(chuàng)建一個工廠類:使用靜態(tài)字段BaseUrl和Client侄刽,每次請求去查驗(yàn)當(dāng)前Client是否為空指黎,url是否改變,如果改變或?yàn)榭談t使用build一個Retrofit創(chuàng)建新的Client州丹。
  2. 為什么不考慮使用官方推薦的動態(tài)url來做醋安,是因?yàn)槊看握埱蟮臅r候都需要跟上自己的全路徑,而這里我們希望將Path能放在一個統(tǒng)一接口的各個具體的調(diào)用方法上墓毒。
    private static String baseUrl;

    private static AppMarketClient marketClient;

    public static AppMarketClient createClient(String newUrl) {

        if (marketClient == null || !baseUrl.equals(newUrl)) {
            baseUrl = newUrl;
            Retrofit retrofit = new Retrofit.Builder()
                    .baseUrl(baseUrl)
                    .addConverterFactory(GsonConverterFactory.create())
                    .build();

            marketClient = retrofit.create(AppMarketClient.class);
        }

        return marketClient;
    }
存在多個BaseUrl,同時 存在多個Client

使用工廠類吓揪,類中使用Map,BaseUrl作key所计,Client作value緩存Client柠辞,痛點(diǎn)在于我每個url都要跟上請求的具體微服務(wù)網(wǎng)關(guān)Path,這樣才能區(qū)分相同BaseUrl不同微服務(wù)的Client主胧,比如http://xxx.com/devops/xxx叭首,與http://xxx.com/market/xxx

    private static final Map<String, RetrofitClient> retrofitClients = new HashMap<>();

    public static RetrofitClient createClient(String baseUrl, Class<? extends RetrofitClient> clientClass) {
        //如果baseUrl被修改或map中不存在這個RetrofitClient踪栋,則創(chuàng)建
        if (!retrofitClients.containsKey(baseUrl)) {
            Retrofit retrofit = new Retrofit.Builder()
                    .baseUrl(baseUrl)
                    .addConverterFactory(GsonConverterFactory.create())
                    .build();
            retrofitClients.put(baseUrl, retrofit.create(clientClass));
        }
        return retrofitClients.get(baseUrl);
    }
在Github上看到Retrofit廢除BaseUrl的Issue焙格,其中提到使用攔截器進(jìn)行統(tǒng)一處理,從而動態(tài)改變BaseUrl
    private static final Retrofit retrofit;
    // 默認(rèn)值
    private static String baseUrl = "http://localhost";
    private static final Map<String, Object> retrofitClients = new HashMap<>();

    static {
        OkHttpClient okHttpClient = new OkHttpClient.Builder()
                .addInterceptor(chain -> {
                    HttpUrl httpUrl = HttpUrl.parse(baseUrl);
                    if (ObjectUtils.isEmpty(httpUrl)) {
                        throw new CommonException("error.retrofit.baseUrl.illegal", baseUrl);
                    }
                    Request request = chain.request();
                    Request.Builder builder = request.newBuilder();
                    HttpUrl oldHttpUrl = request.url();
                    HttpUrl newFullUrl = oldHttpUrl
                            .newBuilder()
                            .host(httpUrl.host())
                            .build();
                    return chain.proceed(builder.url(newFullUrl).build());
                })
                .build();
        retrofit = new Retrofit.Builder()
                .baseUrl(baseUrl)
                .addConverterFactory(GsonConverterFactory.create())
                .client(okHttpClient)
                .build();
    }

    public static Object createClient(String baseUrl, Class clientClass) {
        RetrofitClientFactory.baseUrl = baseUrl;
        String className = clientClass.getSimpleName();
        if (!retrofitClients.containsKey(className)) {
            retrofitClients.put(className, retrofit.create(clientClass));
        }
        return retrofitClients.get(className);
    }
添加到spring管理
    // 原有工廠類
    private ApplicationContext applicationContext;
    private RetrofitConfig retrofitConfig;

    public RetrofitClientFactory(RetrofitConfig retrofitConfig) {
        this.retrofitConfig = retrofitConfig;
    }

    @Override
    public void setApplicationContext(@NonNull ApplicationContext applicationContext) {
        this.applicationContext = applicationContext;
    }

    public Object getRetrofitBean(String baseUrl, Class clientName) {
        retrofitConfig.setBaseUrl(baseUrl);
        return applicationContext.getBean(clientName.getSimpleName());
    }


    // Spring配置文件
    private String baseUrl = "http://localhost";

    // todo 修改全局的baseUrl時的線程安全 (修改baseUrl夷都,獲取RetrofitClient眷唉,發(fā)出請求,這三步是一個原子操作)
    public void setBaseUrl(String baseUrl) {
        this.baseUrl = baseUrl;
    }

    @Bean("RemoteTokenRetrofitClient")
    public RemoteTokenRetrofitClient remoteTokenRetrofitClient() {
        Retrofit retrofit = getCommonRetrofit();
        return retrofit.create(RemoteTokenRetrofitClient.class);
    }

    @Bean("AppMarketRetrofitClient")
    public AppMarketRetrofitClient appMarketRetrofitClient() {
        Retrofit retrofit = getCommonRetrofit();
        return retrofit.create(AppMarketRetrofitClient.class);
    }

    private Retrofit getCommonRetrofit() {
        OkHttpClient okHttpClient = new OkHttpClient.Builder()
                .addInterceptor(chain -> {
                    HttpUrl httpUrl = HttpUrl.parse(baseUrl);
                    if (ObjectUtils.isEmpty(httpUrl)) {
                        throw new CommonException("error.retrofit.baseUrl.illegal", baseUrl);
                    }
                    Request request = chain.request();
                    Request.Builder builder = request.newBuilder();
                    HttpUrl oldHttpUrl = request.url();
                    HttpUrl newFullUrl = oldHttpUrl
                            .newBuilder()
                            .host(httpUrl.host())
                            .port(httpUrl.port())
                            .build();
                    return chain.proceed(builder.url(newFullUrl).build());
                })
                .build();
        return new Retrofit.Builder()
                .baseUrl(baseUrl)
                .addConverterFactory(GsonConverterFactory.create())
                .client(okHttpClient)
                .build();
    }
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末囤官,一起剝皮案震驚了整個濱河市冬阳,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌党饮,老刑警劉巖肝陪,帶你破解...
    沈念sama閱讀 217,542評論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異劫谅,居然都是意外死亡见坑,警方通過查閱死者的電腦和手機(jī)嚷掠,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,822評論 3 394
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來荞驴,“玉大人不皆,你說我怎么就攤上這事⌒苈ィ” “怎么了霹娄?”我有些...
    開封第一講書人閱讀 163,912評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長鲫骗。 經(jīng)常有香客問我犬耻,道長,這世上最難降的妖魔是什么执泰? 我笑而不...
    開封第一講書人閱讀 58,449評論 1 293
  • 正文 為了忘掉前任枕磁,我火速辦了婚禮,結(jié)果婚禮上术吝,老公的妹妹穿的比我還像新娘计济。我一直安慰自己,他們只是感情好排苍,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,500評論 6 392
  • 文/花漫 我一把揭開白布沦寂。 她就那樣靜靜地躺著,像睡著了一般淘衙。 火紅的嫁衣襯著肌膚如雪传藏。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,370評論 1 302
  • 那天彤守,我揣著相機(jī)與錄音毯侦,去河邊找鬼。 笑死具垫,一個胖子當(dāng)著我的面吹牛叫惊,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播做修,決...
    沈念sama閱讀 40,193評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼抡草!你這毒婦竟也來了饰及?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,074評論 0 276
  • 序言:老撾萬榮一對情侶失蹤康震,失蹤者是張志新(化名)和其女友劉穎燎含,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體腿短,經(jīng)...
    沈念sama閱讀 45,505評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡屏箍,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,722評論 3 335
  • 正文 我和宋清朗相戀三年绘梦,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片赴魁。...
    茶點(diǎn)故事閱讀 39,841評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡卸奉,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出颖御,到底是詐尸還是另有隱情榄棵,我是刑警寧澤,帶...
    沈念sama閱讀 35,569評論 5 345
  • 正文 年R本政府宣布潘拱,位于F島的核電站疹鳄,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏芦岂。R本人自食惡果不足惜瘪弓,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,168評論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望禽最。 院中可真熱鬧腺怯,春花似錦、人聲如沸弛随。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,783評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽舀透。三九已至栓票,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間愕够,已是汗流浹背走贪。 一陣腳步聲響...
    開封第一講書人閱讀 32,918評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留惑芭,地道東北人坠狡。 一個月前我還...
    沈念sama閱讀 47,962評論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像遂跟,于是被迫代替她去往敵國和親逃沿。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,781評論 2 354