本文由攜程技術(shù)Butters分享,原題“干貨 | 日均流量200億,攜程高性能全異步網(wǎng)關(guān)實(shí)踐”,下文有修訂和重新排版。
1、引言
本文分享的是攜程API網(wǎng)關(guān)全異步改造的實(shí)踐分享,包括從Zuul 1.0同步架構(gòu)升級為基于Netty的全異步架構(gòu),通過RxJava實(shí)現(xiàn)業(yè)務(wù)流程異步化,結(jié)合流式轉(zhuǎn)發(fā)、ZGC等技術(shù)顯著提升性能,并構(gòu)建控制面實(shí)現(xiàn)多協(xié)議統(tǒng)一治理與模塊化編排。

2、作者介紹
Butters:攜程軟件技術(shù)專家,專注于網(wǎng)絡(luò)架構(gòu)、API網(wǎng)關(guān)、負(fù)載均衡、Service Mesh等領(lǐng)域。
3、專題目錄
本文是專題系列文章的第?13?篇,總目錄如下:
《長連接網(wǎng)關(guān)技術(shù)專題(一):京東京麥的生產(chǎn)級TCP網(wǎng)關(guān)技術(shù)實(shí)踐總結(jié)》
《長連接網(wǎng)關(guān)技術(shù)專題(二):知乎千萬級并發(fā)的高性能長連接網(wǎng)關(guān)技術(shù)實(shí)踐》
《長連接網(wǎng)關(guān)技術(shù)專題(三):手淘億級移動(dòng)端接入層網(wǎng)關(guān)的技術(shù)演進(jìn)之路》
《長連接網(wǎng)關(guān)技術(shù)專題(四):愛奇藝WebSocket實(shí)時(shí)推送網(wǎng)關(guān)技術(shù)實(shí)踐》
《長連接網(wǎng)關(guān)技術(shù)專題(五):喜馬拉雅自研億級API網(wǎng)關(guān)技術(shù)實(shí)踐》
《長連接網(wǎng)關(guān)技術(shù)專題(六):石墨文檔單機(jī)50萬WebSocket長連接架構(gòu)實(shí)踐》
《長連接網(wǎng)關(guān)技術(shù)專題(七):小米小愛單機(jī)120萬長連接接入層的架構(gòu)演進(jìn)》
《長連接網(wǎng)關(guān)技術(shù)專題(八):B站基于微服務(wù)的API網(wǎng)關(guān)從0到1的演進(jìn)之路》
《長連接網(wǎng)關(guān)技術(shù)專題(九):去哪兒網(wǎng)酒店高性能業(yè)務(wù)網(wǎng)關(guān)技術(shù)實(shí)踐》
《長連接網(wǎng)關(guān)技術(shù)專題(十):百度基于Go的千萬級統(tǒng)一長連接服務(wù)架構(gòu)實(shí)踐》
《長連接網(wǎng)關(guān)技術(shù)專題(十一):揭秘騰訊公網(wǎng)TGW網(wǎng)關(guān)系統(tǒng)的技術(shù)架構(gòu)演進(jìn)》
《長連接網(wǎng)關(guān)技術(shù)專題(十二):大模型時(shí)代多模型AI網(wǎng)關(guān)的架構(gòu)設(shè)計(jì)與實(shí)現(xiàn)》
《長連接網(wǎng)關(guān)技術(shù)專題(十三):基于Netty的攜程高性能網(wǎng)關(guān)異步改造實(shí)踐》(* 本文)
4、技術(shù)背景
與許多公司一樣,攜程API網(wǎng)關(guān)也是同微服務(wù)架構(gòu)一起引入的基礎(chǔ)設(shè)施,最早版本發(fā)布于2014年。隨著服務(wù)化在公司的快速推進(jìn),網(wǎng)關(guān)逐漸成為應(yīng)用暴露到外網(wǎng)的標(biāo)準(zhǔn)方案。后來的“ALL IN無線”、國際化、異地多活等,網(wǎng)關(guān)跟隨著公司公共業(yè)務(wù)與基礎(chǔ)架構(gòu)共同演進(jìn)。
技術(shù)方案上,公司微服務(wù)早期發(fā)展受NetflixOSS影響較深,網(wǎng)關(guān)方面最早也是參考了Zuul 1.0進(jìn)行的二次開發(fā)。
核心可概括為四點(diǎn):
1)server端:Tomcat NIO + AsyncServlet;
2)業(yè)務(wù)流程:獨(dú)立線程池,分階段的責(zé)任鏈模式;
3)client端:Apache HttpClient,同步調(diào)用;
4)核心組件:Archaius(動(dòng)態(tài)配置客戶端),Hystrix(熔斷限流),Groovy(熱更新支持)。

