国产精品无码av有声小说-天堂av2024-久久国产剧情-午夜视频一区-国内自拍xxxx18-男女啪啪免费-在线国产福利-中文字幕在线三区-天天艹日日干-色眯眯视频-天天干天天操天天爱-性欧美丰满熟妇xxxx性久久久-精品国产一区二区在线-污视频网站免费在线观看-98久久久-日日夜夜爽爽-乌克兰性欧美精品高清

歡迎來到酷云建站平臺(tái),全網(wǎng)營銷云系統(tǒng)加盟中心!

海量企業(yè)網(wǎng)站模板 · 任您選擇

美出特色,精出品質(zhì),一切為了企業(yè)更好的營銷

隱藏側(cè)欄
Beta
轉(zhuǎn)載

HTTP服務(wù)異步化改造實(shí)踐

       建站經(jīng)驗(yàn)     2018-04-18     admin     55     0    

  背景

  我們?cè)谌肟趯佑幸粋€(gè)提供HTTP服務(wù)的應(yīng)用。隨著業(yè)務(wù)的復(fù)雜,一個(gè)用戶請(qǐng)求的處理過程,涉及多個(gè)對(duì)后端遠(yuǎn)程服務(wù)的調(diào)用。為了實(shí)現(xiàn)的簡(jiǎn)單,目前都是使用同步方式完成的,也就是在一個(gè)請(qǐng)求的處理過程中,會(huì)占用一個(gè)容器線程進(jìn)行邏輯運(yùn)算和同步遠(yuǎn)程調(diào)用。這種開發(fā)方式的好處是直觀,開發(fā)成本低,但也帶來了一些穩(wěn)定性和資源浪費(fèi)的問題。對(duì)于我們的HTTP服務(wù)來說,同步化的實(shí)現(xiàn)帶來下面這3個(gè)問題。

  下游服務(wù)超時(shí)帶來的服務(wù)可用性問題。一部分的請(qǐng)求超時(shí)會(huì)導(dǎo)致HTTP服務(wù)線程池被占滿,從而導(dǎo)致其它的請(qǐng)求無法獲取到線程資源而失敗。

  性能問題,多個(gè)對(duì)遠(yuǎn)程服務(wù)的調(diào)用串行執(zhí)行,導(dǎo)致服務(wù)響應(yīng)時(shí)間長。

  容量問題,服務(wù)吞吐量受限。每個(gè)請(qǐng)求長時(shí)間占用線程,導(dǎo)致線程得不到充分利用。

  為了解決這些問題,結(jié)合目前使用的技術(shù)棧以及適應(yīng)成本,我們對(duì)HTTP服務(wù)進(jìn)行了一次異步化改造。

  


 

  解決方案

  異步化編程中聞名的Callback Hell,讓不少同學(xué)望而止步。當(dāng)業(yè)務(wù)復(fù)雜的時(shí)候,各種call back互相嵌套,使代碼變得更加容易出錯(cuò)和不易理解。業(yè)內(nèi)也有有不少框架提供了異步化編程支持,有以下三個(gè)思路:

  纖程

  纖程可以認(rèn)為是輕量級(jí)的用戶線程,脫離了OS的調(diào)度機(jī)制,在應(yīng)用級(jí)別進(jìn)行調(diào)度管理。由于它只維護(hù)了基本的執(zhí)行棧信息,并不立即分配執(zhí)行資源,因此,它可以輕松創(chuàng)建成千上萬的纖程(受內(nèi)存大小的限制),通過極少的線程完成對(duì)纖程的調(diào)度執(zhí)行。這個(gè)方向的代表有微信團(tuán)隊(duì)開源的libco,以及在語言層面上支持的Go語言等。libco hook了底層IO相關(guān)的系統(tǒng)函數(shù),通過底層IO事件驅(qū)動(dòng)纖程的調(diào)度執(zhí)行。當(dāng)遇到同步調(diào)用網(wǎng)絡(luò)請(qǐng)求時(shí),libco自動(dòng)注冊(cè)回調(diào)監(jiān)聽器,并讓出CPU。而在IO事件完成或者超時(shí)候,自動(dòng)恢復(fù)纖程,然后調(diào)度執(zhí)行。它的實(shí)現(xiàn)機(jī)制決定了它非常適合依賴耗時(shí)IO服務(wù)的實(shí)現(xiàn)。承載了微信千萬級(jí)調(diào)用的一個(gè)基石。不過遺憾的是,libco是一個(gè)高效的c/c++協(xié)程庫,并沒有在JVM上實(shí)現(xiàn)。

  Quasar是在JVM之上實(shí)現(xiàn)了纖程機(jī)制,基本可以在Quasar的類庫基礎(chǔ)上,以同步的模式來編寫異步的代碼。在真正執(zhí)行代碼前,通過編譯或者Instrument Agent的形式織入相關(guān)的字節(jié)碼。從頭起步引入纖程還是一個(gè)不錯(cuò)的選擇。對(duì)現(xiàn)有項(xiàng)目的改造,需要對(duì)現(xiàn)有的線程類修改成纖程類,這需要改動(dòng)我們底層非常多的中間件。另外業(yè)內(nèi)公布的使用經(jīng)驗(yàn)較少,后續(xù)可以持續(xù)關(guān)注它的發(fā)展。

  Actor模型

  Actor模型其實(shí)不是什么新概念了。近些年有逐漸流行的趨勢(shì)。Actor模型中一個(gè)核心概念就是Actor實(shí)體。每個(gè)Actor實(shí)體負(fù)責(zé)一個(gè)邏輯計(jì)算。傳統(tǒng)并發(fā)編程都是基于共享內(nèi)存的方式來達(dá)到多線程之間的通訊的目的。Actor之間不共享數(shù)據(jù),也不直接通訊,而是發(fā)送或者接受mailbox/queque中的消息來達(dá)到通訊的目的。Actor之間通過消息來驅(qū)動(dòng)。正式由于發(fā)送者與接受者的分離,是的Actor具有內(nèi)在的并發(fā)特性,它可以不用考慮actor之間的同步問題,不受限制的調(diào)度執(zhí)行收到消息的Actor,從而優(yōu)化了IO等待的問題。Scala,Golang等在語言層面支持Actor模型。Scala的新版中,推出Akka來完成Actor模型,并有了Java版本。但是需要引入新的API,對(duì)現(xiàn)有業(yè)務(wù)代碼塊改造成Actor模型,對(duì)現(xiàn)有代碼改動(dòng)較大。

  RX

  Rx也是一種編程模型,它嘗試提供統(tǒng)一的異步編程接口封裝來操作一個(gè)可觀察的數(shù)據(jù)流。其吸收了函數(shù)式編程的優(yōu)秀思想,并將觀察者,迭代器模式實(shí)現(xiàn)的淋漓精致。當(dāng)下流行的語言,基本都有相應(yīng)的實(shí)現(xiàn)。 如RxJava類庫,即提供了java版本的實(shí)現(xiàn),RxJava在Netflix的Zuul項(xiàng)目中得到成功的應(yīng)用。Rx看起來更像是一種編程思想的突破。它提供了統(tǒng)一的函數(shù)式的風(fēng)格編程接口來簡(jiǎn)化異步程序的編寫,同時(shí)內(nèi)部也通過callback機(jī)制,比Actor能獲得更好的響應(yīng)速度。在調(diào)研過程中,我們發(fā)現(xiàn)它同樣要求對(duì)現(xiàn)有代碼做較大改動(dòng),并將之前的同步模式轉(zhuǎn)換成函數(shù)式編程風(fēng)格。

  綜合來看,以上一些優(yōu)秀的框架并不能立即利用到我們的項(xiàng)目中,引入成本還是很高的。結(jié)合現(xiàn)有技術(shù)架構(gòu)上,以及產(chǎn)品正在快速迭代的環(huán)境下,我們對(duì)HTTP服務(wù)進(jìn)行了一次輕量級(jí)的異步化改造。這次改造,引入Graph-Based Execution Engine來解決服務(wù)之間復(fù)雜的依賴關(guān)系,集中管理異步狀態(tài)。結(jié)合Servlet 3.0提供了請(qǐng)求及釋放tomcat容器線程的接口,充分利用Servlet容器線程資源。最后,通過spring mvc的異步模塊銜接這兩種異步機(jī)制,達(dá)到了全棧異步化的目的。

  原理分析

  Servlet從3.0開始,增加了異步規(guī)范。spring mvc從3.2開始也支持異步Servlet 3.0。針對(duì)現(xiàn)有技術(shù)棧,實(shí)現(xiàn)全棧異步化可以通過下面的一段代碼來說明:

  


 

  可以看到,orderService.createOrderAsync(request) 這個(gè)調(diào)用在請(qǐng)求發(fā)出后,不等待返回結(jié)果,而是立即返回。在返回的future對(duì)象上注冊(cè)了一個(gè)監(jiān)聽器。最后返回DeferredResult。spring mvc在收到返回結(jié)果為DeferredResult(當(dāng)然也可以是WebAsyncTask和Callable)時(shí),將調(diào)用

  AsyncContext context = HttpServletRequest.startAsync(req, response);

  來獲取上下文,然后退出容器線程。當(dāng)createOrderAsync完成得到結(jié)果后,注冊(cè)在future上的監(jiān)聽器被喚起開始執(zhí)行,此處忽略中間的一些處理,直接將RPC結(jié)果設(shè)置在DeferredResult上。spring mvc在獲得執(zhí)行結(jié)果后,通過調(diào)用Servet的上下文

  context.dispatch();

  來通知容器繼續(xù)執(zhí)行后續(xù)操作,例如重新進(jìn)入spring mvc 攔截器的complete流程,最終輸出結(jié)果到客戶端。整個(gè)流程可以用下圖表示:

  


 

  圖中3個(gè)框表示整個(gè)請(qǐng)求被打散在3個(gè)階段執(zhí)行。第一框到第二個(gè)框之間表示RPC服務(wù)正在執(zhí)行。此時(shí)處理請(qǐng)求的線程已經(jīng)釋放。它可以繼續(xù)接受處理其它請(qǐng)求。RPC服務(wù)有返回值或者超時(shí)的時(shí)候,會(huì)在單獨(dú)的一個(gè)線程池中喚起注冊(cè)的監(jiān)聽器。最終通知Servlet容器來繼續(xù)執(zhí)行第三個(gè)框中的interceptor.complete。通過回調(diào)通知的機(jī)制,將使CPU得到充分的利用。避免了啟動(dòng)一個(gè)寶貴的線程來等待IO的完成。

  Graph-Based Execution Engine

  真實(shí)的業(yè)務(wù)場(chǎng)景要比上面的代碼復(fù)雜的多。例如下單業(yè)務(wù),一般都會(huì)依賴用戶,報(bào)價(jià),支付,優(yōu)惠等服務(wù)。服務(wù)之間存在依賴關(guān)系,如黑名單服務(wù)校驗(yàn)通過才能提交訂單。還有一些服務(wù)之間處于對(duì)等關(guān)系,互相之間沒有依賴,可以并行調(diào)用,以降低服務(wù)的整體響應(yīng)時(shí)間。如下圖所示,這是一個(gè)常見的服務(wù)依賴關(guān)系:

  


 

  圖中A、B、C沒有依賴關(guān)系,實(shí)際上可以并行執(zhí)行。C服務(wù)不關(guān)心返回結(jié)果,因此將調(diào)用通知發(fā)出后及可結(jié)束。D服務(wù)需要等待A的結(jié)果,E需要等待B、D的執(zhí)行結(jié)果。使用傳統(tǒng)的異步編程的話,大概是這個(gè)樣子:

  


 

  可以看到服務(wù)的依賴關(guān)系隱藏在代碼行間,業(yè)務(wù)邏輯穿插在各個(gè)callback中,中間引入了ListeableFuturefutureBT 管理異步狀態(tài)。不太易于閱讀及維護(hù)。為此,我們提供了一個(gè)Graph-Based Execution Engine(GBEE)。GBEE的主要目標(biāo)在于解決以下:

  (1)管理服務(wù)之間的依賴關(guān)系

  將服務(wù)之間的依賴關(guān)系從業(yè)務(wù)代碼中分離出來,通過一個(gè)有向無環(huán)圖的數(shù)據(jù)結(jié)構(gòu)來描述服務(wù)之間的依賴關(guān)系。圖中每個(gè)節(jié)點(diǎn)保存了其前驅(qū)(后驅(qū))節(jié)點(diǎn)。每個(gè)節(jié)點(diǎn)可以執(zhí)行的前提條件是其所有前驅(qū)節(jié)點(diǎn)都完成。

  (2)統(tǒng)一注冊(cè)callback

  每個(gè)節(jié)點(diǎn)可以覆寫callback,用來注冊(cè)自身的監(jiān)聽器。一般用來轉(zhuǎn)換結(jié)果,記錄監(jiān)控。callback統(tǒng)一由執(zhí)行器管理注冊(cè)。避免在代碼嵌套中注冊(cè)監(jiān)聽器。

  (3)使用異步事件驅(qū)動(dòng)執(zhí)行

  在GBEE中統(tǒng)一注冊(cè)異步事件監(jiān)聽器,在事件發(fā)生時(shí)驅(qū)動(dòng)執(zhí)行callback,或者在條件成熟時(shí),喚起下一個(gè)節(jié)點(diǎn)的執(zhí)行。

  具體做法:

  (1)將業(yè)務(wù)邏輯分離成多個(gè)節(jié)點(diǎn),每個(gè)節(jié)點(diǎn)負(fù)責(zé)具體的業(yè)務(wù)邏輯執(zhí)行,但沒有任何狀態(tài),例如發(fā)起異步RPC調(diào)用,并返回ListenableFuture。

  


 

  (2)通過配置文件來定義依賴管理

  每個(gè)Node定義了自己的parents,即表示依賴關(guān)系。spring本身提供了服務(wù)的依賴管理能力。因此其依賴關(guān)系定義如下:

  


 

  (3)提供了一個(gè)執(zhí)行器Graph-Based Executor 來負(fù)責(zé)統(tǒng)一注冊(cè)監(jiān)聽器以及管理異步狀態(tài)。

  每個(gè)請(qǐng)求到達(dá)后,通過上面的依賴配置,可以構(gòu)造出一個(gè)Graph-Based執(zhí)行器:

  


 

  Graph會(huì)找到根節(jié)點(diǎn),多個(gè)根節(jié)點(diǎn)可以同時(shí)并行。

  


 

  apply(node, context) 是一個(gè)遞歸調(diào)用,每次執(zhí)行完當(dāng)前node,主動(dòng)探測(cè)下是否可以執(zhí)行父節(jié)點(diǎn)為自己的節(jié)點(diǎn):

  


 

  Graph-Based Executor 將業(yè)務(wù)代碼與底層的異步機(jī)制解耦,使得各個(gè)節(jié)點(diǎn)更加關(guān)注自身業(yè)務(wù)。

  后記

  在遷移具體業(yè)務(wù)時(shí),也遇到一些比較常見的問題,供后續(xù)的實(shí)施者參考。

  (1)公司RPC服務(wù)主要送是dubbo,利用公司的基礎(chǔ)組件,可以方便使用異步調(diào)用。

  (2)線上還有很多應(yīng)用使用tomcat 6,Servlet 3 從tomcat 7開始支持,應(yīng)該將相關(guān)應(yīng)用升級(jí)到tomcat 7.

  (3)web.xml 配置有幾個(gè)比較重要的配置。

  為了讓spring mvc真正啟用異步支持,除了需要將org.springframework.web.servlet.DispatcherServlet的異步選項(xiàng)激活,即:true

  


 

  還需要將此servlet之前的所有filter的async-supported設(shè)置成true。只要中間有一個(gè)filter沒有設(shè)置,后面的設(shè)置都是無效的。并且在后續(xù)開發(fā)中,如果增加了filter,也一定要配置上。

  (4)ThreadLocal 問題。

  現(xiàn)有系統(tǒng)的一些通用的上下文參數(shù)通過ThreadLocal傳遞。異步化改造后,代碼并不是始終在請(qǐng)求線程中執(zhí)行。這就使得通過ThreadLocal傳遞的變量失效。我們采用了兩種方法來解決,一是一些業(yè)務(wù)代碼的改造,通過參數(shù)的形式來傳遞。另一種是將一些通用變量存入HttpServletRequest的Attribute里。異步上下文中保持了對(duì)HttpServletRequest的引用。然后通過工具類直接從HttpServletRequest提取公共變量。

  (5)異常處理

  在同步代碼中,一般我們會(huì)自定義一些業(yè)務(wù)異常,這些業(yè)務(wù)異常被捕獲后,根據(jù)異常理性及狀態(tài)碼,做一些業(yè)務(wù)邏輯。ListeableFuture繼承的Future接口規(guī)定了,在異步計(jì)算過程中拋出的所有異常封裝在ExecutionException中。此時(shí),同步代碼中的catch,就不能捕獲ExecutionException了。此時(shí)業(yè)務(wù)代碼就需要修改捕獲的具體類型,然后通過Exception.getCause()來獲取原始異常。這塊可以通過Graph-Based Execution Engine統(tǒng)一處理。將原始異常轉(zhuǎn)換后,調(diào)用節(jié)點(diǎn)的onException.


