最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Spring?WebClient實(shí)戰(zhàn)示例

 更新時(shí)間:2022年01月12日 09:55:18   作者:程猿薇蔦  
本文主要介紹了Spring?WebClient實(shí)戰(zhàn)示例,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下

WebClient實(shí)戰(zhàn)

本文代碼地址https://github.com/bigbirditedu/webclient

Spring Webflux 是 Spring Framework 5.0 的新特性,是隨著當(dāng)下流行的 Reactive Programming 而誕生的高性能框架。傳統(tǒng)的 Web 應(yīng)用框架,比如我們所熟知的 Struts2,Spring MVC 等都是基于 Servlet API 和 Servlet 容器之上運(yùn)行的,本質(zhì)上都是阻塞式的。Servlet 直到 3.1 版本之后才對異步非阻塞進(jìn)行了支持。而 WebFlux天生就是一個(gè)典型的異步非阻塞框架,其核心是基于 Reactor 相關(guān) API 實(shí)現(xiàn)的。相比傳統(tǒng)的 Web 框架,WebFlux 可以運(yùn)行在例如 Netty、Undertow 以及 Servlet 3.1 容器之上,其運(yùn)行環(huán)境比傳統(tǒng) Web 框架更具靈活性。

WebFlux 的主要優(yōu)勢有:

  • 非阻塞性:WebFlux 提供了一種比 Servlet 3.1 更完美的異步非阻塞解決方案。非阻塞的方式可以使用較少的線程以及硬件資源來處理更多的并發(fā)。
  • 函數(shù)式編程:函數(shù)式編程是 Java 8 重要的特性,WebFlux 完美支持。

webclient的HTTP API請參考:https://github.com/bigbirditedu/webclient

服務(wù)端性能對比

比較的是Spring MVC 與 Spring WebFlux 作為HTTP 應(yīng)用框架誰的性能更好。

Spring WebFlux

先看看Spring WebFlux

引入pom依賴

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-webflux</artifactId>
        </dependency>

編寫http接口

@RestController
@RequestMapping("/webflux")
public class WebFluxController {

    public static AtomicLong COUNT = new AtomicLong(0);

