成人无码视频,亚洲精品久久久久av无码,午夜精品久久久久久毛片,亚洲 中文字幕 日韩 无码

資訊專欄INFORMATION COLUMN

如何使用OpenFeign+WebClient實(shí)現(xiàn)非阻塞的接口聚合

王巖威 / 1376人閱讀

摘要:隨著微服務(wù)的遍地開(kāi)花,越來(lái)越多的公司開(kāi)始采用用于公司內(nèi)部的微服務(wù)框架。

隨著微服務(wù)的遍地開(kāi)花,越來(lái)越多的公司開(kāi)始采用SpringCloud用于公司內(nèi)部的微服務(wù)框架。

按照微服務(wù)的理念,每個(gè)單體應(yīng)用的功能都應(yīng)該按照功能正交,也就是功能相互獨(dú)立的原則,劃分成一個(gè)個(gè)功能獨(dú)立的微服務(wù)(模塊),再通過(guò)接口聚合的方式統(tǒng)一對(duì)外提供服務(wù)!

然而隨著微服務(wù)模塊的不斷增多,通過(guò)接口聚合對(duì)外提供服務(wù)的中層服務(wù)需要聚合的接口也越來(lái)越多!慢慢地,接口聚合就成分布式微服務(wù)架構(gòu)里一個(gè)非常棘手的性能瓶頸!

舉個(gè)例子,有個(gè)聚合服務(wù),它需要聚合Service、Route和Plugin三個(gè)服務(wù)的數(shù)據(jù)才能對(duì)外提供服務(wù):

@Headers({ "Accept: application/json" })

public interface ServiceClient {

    @RequestLine("GET /")

    List list();

}
@Headers({ "Accept: application/json" })

public interface RouteClient {

    @RequestLine("GET /")

    List list();

}
@Headers({ "Accept: application/json" })

public interface PluginClient {

    @RequestLine("GET /")

    List list();

}

使用聲明式的OpenFeign代替HTTP Client進(jìn)行網(wǎng)絡(luò)請(qǐng)求

編寫(xiě)單元測(cè)試

public class SyncFeignClientTest {

    public static final String SERVER = "http://devops2:8001";

    private ServiceClient serviceClient;

    private RouteClient routeClient;

    private PluginClient pluginClient;

    @Before

    public void setup(){

        BasicConfigurator.configure();

        Logger.getRootLogger().setLevel(Level.INFO);

        String service = SERVER + "/services";

        serviceClient = Feign.builder()

                .target(ServiceClient.class, service);

        String route = SERVER + "/routes";

        routeClient = Feign.builder()

                .target(RouteClient.class, route);

        String plugin = SERVER + "/plugins";

        pluginClient = Feign.builder()

                .target(PluginClient.class, plugin);

    }

    @Test

    public void aggressionTest() {

        long current = System.currentTimeMillis();

        System.out.println("開(kāi)始調(diào)用聚合查詢");

        serviceTest();

        routeTest();

        pluginTest();

        System.out.println("調(diào)用聚合查詢結(jié)束!耗時(shí):" + (System.currentTimeMillis() - current) + "毫秒");

    }

    @Test

    public void serviceTest(){

        long current = System.currentTimeMillis();

        System.out.println("開(kāi)始獲取Service");

        String service = serviceClient.list();

        System.out.println(service);

        System.out.println("獲取Service結(jié)束!耗時(shí):" + (System.currentTimeMillis() - current) + "毫秒");

    }

    @Test

    public void routeTest(){

        long current = System.currentTimeMillis();

        System.out.println("開(kāi)始獲取Route");

        String route = routeClient.list();

        System.out.println(route);

        System.out.println("獲取Route結(jié)束!耗時(shí):" + (System.currentTimeMillis() - current) + "毫秒");

    }

    @Test

    public void pluginTest(){

        long current = System.currentTimeMillis();

        System.out.println("開(kāi)始獲取Plugin");

        String plugin = pluginClient.list();

        System.out.println(plugin);

        System.out.println("獲取Plugin結(jié)束!耗時(shí):" + (System.currentTimeMillis() - current) + "毫秒");

    }

}
測(cè)試結(jié)果:
開(kāi)始調(diào)用聚合查詢

開(kāi)始獲取Service

{"next":null,"data":[]}

獲取Service結(jié)束!耗時(shí):134毫秒

開(kāi)始獲取Route

{"next":null,"data":[]}

獲取Route結(jié)束!耗時(shí):44毫秒

