日流量200億,攜程網(wǎng)關(guān)的架構(gòu)設(shè)計(jì)
日流量200億,攜程網(wǎng)關(guān)的架構(gòu)設(shè)計(jì)
方案的作者:Butters,攜程軟件技術(shù)專(zhuān)家,專(zhuān)注于網(wǎng)絡(luò)架構(gòu)、API網(wǎng)關(guān)、負(fù)載均衡、Service Mesh等領(lǐng)域。
一、概述
類(lèi)似于許多企業(yè)的做法,攜程 API 網(wǎng)關(guān)是伴隨著微服務(wù)架構(gòu)一同引入的基礎(chǔ)設(shè)施,其最初版本于 2014 年發(fā)布。隨著服務(wù)化在公司內(nèi)的迅速推進(jìn),網(wǎng)關(guān)逐步成為應(yīng)用程序暴露在外網(wǎng)的標(biāo)準(zhǔn)解決方案。后續(xù)的“ALL IN 無(wú)線(xiàn)”、國(guó)際化、異地多活等項(xiàng)目,網(wǎng)關(guān)都隨著公司公共業(yè)務(wù)與基礎(chǔ)架構(gòu)的共同演進(jìn)而不斷發(fā)展。截至 2021 年 7 月,整體接入服務(wù)數(shù)量超過(guò) 3000 個(gè),日均處理流量達(dá)到 200 億。
在技術(shù)方案方面,公司微服務(wù)的早期發(fā)展深受 NetflixOSS 的影響,網(wǎng)關(guān)部分最早也是參考了 Zuul 1.0 進(jìn)行的二次開(kāi)發(fā),其核心可以總結(jié)為以下四點(diǎn):
- server端:Tomcat NIO + AsyncServlet
- 業(yè)務(wù)流程:獨(dú)立線(xiàn)程池,分階段的責(zé)任鏈模式
- client端:Apache HttpClient,同步調(diào)用
- 核心組件:Archaius(動(dòng)態(tài)配置客戶(hù)端),Hystrix(熔斷限流),Groovy(熱更新支持)
圖片
眾所周知,同步調(diào)用會(huì)阻塞線(xiàn)程,系統(tǒng)的吞吐能力受 IO 影響較大。
作為行業(yè)的領(lǐng)先者,Zuul 在設(shè)計(jì)時(shí)已經(jīng)考慮到了這個(gè)問(wèn)題:通過(guò)引入 Hystrix,實(shí)現(xiàn)資源隔離和限流,將故障(慢 IO)限制在一定范圍內(nèi);結(jié)合熔斷策略,可以提前釋放部分線(xiàn)程資源;最終達(dá)到局部異常不會(huì)影響整體的目標(biāo)。
然而,隨著公司業(yè)務(wù)的不斷發(fā)展,上述策略的效果逐漸減弱,主要原因有兩方面:
- 業(yè)務(wù)出海:網(wǎng)關(guān)作為海外接入層,部分流量需要轉(zhuǎn)回國(guó)內(nèi),慢 IO 成為常態(tài)
- 服務(wù)規(guī)模增長(zhǎng):局部異常成為常態(tài),加上微服務(wù)異常擴(kuò)散的特性,線(xiàn)程池可能長(zhǎng)期處于亞健康狀態(tài)
圖片
全異步改造是攜程 API 網(wǎng)關(guān)近年來(lái)的一項(xiàng)核心工作,本文也將圍繞此展開(kāi),探討我們?cè)诰W(wǎng)關(guān)方面的工作與實(shí)踐經(jīng)驗(yàn)。
重點(diǎn)包括:性能優(yōu)化、業(yè)務(wù)形態(tài)、技術(shù)架構(gòu)、治理經(jīng)驗(yàn)等。
二、高性能網(wǎng)關(guān)核心設(shè)計(jì)
2.1. 異步流程設(shè)計(jì)
全異步 = server端異步 + 業(yè)務(wù)流程異步 + client端異步
對(duì)于server與client端,我們采用了 Netty 框架,其 NIO/Epoll + Eventloop 的本質(zhì)就是事件驅(qū)動(dòng)的設(shè)計(jì)。
我們改造的核心部分是將業(yè)務(wù)流程進(jìn)行異步化,常見(jiàn)的異步場(chǎng)景有:
- 業(yè)務(wù) IO 事件:例如請(qǐng)求校驗(yàn)、身份驗(yàn)證,涉及遠(yuǎn)程調(diào)用
- 自身 IO 事件:例如讀取到了報(bào)文的前 xx 字節(jié)
- 請(qǐng)求轉(zhuǎn)發(fā):包括 TCP 連接,HTTP 請(qǐng)求
從經(jīng)驗(yàn)上看,異步編程在設(shè)計(jì)和讀寫(xiě)方面相比同步會(huì)稍微困難一些,主要包括:
- 流程設(shè)計(jì)&狀態(tài)轉(zhuǎn)換
- 異常處理,包括常規(guī)異常與超時(shí)
- 上下文傳遞,包括業(yè)務(wù)上下文與trace log
- 線(xiàn)程調(diào)度
- 流量控制
特別是在Netty上下文內(nèi),如果對(duì) ByteBuf 的生命周期設(shè)計(jì)不完善,很容易導(dǎo)致內(nèi)存泄漏。
圍繞這些問(wèn)題,我們?cè)O(shè)計(jì)了對(duì)應(yīng)外圍框架,最大努力對(duì)業(yè)務(wù)代碼抹平同步/異步差異,方便開(kāi)發(fā);同時(shí)默認(rèn)兜底與容錯(cuò),保證程序整體安全。
在工具方面,我們使用了 RxJava,其主要流程如下圖所示。
圖片
- Maybe
- RxJava 的內(nèi)置容器類(lèi),表示正常結(jié)束、有且僅有一個(gè)對(duì)象返回、異常三種狀態(tài)
- 響應(yīng)式,便于整體狀態(tài)機(jī)設(shè)計(jì),自帶異常處理、超時(shí)、線(xiàn)程調(diào)度等封裝
- Maybe.empty()/Maybe.just(T),適用同步場(chǎng)景
- 工具類(lèi)RxJavaPlugins,方便切面邏輯封裝
- Filter
- 代表一塊獨(dú)立的業(yè)務(wù)邏輯,同步&異步業(yè)務(wù)統(tǒng)一接口,返回Maybe
- 異步場(chǎng)景(如遠(yuǎn)程調(diào)用)統(tǒng)一封裝,如涉及線(xiàn)程切換,通過(guò)maybe.obesrveOn(eventloop)切回
- 異步filter默認(rèn)增加超時(shí),并按弱依賴(lài)處理,忽略錯(cuò)誤
public interface Processor<T> {
ProcessorType getType();
int getOrder();
boolean shouldProcess(RequestContext context);
//對(duì)外統(tǒng)一封裝為Maybe
Maybe<T> process(RequestContext context) throws Exception;
}
public abstract class AbstractProcessor implements Processor {
//同步&無(wú)響應(yīng),繼承此方法
//場(chǎng)景:常規(guī)業(yè)務(wù)處理
protected void processSync(RequestContext context) throws Exception {}
//同步&有響應(yīng),繼承此方法,健康檢測(cè)
//場(chǎng)景:健康檢測(cè)、未通過(guò)校驗(yàn)時(shí)的靜態(tài)響應(yīng)
protected T processSyncAndGetReponse(RequestContext context) throws Exception {
process(context);
return null;
};
//異步,繼承此方法
//場(chǎng)景:認(rèn)證、鑒權(quán)等涉及遠(yuǎn)程調(diào)用的模塊
protected Maybe<T> processAsync(RequestContext context) throws Exception
{
T response = processSyncAndGetReponse(context);
if (response == null) {
return Maybe.empty();
} else {
return Maybe.just(response);
}
};
@Override
public Maybe<T> process(RequestContext context) throws Exception {
Maybe<T> maybe = processAsync(context);
if (maybe instanceof ScalarCallable) {
//標(biāo)識(shí)同步方法,無(wú)需額外封裝
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();
}
}
- 整體流程
- 沿用責(zé)任鏈的設(shè)計(jì),分為inbound、outbound、error、log四階段
- 各階段由一或多個(gè)filter組成
- filter順序執(zhí)行,遇到異常則中斷,inbound期間任意filter返回response也觸發(fā)中斷
public class RxUtil{
//組合某階段(如Inbound)內(nèi)的多個(gè)filter(即Callable<Maybe<T>>)
public static <T> Maybe<T> concat(Iterable<? extends Callable<Maybe<T>>> iterable) {
Iterator<? extends Callable<Maybe<T>>> sources = 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()
);
}
}
2.2. 流式轉(zhuǎn)發(fā)&單線(xiàn)程
以HTTP為例,報(bào)文可劃分為initial line/header/body三個(gè)組成部分。
圖片
在攜程,網(wǎng)關(guān)層業(yè)務(wù)不涉及請(qǐng)求體body。
因?yàn)闊o(wú)需全量存,所以解析完請(qǐng)求頭header后可直接進(jìn)入業(yè)務(wù)流程。
同時(shí),如果收到請(qǐng)求體body部分:
①若已向upstream轉(zhuǎn)發(fā)請(qǐng)求,則直接轉(zhuǎn)發(fā);
②否則,需要將其暫時(shí)存儲(chǔ),等待業(yè)務(wù)流程處理完畢后,再將其與initial line/header一并發(fā)送;
③對(duì)upstream端響應(yīng)的處理方式亦然。
對(duì)比完整解析HTTP報(bào)文的方式,這樣處理:
- 更早進(jìn)入業(yè)務(wù)流程,意味著upstream更早接收到請(qǐng)求,可以有效地降低網(wǎng)關(guān)層引入的延遲
- body生命周期被壓縮,可降低網(wǎng)關(guān)自身的內(nèi)存開(kāi)銷(xiāo)
盡管性能有所提升,但流式處理也大大增加了整個(gè)流程的復(fù)雜性。
圖片
在非流式場(chǎng)景下,Netty Server端編解碼、入向業(yè)務(wù)邏輯、Netty Client端的編解碼、出向業(yè)務(wù)邏輯,各個(gè)子流程相互獨(dú)立,各自處理完整的HTTP對(duì)象。而采用流式處理后,請(qǐng)求可能同時(shí)處于多個(gè)流程中,這帶來(lái)了以下三個(gè)挑戰(zhàn):
- 線(xiàn)程安全問(wèn)題:如果各個(gè)流程使用不同的線(xiàn)程,那么可能會(huì)涉及到上下文的并發(fā)修改;
- 多階段聯(lián)動(dòng):比如Netty Server請(qǐng)求接收一半遇到了連接中斷,此時(shí)已經(jīng)連上了upstream,那么upstream側(cè)的協(xié)議棧是走不完的,也必須隨之關(guān)閉連接;
- 邊緣場(chǎng)景處理:比如upstream在請(qǐng)求未完整發(fā)送情況下返回了404/413,是選擇繼續(xù)發(fā)送、走完協(xié)議棧、讓連接能夠復(fù)用,還是選擇提前終止流程,節(jié)約資源,但同時(shí)放棄連接?再比如,upstream已收到請(qǐng)求但未響應(yīng),此時(shí)Netty Server突然斷開(kāi),Netty Client是否也要隨之?dāng)嚅_(kāi)?等等。
為了應(yīng)對(duì)這些挑戰(zhàn),我們采用了單線(xiàn)程的方式,核心設(shè)計(jì)包括:
- 上線(xiàn)文綁定Eventloop,Netty Server/業(yè)務(wù)流程/Netty Client在同個(gè)eventloop執(zhí)行;
- 異步filter如因IO庫(kù)的關(guān)系,必須使用獨(dú)立線(xiàn)程池,那在后置處理上必須切回;
- 流程內(nèi)資源做必要的線(xiàn)程隔離(如連接池);
單線(xiàn)程方式避免了并發(fā)問(wèn)題,在處理多階段聯(lián)動(dòng)、邊緣場(chǎng)景問(wèn)題時(shí),整個(gè)系統(tǒng)處于確定的狀態(tài)下,有效降低了開(kāi)發(fā)難度和風(fēng)險(xiǎn);此外,減少線(xiàn)程切換,也能在一定程度上提升性能。然而,由于 worker 線(xiàn)程數(shù)較少(一般等于 CPU 核數(shù)),eventloop 內(nèi)必須完全避免 IO 操作,否則將對(duì)系統(tǒng)的吞吐量造成重大影響。
2.3 其他優(yōu)化
- 內(nèi)部變量懶加載
對(duì)于請(qǐng)求的 cookie/query 等字段,如果沒(méi)有必要,不提前進(jìn)行字符串解析
- 堆外內(nèi)存&零拷貝
結(jié)合前文的流式轉(zhuǎn)發(fā)設(shè)計(jì),進(jìn)一步減少系統(tǒng)內(nèi)存占用。
- ZGC
由于項(xiàng)目升級(jí)到 TLSv1.3,引入了 JDK11(JDK8 支持較晚,8u261 版本,2020.7.14),同時(shí)也嘗試了新一代的垃圾回收算法,其實(shí)際表現(xiàn)確實(shí)如人們所期待的那樣出色。盡管 CPU 占用有所增加,但整體 GC 耗時(shí)下降非常顯著。
圖片
圖片
- 定制的HTTP編解碼
由于 HTTP 協(xié)議的歷史悠久及其開(kāi)放性,產(chǎn)生了很多“不良實(shí)踐”,輕則影響請(qǐng)求成功率,重則對(duì)網(wǎng)站安全構(gòu)成威脅。
- 流量治理
對(duì)于請(qǐng)求體過(guò)大(413)、URI 過(guò)長(zhǎng)(414)、非 ASCII 字符(400)等問(wèn)題,一般的 Web 服務(wù)器會(huì)選擇直接拒絕并返回相應(yīng)的狀態(tài)碼。由于這類(lèi)問(wèn)題跳過(guò)了業(yè)務(wù)流程,因此在統(tǒng)計(jì)、服務(wù)定位和故障排查方面會(huì)帶來(lái)一些麻煩。通過(guò)擴(kuò)展編解碼,讓問(wèn)題請(qǐng)求也能完成路由流程,有助于解決非標(biāo)準(zhǔn)流量的管理問(wèn)題。
- 請(qǐng)求過(guò)濾
例如 request smuggling(Netty 4.1.61.Final 修復(fù),2021.3.30 發(fā)布)。通過(guò)擴(kuò)展編解碼,增加自定義校驗(yàn)邏輯,可以讓安全補(bǔ)丁更快地得以應(yīng)用。
三、網(wǎng)關(guān)業(yè)務(wù)形態(tài)
作為獨(dú)立的、統(tǒng)一的入向流量收口點(diǎn),網(wǎng)關(guān)對(duì)企業(yè)的價(jià)值主要展現(xiàn)在三個(gè)方面:
- 解耦不同網(wǎng)絡(luò)環(huán)境:典型場(chǎng)景包括內(nèi)網(wǎng)&外網(wǎng)、生產(chǎn)環(huán)境&辦公區(qū)、IDC內(nèi)部不同安全域、專(zhuān)線(xiàn)等;
- 天然的公共業(yè)務(wù)切面:包括安全&認(rèn)證&反爬、路由&灰度、限流&熔斷&降級(jí)、監(jiān)控&告警&排障等;
圖片
圖片
- 高效、靈活的流量控制
這里展開(kāi)講幾個(gè)細(xì)分場(chǎng)景:
- 私有協(xié)議
在收口的客戶(hù)端(APP)中,框架層會(huì)攔截用戶(hù)發(fā)起的 HTTP 請(qǐng)求,通過(guò)私有協(xié)議(SOTP)的方式傳送到服務(wù)端。
選址方面:①通過(guò)服務(wù)端分配 IP,防止 DNS 劫持;②進(jìn)行連接預(yù)熱;③采用自定義的選址策略,可以根據(jù)網(wǎng)絡(luò)狀況、環(huán)境等因素自行切換。
交互方式上:①采用更輕量的協(xié)議體;②統(tǒng)一進(jìn)行加密與壓縮與多路復(fù)用;③在入口處由網(wǎng)關(guān)統(tǒng)一轉(zhuǎn)換協(xié)議,對(duì)業(yè)務(wù)無(wú)影響。
- 鏈路優(yōu)化
關(guān)鍵在于引入接入層,讓遠(yuǎn)程用戶(hù)就近訪(fǎng)問(wèn),解決握手開(kāi)銷(xiāo)過(guò)大的問(wèn)題。同時(shí),由于接入層與 IDC 兩端都是可控的,因此在網(wǎng)絡(luò)鏈路選擇、協(xié)議交互模式等方面都有更大的優(yōu)化空間。
- 異地多活
與按比例分配、就近訪(fǎng)問(wèn)策略等不同,在異地多活模式下,網(wǎng)關(guān)(接入層)需要根據(jù)業(yè)務(wù)維度的 shardingKey 進(jìn)行分流(如 userId),防止底層數(shù)據(jù)沖突。
圖片
四、網(wǎng)關(guān)治理
下所示的圖表概括了網(wǎng)上網(wǎng)關(guān)的工作狀態(tài)。縱向?qū)?yīng)我們的業(yè)務(wù)流程:各種渠道(如 APP、H5、小程序、供應(yīng)商)和各種協(xié)議(如 HTTP、SOTP)的流量通過(guò)負(fù)載均衡分配到網(wǎng)關(guān),通過(guò)一系列業(yè)務(wù)邏輯處理后,最終被轉(zhuǎn)發(fā)到后端服務(wù)。經(jīng)過(guò)第二章的改進(jìn)后,橫向業(yè)務(wù)在性能和穩(wěn)定性方面都得到了顯著提升。
圖片
另一方面,由于多渠道/協(xié)議的存在,網(wǎng)上網(wǎng)關(guān)根據(jù)業(yè)務(wù)進(jìn)行了獨(dú)立集群的部署。早期,業(yè)務(wù)差異(如路由數(shù)據(jù)、功能模塊)通過(guò)獨(dú)立的代碼分支進(jìn)行管理,但是隨著分支數(shù)量的增加,整體運(yùn)維的復(fù)雜性也在不斷提高。在系統(tǒng)設(shè)計(jì)中,復(fù)雜性通常也意味著風(fēng)險(xiǎn)。因此,如何對(duì)多協(xié)議、多角色的網(wǎng)關(guān)進(jìn)行統(tǒng)一管理,如何以較低的成本快速為新業(yè)務(wù)構(gòu)建定制化的網(wǎng)關(guān),成為了我們下一階段的工作重點(diǎn)。
解決方案已經(jīng)在圖中直觀(guān)地呈現(xiàn)出來(lái),一是在協(xié)議上進(jìn)行兼容處理,使網(wǎng)上代碼在一個(gè)框架下運(yùn)行;二是引入控制面,對(duì)網(wǎng)上網(wǎng)關(guān)的差異特性進(jìn)行統(tǒng)一管理。
圖片
4.1 多協(xié)議兼容
多協(xié)議兼容的方法并不新穎,可以參考 Tomcat 對(duì) HTTP/1.0、HTTP/1.1、HTTP/2.0 的抽象處理。盡管 HTTP 在各個(gè)版本中增加了許多新特性,但在進(jìn)行業(yè)務(wù)開(kāi)發(fā)時(shí),我們通常無(wú)法感知到這些變化,關(guān)鍵在于 HttpServletRequest 接口的抽象。
在攜程,網(wǎng)上網(wǎng)關(guān)處理的都是請(qǐng)求 - 響應(yīng)模式的無(wú)狀態(tài)協(xié)議,報(bào)文結(jié)構(gòu)也可以劃分為元數(shù)據(jù)、擴(kuò)展頭、業(yè)務(wù)報(bào)文三部分,因此可以方便地進(jìn)行類(lèi)似的嘗試。相關(guān)工作可以用以下兩點(diǎn)來(lái)概括:
- 協(xié)議適配層:用于屏蔽不同協(xié)議的編解碼、交互模式、對(duì) TCP 連接的處理等
- 定義通用中間模型與接口:業(yè)務(wù)面向中間模型與接口進(jìn)行編程,更好地關(guān)注到協(xié)議對(duì)應(yīng)的業(yè)務(wù)屬性上
圖片
4.2 路由模塊
路由模塊是控制面的兩個(gè)主要組成部分之一,除了管理網(wǎng)關(guān)與服務(wù)之間的映射關(guān)系外,服務(wù)本身可以用以下模型來(lái)概括:
{
//匹配方式
"type": "uri",
//HTTP默認(rèn)采用uri前綴匹配,內(nèi)部通過(guò)樹(shù)結(jié)構(gòu)尋址;私有協(xié)議(SOTP)通過(guò)服務(wù)唯一標(biāo)識(shí)定位。
"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用于二級(jí)路由,如按app版本劃分、按query重分配等
"condition": "true",
"conditionParam": {},
"zone": "PRO",
//具體服務(wù)地址,權(quán)重用于灰度場(chǎng)景
"targets": [{
"url": "http://test.ctrip.com/hotel",
"weight": 100
}
]
}]
}
4.3 模塊編排
模塊調(diào)度是控制面的另一個(gè)關(guān)鍵組成部分。我們?cè)诰W(wǎng)關(guān)處理流程中設(shè)置了多個(gè)階段(圖中用粉色表示)。除了熔斷、限流、日志等通用功能外,運(yùn)行時(shí),不同網(wǎng)關(guān)需要執(zhí)行的業(yè)務(wù)功能由控制面統(tǒng)一分配。這些功能在網(wǎng)關(guān)內(nèi)部有獨(dú)立的代碼模塊,而控制面則額外定義了這些功能對(duì)應(yīng)的執(zhí)行條件、參數(shù)、灰度比例和錯(cuò)誤處理方式等。這種調(diào)度方式也在一定程度上保證了模塊之間的解耦。
圖片
{
//模塊名稱(chēng),對(duì)應(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}"
},
//異常處理方式,可以?huà)伋龌蚝雎? "exceptionHandle": "return"
}
五、總結(jié)
網(wǎng)關(guān)在各種技術(shù)交流平臺(tái)上一直是備受關(guān)注的話(huà)題,有很多成熟的解決方案:易于上手且發(fā)展較早的 Zuul 1.0、高性能的 Nginx、集成度高的 Spring Cloud Gateway、日益流行的 Istio 等等。
最終的選型還是取決于各公司的業(yè)務(wù)背景和技術(shù)生態(tài)。
因此,在攜程,我們選擇了自主研發(fā)的道路。
技術(shù)在不斷發(fā)展,我們也在持續(xù)探索,包括公共網(wǎng)關(guān)與業(yè)務(wù)網(wǎng)關(guān)的關(guān)系、新協(xié)議(如 HTTP3)的應(yīng)用、與 ServiceMesh 的關(guān)聯(lián)等等。