    @GetMapping("/hello/{latency}")
    public Mono<String> hello(@PathVariable long latency) {
        System.out.println("Start:" + LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS")));
        System.out.println("Page count:" + COUNT.incrementAndGet());
        Mono<String> res = Mono.just("welcome to Spring Webflux").delayElement(Duration.ofSeconds(latency));//阻塞latency秒,模擬處理耗時(shí)
        System.out.println("End:  " + LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS")));
        return res;
    }
}

啟動服務(wù)器

可以看到webflux 默認(rèn)選擇Netty作為服務(wù)器

在這里插入圖片描述

使用JMeter進(jìn)行壓測:File->新建測試計(jì)劃->添加用戶線程組->在線程組上添加一個(gè)取樣器,選擇Http Request

配置Http請求,并在HTTP Request上添加監(jiān)聽器;這里不做復(fù)雜的壓測分析,選擇結(jié)果樹和聚合報(bào)告即可

在這里插入圖片描述

設(shè)置http請求超時(shí)時(shí)間

在這里插入圖片描述

設(shè)置并發(fā)用戶數(shù),60秒內(nèi)全部啟起來;

不斷調(diào)整進(jìn)行測試;每次開始前先Clear All清理一下舊數(shù)據(jù),再點(diǎn)save保存一下,再點(diǎn)Start開始

在這里插入圖片描述

1000用戶,99線大約24毫秒的延遲

在這里插入圖片描述

2000用戶,99線大約59毫秒的延遲

在這里插入圖片描述

3000用戶,99線大約89毫秒的延遲

在這里插入圖片描述

4000用戶

webflux到4000并發(fā)用戶時(shí)還是很穩(wěn)

在這里插入圖片描述

Spring MVC

再來看看SpringMVC的性能

引入pom文件

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

編寫http接口

@RestController
@RequestMapping("/springmvc")
public class SpringMvcController {

    public static AtomicLong COUNT = new AtomicLong(0);

    @GetMapping("/hello/{latency}")
    public String hello(@PathVariable long latency) {
        System.out.println("Start:" + LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS")));
        System.out.println("Page count:" + COUNT.incrementAndGet());
        try {
            //阻塞latency秒,模擬處理耗時(shí)
            TimeUnit.SECONDS.sleep(latency);
        } catch (InterruptedException e) {
            e.printStackTrace();
            return "Exception during thread sleep";
        }
        System.out.println("End:" + LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS")));
        return "welcome to Spring MVC";
    }
}

啟動服務(wù)器??梢钥吹絊pringMVC默認(rèn)選擇Tomcat作為服務(wù)器

在這里插入圖片描述

設(shè)置請求路徑

在這里插入圖片描述

100用戶

在這里插入圖片描述

200用戶

在這里插入圖片描述

300用戶

從300用戶開始,響應(yīng)時(shí)間就開始增加

在這里插入圖片描述

400用戶

在這里插入圖片描述

500用戶

在這里插入圖片描述

550用戶

本例中,傳統(tǒng)Web技術(shù)(Tomcat+SpringMVC)在處理550用戶并發(fā)時(shí),就開始有超時(shí)失敗的

在這里插入圖片描述

600用戶

在處理600用戶并發(fā)時(shí),失敗率就已經(jīng)很高;用戶并發(fā)數(shù)更高時(shí)幾乎都會處理不過來,接近100%的請求超時(shí)。

在這里插入圖片描述

1000用戶

在這里插入圖片描述

2000用戶

在這里插入圖片描述

3000用戶

在這里插入圖片描述

4000用戶

在這里插入圖片描述

客戶端性能比較

我們來比較一下HTTP客戶端的性能。

先建一個(gè)單獨(dú)的基于Springboot的Http Server工程提供標(biāo)準(zhǔn)的http接口供客戶端調(diào)用。

/**
 * Http服務(wù)提供方接口;模擬一個(gè)基準(zhǔn)的HTTP Server接口
 */
@RestController
public class HttpServerController {

    @RequestMapping("product")
    public Product getAllProduct(String type, HttpServletRequest request, HttpServletResponse response) throws InterruptedException {
        long start = System.currentTimeMillis();
        System.out.println("Start:" + LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS")));

        //輸出請求頭
        Enumeration<String> headerNames = request.getHeaderNames();
        while (headerNames.hasMoreElements()) {
            String head = headerNames.nextElement();
            System.out.println(head + ":" + request.getHeader(head));
        }

        System.out.println("cookies=" + request.getCookies());

        Product product = new Product(type + "A", "1", 56.67);
        Thread.sleep(1000);

        //設(shè)置響應(yīng)頭和cookie
        response.addHeader("X-appId", "android01");
        response.addCookie(new Cookie("sid", "1000101111"));
        System.out.println("End:" + LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS")));
        System.out.println("cost:" + (System.currentTimeMillis() - start) + product);

        return product;
    }

    @RequestMapping("products")
    public List<Product> getAllProducts(String type) throws InterruptedException {
        long start = System.currentTimeMillis();
        List<Product> products = new ArrayList<>();
        products.add(new Product(type + "A", "1", 56.67));
        products.add(new Product(type + "B", "2", 66.66));
        products.add(new Product(type + "C", "3", 88.88));
        Thread.sleep(1000);
        System.out.println("cost:" + (System.currentTimeMillis() - start) + products);
        return products;
    }

    @RequestMapping("product/{pid}")
    public Product getProductById(@PathVariable String pid, @RequestParam String name, @RequestParam double price) throws InterruptedException {
        long start = System.currentTimeMillis();
        Product product = new Product(name, pid, price);
        Thread.sleep(1000);
        System.out.println("cost:" + (System.currentTimeMillis() - start) + product);
        return product;
    }

    @RequestMapping("postProduct")
    public Product postProduct(@RequestParam String id, @RequestParam String name, @RequestParam double price) throws InterruptedException {
        long start = System.currentTimeMillis();
        Product product = new Product(name, id, price);
        Thread.sleep(1000);
        System.out.println("cost:" + (System.currentTimeMillis() - start) + product);
        return product;
    }

    @RequestMapping("postProduct2")
    public Product postProduct(@RequestBody Product product) throws InterruptedException {
        long start = System.currentTimeMillis();
        Thread.sleep(1000);
        System.out.println("cost:" + (System.currentTimeMillis() - start) + product);
        return product;
    }

    @RequestMapping("uploadFile")
    public String uploadFile(MultipartFile file, int age) throws InterruptedException {
        long start = System.currentTimeMillis();
        System.out.println("age=" + age);
        String filePath = "";
        try {
            String filename = file.getOriginalFilename();
            //String extension = FilenameUtils.getExtension(file.getOriginalFilename());
            String dir = "D:\\files";
            filePath = dir + File.separator + filename;
            System.out.println(filePath);
            if (!Files.exists(Paths.get(dir))) {
                new File(dir).mkdirs();
            }
            file.transferTo(Paths.get(filePath));
        } catch (IOException e) {
            e.printStackTrace();
        }
        Thread.sleep(1000);
        System.out.println("cost:" + (System.currentTimeMillis() - start));
        return filePath;
    }
}

Tip

其它客戶端代碼請?jiān)L問:https://github.com/bigbirditedu/webclient

webclient

和測試服務(wù)端時(shí)單獨(dú)依賴不同的服務(wù)器相比,這次同時(shí)引入兩個(gè)依賴。

   <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-webflux</artifactId>
   </dependency>

   <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
   </dependency>

引入starter-web是為了啟動Tomcat服務(wù)器,測試時(shí)統(tǒng)一使用Tomcat服務(wù)器跑http客戶端應(yīng)用程序;

引入starter-webflux是為了單獨(dú)使用webclient api,而不是為了使用Netty作為Http服務(wù)器;

500用戶(超時(shí)時(shí)間設(shè)置6秒)

在這里插入圖片描述

1000用戶(超時(shí)時(shí)間設(shè)置6秒)

在這里插入圖片描述

1100用戶(超時(shí)時(shí)間設(shè)置6秒)

可以看到已經(jīng)開始有響應(yīng)超時(shí)的了

在這里插入圖片描述

1200用戶(超時(shí)時(shí)間設(shè)置10秒)

在這里插入圖片描述

resttemplate(不帶連接池)

500用戶(超時(shí)時(shí)間設(shè)置6秒)

在這里插入圖片描述

1000用戶并發(fā)(超時(shí)時(shí)間設(shè)置6秒)

在這里插入圖片描述

1100用戶并發(fā)(超時(shí)時(shí)間設(shè)置6秒)

在這里插入圖片描述

1200用戶(超時(shí)時(shí)間設(shè)置10秒),有少量響應(yīng)超時(shí)

在這里插入圖片描述

resttemplate(帶連接池)

        <dependency>
            <groupId>org.apache.httpcomponents</groupId>
            <artifactId>httpclient</artifactId>
            <version>4.5.6</version>
        </dependency>

500用戶(超時(shí)時(shí)間設(shè)置6秒)

在這里插入圖片描述

1000用戶(超時(shí)時(shí)間設(shè)置6秒)

在這里插入圖片描述

1100用戶(超時(shí)時(shí)間設(shè)置6秒)

和 不帶連接池相比,錯誤率減少

在這里插入圖片描述

1200用戶(超時(shí)時(shí)間設(shè)置10秒),效果比不帶連接池的resttemplate好點(diǎn),但是響應(yīng)耗時(shí)普遍還是比帶連接池的webclient高

在這里插入圖片描述

綜合來看,是否使用http連接池對于單個(gè)接口影響有限,池的效果不明顯;在多http地址、多接口路由時(shí)連接池的效果可能更好。

webclient連接池

默認(rèn)情況下,WebClient使用連接池運(yùn)行。池的默認(rèn)設(shè)置是最大500個(gè)連接和最大1000個(gè)等待請求。如果超過此配置,就會拋異常。

reactor.netty.internal.shaded.reactor.pool.PoolAcquirePendingLimitException: Pending acquire queue has reached its maximum size of 1000

報(bào)錯日志顯示已經(jīng)達(dá)到了默認(rèn)的掛起隊(duì)列長度限制1000,因此我們可以自定義線程池配置,以獲得更高的性能。

關(guān)于Reactor Netty連接池請參考Netty官方和Spring官方的文檔:

https://projectreactor.io/docs/netty/snapshot/reference/index.html#_connection_pool_2

https://docs.spring.io/spring-framework/docs/current/reference/html/web-reactive.html#webflux-client-builder-reactor-resources

1000用戶(超時(shí)時(shí)間設(shè)置6秒)

在這里插入圖片描述

1100用戶(超時(shí)時(shí)間設(shè)置6秒)

帶連接池的效果好些,沒有出現(xiàn)失敗的

在這里插入圖片描述

1200用戶(超時(shí)時(shí)間設(shè)置10秒),響應(yīng)延遲比默認(rèn)配置的webclient好些

在這里插入圖片描述

webclient阻塞方式獲取結(jié)果;不自定義webclient線程池配置,2000用戶(JMeter不配置超時(shí)時(shí)間)

在這里插入圖片描述

webclient+CompletableFuture方式獲取結(jié)果;不自定義webclient線程池配置,2000用戶(JMeter不配置超時(shí)時(shí)間)

在這里插入圖片描述

雖然測試效果幾乎沒有差別,但是我們要清楚地知道調(diào)用block方法是會引發(fā)實(shí)時(shí)阻塞的,會一定程度上增加對CPU的消耗;

實(shí)際開發(fā)中通常是為了使用異步特性才用webclient,如果用block方式就白瞎了webclient了,還不如直接用restTemplate。

2000用戶性能比較

pooled webclient

在這里插入圖片描述

rest

在這里插入圖片描述

pooled rest

在這里插入圖片描述

3000用戶性能比較

pooled webclient

在這里插入圖片描述

rest

在這里插入圖片描述

pooled rest

在這里插入圖片描述

webclient 的HTTP API

WebClient 作為一個(gè) HTTP 客戶端工具,其提供了標(biāo)準(zhǔn) HTTP 請求方式,支持 Get、Post、Put、Delete、Head 等方法,可以作為替代 resttemplate 的一個(gè)強(qiáng)有力的工具。

API演示代碼地址:https://github.com/bigbirditedu/webclient

小結(jié)

使用webClient在等待遠(yuǎn)程響應(yīng)的同時(shí)不會阻塞本地正在執(zhí)行的線程 ;本地線程處理完一個(gè)請求緊接著可以處理下一個(gè),能夠提高系統(tǒng)的吞吐量;而restTemplate 這種方式是阻塞的,會一直占用當(dāng)前線程資源,直到http返回響應(yīng)。如果等待的請求發(fā)生了堆積,應(yīng)用程序?qū)?chuàng)建大量線程,直至耗盡線程池所有可用線程,甚至出現(xiàn)OOM。另外頻繁的CPU上下文切換,也會導(dǎo)致性能下降。

但是作為上述兩種方式的調(diào)用方(消費(fèi)者)而言,其最終獲得http響應(yīng)結(jié)果的耗時(shí)并未減少。比如文章案例中,通過瀏覽器訪問后端的的兩個(gè)接口(SpringMVC、SpringWebFlux)時(shí),返回?cái)?shù)據(jù)的耗時(shí)相同。即最終獲取(消費(fèi))數(shù)據(jù)的地方還會等待。

使用webclient替代restTemplate的好處是可以異步等待http響應(yīng),使得線程不需要阻塞;單位時(shí)間內(nèi)有限資源下支持更高的并發(fā)量。但是建議webclient和webflux配合使用,使整個(gè)流程全異步化;如果單獨(dú)使用webclient,筆者實(shí)測,和resttemplate差別不大!歡迎留言指教!

到此這篇關(guān)于Spring WebClient實(shí)戰(zhàn)示例的文章就介紹到這了,更多相關(guān)Spring WebClient 內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • SpringTask-Timer實(shí)現(xiàn)定時(shí)任務(wù)的詳細(xì)代碼

