閱讀對象
傳統(tǒng)企業(yè)正在做微服務架構轉型的開發(fā)人員或者架構師玫霎,希望本文對您能起到一定的引導作用驹愚。
API網關介紹
網關一詞較早出現(xiàn)在網絡設備里面包颁,比如兩個相互獨立的局域網段之間通過路由器或者橋接設備進行通信宗兼,這中間的路由或者橋接設備我們稱之為網關氯檐。
相應的API網關將各系統(tǒng)對外暴露的服務聚合起來,所有要調用這些服務的系統(tǒng)都需要通過API網關進行訪問寂玲,基于這種方式網關可以對API進行統(tǒng)一管控塔插,例如:認證、鑒權敢茁、流量控制佑淀、協(xié)議轉換、監(jiān)控等等彰檬。
API網關的流行得益于近幾年微服務架構的興起伸刃,原本一個龐大的業(yè)務系統(tǒng)被拆分成許多粒度更小的系統(tǒng)進行獨立部署和維護,這種模式勢必會帶來更多的跨系統(tǒng)交互逢倍,企業(yè)API的規(guī)模也會成倍增加捧颅,API網關(或者微服務網關)就逐漸成為了微服務架構的標配組件。
如下是我們整理的API網關的幾種典型應用場景:
1较雕、面向Web或者移動App
這類場景碉哑,在物理形態(tài)上類似前后端分離,前端應用通過API調用后端服務亮蒋,需要網關具有認證扣典、鑒權、緩存慎玖、服務編排贮尖、監(jiān)控告警等功能。
2趁怔、面向合作伙伴開放API
這類場景湿硝,主要為了滿足業(yè)務形態(tài)對外開放,與企業(yè)外部合作伙伴建立生態(tài)圈润努,此時的API 網關注重安全認證关斜、權限分級、流量管控铺浇、緩存等功能的建設痢畜。
3、企業(yè)內部系統(tǒng)互聯(lián)互通
對于中大型的企業(yè)內部往往有幾十随抠、甚至上百個系統(tǒng)裁着,尤其是微服務架構的興起系統(tǒng)數(shù)量更是急劇增加。系統(tǒng)之間相互依賴拱她,逐漸形成網狀調用關系不便于管理和維護,需要API網關進行統(tǒng)一的認證扔罪、鑒權秉沼、流量管控、超時熔斷、監(jiān)控告警管理唬复,從而提高系統(tǒng)的穩(wěn)定性矗积、降低重復建設、運維管理等成本敞咧。
設計目標
1棘捣、純Java實現(xiàn);
2休建、支持插件化乍恐,方便開發(fā)人員自定義組件;
3测砂、支持橫向擴展茵烈,高性能;
4砌些、避免單點故障呜投,穩(wěn)定性要高,不能因為某個API故障導致整個網關停止服務存璃;
5仑荐、管理控制臺配置更新可自動生效,不需要重啟網關纵东;
應用架構設計
整個平臺拆分成3個子系統(tǒng)粘招,Gateway-Core(核心子系統(tǒng))、Gateway-Admin(管理中心)篮迎、Gateway-Monitor(監(jiān)控中心)男图。
Gateway-Core負責接收客戶端請求,調度甜橱、加載和執(zhí)行組件逊笆,將請求路由到上游服務端,處理上游服務端返回的結果等岂傲;
Gateway-Admin提供統(tǒng)一的管理界面难裆,用戶可在此進行API、組件镊掖、系統(tǒng)基礎信息的設置和維護乃戈;
Gateway-Monitor負責收集監(jiān)控日志、生成各種運維管理報表亩进、自動告警等症虑;
系統(tǒng)架構設計
說明:
1、網關核心子系統(tǒng)通過HAProxy或者Nginx進行負載均衡归薛,為避免正好路由的LB節(jié)點服務不可用谍憔,可以考慮在此基礎上增加Keepalived來實現(xiàn)LB的失效備援匪蝙,當LB Node1停止服務,Keepalived會將虛擬IP自動飄移到LB Node2习贫,從而避免因為負載均衡器導致單點故障逛球。DNS可以直接指向Keepalived的虛擬IP。
2苫昌、網關除了對性能要求很高外颤绕,對穩(wěn)定性也有很高的要求,引入Zookeeper及時將Admin對API的配置更改同步刷新到各網關節(jié)點祟身。
3奥务、管理中心和監(jiān)控中心可以采用類似網關子系統(tǒng)的高可用策略,如果嫌麻煩管理中心可以省去Keepalived月而,相對來說管理中心沒有這么高的可用性要求汗洒。
4、理論上監(jiān)控中心需要承載很大的數(shù)據量父款,比如有1000個API溢谤,平均每個API一天調用10萬次,對于很多互聯(lián)網公司單個API的量遠遠大于10萬憨攒,如果將每次調用的信息都存儲起來太浪費世杀,也沒有太大的必要「渭可以考慮將API每分鐘的調用情況匯總后進行存儲瞻坝,比如1分鐘的平均響應時間、調用次數(shù)杏瞻、流量所刀、正確率等等。
5捞挥、數(shù)據庫選型可以靈活考慮浮创,原則上網關在運行時要盡可能減少對DB的依賴,否則IO延時會嚴重影響網關性能砌函≌杜可以考慮首次訪問后將API配置信息緩存,Admin對API配置更改后通過Zookeeper通知網關刷新讹俊,這樣一來DB的訪問量可以忽略不計垦沉,團隊可根據自身偏好靈活選型。
非阻塞式HTTP服務
管理和監(jiān)控中心可以根據團隊的情況采用自己熟悉的Servlet容器部署仍劈,網關核心子系統(tǒng)對性能的要求非常高厕倍,考慮采用NIO的網絡模型,實現(xiàn)純HTTP服務即可贩疙,不需要實現(xiàn)Servlet容器绑青,推薦Netty框架(設計優(yōu)雅诬像,大名鼎鼎的Spring Webflux默認都是使用的Netty屋群,更多的優(yōu)勢就不在此詳述了)闸婴,內部測試在相同的機器上分別通過Tomcat和Netty生成UUID,Netty的性能大約有20%的提升芍躏,如果后端服務響應耗時較高的話吞吐量還有更大的提升邪乍。(補充:Netty4.x的版本即可,不要采用5以上的版本对竣,有嚴重的缺陷沒有解決)
采用Netty作為Http容器首先需要解決的是Http協(xié)議的解析和封裝庇楞,好在Netty本身提供了這樣的Handler,具體參考如下代碼:
1否纬、構建一個單例的HttpServer吕晌,在SpringBoot啟動的時候同時加載并啟動Netty服務
??????????? int sobacklog =Integer.parseInt(AppConfigUtil.getValue("netty.sobacklog"));
??????????? ServerBootstrap b = newServerBootstrap();
? ??????????b.group(bossGroup, workerGroup)
???????????????????.channel(NioServerSocketChannel.class)
???????????????????.localAddress(new InetSocketAddress(this.portHTTP))
???????????????????.option(ChannelOption.SO_BACKLOG, sobacklog)
???????????????????.childHandler(new ChannelHandlerInitializer(null));
??????????? //綁定端口
??????????? ChannelFuture f =b.bind(this.portHTTP).sync();
???????????logger.info("HttpServer name is " + HttpServer.class.getName()+ " started and listen on " + f.channel().localAddress());
2、初始化Handler
@Override
??? protected voidinitChannel(SocketChannel ch) throws Exception {
??????? ChannelPipeline p =ch.pipeline();
??????? p.addLast(newHttpRequestDecoder());
??????? p.addLast(newHttpResponseEncoder());
??????? int maxContentLength = 2000;
??????? try {
??????????? maxContentLength =Integer.parseInt(AppConfigUtil.getValue("netty.maxContentLength"));
??????? } catch (Exception e) {
???????????logger.warn("netty.maxContentLength配置異常临燃,系統(tǒng)默認為:2000KB");
????? ??}
??????? p.addLast(new
HttpObjectAggregator(maxContentLength * 1024));// HTTP 消息的合并處理
??????? p.addLast(newHttpServerInboundHandler());
??? }
HttpRequestDecoder和HttpResponseEncoder分別實現(xiàn)Http協(xié)議的解析和封裝睛驳,Http Post內容超過一個數(shù)據包大小會自動分組,通過HttpObjectAggregator可以自動將這些數(shù)據粘合在一起膜廊,對于上層收到是一個完整的Http請求乏沸。
3、通過HttpServerInboundHandler將網絡請求轉發(fā)給網關執(zhí)行器
@Override
??? public voidchannelRead0(ChannelHandlerContext ctx, Object msg)
??????????? throws Exception {
??????? try {
??????????? if (msg instanceofHttpRequest && msg instanceof HttpContent) {
??????????????? CmptRequestcmptRequest = CmptRequestUtil.convert(ctx, msg);
??????????????? CmptResultcmptResult = this.gatewayExecutor.execute(cmptRequest);
??????????????? FullHttpResponseresponse = encapsulateResponse(cmptResult);
??????????????? ctx.write(response);
??????????????? ctx.flush();
??????????? }
??????? } catch (Exception e) {
??????????? logger.error("網關入口異常爪瓜," + e.getMessage());
??????????? e.printStackTrace();
??????? }
??? }
設計上建議將Netty接入層代碼跟網關核心邏輯代碼分離蹬跃,不要將Netty收到HttpRequest和HttpContent直接給到網關執(zhí)行器,可以考慮做一層轉換封裝成自己的Request給到執(zhí)行器铆铆,方便后續(xù)可以很容易的將Netty替換成其它Http容器蝶缀。(如上代碼所示,CmptRequest即為自定義的Http請求封裝類薄货,CmptResult為網關執(zhí)行結果類)
組件化及自定義組件支持
組件是網關的核心翁都,大部分功能特性都可以基于組件的形式提供,組件化可以有效提高網關的擴展性菲驴。
先來看一個簡單的微信認證組件的例子:
如下實現(xiàn)的功能是對API請求傳入的Token進行校驗荐吵,其結果分別是認證通過、Token過期和無效Token赊瞬,認證通過后再將微信OpenID攜帶給上游服務系統(tǒng)先煎。
/**
?*微信token認證,token格式:
?*{appID:'',openID:'',timestamp:132525144172,sessionKey: ''}
?*/
public class WeixinAuthTokenCmpt extends AbstractCmpt {
??? private static Logger logger =LoggerFactory.getLogger(WeixinAuthTokenCmpt.class);
??? private final CmptResultSUCCESS_RESULT;
??? public WeixinAuthTokenCmpt() {
??????? SUCCESS_RESULT =buildSuccessResult();
??? }
??? @Override
?? ?public CmptResult execute(CmptRequest request,Map config) {
??????? if (logger.isDebugEnabled()){
???????????logger.debug("WeixinTokenCmpt ......");
??????? }
??????? CmptResult cmptResult =null;
??????? //Token認證超時間(傳入單位:分)
??????? long authTokenExpireTime =getAuthTokenExpireTime(config);
??????? WeixinTokenDTO authTokenDTO= this.getAuthTokenDTO(request);
???????logger.debug("Token=" + authTokenDTO);
??????? AuthTokenStateauthTokenState = validateToken(authTokenDTO, authTokenExpireTime);
??????? switch (authTokenState) {
??????????? case ACCESS: {
??????????????? cmptResult =SUCCESS_RESULT;
??????????????? Map header = new HashMap<>();
???????????????header.put(HeaderKeyConstants.HEADER_APP_ID_KEY,authTokenDTO.getAppID());
??????????????? header.put(CmptHeaderKeyConstants.HEADER_WEIXIN_OPENID_KEY,authTokenDTO.getOpenID());
???????????????header.put(CmptHeaderKeyConstants.HEADER_WEIXIN_SESSION_KEY,authTokenDTO.getSessionKey());
???????????????cmptResult.setHeader(header);
??????????????? break;
?????? ?????}
??????????? case EXPIRED: {
??????????????? cmptResult =buildCmptResult(RespErrCode.AUTH_TOKEN_EXPIRED, "token過期,請重新獲取Token巧涧!");
??????????????? break;
??????????? }
??????????? case INVALID: {
??????????????? cmptResult =buildCmptResult(RespErrCode.AUTH_INVALID_TOKEN, "Token無效薯蝎!");
??????????????? break;
??????????? }
??????? }
??????? return cmptResult;
??? }
...
}
上面例子看不懂沒關系,接下來會詳細闡述組件的設計思路谤绳。
1占锯、組件接口定義
public interface ICmpt {
??? /**
???? *組件執(zhí)行入口
???? *
???? * @param request
???? * @param config袒哥,組件實例的參數(shù)配置
???? * @return
???? */
??? CmptResult execute(CmptRequestrequest, Map config);
??? /**
???? *銷毀組件持有的特殊資源,比如線程消略。
???? */
??? void destroy();
}
execute是組件執(zhí)行的入口方法堡称,request前面提到過是http請求的封裝,config是組件的特殊配置艺演,比如上面例子提到的微信認證組件就有一個自定義配置-Token的有效期却紧,不同的API使用該組件可以設置不同的有效期。
FieldDTO定義如下:
public class FieldDTO {
??? private String title;
??? private String name;
??? private FieldType fieldType =FieldType.STRING;
??? private String defaultValue;
??? private boolean required;
??? private String regExp;
??? private String description;
}
CmptResult為組件執(zhí)行后的返回結果胎撤,其定義如下:
public class CmptResult {
??? RespErrMsg respErrMsg;//組件返回錯誤信息
??? private boolean passed;//組件過濾是否通過
??? private byte[] data;//組件返回數(shù)據
??? private Map header = new HashMap();//透傳后端服務響應頭信息
??? private MediaType mediaType;//返回響應數(shù)據類型
??? private Integer statusCode =200;//默認返回狀態(tài)碼為200
}
2晓殊、組件類型定義
執(zhí)行器需要根據組件類型和組件執(zhí)行結果判斷是要直接返回客戶端還是繼續(xù)往下面執(zhí)行,比如認證類型的組件伤提,如果認證失敗是不能繼續(xù)往下執(zhí)行的巫俺,但緩存類型的組件沒有命中才繼續(xù)往下執(zhí)行。當然這樣設計存在一些缺陷肿男,比如新增組件類型需要執(zhí)行器配合調整處理邏輯介汹。(Kong也提供了大量的功能組件,沒有研究過其網關框架是如何跟組件配合的次伶,是否支持用戶自定義組件類型痴昧,知道的朋友詳細交流下。)
初步定義如下組件類型:
認證冠王、鑒權赶撰、流量管控、緩存柱彻、路由豪娜、日志等。
其中路由類型的組件涵蓋了協(xié)議轉換的功能哟楷,其負責調用上游系統(tǒng)提供的服務瘤载,可以根據上游系統(tǒng)提供API的協(xié)議定制不同的路由組件,比如:Restful卖擅、WebService鸣奔、Dubbo、EJB等等惩阶。
3挎狸、組件執(zhí)行位置和優(yōu)先級設定
執(zhí)行位置:Pre、Routing断楷、After锨匆,分別代表后端服務調用前、后端服務調用中和后端服務調用完成后冬筒,相同位置的組件根據優(yōu)先級決定執(zhí)行的先后順序恐锣。
4茅主、組件發(fā)布形式
組件打包成標準的Jar包,通過Admin管理界面上傳發(fā)布土榴。
附-組件可視化選擇UI設計
組件熱插拔設計和實現(xiàn)
JVM中Class是通過類加載器+全限定名來唯一標識的诀姚,上面章節(jié)談到組件是以Jar包的形式發(fā)布的,但相同組件的多個版本的入口類名需要保持不變鞭衩,因此要實現(xiàn)組件的熱插拔和多版本并存就需要自定義類加載器來實現(xiàn)学搜。
大致思路如下:
網關接收到API調用請求后根據請求參數(shù)從緩存里拿到API配置的組件列表,然后再逐一參數(shù)從緩存里獲取組件對應的類實例论衍,如果找不到則嘗試通過自定義類加載器載入Jar包,并初始化組件實例及緩存聚磺。
附-參考示例
public static ICmpt newInstance(final CmptDef cmptDef) {
??? ICmpt cmpt = null;
??? try { ?
?????? final String jarPath = getJarPath(cmptDef);
??????? if (logger.isDebugEnabled()) {
??????????? logger.debug("嘗試載入jar包,jar包路徑: " + jarPath);
??????? }
??????? //加載依賴jar
??????? CmptClassLoader cmptClassLoader = CmptClassLoaderManager.loadJar(jarPath, true);
??????? // 創(chuàng)建實例
??????? if (null != cmptClassLoader) {
??????????? cmpt = LoadClassUtil.newObject(cmptDef.getFullQualifiedName(), ICmpt.class, cmptClassLoader);
??????? } else { ??????????? logger.error("加載組件jar包失敗! jarPath: " + jarPath); ??????? } ??? }
catch (Exception e) {
??????? logger.error("組件類加載失敗坯台,請檢查類名和版本是否正確。ClassName=" + cmptDef.getFullQualifiedName() + ", Version=" + cmptDef.getVersion()); ??????? e.printStackTrace();
??? }
??? return cmpt;
}
補充說明:
自定義類加載器可直接需要繼承至URLClassLoader瘫寝,另外必須指定其父類加載器為執(zhí)行器的加載器蜒蕾,否則組件沒法引用網關的其它類。
API故障隔離及超時焕阿、熔斷處理
在詳細闡述設計前先講個實際的案例咪啡,大概12年的時候某公司自研了一款ESB的中間件(企業(yè)服務總線跟API網關很類似,當年SOA理念大行其道的時候都推崇的是ESB暮屡,側重服務的編排和異構系統(tǒng)的整合撤摸。),剛開始用的還行褒纲,但隨著接入系統(tǒng)的增多准夷,突然某天運維發(fā)現(xiàn)大量API出現(xiàn)緩慢甚至超時,初步檢查發(fā)現(xiàn)ESB每個節(jié)點的線程幾乎消耗殆盡莺掠,起初判斷是資源不夠衫嵌,緊急擴容后還是很快線程占滿,最終導致上百個系統(tǒng)癱瘓彻秆。
最終找到問題的癥結是某個業(yè)務系統(tǒng)自身的原因導致服務不可用楔绞,下游業(yè)務系統(tǒng)請求大量堆積到ESB中,從而導致大量線程堵塞唇兑。
以上案例說明了一個在企業(yè)應用架構設計里面的經典原則-故障隔離酒朵,由于所有的API請求都要經過網關,必須隔離API之間的相互影響幔亥,尤其是個別API故障導致整個網關集群服務中斷耻讽。
接下來分別介紹故障隔離、超時管控帕棉、熔斷的實現(xiàn)思路针肥。
1饼记、故障隔離
有兩種方式可以實現(xiàn),一是為每個API創(chuàng)建一個線程池慰枕,每個線程分配10~20個線程具则,這也是常用的隔離策略,但這種方式有幾個明顯的缺點:
1)線程數(shù)會隨著API接入數(shù)量遞增具帮,1000個API就需要2萬個線程博肋,光線程切換對CPU就是不小的開銷,而其線程還需要占用一定的內存資源蜂厅;
2)平均分配線程池大小導致個別訪問量較大且響應時間相對較長的API吞吐量上不去匪凡;
3)本身就有工作線程池了,再增加API的線程池掘猿,導致某些需要ThreadLocal特性的編程變得困難病游。
二是用信號量隔離,直接復用Netty的工作線程稠通,上面線程池隔離提到的3個缺點都可以基本避免衬衬, 建議設置單個API的信號量個數(shù)小于等于Netty工作線程池數(shù)量的1/3,這樣既兼顧了單個API的性能又不至于單個API的問題導致整個網關堵塞改橘。
具體實現(xiàn)可以考慮直接引用成熟的開源框架滋尉,推薦Hystrix,可以同時解決超時控制和熔斷飞主。
參考配置如下:
Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey(groupKey))
???????.andCommandKey(HystrixCommandKey.Factory.asKey(commandKey ))
???????.andCommandPropertiesDefaults(HystrixCommandProperties.Setter()
? ??????//艙壁隔離策略-信號量
? ? ? ? .withExecutionIsolationStrategy(HystrixCommandProperties.ExecutionIsolationStrategy.SEMAPHORE)
? ??????//設置每組command可以申請的信號量最大數(shù)
? ? ? ? .withExecutionIsolationSemaphoreMaxConcurrentRequests(CmptInvoker.maxSemaphore)
? ??????/*開啟超時設置*/
? ? ? ? .withExecutionIsolationThreadInterruptOnTimeout(true)
? ??????/*超時時間設置*/
? ? ? ? .withExecutionIsolationThreadTimeoutInMilliseconds(timeout)
? ? ? ? .withCircuitBreakerEnabled(true)//開啟熔斷? ? ? ????????.withCircuitBreakerSleepWindowInMilliseconds(Constants.DEFAULT_CIRCUIT_BREAKER_SLEEP_WINDOW_IN_MILLISECONDS)//5秒后會嘗試閉合回路
2狮惜、 超時管控
API的超時控制是必須要做的,否則上游服務即便是間歇性響應緩慢也會堵塞大量線程(雖然通過信號量隔離后不會導致整個網關線程堵塞)既棺。
其次讽挟,每個API最好可以單獨配置超時時間,但不建議可以讓用戶隨意設置丸冕,還是要有個最大閾值耽梅。(API網關不適合需要長時間傳輸數(shù)據的場景,比如大文件上傳或者下載胖烛、DB數(shù)據同步等)
實現(xiàn)上可以直接復用開源組件的功能眼姐,比如:HttpClient可以直接設置獲取連接和Socket響應的超時時間,Hystrix可以對整個調用進行超時控制等佩番。
3众旗、熔斷
熔斷類似電路中的保險絲,當超過負荷或者電阻被擊穿的時候自動斷開對設備起到保護作用趟畏。在API網關中設置熔斷的目的是快速響應請求贡歧,避免不必要的等待,比如某個API后端服務正常情況下1s以內響應,但現(xiàn)在因為各種原因出現(xiàn)堵塞大部分請求20s才能響應利朵,雖然設置了10s的超時控制律想,但讓請求線程等待10s超時不僅沒有意義,反而會增加服務提供方的負擔绍弟。
為此我們可以設置單位時間內超過多少比例的請求超時或者異常技即,則直接熔斷鏈路,等待一段時間后再次嘗試恢復鏈路樟遣。
實現(xiàn)層面可以直接復用Hystrix而叼。
運行時配置更新機制
前面章節(jié)提到過出于性能考慮網關在運行時要盡可能減小對DB的訪問,設計上可以將API豹悬、組件等關鍵內容進行緩存葵陵,這樣一來性能是提升了,但也帶來了新的問題屿衅,比如Admin對API或者組件進行配置調整后如何及時更新到集群的各個網關節(jié)點埃难。
解決方案很多,比如引入消息中間件涤久,當Admin調整配置后就往消息中心發(fā)布一條消息,各網關節(jié)點訂閱消息忍弛,收到消息后刷新緩存數(shù)據响迂。
我們在具體實現(xiàn)過程中采用的是Zookeeper集群數(shù)據同步機制,其實現(xiàn)原理跟消息中間件很類似细疚,只不過網關在啟動的時候就會向ZK節(jié)點進行注冊蔗彤,也是被動更新機制。
性能考慮
性能是網關一項非常重要的衡量指標疯兼,尤其是響應時間然遏,客戶端本來可以直連服務端的,現(xiàn)在增加了一個網關層吧彪,對于一個本身耗時幾百毫秒的服務接入網關后增加幾毫秒待侵,影響倒是可以忽略不計;但如果服務本身只需要幾毫秒姨裸,因為接入網關再增加一倍的延時秧倾,用戶感受就會比較明顯。
建議在設計上需要遵循如下原則:
1傀缩、核心網關子系統(tǒng)必須是無狀態(tài)的那先,便于橫向擴展。
2赡艰、運行時不依賴本地存儲售淡,盡量在內存里面完成服務的處理和中轉。
3、減小對線程的依賴揖闸,采用非阻塞式IO和異步事件響應機制揍堕。
4、后端服務如果是HTTP協(xié)議楔壤,盡量采用連接池或者Http2鹤啡,測試連接復用和不復用性能有幾倍的差距。(TCP建立連接成本很高)
附-HttpClient連接池設置
PoolingHttpClientConnectionManager
cmOfHttp = new PoolingHttpClientConnectionManager();
cmOfHttp.setMaxTotal(maxConn);
cmOfHttp.setDefaultMaxPerRoute(maxPerRoute);
httpClient = HttpClients.custom() .setConnectionManager(cmOfHttp).setConnectionManagerShared(true)
??????? .build();
說明:
httpClient對象可以作為類的成員變量長期駐留內存蹲嚣,這個是連接池復用的前提递瑰。
結語
API網關作為企業(yè)API服務的匯聚中心,其良好的性能隙畜、穩(wěn)定性和可擴展性是基礎抖部,只有這個基礎打扎實了,我們才能在上面擴展更多的特性议惰。
這篇文章主要介紹網關的總體架構設計慎颗, 后面的篇幅在詳細探討下各種組件的具體設計和實現(xiàn)。
有興趣的朋友可以加入QQ交流群:244054462言询,備注:API網關架構設計交流俯萎。
附-產品介紹
http://www.xbgateway.com/