眾所周知,同步調(diào)用阻塞線程,系統(tǒng)吞吐受IO影響大。作為行業(yè)先驅(qū),Zuul在設(shè)計(jì)上也考慮到了這點(diǎn)——通過引入Hystrix,資源隔離配合限流,將故障(慢IO)框在一定范圍內(nèi);配合熔斷策略,可提前釋放部分線程資源;最終達(dá)到局部異常不影響全局的目的。
但隨著公司業(yè)務(wù)的發(fā)展,上述策略效果逐漸減弱。
主要原因在于兩方面的變動(dòng):
1)業(yè)務(wù)出海:網(wǎng)關(guān)作為海外接入層,部分流量需轉(zhuǎn)回國內(nèi),慢IO成為常態(tài);
2)服務(wù)規(guī)模增長:局部異常常態(tài)化,加上微服務(wù)異常擴(kuò)散的特性,線程池可能長期處于亞健康狀態(tài)。

全異步改造是攜程API網(wǎng)關(guān)近年的一項(xiàng)核心工作點(diǎn),本文也將由此展開,聊一聊我們在網(wǎng)關(guān)方面的工作與實(shí)踐。重點(diǎn)包括:性能優(yōu)化、業(yè)務(wù)形態(tài)、技術(shù)架構(gòu)、治理經(jīng)驗(yàn)等。
5、高性能網(wǎng)關(guān)核心設(shè)計(jì)1:異步流程設(shè)計(jì)
全異步 = server端異步 + 業(yè)務(wù)流程異步 + client端異步
對于server與client端,我們選擇了Netty框架,NIO/Epoll + Eventloop本身就是事件驅(qū)動(dòng)的設(shè)計(jì)。
改造核心在于業(yè)務(wù)流程的異步化,常見異步場景包括:
1)業(yè)務(wù)IO事件:如請求校驗(yàn)、身份認(rèn)證,涉及遠(yuǎn)程調(diào)用;
2)自身IO事件:如讀取到了報(bào)文的前xx字節(jié);
3)請求轉(zhuǎn)發(fā):包括TCP連接,HTTP請求。
經(jīng)驗(yàn)上,異步編程相比同步在設(shè)計(jì)、讀寫上都會(huì)困難一些。
所謂的困難,一般包括:
1)流程設(shè)計(jì)&狀態(tài)轉(zhuǎn)換;
2)異常處理,包括常規(guī)異常與超時(shí);
3)上下文傳遞,包括業(yè)務(wù)上下文與trace log;
4)線程調(diào)度;
5)流量控制。
尤其在Netty上下文內(nèi),對ByteBuf生命周期設(shè)計(jì)的不完善,很容易造成內(nèi)存泄漏。圍繞這些問題,我們設(shè)計(jì)了對應(yīng)外圍框架,最大努力對業(yè)務(wù)代碼抹平同步/異步差異,方便開發(fā);同時(shí)默認(rèn)兜底與容錯(cuò),保證程序整體安全。工具上借助了RxJava,主要流程如下圖所示。