    SpringTask-Timer實(shí)現(xiàn)定時(shí)任務(wù)的詳細(xì)代碼

    在項(xiàng)目中開發(fā)定時(shí)任務(wù)應(yīng)該一種比較常見的需求,今天通過示例代碼給大家講解SpringTask-Timer實(shí)現(xiàn)定時(shí)任務(wù)的相關(guān)知識,感興趣的朋友一起看看吧
    2024-06-06
  • 解決J2EE-session在瀏覽器關(guān)閉后失效問題

    解決J2EE-session在瀏覽器關(guān)閉后失效問題

    最近做項(xiàng)目使用的是Spring+SpringMVC+Mybatis框架,maven管理目錄的javaweb端系統(tǒng),對于session的一些問題,在此小編給大家分享到腳本之家平臺,需要的朋友參考下吧
    2018-01-01
  • SpringMVC異常處理知識點(diǎn)總結(jié)

    SpringMVC異常處理知識點(diǎn)總結(jié)

    在本篇文章里小編給大家整理的是關(guān)于SpringMVC異常處理相關(guān)知識點(diǎn)內(nèi)容,需要的朋友們學(xué)習(xí)下。
    2019-10-10
  • Java反射通過Getter方法獲取對象VO的屬性值過程解析

    Java反射通過Getter方法獲取對象VO的屬性值過程解析

