欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

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 版本之后才對(duì)異步非阻塞進(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)勢(shì)有:

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

webclient的HTTP API請(qǐng)參考:https://github.com/bigbirditedu/webclient

服務(wù)端性能對(duì)比

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

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;
    }
}

啟動(dòng)服務(wù)器

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

在這里插入圖片描述

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

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

在這里插入圖片描述

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

在這里插入圖片描述

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

不斷調(diào)整進(jìn)行測(cè)試;每次開始前先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";
    }
}

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

在這里插入圖片描述

設(shè)置請(qǐng)求路徑

在這里插入圖片描述

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í)幾乎都會(huì)處理不過來,接近100%的請(qǐng)求超時(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")));

        //輸出請(qǐng)求頭
        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

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

webclient

和測(cè)試服務(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是為了啟動(dòng)Tomcat服務(wù)器,測(cè)試時(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秒)

和 不帶連接池相比,錯(cuò)誤率減少

在這里插入圖片描述

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

在這里插入圖片描述

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

webclient連接池

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

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

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

關(guān)于Reactor Netty連接池請(qǐng)參考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í)間)

在這里插入圖片描述

雖然測(cè)試效果幾乎沒有差別,但是我們要清楚地知道調(diào)用block方法是會(huì)引發(fā)實(shí)時(shí)阻塞的,會(huì)一定程度上增加對(duì)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 請(qǐng)求方式,支持 Get、Post、Put、Delete、Head 等方法,可以作為替代 resttemplate 的一個(gè)強(qiáng)有力的工具。

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

小結(jié)

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

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

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

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

相關(guān)文章

  • java獲取中文拼音首字母的實(shí)例

    java獲取中文拼音首字母的實(shí)例

    下面小編就為大家?guī)硪黄猨ava獲取中文拼音首字母的實(shí)例。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2017-09-09
  • Spring框架中Bean的三種配置和實(shí)例化方法總結(jié)

    Spring框架中Bean的三種配置和實(shí)例化方法總結(jié)

    在Spring框架中,Bean的配置和實(shí)例化是很重要的基礎(chǔ)內(nèi)容,掌握各種配置方式,才能靈活管理Bean對(duì)象,本文將全面介紹Bean的別名配置、作用范圍配置,以及構(gòu)造器實(shí)例化、工廠實(shí)例化等方式
    2023-10-10
  • Java虛擬機(jī)運(yùn)行時(shí)棧的棧幀

    Java虛擬機(jī)運(yùn)行時(shí)棧的棧幀

    本節(jié)將會(huì)介紹一下Java虛擬機(jī)棧中的棧幀,會(huì)對(duì)棧幀的組成部分(局部變量表、操作數(shù)棧、動(dòng)態(tài)鏈接、方法出口)分別進(jìn)行介紹,最后還會(huì)通過javap命令反解析編譯后的.class文件,進(jìn)行分析方法執(zhí)行時(shí)的局部變量表、操作數(shù)棧等
    2021-09-09
  • 如何使用NSSM將jar包打成Windows服務(wù)

    如何使用NSSM將jar包打成Windows服務(wù)

    這篇文章主要介紹了如何使用NSSM將jar包打成Windows服務(wù),本文通過圖文并茂的形式給大家介紹的非常詳細(xì),感興趣的朋友跟隨小編一起看看吧
    2024-03-03
  • SpringBoot獲取客戶端的IP地址的實(shí)現(xiàn)示例

    SpringBoot獲取客戶端的IP地址的實(shí)現(xiàn)示例

    在Web應(yīng)用程序中,獲取客戶端的IP地址是一項(xiàng)非常常見的需求,本文主要介紹了SpringBoot獲取客戶端的IP地址的實(shí)現(xiàn)示例,具有一定的參考價(jià)值,感興趣的可以了解一下
    2023-09-09
  • spring boot與ktor整合的實(shí)現(xiàn)方法

    spring boot與ktor整合的實(shí)現(xiàn)方法

    這篇文章主要給大家介紹了關(guān)于spring boot與ktor整合的實(shí)現(xiàn)方法,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-09-09
  • Java中的this和super實(shí)例淺析

    Java中的this和super實(shí)例淺析

    要說this和super就不得不說Java的封裝和繼承了。這篇文章主要介紹了Java中的this和super實(shí)例淺析,需要的朋友可以參考下
    2017-03-03
  • Java 二叉樹遍歷的常用方法

    Java 二叉樹遍歷的常用方法

    二叉樹的遍歷可以說是解決二叉樹問題的基礎(chǔ)。我們常用的遍歷方式無(wú)外乎就四種 前序遍歷、中序遍歷、后續(xù)遍歷、層次遍歷 這四種。
    2021-05-05
  • Mybatis(ParameterType)傳遞多個(gè)不同類型的參數(shù)方式

    Mybatis(ParameterType)傳遞多個(gè)不同類型的參數(shù)方式

    這篇文章主要介紹了Mybatis(ParameterType)傳遞多個(gè)不同類型的參數(shù)方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-04-04
  • Java實(shí)現(xiàn)圖片對(duì)比功能

    Java實(shí)現(xiàn)圖片對(duì)比功能

    個(gè)人從來沒有研究過圖像學(xué),也沒看過什么論文或者相關(guān)文檔,寫這個(gè)完全是靠google和百度,自己寫了個(gè)實(shí)驗(yàn)了下,測(cè)試用例也少,估計(jì)有大BUG的存在,所以看的人權(quán)當(dāng)學(xué)習(xí)交流,切勿生產(chǎn)使用。
    2014-09-09

最新評(píng)論