Maybe:
1)RxJava內(nèi)置容器類,標(biāo)識正常結(jié)束、有且僅有一個(gè)對象返回、異常三種狀態(tài);
2)響應(yīng)式,方便整體狀態(tài)機(jī)設(shè)計(jì),自帶異常處理、超時(shí)、線程調(diào)度等封裝;
3)Maybe.empty()/Maybe.just(T),適用同步場景;
4)工具類RxJavaPlugins,方便切面邏輯封裝。
Filter:
1)代表一塊獨(dú)立的業(yè)務(wù)邏輯,同步&異步業(yè)務(wù)統(tǒng)一接口,返回Maybe;
2)異步場景(如遠(yuǎn)程調(diào)用)統(tǒng)一封裝,如涉及線程切換,通過maybe.obesrveOn(eventloop)切回;
3)異步filter默認(rèn)增加超時(shí),并按弱依賴處理,忽略錯(cuò)誤。
public?interface?Processor<T> {???
????ProcessorType getType();
????int?getOrder();
????boolean?shouldProcess(RequestContext context);
????//對外統(tǒng)一封裝為Maybe???
????Maybe process(RequestContext context)?throws?Exception;
}
public?abstract?class?AbstractProcessor?implements?Processor {
????//同步&無響應(yīng),繼承此方法
????//場景:常規(guī)業(yè)務(wù)處理
????protected?void?processSync(RequestContext context)?throws?Exception {}
????//同步&有響應(yīng),繼承此方法,健康檢測
????//場景:健康檢測、未通過校驗(yàn)時(shí)的靜態(tài)響應(yīng)
????protected?T processSyncAndGetReponse(RequestContext context)?throws?Exception {
????????process(context);
????????return?null;
????};
????//異步,繼承此方法
????//場景:認(rèn)證、鑒權(quán)等涉及遠(yuǎn)程調(diào)用的模塊
????protected?Maybe processAsync(RequestContext context)?throws?Exception
{
????????T response = processSyncAndGetReponse(context);
????????if?(response ==?null) {
????????????return?Maybe.empty();
????????}?else?{
????????????return?Maybe.just(response);
????????}
????};
????@Override
????public?Maybe process(RequestContext context)?throws?Exception {
????????Maybe<T> maybe = processAsync(context);
????????if?(maybe?instanceof?ScalarCallable) {
????????????//標(biāo)識同步方法,無需額外封裝
????????????return?maybe;
????????}?else?{
????????????//統(tǒng)一加超時(shí),默認(rèn)忽略錯(cuò)誤
????????????return?maybe.timeout(getAsyncTimeout(context), TimeUnit.MILLISECONDS,
????????????????????Schedulers.from(context.getEventloop()), timeoutFallback(context));
????????}
????}
????protected?long?getAsyncTimeout(RequestContext context) {
????????return?2000;
????}
????protected?Maybe<T> timeoutFallback(RequestContext context) {
????????return?Maybe.empty();
????}
整體流程:
1)沿用責(zé)任鏈的設(shè)計(jì),分為inbound、outbound、error、log四階段;
2)各階段由一或多個(gè)filter組成;
3)filter順序執(zhí)行,遇到異常則中斷,inbound期間任意filter返回response也觸發(fā)中斷。
public?class?RxUtil{
??????//組合某階段(如Inbound)內(nèi)的多個(gè)filter(即Callable<Maybe<T>>)
??????public?static? Maybe concat(Iterable
??????????Iterator
??????????while?(sources.hasNext()) {
??????????????Maybe<T> maybe;
??????????????try?{
??????????????????maybe = sources.next().call();
??????????????}?catch?(Exception e) {
??????????????????return?Maybe.error(e);
??????????????}
??????????????if?(maybe !=?null) {
??????????????????if?(maybe?instanceof?ScalarCallable) {
??????????????????????//同步方法
??????????????????????T response = ((ScalarCallable<T>)maybe).call();
??????????????????????if?(response !=?null) {
??????????????????????????//有response,中斷
??????????????????????????return?maybe;
??????????????????????}
??????????????????}?else?{
??????????????????????//異步方法
??????????????????????if?(sources.hasNext()) {
??????????????????????????//將sources傳入回調(diào),后續(xù)filter重復(fù)此邏輯
??????????????????????????return?new?ConcattedMaybe(maybe, sources);
??????????????????????}?else?{
??????????????????????????return?maybe;
??????????????????????}
??????????????????}
??????????????}
??????????}
??????????return?Maybe.empty();
??????}
??}
public?class?ProcessEngine{
??????//各個(gè)階段,增加默認(rèn)超時(shí)與錯(cuò)誤處理
????private?void?process(RequestContext context) {
??????????List<Callable<Maybe<Response>>> inboundTask = get(ProcessorType.INBOUND, context);
??????????List<Callable<Maybe<Void>>> outboundTask = get(ProcessorType.OUTBOUND, context);
??????????List<Callable<Maybe<Response>>> errorTask = get(ProcessorType.ERROR, context);
??????????List<Callable<Maybe<Void>>> logTask = get(ProcessorType.LOG, context);
?????????RxUtil.concat(inboundTask)?//inbound階段???????????????????
??????????????.toSingle()?//獲取response?????????????????????????
??????????????.flatMapMaybe(response -> {
??????????????????context.setOriginResponse(response);
??????????????????return?RxUtil.concat(outboundTask);
??????????????})?//進(jìn)入outbound
??????????????.onErrorResumeNext(e -> {
??????????????????context.setThrowable(e);
??????????????????return?RxUtil.concat(errorTask).flatMap(response -> {
??????????????????????context.resetResponse(response);
??????????????????????return?RxUtil.concat(outboundTask);
??????????????????});
??????????????})?//異常則進(jìn)入error,并重新進(jìn)入outbound
??????????????.flatMap(response -> RxUtil.concat(logTask))?//日志階段
??????????????.timeout(asyncTimeout.get(), TimeUnit.MILLISECONDS, Schedulers.from(context.getEventloop()),
??????????????????????Maybe.error(new?ServerException(500,?"Async-Timeout-Processing"))
??????????????)?//全局兜底超時(shí)
??????????????.subscribe(?//釋放資源
??????????????????????unused -> {
??????????????????????????logger.error("this should not happen, "?+ context);
??????????????????????????context.release();
??????????????????????},
??????????????????????e -> {
??????????????????????????logger.error("this should not happen, "?+ context, e);
??????????????????????????context.release();
??????????????????????},
??????????????????????() -> context.release()
??????????????);
??????}??
??}
6、高性能網(wǎng)關(guān)核心設(shè)計(jì)2:流式轉(zhuǎn)發(fā)&單線程
以HTTP為例,報(bào)文可劃分為initial line/header/body三個(gè)組成部分:

在攜程,網(wǎng)關(guān)層業(yè)務(wù)不涉及body。因?yàn)闊o需全量存,所以解析完header后可直接進(jìn)入業(yè)務(wù)流程。
于此同時(shí),如果接收到body部分:
1)若已向upstream轉(zhuǎn)發(fā)請求,則直接轉(zhuǎn)發(fā);
2)否則需要將其暫存,待業(yè)務(wù)流程處理完畢,同initial line/header一并發(fā)送;
3)對upstream端響應(yīng)的處理方式亦然。
對比完整解析HTTP報(bào)文的方式,這樣處理:
1)更早進(jìn)入業(yè)務(wù)流程,意味著upstream更早接收到請求,能有效降低網(wǎng)關(guān)這層引入的延遲;
2)body生命周期被壓縮,可降低網(wǎng)關(guān)自身的內(nèi)存開銷。
雖說提升了性能,但流式的方式也極大提升了整個(gè)流程的復(fù)雜度

非流式場景下,Netty Server端編解碼、入向業(yè)務(wù)邏輯、Netty Cerver端編解碼、出向業(yè)務(wù)邏輯,各子流程相互獨(dú)立,各自處理完整的HTTP對象。
采取流式后,請求則可能同時(shí)處于多流程內(nèi),引入的困難可歸納為以下三點(diǎn):
1)線程安全問題:不同流程若采用不同線程,會(huì)涉及上下文的并發(fā)修改;
2)多階段聯(lián)動(dòng):比如Netty Server請求接收一半遇到了連接中斷,此時(shí)已經(jīng)連上了upstream,那么upstream側(cè)的協(xié)議棧是走不完的,也必須隨之關(guān)閉連接;
3)邊緣場景處理:比如upstream在請求未完整發(fā)送情況下返回了404/413,是選擇繼續(xù)發(fā)送、走完協(xié)議棧、讓連接能夠復(fù)用,還是選擇提前終止流程,節(jié)約資源,但同時(shí)放棄連接?再比如,upstream已收到請求但未響應(yīng),此時(shí)Netty Server突然斷開,Netty Client是否也要隨之?dāng)嚅_?等等。
針對這些場景,我們采用了單線程的方式,核心設(shè)計(jì):
1)上線文綁定Eventloop,Netty Server/業(yè)務(wù)流程/Netty Client在同個(gè)eventloop執(zhí)行;
2)異步filter如因IO庫的關(guān)系,必須使用獨(dú)立線程池,那在后置處理上必須切回;
3)流程內(nèi)資源做必要的線程隔離(如連接池)。
采用單線程的好處:
1)杜絕了并發(fā)問題,在多階段聯(lián)動(dòng)、邊緣場景問題處理時(shí),整個(gè)系統(tǒng)也處于確定的狀態(tài)下,有效降低了開發(fā)難度與風(fēng)險(xiǎn);
2)減少線程切換,一定程度上也能夠提升性能。
與之相對的,因?yàn)閣orker線程數(shù)較少(一般等于CPU核數(shù)),eventloop內(nèi)必須完全杜絕IO操作,否則將對系統(tǒng)吞吐造成毀滅性打擊。
7、高性能網(wǎng)關(guān)核心設(shè)計(jì)3:其他優(yōu)化
內(nèi)部變量懶加載:針對請求的cookie/query等字段,如無必要,不提前進(jìn)行字符串解析
堆外內(nèi)存&零拷貝:結(jié)合前文流式轉(zhuǎn)發(fā)的設(shè)計(jì),進(jìn)一步降低系統(tǒng)內(nèi)存開銷
ZGC:項(xiàng)目因TLSv1.3而引入了JDK11(JDK8支持相對較晚,8u261版本,2020.7.14),自然也對新一代的GC算法進(jìn)行了嘗試,實(shí)際表現(xiàn)也確實(shí)不負(fù)盛名。除CPU占用有少量提升,整體GC耗時(shí)下降非常明顯。