    這篇文章主要介紹了Java反射通過Getter方法獲取對象VO的屬性值過程解析,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-02-02
  • Springboot實(shí)現(xiàn)視頻上傳及壓縮功能

    Springboot實(shí)現(xiàn)視頻上傳及壓縮功能

    這篇文章主要介紹了Springboot實(shí)現(xiàn)視頻上傳及壓縮功能,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2023-03-03
  • java實(shí)現(xiàn)俄羅斯方塊

    java實(shí)現(xiàn)俄羅斯方塊

    這篇文章主要為大家詳細(xì)介紹了java實(shí)現(xiàn)俄羅斯方塊,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2018-06-06
  • 一文簡介Java中BlockingQueue阻塞隊(duì)列

    一文簡介Java中BlockingQueue阻塞隊(duì)列

    本文主要介紹了一文簡介Java中BlockingQueue阻塞隊(duì)列,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-06-06
  • 關(guān)于ArrayList的動態(tài)擴(kuò)容機(jī)制解讀

    關(guān)于ArrayList的動態(tài)擴(kuò)容機(jī)制解讀

    這篇文章主要介紹了關(guān)于ArrayList的動態(tài)擴(kuò)容機(jī)制解讀,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-10-10
  • jenkins配置詳細(xì)指南(附j(luò)dk多個(gè)版本配置)