--結(jié)束END--

本文鏈接: http://www.sh-linbin.cn/station/experience/1997.html (轉(zhuǎn)載時(shí)請(qǐng)注明來源鏈接)

 
本文標(biāo)簽: 全部

下班PC閱讀不方便?

手機(jī)也可以隨時(shí)學(xué)習(xí)開發(fā)

微信關(guān)注公眾號(hào)“酷云”
"酷云平臺(tái)前端開發(fā)教學(xué)"
每日干貨技術(shù)分享
 

×

成為 酷云平臺(tái) 代理商!

關(guān)注

微信
關(guān)注

微信掃一掃
獲取最新優(yōu)惠信息

酷云平臺(tái)公眾號(hào)

客服

聯(lián)系
客服

很高興為您服務(wù)
尊敬的用戶,歡迎您咨詢,我們?yōu)樾掠脩魷?zhǔn)備了優(yōu)惠好禮。 咨詢客服

聯(lián)系客服:

在線QQ: 3206174

客服電話: 0516-83703228

售前咨詢 售后服務(wù)
在線交談 智能小云

工作時(shí)間:

周一至周五: 09:00 - 17:00

WAP

手機(jī)
訪問

移動(dòng)端訪問
手機(jī)上也能選模板

酷云平臺(tái)手機(jī)端

浪卡子县| 丰原市| 南木林县| 罗山县| 通渭县| 怀宁县| 尼玛县| 威信县| 兴文县| 乐山市| 嘉鱼县| 南昌市| 西乌珠穆沁旗| 瑞安市| 英山县| 昌乐县| 海伦市| 永年县| 琼结县| 禹州市| 鹿邑县| 靖宇县| 赞皇县| 娄烦县| 陕西省| 新泰市| 沁水县| 威远县| 漳浦县| 永登县| 郑州市| 拜城县| 潞西市| 绥芬河市| 台北市| 五莲县| 达日县| 东兴市| 时尚| 利辛县| 怀仁县|