自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

聽我一句勸,業(yè)務(wù)代碼中,別用多線程

開發(fā) 前端
Http 調(diào)用或者 RPC 調(diào)用,框架中本來就已經(jīng)有一個(gè)線程池了,而且也給你提供了對(duì)應(yīng)的性能調(diào)優(yōu)參數(shù)配置,那么首先考慮的應(yīng)該是把這個(gè)線程池充分利用起來。

你好呀,我是歪歪。

前幾天我在網(wǎng)上沖浪,看到一個(gè)哥們?cè)谕虏郏f他工作三年多了,沒使用過多線程。

雖然八股文背的滾瓜爛熟,但是沒有在實(shí)際開發(fā)過程中寫的都是業(yè)務(wù)代碼,沒有使用過線程池,心里還是慌得一比。

我只是微微一笑,這不是很正常嗎?

業(yè)務(wù)代碼中一般也使不上多線程,或者說,業(yè)務(wù)代碼中不知不覺你以及在使用線程池了,你再 duang 的一下搞一個(gè)出來,反而容易出事。

所以提到線程池的時(shí)候,我個(gè)人的觀點(diǎn)是必須把它吃得透透的,但是在業(yè)務(wù)代碼中少用或者不用多線程。

關(guān)于這個(gè)觀點(diǎn),我給你盤一下。

Demo

首先我們還是花五分鐘搭個(gè) Demo 出來。

我手邊剛好有一個(gè)之前搭的一個(gè)關(guān)于 Dubbo 的 Demo,消費(fèi)者、生產(chǎn)者都有,我就直接拿來用了:

圖片圖片

這個(gè) Demo 我也是跟著網(wǎng)上的 quick start 搞的:https://cn.dubbo.apache.org/zh-cn/overview/quickstart/java/spring-boot/

圖片圖片

可以說寫的非常詳細(xì)了,你就跟著官網(wǎng)的步驟一步步的搞就行了。

我這個(gè) Demo 稍微不一樣的是我在消費(fèi)者模塊里面搞了一個(gè) Http 接口:

圖片圖片

在接口里面發(fā)起了 RPC 調(diào)用,模擬從前端頁面發(fā)起請(qǐng)求的場景,更加符合我們的開發(fā)習(xí)慣。

而官方的示例中,是基于了 SpringBoot 的 CommandLineRunner 去發(fā)起調(diào)用:

圖片圖片

只是發(fā)起調(diào)用的方式不一樣而已,其他沒啥大區(qū)別。

需要說明的是,我只是手邊剛好有一個(gè) Dubbo 的 Demo,隨手就拿來用了,但是本文想要表達(dá)的觀點(diǎn),和你使不使用 Dubbo 作為 RPC 框架,沒有什么關(guān)系,道理是通用的。

上面這個(gè) Demo 啟動(dòng)起來之后,通過 Http 接口發(fā)起一次調(diào)用,看到控制臺(tái)服務(wù)提供方和服務(wù)消費(fèi)方都有對(duì)應(yīng)的日志輸出,準(zhǔn)備工作就算是齊活兒了:

圖片圖片

上菜

在上面的 Demo 中,這是消費(fèi)者的代碼:

圖片圖片

這是提供者的代碼:

圖片圖片

整個(gè)調(diào)用鏈路非常的清晰:

圖片圖片

來,請(qǐng)你告訴我這里面有線程池嗎?

沒有!

是的,在日常的開發(fā)中,我就是寫個(gè)接口給別人調(diào)用嘛,在我的接口里面并沒有線程池相關(guān)的代碼,只有 CRUD 相關(guān)的業(yè)務(wù)代碼。

同時(shí),在日常的開發(fā)中,我也經(jīng)常調(diào)用別人提供給我的接口,也是一把梭,擼到底,根本就不會(huì)用到線程池。

所以,站在我,一個(gè)開發(fā)人員的角度,這個(gè)里面沒有線程池。

合理,非常合理。

但是,當(dāng)我們換個(gè)角度,再看看,它也是可以有的。

比如這樣:

圖片圖片

反應(yīng)過來沒有?

我們發(fā)起一個(gè) Http 調(diào)用,是由一個(gè) web 容器來處理這個(gè)請(qǐng)求的,你甭管它是 Tomcat,還是 Jetty、Netty、Undertow 這些玩意,反正是個(gè) web 容器在處理。

那你說,這個(gè)里面有線程池嗎?

在方法入口處打個(gè)斷點(diǎn),這個(gè) http-nio-8081-exec-1 不就是 Tomcat 容器線程池里面的一個(gè)線程嗎:

圖片圖片

通過 dump 堆棧信息,過濾關(guān)鍵字可以看到這樣的線程,在服務(wù)啟動(dòng)起來,啥也沒干的情況下,一共有 10 個(gè):

圖片圖片

朋友,這不就是線程池嗎?

雖然不是你寫的,但是你確實(shí)用了。

我寫出來的這個(gè) test 接口,就是會(huì)由 web 容器中的一個(gè)線程來進(jìn)行調(diào)用。所以,站在 web 容器的角度,這里是有一個(gè)線程池的:

圖片圖片

同理,在 RPC 框架中,不管是消費(fèi)方,還是服務(wù)提供方,也都存在著線程池。

比如 Dubbo 的線程池,你可以看一下官方的文檔:

https://cn.dubbo.apache.org/zh-cn/overview/mannual/java-sdk/advanced-features-and-usage/performance/threading-model/

圖片圖片

而對(duì)于大多數(shù)的框架來說,它絕不可能只有一個(gè)線程池,為了做資源隔離,它會(huì)啟用好幾個(gè)線程池,達(dá)到線程池隔離,互不干擾的效果。

比如參與 Dubbo 一次調(diào)用的其實(shí)不僅一個(gè)線程池,至少還有 IO 線程池和業(yè)務(wù)線程池,它們各司其職:

圖片圖片

我們主要關(guān)注這個(gè)業(yè)務(wù)線程池。

反正站在 Dubbo 框架的角度,又可以補(bǔ)充一下這個(gè)圖片了:

圖片圖片

那么問題來了,在當(dāng)前的這個(gè)情況下?

當(dāng)有人反饋:哎呀,這個(gè)服務(wù)吞吐量怎么上不去???

你怎么辦?

你會(huì) duang 的一下在業(yè)務(wù)邏輯里面加一個(gè)線程池嗎?

圖片圖片

大哥,前面有個(gè) web 容器的線程池,后面有個(gè)框架的線程池,兩頭不調(diào)整,你在中間加個(gè)線程池,加它有啥用???

web 容器,拿 Tomcat 來說,人家給你提供了線程池參數(shù)調(diào)整的相關(guān)配置,這么一大坨配置,你得用起來啊:

https://tomcat.apache.org/tomcat-9.0-doc/config/executor.html

圖片圖片

再比如 Dubbo 框架,都給你明說了,這些參數(shù)屬于性能調(diào)優(yōu)的范疇,感覺不對(duì)勁了,你先動(dòng)手調(diào)調(diào)?。?/p>

圖片圖片

你把這些參數(shù)調(diào)優(yōu)弄好了,絕對(duì)比你直接懟個(gè)線程池在業(yè)務(wù)代碼中,效果好的多。

甚至,你在業(yè)務(wù)代碼中加入一個(gè)線程池之后,反而會(huì)被“反噬”。

比如,你 duang 的一下懟個(gè)線程池在這里,我們先只看 web 容器和業(yè)務(wù)代碼對(duì)應(yīng)的部分:

圖片圖片

由于你的業(yè)務(wù)代碼中有線程池的存在,所以當(dāng)接受到一個(gè) web 請(qǐng)求之后,立馬就把請(qǐng)求轉(zhuǎn)發(fā)到了業(yè)務(wù)線程池中,由線程池中的線程來處理本次請(qǐng)求,從而釋放了 web 請(qǐng)求對(duì)應(yīng)的線程,該線程又可以里面去處理其他請(qǐng)求。

這樣來看,你的吞吐量確實(shí)上去了。

在前端來看,非常的 nice,請(qǐng)求立馬得到了響應(yīng)。

但是,你考慮過下游嗎?

你的吞吐量上漲了,下游同一時(shí)間處理的請(qǐng)求就變多了。如果下游跟不上處理,頂不住了,直接就是崩給你看怎么辦?

圖片圖片

而且下游不只是你一個(gè)調(diào)用方,由于你調(diào)用的太猛,導(dǎo)致其他調(diào)用方的請(qǐng)求響應(yīng)不過來,是會(huì)引起連鎖反應(yīng)的。

所以,這種場景下,為了異步懟個(gè)線程池放著,我覺得還不如用消息隊(duì)列來實(shí)現(xiàn)異步化,頂天了也就是消息堆積嘛,總比服務(wù)崩了好,這樣更加穩(wěn)妥。

或者至少和下游勾兌一下,問問我們這邊吞吐量上升,你們扛得住不。

有的小伙伴看到這里可能就會(huì)產(chǎn)生一個(gè)疑問了:歪師傅,你這個(gè)講得怎么和我背的八股文不一樣啊?

巧了,你背過的八股文我也背過,現(xiàn)在我們來溫習(xí)一下我們背過的八股文。

什么時(shí)候使用線程池呢?

比如一個(gè)請(qǐng)求要經(jīng)過若干個(gè)服務(wù)獲取數(shù)據(jù),且這些數(shù)據(jù)沒有先后依賴,最終需要把這些數(shù)據(jù)組合起來,一并返回,這樣經(jīng)典的場景:

圖片圖片

用戶點(diǎn)商品詳情,你要等半天才展示給用戶,那用戶肯定罵罵咧咧的久走了。

這個(gè)時(shí)候,八股文上是怎么說的:用線程池來把串行的動(dòng)作改成并行。

圖片圖片

這個(gè)場景也是增加了服務(wù) A 的吞吐量,但是用線程池就是非常正確的,沒有任何毛病。

但是你想想,我們最開始的這個(gè)案例,是這個(gè)場景嗎?

圖片圖片