開(kāi)始獲取Plugin

{"next":null,"data":[]}

獲取Plugin結(jié)束!耗時(shí):45毫秒

調(diào)用聚合查詢結(jié)束!耗時(shí):223毫秒

Process finished with exit code 0

可以明顯看出:聚合查詢查詢所用的時(shí)間223毫秒 = 134毫秒 + 44毫秒 + 45毫秒

也就是聚合服務(wù)的請(qǐng)求時(shí)間與接口數(shù)量成正比關(guān)系,這種做法顯然不能接受!

而解決這種問(wèn)題的最常見(jiàn)做法就是預(yù)先創(chuàng)建線程池,通過(guò)多線程并發(fā)請(qǐng)求接口進(jìn)行接口聚合!

這種方案在網(wǎng)上隨便百度一下就能找到好多,今天我就不再把它的代碼貼出來(lái)!而是說(shuō)一下這個(gè)方法的缺點(diǎn):

原本JavaWeb的主流Servlet容器采用的方案是一個(gè)HTTP請(qǐng)求就使用一個(gè)線程和一個(gè)Servlet進(jìn)行處理!這種做法在并發(fā)量不高的情況沒(méi)有太大問(wèn)題,但是由于摩爾定律失效了,單臺(tái)機(jī)器的線程數(shù)量仍舊停留在一萬(wàn)左右,在網(wǎng)站動(dòng)輒上千萬(wàn)點(diǎn)擊量的今天,單機(jī)的線程數(shù)量根本無(wú)法應(yīng)付上千萬(wàn)級(jí)的并發(fā)量!

而為了解決接口聚合的耗時(shí)過(guò)長(zhǎng)問(wèn)題,采用線程池多線程并發(fā)網(wǎng)絡(luò)請(qǐng)求的做法,更是火上澆油!原本只需一個(gè)線程就搞定的請(qǐng)求,通過(guò)多線程并發(fā)進(jìn)行接口聚合,就把處理每個(gè)請(qǐng)求所需要的線程數(shù)量給放大了,急速降低系統(tǒng)可用線程的數(shù)量,自然也降低系統(tǒng)的并發(fā)數(shù)量!

這時(shí),人們想起從Java5開(kāi)始就支持的NIO以及它的開(kāi)源框架Netty!基于Netty以及Reactor模式,Java生態(tài)圈出現(xiàn)了SpringWebFlux等異步非阻塞的JavaWeb框架!Spring5也是基于SpringWebFlux進(jìn)行開(kāi)發(fā)的!有了異步非阻塞服務(wù)器,自然也有異步非阻塞網(wǎng)絡(luò)請(qǐng)求客戶端WebClient!

今天我就使用WebClient和ReactiveFeign做一個(gè)異步非阻塞的接口聚合教程:

首先,引入依賴



    com.playtika.reactivefeign

    feign-reactor-core

    1.0.30

    test





    com.playtika.reactivefeign

    feign-reactor-webclient

    1.0.30

    test


然而基于Reactor Core重寫(xiě)Feign客戶端,就是把原本接口返回值:List<實(shí)體>改成FLux<實(shí)體>,實(shí)體改成Mono<實(shí)體>

@Headers({ "Accept: application/json" })

public interface ServiceClient {

    @RequestLine("GET /")

    Flux list();

}
@Headers({ "Accept: application/json" })

public interface RouteClient {

    @RequestLine("GET /")

    Flux list();

}
@Headers({ "Accept: application/json" })

public interface PluginClient {

    @RequestLine("GET /")

    Flux list();

}
然后編寫(xiě)單元測(cè)試
public class AsyncFeignClientTest {

    public static final String SERVER = "http://devops2:8001";

    private CountDownLatch latch;

    private ServiceClient serviceClient;

    private RouteClient routeClient;

    private PluginClient pluginClient;

    @Before

    public void setup(){

        BasicConfigurator.configure();

        Logger.getRootLogger().setLevel(Level.INFO);

        latch= new CountDownLatch(3);

        String service= SERVER + "/services";

        serviceClient= WebReactiveFeign

                .builder()

                .target(ServiceClient.class, service);

        String route= SERVER + "/routes";

        routeClient= WebReactiveFeign

                .builder()

                .target(RouteClient.class, route);

        String plugin= SERVER + "/plugins";

        pluginClient= WebReactiveFeign

                .builder()

                .target(PluginClient.class, plugin);

}