定制的HTTP編解碼:HTTP的悠久歷史,加之協(xié)議自身的開放性,催生了許多“壞實(shí)踐”,輕則影響成功率,重則威脅網(wǎng)站安全,舉兩個(gè)例子:
流量治理:諸如請求體過大(413)、uri過長(414)、非ASCII字符(400)等問題,一般WebServer會(huì)選擇直接拒絕并返回對應(yīng)狀態(tài)碼。由于直接跳過了業(yè)務(wù)流程,這類問題在統(tǒng)計(jì)、服務(wù)定為、排障上都會(huì)比較麻煩。擴(kuò)展編解碼,讓問題請求也能夠走完路由流程,可以幫助解決非標(biāo)流量的治理問題。
請求過濾:如request smuggling(Netty 4.1.61.Final修復(fù),2021.3.30發(fā)布)。擴(kuò)展編解碼,增加自定義的校驗(yàn)邏輯,讓安全補(bǔ)丁能夠更快落地。
8、網(wǎng)關(guān)業(yè)務(wù)形態(tài)
作為獨(dú)立、統(tǒng)一的入向流量收口點(diǎn),網(wǎng)關(guān)對公司的價(jià)值主要體現(xiàn)在三方面:
1)解耦不同網(wǎng)絡(luò)環(huán)境:典型場景包括內(nèi)網(wǎng)&外網(wǎng)、生產(chǎn)環(huán)境&辦公區(qū)、IDC內(nèi)部不同安全域、專線等;
2)天然的公共業(yè)務(wù)切面:包括安全&認(rèn)證&反爬、路由&灰度、限流&熔斷&降級、監(jiān)控&告警&排障等;
3)高效、靈活的流量控制。


這里展開講幾個(gè)細(xì)分場景。
私有協(xié)議:
1)在收口的客戶端(APP),由框架層攔截用戶發(fā)起的HTTP請求,通過私有協(xié)議(SOTP)的方式發(fā)往服務(wù)端;
2)選址方面:①通過服務(wù)端下發(fā)IP,杜絕DNS劫持;②連接預(yù)熱;③自定義的選址策略,可依據(jù)網(wǎng)絡(luò)質(zhì)量、環(huán)境等自行切換;
3)交互方式上:①更加輕量的協(xié)議體;②統(tǒng)一加密&壓縮&多路復(fù)用;③協(xié)議在入口處由網(wǎng)關(guān)統(tǒng)一轉(zhuǎn)換,對業(yè)務(wù)透明。
鏈路優(yōu)化:核心是引入接入層,讓遠(yuǎn)距離用戶就近訪問,緩解握手開銷過大的問題。同時(shí),因?yàn)榻尤雽优cIDC是可控的兩端,網(wǎng)絡(luò)鏈路選擇、協(xié)議交互模式上都有更大的優(yōu)化空間。
異地多活:區(qū)別于按比例分配、就近訪問策略等,異地多活模式下,網(wǎng)關(guān)(接入層)需按照業(yè)務(wù)維度的shardingKey進(jìn)行分流(如userId),防止底層數(shù)據(jù)沖突。