我們最開始的案例是想要在業(yè)務(wù)邏輯中增加一個(gè)線程池,對(duì)著一個(gè)下游服務(wù)就是一頓猛攻,不是所謂的串行改并行,而是用更多的線程,帶來更多的串行。

這已經(jīng)不是一個(gè)概念了。

還有一種場景下,使用線程池也是合理的。

比如你有一個(gè)定時(shí)任務(wù),要從數(shù)據(jù)庫中撈出狀態(tài)為初始化的數(shù)據(jù),然后去調(diào)用另外一個(gè)服務(wù)的接口查詢數(shù)據(jù)的最終狀態(tài)。

圖片圖片

如果你的業(yè)務(wù)代碼是這樣的:

//獲取訂單狀態(tài)為初始化的數(shù)據(jù)(0:初始化 1:處理中 2:成功 3:失敗)
//select * from order where order_status=0;
ArrayList initOrderInfoList = queryInitOrderInfoList();
//循環(huán)處理這批數(shù)據(jù)
for(OrderInfo orderInfo : initOrderInfoList){
    //捕獲異常以免一條數(shù)據(jù)錯(cuò)誤導(dǎo)致循環(huán)結(jié)束
    try{
        //發(fā)起rpc調(diào)用
        String orderStatus = queryOrderStatus(orderInfo.getOrderId);
        //更新訂單狀態(tài)
        updateOrderInfo(orderInfo.getOrderId,orderStatus);  
    } catch (Exception e){
        //打印異常
    }
}

雖然你框架中使用了線程池,但是你就是在一個(gè) for 循環(huán)中不停的去調(diào)用下游服務(wù)查詢數(shù)據(jù)狀態(tài),是一條數(shù)據(jù)一條數(shù)據(jù)的進(jìn)行處理,所以其實(shí)同一時(shí)間,只是使用了框架的線程池中的一個(gè)線程。

為了更加快速的處理完這批數(shù)據(jù),這個(gè)時(shí)候,你就可以懟一個(gè)線程池放在 for 循環(huán)里面了:

//循環(huán)處理這批數(shù)據(jù)
for(OrderInfo orderInfo : initOrderInfoList){
    //使用線程池
    executor.execute(() -> {
        //捕獲異常以免一條數(shù)據(jù)錯(cuò)誤導(dǎo)致循環(huán)結(jié)束
        try {
            //發(fā)起rpc調(diào)用
            String orderStatus = queryOrderStatus(orderInfo.getOrderId);
            //更新訂單狀態(tài)
            updateOrderInfo(orderInfo.getOrderId, orderStatus);
        } catch (Exception e) {
            //打印異常
        }
    });
}

圖片圖片

需要注意的是,這個(gè)線程池的參數(shù)怎么去合理的設(shè)置,是需要考慮的事情。

同時(shí)這個(gè)線程池的定位,就類似于 web 容器線程池的定位。

或者這樣對(duì)比起來看更加清晰一點(diǎn):

圖片圖片

定時(shí)任務(wù)觸發(fā)的時(shí)候,在發(fā)起遠(yuǎn)程接口調(diào)用之前,沒有線程池,所以我們可以啟用一個(gè)線程池來加快數(shù)據(jù)的處理。

而 Http 調(diào)用或者 RPC 調(diào)用,框架中本來就已經(jīng)有一個(gè)線程池了,而且也給你提供了對(duì)應(yīng)的性能調(diào)優(yōu)參數(shù)配置,那么首先考慮的應(yīng)該是把這個(gè)線程池充分利用起來。

如果僅僅是因?yàn)楫惒交罂梢蕴嵘?wù)響應(yīng)速度,沒有達(dá)到串行改并行的效果,那么我更加建議使用消息隊(duì)列。

好了,本文的技術(shù)部分就到這里啦。

責(zé)任編輯:武曉燕 來源: why技術(shù)
相關(guān)推薦

2021-05-19 10:09:23

消息隊(duì)列架構(gòu)

2011-03-24 10:24:45

批量數(shù)據(jù)綁定

2011-03-28 15:48:52

批量數(shù)據(jù)綁定

2012-02-09 09:41:22

2021-05-11 15:34:04

Task.Result代碼Winform

2009-03-10 18:10:12

LinuxUbuntu技巧

2013-03-22 10:53:42

PyConPython

2011-06-13 09:25:01

斷號(hào)

2015-08-03 10:21:04

設(shè)計(jì)模式表達(dá)

2020-11-27 09:57:11

Python代碼PyPy

2022-08-01 10:01:11

JavaScript語言代碼庫

2023-07-12 08:01:28

FOADMROADMOXC

2021-12-17 08:55:26

Python微博機(jī)器人

2019-11-15 18:00:18

MySQLSQL數(shù)據(jù)庫

2013-05-10 10:56:09

2023-09-05 23:34:52

Kubernetes云原生

2017-02-24 14:05:14

AndroidMVCMVP

2021-03-10 08:16:06

Nacos集群搭建微服務(wù)

2025-03-13 11:09:47

2016-09-12 15:26:06

戴爾
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)