    @Test

    public void aggressionTest() throws InterruptedException {

        long current= System.currentTimeMillis();

        System.out.println("開(kāi)始調(diào)用聚合查詢");

        serviceTest();

        routeTest();

        pluginTest();

        latch.await();

        System.out.println("調(diào)用聚合查詢結(jié)束!耗時(shí):" + (System.currentTimeMillis() - current) + "毫秒");

}

    @Test

    public void serviceTest(){

        long current= System.currentTimeMillis();

        System.out.println("開(kāi)始獲取Service");

        serviceClient.list()

                .subscribe(result ->{

                    System.out.println(result);

                    latch.countDown();

                    System.out.println("獲取Service結(jié)束!耗時(shí):" + (System.currentTimeMillis() - current) + "毫秒");

});

}

    @Test

    public void routeTest(){

        long current= System.currentTimeMillis();

        System.out.println("開(kāi)始獲取Route");

        routeClient.list()

                .subscribe(result ->{

                    System.out.println(result);

                    latch.countDown();

                    System.out.println("獲取Route結(jié)束!耗時(shí):" + (System.currentTimeMillis() - current) + "毫秒");

});

}

    @Test

    public void pluginTest(){

        long current= System.currentTimeMillis();

        System.out.println("開(kāi)始獲取Plugin");

        pluginClient.list()

                .subscribe(result ->{

                    System.out.println(result);

                    latch.countDown();

                    System.out.println("獲取Plugin結(jié)束!耗時(shí):" + (System.currentTimeMillis() - current) + "毫秒");

});

}

}

這里的關(guān)鍵點(diǎn)就在于原本同步阻塞的請(qǐng)求,現(xiàn)在改成異步非阻塞了,所以需要使用CountDownLatch來(lái)同步,在獲取到接口后調(diào)用CountDownLatch.coutdown(),在調(diào)用所有接口請(qǐng)求后調(diào)用CountDownLatch.await()等待所有的接口返回結(jié)果再進(jìn)行下一步操作!

測(cè)試結(jié)果:

開(kāi)始調(diào)用聚合查詢

開(kāi)始獲取Service

開(kāi)始獲取Route

開(kāi)始獲取Plugin

{"next":null,"data":[]}

{"next":null,"data":[]}

獲取Plugin結(jié)束!耗時(shí):215毫秒

{"next":null,"data":[]}

獲取Route結(jié)束!耗時(shí):216毫秒

獲取Service結(jié)束!耗時(shí):1000毫秒

調(diào)用聚合查詢結(jié)束!耗時(shí):1000毫秒

Process finished with exit code 0

顯然,聚合查詢所消耗的時(shí)間不再等于所有接口請(qǐng)求的時(shí)間之和,而是接口請(qǐng)求時(shí)間中的最大值!

下面開(kāi)始性能測(cè)試:

普通Feign接口聚合測(cè)試調(diào)用1000次:

開(kāi)始調(diào)用聚合查詢
開(kāi)始獲取Service
{"next":null,"data":[]}
獲取Service結(jié)束!耗時(shí):169毫秒
開(kāi)始獲取Route
{"next":null,"data":[]}
獲取Route結(jié)束!耗時(shí):81毫秒
開(kāi)始獲取Plugin
{"next":null,"data":[]}
獲取Plugin結(jié)束!耗時(shí):93毫秒
調(diào)用聚合查詢結(jié)束!耗時(shí):343毫秒
summary: 238515, average: 238

使用WebClient進(jìn)行接口聚合查詢1000次:

開(kāi)始調(diào)用聚合查詢
開(kāi)始獲取Service
開(kāi)始獲取Route
開(kāi)始獲取Plugin
{"next":null,"data":[]}
{"next":null,"data":[]}
獲取Route結(jié)束!耗時(shí):122毫秒
{"next":null,"data":[]}
獲取Service結(jié)束!耗時(shí):122毫秒
獲取Plugin結(jié)束!耗時(shí):121毫秒
調(diào)用聚合查詢結(jié)束!耗時(shí):123毫秒
summary: 89081, average: 89

測(cè)試結(jié)果中,WebClient的測(cè)試結(jié)果恰好相當(dāng)于普通FeignClient的三分之一!正好在意料之中!

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://m.hztianpu.com/yun/75367.html