9、網(wǎng)關(guān)治理概述
下圖總結(jié)了線上網(wǎng)關(guān)的工作狀態(tài):

橫向?qū)?yīng)我們的業(yè)務(wù)流程:不同渠道(APP、H5、小程序、供應(yīng)商)、不同協(xié)議(HTTP、SOTP)的流量經(jīng)由負(fù)載均衡打到網(wǎng)關(guān),經(jīng)過系列業(yè)務(wù)邏輯的處理,最終轉(zhuǎn)發(fā)至后端服務(wù)。經(jīng)歷了第二章的改造后,橫向業(yè)務(wù)在性能、穩(wěn)定性上都得到了較好的提升。
另一方面:由于多渠道/協(xié)議的存在,線上網(wǎng)關(guān)按業(yè)務(wù)劃分,進(jìn)行了獨(dú)立集群的部署。業(yè)務(wù)差異(路由數(shù)據(jù)、功能模塊)早期通過獨(dú)立代碼分支管理,隨著分支數(shù)的增加,整體的運(yùn)維復(fù)雜度越來越高。系統(tǒng)設(shè)計(jì)中,復(fù)雜度往往也意味著風(fēng)險(xiǎn)。如何對多協(xié)議、多角色的網(wǎng)關(guān)實(shí)施統(tǒng)一治理,如何以較低的成本,快速為新業(yè)務(wù)搭建定制化網(wǎng)關(guān),成為了我們后一階段的工作重心。
解決方案也比較直觀地在圖中畫了出來:
1)是協(xié)議上兼容處理,讓線上代碼跑在一套框架下;
2)是引入控制面,對線上網(wǎng)關(guān)的差異特性進(jìn)行統(tǒng)一管理。

10、網(wǎng)關(guān)治理能力1:多協(xié)議兼
協(xié)議兼容的做法并不新鮮,整體可以參考Tomcat對HTTP/1.0、HTTP/1.1、HTTP/2.0的抽象。HTTP自身雖然在各個(gè)版本內(nèi)新增了大量feature,但我們在做業(yè)務(wù)開發(fā)時(shí)通常感知不到這些,核心在于HttpServletRequest接口的抽象。
在攜程,網(wǎng)關(guān)面對的都是請求—響應(yīng)模式的無狀態(tài)協(xié)議,報(bào)文組成上也可以劃分為元數(shù)據(jù)、擴(kuò)展頭、業(yè)務(wù)報(bào)文三部分,因此可以比較方便地進(jìn)行類似的嘗試。
對應(yīng)工作可以用以下兩點(diǎn)概括:
1)協(xié)議適配層:用于屏蔽不同協(xié)議的編解碼、交互模式、對TCP連接的處理等;
2)定義通用中間模型與接口:業(yè)務(wù)面向中間模型與接口編程,更好地聚焦到協(xié)議對應(yīng)的業(yè)務(wù)屬性上去。

11、網(wǎng)關(guān)治理能力2:路由模塊
路由模塊是控制面的兩個(gè)主要組成部分之一。
除了管理網(wǎng)關(guān)—服務(wù)間的映射關(guān)系,服務(wù)本身可以用以下模型概括:
{
??????//匹配方式
??????"type":?"uri",
??????//HTTP默認(rèn)采用uri前綴匹配,內(nèi)部通過樹結(jié)構(gòu)尋址;私有協(xié)議(SOTP)通過服務(wù)唯一標(biāo)識定位。
??????"value":?"/hotel/order",
??????"matcherType":?"prefix",
??????//標(biāo)簽與屬性
??????//用于portal端權(quán)限管理、切面邏輯運(yùn)行(如按核心/非核心)等
??????"tags": [
??????????"owner_admin",
??????????"org_framework",
??????????"appId_123456"
??????],
??????"properties": {
??????????"core":?"true"
??????},
????//endpoint信息
??????"routes": [{
??????????//condition用于二級路由,如按app版本劃分、按query重分配等
??????????"condition":?"true",
??????????"conditionParam": {},
??????????"zone":?"PRO",
??????????//具體服務(wù)地址,權(quán)重用于灰度場景
??????????"targets": [{
??????????????"url":?"http://test.ctrip.com/hotel",
??????????????"weight": 100
??????????}
????????]
??????}]
??}
12、網(wǎng)關(guān)治理能力3:模塊編排
模塊編排是控制面的另一項(xiàng)核心部分。