    jenkins配置詳細(xì)指南(附j(luò)dk多個(gè)版本配置)

    Jenkins是一款CICD(持續(xù)集成與持續(xù)交付)工具,Jenkins可以幫你在寫完代碼后,一鍵完成開發(fā)過程中的一系列自動化部署的工作,這篇文章主要給大家介紹了關(guān)于jenkins配置的相關(guān)資料,文中還附j(luò)dk多個(gè)版本配置指南,需要的朋友可以參考下
    2024-02-02
  • MyBatisPlus-QueryWrapper多條件查詢及修改方式

    MyBatisPlus-QueryWrapper多條件查詢及修改方式

    這篇文章主要介紹了MyBatisPlus-QueryWrapper多條件查詢及修改方式,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-06-06

最新評論

中方县| 南和县| 光山县| 西平县| 和林格尔县| 松滋市| 开封县| 开化县| 江安县| 太仆寺旗| 庆云县| 郁南县| 织金县| 永靖县| 迁西县| 定州市| 新源县| 曲水县| 海伦市| 临武县| 五原县| 阳高县| 哈巴河县| 临洮县| 镇坪县| 涞水县| 弥渡县| 新巴尔虎右旗| 临澧县| 大悟县| 扶余县| 永济市| 英超| 迁西县| 孟津县| 抚宁县| 延津县| 北宁市| 合水县| 恩施市| 洛阳市|