相關(guān)文章

  • Spring Cloud Alibaba基礎(chǔ)教程:支持幾種服務(wù)消費(fèi)方式

    摘要:那么為什么可以帶給我們這樣的完美編碼體驗(yàn)?zāi)貙?shí)際上,這完全歸功于的封裝,由于在服務(wù)注冊(cè)與發(fā)現(xiàn)客戶端負(fù)載均衡等方面都做了很好的抽象,而上層應(yīng)用方面依賴的都是這些抽象接口,而非針對(duì)某個(gè)具體中間件的實(shí)現(xiàn)。 通過(guò)《Spring Cloud Alibaba基礎(chǔ)教程:使用Nacos實(shí)現(xiàn)服務(wù)注冊(cè)與發(fā)現(xiàn)》一文的學(xué)習(xí),我們已經(jīng)學(xué)會(huì)如何使用Nacos來(lái)實(shí)現(xiàn)服務(wù)的注冊(cè)與發(fā)現(xiàn),同時(shí)也介紹如何通過(guò)LoadBala...

    curlyCheng 評(píng)論0 收藏0
  • SpringCloud升級(jí)之路2020.0.x版-37. 實(shí)現(xiàn)異步客戶端封裝配置管理意義與設(shè)計(jì)

    摘要:對(duì)于異步的請(qǐng)求,使用的是異步客戶端即。要實(shí)現(xiàn)的配置設(shè)計(jì)以及使用舉例要實(shí)現(xiàn)的配置設(shè)計(jì)以及使用舉例首先,我們要實(shí)現(xiàn)的,其包含三個(gè)重試重試的要在負(fù)載均衡之前,因?yàn)橹卦嚨臅r(shí)候,我們會(huì)從負(fù)載均衡器獲取另一個(gè)實(shí)例進(jìn)行重試,而不是在同一個(gè)實(shí)例上重試多次。 本系列代碼地址:https://github.com/JoJoTec/spring-cloud-parent 為何需要封裝異步 HT...

    fxp 評(píng)論0 收藏0
  • Spring Cloud 參考文檔(Spring Cloud Commons:通用抽象)

    摘要:通用的抽象服務(wù)發(fā)現(xiàn)負(fù)載均衡和斷路器等模式適用于所有客戶端都可以使用的通用抽象層,獨(dú)立于實(shí)現(xiàn)例如,使用或發(fā)現(xiàn)。重試失敗的請(qǐng)求可以將負(fù)載均衡的配置為重試失敗的請(qǐng)求,默認(rèn)情況下,禁用此邏輯,你可以通過(guò)將添加到應(yīng)用程序的類路徑來(lái)啟用它。 Spring Cloud Commons:通用的抽象 服務(wù)發(fā)現(xiàn)、負(fù)載均衡和斷路器等模式適用于所有Spring Cloud客戶端都可以使用的通用抽象層,獨(dú)立于實(shí)...

    yangrd 評(píng)論0 收藏0
  • SpringCloud打造微服務(wù)平臺(tái)--概覽

    摘要:授權(quán)框架使第三方應(yīng)用程序來(lái)獲取對(duì)服務(wù)的有限訪問(wèn)機(jī)會(huì)。無(wú)論是通過(guò)編排資源所有者和服務(wù)之間的交互批準(zhǔn)的資源所有者,或通過(guò)允許第三方應(yīng)用程序來(lái)獲取自己的訪問(wèn)權(quán)限。 SpringCloud打造微服務(wù)平臺(tái)--概覽 簡(jiǎn)述 SpringCloud是什么 Spring Boot和SpringCloud是什么關(guān)系 Spring Boot是Spring的一套快速WEB開(kāi)發(fā)的腳手架,可建立獨(dú)立的Sprin...

    siberiawolf 評(píng)論0 收藏0
  • Spring Cloud 參考文檔(聲明式REST客戶端:Feign)

    摘要:繼承支持通過(guò)單繼承接口支持樣板,這允許將通用操作分組為方便的基本接口。,記錄基本信息以及請(qǐng)求和響應(yīng)。例如,類定義參數(shù)和以下客戶端使用注解使用類 聲明式REST客戶端:Feign Feign是一個(gè)聲明式的Web服務(wù)客戶端,它使編寫(xiě)Web服務(wù)客戶端變得更容易,要使用Feign,請(qǐng)創(chuàng)建一個(gè)接口并對(duì)其進(jìn)行注解,它具有可插拔的注解支持,包括Feign注解和JAX-RS注解,F(xiàn)eign還支持可插拔...

    wqj97 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

閱讀需要支付1元查看
<