我們在網(wǎng)關(guān)處理流程內(nèi)預(yù)留了多個(gè)階段(上圖中用粉色標(biāo)記)。除開熔斷、限流、日志等通用功能,運(yùn)行時(shí)不同網(wǎng)關(guān)所需執(zhí)行的業(yè)務(wù)功能由控制面統(tǒng)一下發(fā)。功能本身在網(wǎng)關(guān)內(nèi)部有獨(dú)立的代碼模塊,控制面額外定義了功能對應(yīng)的執(zhí)行條件、參數(shù)、灰度比例、錯(cuò)誤處理方式等。這種編排方式也在側(cè)面保證了模塊間的解耦。
{
??????//模塊名稱,對應(yīng)網(wǎng)關(guān)內(nèi)部某個(gè)具體模塊
??????"name":?"addResponseHeader",
??????//執(zhí)行階段
??????"stage":?"PRE_RESPONSE",
??????//執(zhí)行順序
??????"ruleOrder": 0,
??????//灰度比例
??????"grayRatio": 100,
??????//執(zhí)行條件
??????"condition":?"true",
??????"conditionParam": {},
??????//執(zhí)行參數(shù)
??????//大量${}形式的內(nèi)置模板,用于獲取運(yùn)行時(shí)數(shù)據(jù)
??????"actionParam": {
????????"connection":?"keep-alive",
????????"x-service-call":?"${request.func.remoteCost}",
????????"Access-Control-Expose-Headers":?"x-service-call",
????????"x-gate-root-id":?"${func.catRootMessageId}"
??????},
??????//異常處理方式,可以拋出或忽略
??????"exceptionHandle":?"return"
???}
13、本文小結(jié)
網(wǎng)關(guān)長期以來都是各類技術(shù)交流平臺(tái)上的熱點(diǎn),方案也非常豐富:發(fā)展早、易上手的Zuul1.0、高性能的Nginx、集成度高的SpringCloud Gateway、如日中天的Istio等等。最終決定選型的還是各公司自身的業(yè)務(wù)背景與技術(shù)生態(tài)。也正因此,在攜程我們選擇了自研的道路。
技術(shù)不斷發(fā)展,我們也在持續(xù)探索,公共網(wǎng)關(guān)同業(yè)務(wù)網(wǎng)關(guān)的關(guān)系、新協(xié)議的落地(HTTP3)、與ServiceMesh的關(guān)系等等,真誠歡迎有興趣的同學(xué)一起參與討論。
14、參考資料
[1]?京東京麥的生產(chǎn)級TCP網(wǎng)關(guān)技術(shù)實(shí)踐總結(jié)
[2]?手淘億級移動(dòng)端接入層網(wǎng)關(guān)的技術(shù)演進(jìn)之路
[3]?喜馬拉雅自研億級API網(wǎng)關(guān)技術(shù)實(shí)踐
[4]?小米小愛單機(jī)120萬長連接接入層的架構(gòu)演進(jìn)
[5]?B站基于微服務(wù)的API網(wǎng)關(guān)從0到1的演進(jìn)之路
[6]?去哪兒網(wǎng)酒店高性能業(yè)務(wù)網(wǎng)關(guān)技術(shù)實(shí)踐
[7]?少啰嗦!一分鐘帶你讀懂Java的NIO和經(jīng)典IO的區(qū)別
[8]?史上最強(qiáng)Java NIO入門:擔(dān)心從入門到放棄的,請讀這篇!
[9]?Java的BIO和NIO很難懂?用代碼實(shí)踐給你看,再不懂我轉(zhuǎn)行!