一个简单可参考的API网关架构设计
http://www.infoq.com/cn/articles/api-gateway-architecture-design
網(wǎng)關(guān)一詞較早出現(xiàn)在網(wǎng)絡(luò)設(shè)備里面,比如兩個(gè)相互獨(dú)立的局域網(wǎng)段之間通過(guò)路由器或者橋接設(shè)備進(jìn)行通信, 這中間的路由或者橋接設(shè)備我們稱之為網(wǎng)關(guān)。
相應(yīng)的 API 網(wǎng)關(guān)將各系統(tǒng)對(duì)外暴露的服務(wù)聚合起來(lái),所有要調(diào)用這些服務(wù)的系統(tǒng)都需要通過(guò) API 網(wǎng)關(guān)進(jìn)行訪問(wèn),基于這種方式網(wǎng)關(guān)可以對(duì) API 進(jìn)行統(tǒng)一管控,例如:認(rèn)證、鑒權(quán)、流量控制、協(xié)議轉(zhuǎn)換、監(jiān)控等等。
API 網(wǎng)關(guān)的流行得益于近幾年微服務(wù)架構(gòu)的興起,原本一個(gè)龐大的業(yè)務(wù)系統(tǒng)被拆分成許多粒度更小的系統(tǒng)進(jìn)行獨(dú)立部署和維護(hù),這種模式勢(shì)必會(huì)帶來(lái)更多的跨系統(tǒng)交互,企業(yè) API 的規(guī)模也會(huì)成倍增加,API 網(wǎng)關(guān)(或者微服務(wù)網(wǎng)關(guān))就逐漸成為了微服務(wù)架構(gòu)的標(biāo)配組件。
如下是我們整理的 API 網(wǎng)關(guān)的幾種典型應(yīng)用場(chǎng)景:
1、面向 Web 或者移動(dòng) App
這類場(chǎng)景,在物理形態(tài)上類似前后端分離,前端應(yīng)用通過(guò) API 調(diào)用后端服務(wù),需要網(wǎng)關(guān)具有認(rèn)證、鑒權(quán)、緩存、服務(wù)編排、監(jiān)控告警等功能。
2、面向合作伙伴開(kāi)放 API
這類場(chǎng)景,主要為了滿足業(yè)務(wù)形態(tài)對(duì)外開(kāi)放,與企業(yè)外部合作伙伴建立生態(tài)圈,此時(shí)的 API 網(wǎng)關(guān)注重安全認(rèn)證、權(quán)限分級(jí)、流量管控、緩存等功能的建設(shè)。
3、企業(yè)內(nèi)部系統(tǒng)互聯(lián)互通
對(duì)于中大型的企業(yè)內(nèi)部往往有幾十、甚至上百個(gè)系統(tǒng),尤其是微服務(wù)架構(gòu)的興起系統(tǒng)數(shù)量更是急劇增加。系統(tǒng)之間相互依賴,逐漸形成網(wǎng)狀調(diào)用關(guān)系不便于管理和維護(hù),需要 API 網(wǎng)關(guān)進(jìn)行統(tǒng)一的認(rèn)證、鑒權(quán)、流量管控、超時(shí)熔斷、監(jiān)控告警管理,從而提高系統(tǒng)的穩(wěn)定性、降低重復(fù)建設(shè)、運(yùn)維管理等成本。
設(shè)計(jì)目標(biāo)
應(yīng)用架構(gòu)設(shè)計(jì)
整個(gè)平臺(tái)拆分成 3 個(gè)子系統(tǒng),Gateway-Core(核心子系統(tǒng))、Gateway-Admin(管理中心)、Gateway-Monitor(監(jiān)控中心)。
- Gateway-Core 負(fù)責(zé)接收客戶端請(qǐng)求,調(diào)度、加載和執(zhí)行組件,將請(qǐng)求路由到上游服務(wù)端,處理上游服務(wù)端返回的結(jié)果等;
- Gateway-Admin 提供統(tǒng)一的管理界面,用戶可在此進(jìn)行 API、組件、系統(tǒng)基礎(chǔ)信息的設(shè)置和維護(hù);
- Gateway-Monitor 負(fù)責(zé)收集監(jiān)控日志、生成各種運(yùn)維管理報(bào)表、自動(dòng)告警等;
系統(tǒng)架構(gòu)設(shè)計(jì)
說(shuō)明:
非阻塞式 HTTP 服務(wù)
管理和監(jiān)控中心可以根據(jù)團(tuán)隊(duì)的情況采用自己熟悉的 Servlet 容器部署,網(wǎng)關(guān)核心子系統(tǒng)對(duì)性能的要求非常高,考慮采用 NIO 的網(wǎng)絡(luò)模型,實(shí)現(xiàn)純 HTTP 服務(wù)即可,不需要實(shí)現(xiàn) Servlet 容器,推薦 Netty 框架(設(shè)計(jì)優(yōu)雅,大名鼎鼎的 Spring Webflux 默認(rèn)都是使用的 Netty,更多的優(yōu)勢(shì)就不在此詳述了),內(nèi)部測(cè)試在相同的機(jī)器上分別通過(guò) Tomcat 和 Netty 生成 UUID,Netty 的性能大約有 20% 的提升,如果后端服務(wù)響應(yīng)耗時(shí)較高的話吞吐量還有更大的提升。(補(bǔ)充:Netty4.x 的版本即可,不要采用 5 以上的版本,有嚴(yán)重的缺陷沒(méi)有解決)
采用 Netty 作為 Http 容器首先需要解決的是 Http 協(xié)議的解析和封裝,好在 Netty 本身提供了這樣的 Handler,具體參考如下代碼:
1、構(gòu)建一個(gè)單例的 HttpServer,在 SpringBoot 啟動(dòng)的時(shí)候同時(shí)加載并啟動(dòng) Netty 服務(wù)
int sobacklog = Integer.parseInt(AppConfigUtil.getValue("netty.sobacklog"));ServerBootstrap b = new ServerBootstrap();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
@Overrideprotected void initChannel(SocketChannel ch) throws Exception {ChannelPipeline p = ch.pipeline();p.addLast(new HttpRequestDecoder());p.addLast(new HttpResponseEncoder());int maxContentLength = 2000;try {maxContentLength = Integer.parseInt(AppConfigUtil.getValue("netty.maxContentLength"));} catch (Exception e) {logger.warn("netty.maxContentLength 配置異常,系統(tǒng)默認(rèn)為:2000KB");}p.addLast(new HttpObjectAggregator(maxContentLength * 1024));// HTTP 消息的合并處理p.addLast(new HttpServerInboundHandler());}HttpRequestDecoder 和 HttpResponseEncoder 分別實(shí)現(xiàn) Http 協(xié)議的解析和封裝,Http Post 內(nèi)容超過(guò)一個(gè)數(shù)據(jù)包大小會(huì)自動(dòng)分組,通過(guò) HttpObjectAggregator 可以自動(dòng)將這些數(shù)據(jù)粘合在一起,對(duì)于上層收到是一個(gè)完整的 Http 請(qǐng)求。
3、通過(guò) HttpServerInboundHandler 將網(wǎng)絡(luò)請(qǐng)求轉(zhuǎn)發(fā)給網(wǎng)關(guān)執(zhí)行器
@Overridepublic void channelRead0(ChannelHandlerContext ctx, Object msg)throws Exception {try {if (msg instanceof HttpRequest && msg instanceof HttpContent) {CmptRequest cmptRequest = CmptRequestUtil.convert(ctx, msg);CmptResult cmptResult = this.gatewayExecutor.execute(cmptRequest);FullHttpResponse response = encapsulateResponse(cmptResult);ctx.write(response);ctx.flush();}} catch (Exception e) {logger.error("網(wǎng)關(guān)入口異常," \+ e.getMessage());e.printStackTrace();}}設(shè)計(jì)上建議將 Netty 接入層代碼跟網(wǎng)關(guān)核心邏輯代碼分離,不要將 Netty 收到 HttpRequest 和 HttpContent 直接給到網(wǎng)關(guān)執(zhí)行器,可以考慮做一層轉(zhuǎn)換封裝成自己的 Request 給到執(zhí)行器,方便后續(xù)可以很容易的將 Netty 替換成其它 Http 容器。(如上代碼所示,CmptRequest 即為自定義的 Http 請(qǐng)求封裝類,CmptResult 為網(wǎng)關(guān)執(zhí)行結(jié)果類)
組件化及自定義組件支持
組件是網(wǎng)關(guān)的核心,大部分功能特性都可以基于組件的形式提供,組件化可以有效提高網(wǎng)關(guān)的擴(kuò)展性。
先來(lái)看一個(gè)簡(jiǎn)單的微信認(rèn)證組件的例子:
如下實(shí)現(xiàn)的功能是對(duì) API 請(qǐng)求傳入的 Token 進(jìn)行校驗(yàn),其結(jié)果分別是認(rèn)證通過(guò)、Token 過(guò)期和無(wú)效 Token,認(rèn)證通過(guò)后再將微信 OpenID 攜帶給上游服務(wù)系統(tǒng)。
/*** 微信 token 認(rèn)證,token 格式:* {appID:'',openID:'',timestamp:132525144172,sessionKey: ''}*public class WeixinAuthTokenCmpt extends AbstractCmpt {private static Logger logger = LoggerFactory.getLogger(WeixinAuthTokenCmpt.class);private final CmptResult SUCCESS_RESULT;public WeixinAuthTokenCmpt() {SUCCESS_RESULT = buildSuccessResult();}@Overridepublic CmptResult execute(CmptRequest request, Map<String, FieldDTO> config) {if (logger.isDebugEnabled()) {logger.debug("WeixinTokenCmpt ......");}CmptResult cmptResult = null;//Token 認(rèn)證超時(shí)間 (傳入單位: 分)long authTokenExpireTime = getAuthTokenExpireTime(config);WeixinTokenDTO authTokenDTO = this.getAuthTokenDTO(request);logger.debug("Token=" + authTokenDTO);AuthTokenState authTokenState = validateToken(authTokenDTO, authTokenExpireTime);switch (authTokenState) {case ACCESS: {cmptResult = SUCCESS_RESULT;Map<String, String> 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 過(guò)期, 請(qǐng)重新獲取 Token!");break;}case INVALID: {cmptResult = buildCmptResult(RespErrCode.AUTH\_INVALID\_TOKEN, "Token 無(wú)效!");break;}}return cmptResult;}...}上面例子看不懂沒(méi)關(guān)系,接下來(lái)會(huì)詳細(xì)闡述組件的設(shè)計(jì)思路。
1、組件接口定義
public interface ICmpt {/** ? ? ? ?* 組件執(zhí)行入口** @param request* @param config,組件實(shí)例的參數(shù)配置* @return*/ ? ?CmptResult execute(CmptRequest request, Map<String, FieldDTO> config);/*** 銷毀組件持有的特殊資源,比如線程。*/void destroy(); }execute 是組件執(zhí)行的入口方法,request 前面提到過(guò)是 http 請(qǐng)求的封裝,config 是組件的特殊配置,比如上面例子提到的微信認(rèn)證組件就有一個(gè)自定義配置 -Token 的有效期,不同的 API 使用該組件可以設(shè)置不同的有效期。
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í)行后的返回結(jié)果,其定義如下:
public class CmptResult {RespErrMsg respErrMsg;// 組件返回錯(cuò)誤信息private boolean passed;// 組件過(guò)濾是否通過(guò)private byte\[\] data;// 組件返回?cái)?shù)據(jù)private Map<String, String> header = new HashMap<String, String>();// 透?jìng)骱蠖朔?wù)響應(yīng)頭信息private MediaType mediaType;// 返回響應(yīng)數(shù)據(jù)類型private Integer statusCode = 200;// 默認(rèn)返回狀態(tài)碼為 200}2、組件類型定義
執(zhí)行器需要根據(jù)組件類型和組件執(zhí)行結(jié)果判斷是要直接返回客戶端還是繼續(xù)往下面執(zhí)行,比如認(rèn)證類型的組件,如果認(rèn)證失敗是不能繼續(xù)往下執(zhí)行的,但緩存類型的組件沒(méi)有命中才繼續(xù)往下執(zhí)行。當(dāng)然這樣設(shè)計(jì)存在一些缺陷,比如新增組件類型需要執(zhí)行器配合調(diào)整處理邏輯。(Kong 也提供了大量的功能組件,沒(méi)有研究過(guò)其網(wǎng)關(guān)框架是如何跟組件配合的,是否支持用戶自定義組件類型,知道的朋友詳細(xì)交流下。)
初步定義如下組件類型:
認(rèn)證、鑒權(quán)、流量管控、緩存、路由、日志等。
其中路由類型的組件涵蓋了協(xié)議轉(zhuǎn)換的功能,其負(fù)責(zé)調(diào)用上游系統(tǒng)提供的服務(wù),可以根據(jù)上游系統(tǒng)提供 API 的協(xié)議定制不同的路由組件,比如:Restful、WebService、Dubbo、EJB 等等。
3、組件執(zhí)行位置和優(yōu)先級(jí)設(shè)定
執(zhí)行位置:Pre、Routing、After,分別代表后端服務(wù)調(diào)用前、后端服務(wù)調(diào)用中和后端服務(wù)調(diào)用完成后,相同位置的組件根據(jù)優(yōu)先級(jí)決定執(zhí)行的先后順序。
4、組件發(fā)布形式
組件打包成標(biāo)準(zhǔn)的 Jar 包,通過(guò) Admin 管理界面上傳發(fā)布。
附 - 組件可視化選擇 UI 設(shè)計(jì)
組件熱插拔設(shè)計(jì)和實(shí)現(xiàn)
JVM 中 Class 是通過(guò)類加載器 + 全限定名來(lái)唯一標(biāo)識(shí)的,上面章節(jié)談到組件是以 Jar 包的形式發(fā)布的,但相同組件的多個(gè)版本的入口類名需要保持不變,因此要實(shí)現(xiàn)組件的熱插拔和多版本并存就需要自定義類加載器來(lái)實(shí)現(xiàn)。
大致思路如下:
網(wǎng)關(guān)接收到 API 調(diào)用請(qǐng)求后根據(jù)請(qǐng)求參數(shù)從緩存里拿到 API 配置的組件列表,然后再逐一參數(shù)從緩存里獲取組件對(duì)應(yīng)的類實(shí)例,如果找不到則嘗試通過(guò)自定義類加載器載入 Jar 包,并初始化組件實(shí)例及緩存。
附 - 參考示例
public static ICmpt newInstance(final CmptDef cmptDef) {ICmpt cmpt = null;try {final String jarPath = getJarPath(cmptDef);if (logger.isDebugEnabled()) {logger.debug("嘗試載入 jar 包,jar 包路徑: " + jarPath);}// 加載依賴 jarCmptClassLoader cmptClassLoader = CmptClassLoaderManager.loadJar(jarPath, true);// 創(chuàng)建實(shí)例if (null != cmptClassLoader) {cmpt = LoadClassUtil.newObject(cmptDef.getFullQualifiedName(), ICmpt.class, cmptClassLoader);} else {logger.error("加載組件 jar 包失敗! jarPath: " + jarPath);}} catch (Exception e) {logger.error("組件類加載失敗,請(qǐng)檢查類名和版本是否正確。ClassName=" + cmptDef.getFullQualifiedName() + ", Version=" + cmptDef.getVersion());e.printStackTrace();}return cmpt; }補(bǔ)充說(shuō)明:
自定義類加載器可直接需要繼承至 URLClassLoader,另外必須指定其父類加載器為執(zhí)行器的加載器,否則組件沒(méi)法引用網(wǎng)關(guān)的其它類。
API 故障隔離及超時(shí)、熔斷處理
在詳細(xì)闡述設(shè)計(jì)前先講個(gè)實(shí)際的案例,大概 12 年的時(shí)候某公司自研了一款 ESB 的中間件(企業(yè)服務(wù)總線跟 API 網(wǎng)關(guān)很類似,當(dāng)年 SOA 理念大行其道的時(shí)候都推崇的是 ESB,側(cè)重服務(wù)的編排和異構(gòu)系統(tǒng)的整合。),剛開(kāi)始用的還行,但隨著接入系統(tǒng)的增多,突然某天運(yùn)維發(fā)現(xiàn)大量 API 出現(xiàn)緩慢甚至超時(shí),初步檢查發(fā)現(xiàn) ESB 每個(gè)節(jié)點(diǎn)的線程幾乎消耗殆盡,起初判斷是資源不夠,緊急擴(kuò)容后還是很快線程占滿,最終導(dǎo)致上百個(gè)系統(tǒng)癱瘓。
最終找到問(wèn)題的癥結(jié)是某個(gè)業(yè)務(wù)系統(tǒng)自身的原因?qū)е路?wù)不可用,下游業(yè)務(wù)系統(tǒng)請(qǐng)求大量堆積到 ESB 中,從而導(dǎo)致大量線程堵塞。
以上案例說(shuō)明了一個(gè)在企業(yè)應(yīng)用架構(gòu)設(shè)計(jì)里面的經(jīng)典原則 - 故障隔離,由于所有的 API 請(qǐng)求都要經(jīng)過(guò)網(wǎng)關(guān),必須隔離 API 之間的相互影響,尤其是個(gè)別 API 故障導(dǎo)致整個(gè)網(wǎng)關(guān)集群服務(wù)中斷。
接下來(lái)分別介紹故障隔離、超時(shí)管控、熔斷的實(shí)現(xiàn)思路。
1、故障隔離
有兩種方式可以實(shí)現(xiàn),一是為每個(gè) API 創(chuàng)建一個(gè)線程池,每個(gè)線程分配 10~20 個(gè)線程,這也是常用的隔離策略,但這種方式有幾個(gè)明顯的缺點(diǎn):
- 線程數(shù)會(huì)隨著 API 接入數(shù)量遞增,1000 個(gè) API 就需要 2 萬(wàn)個(gè)線程,光線程切換對(duì) CPU 就是不小的開(kāi)銷,而其線程還需要占用一定的內(nèi)存資源;
- 平均分配線程池大小導(dǎo)致個(gè)別訪問(wèn)量較大且響應(yīng)時(shí)間相對(duì)較長(zhǎng)的 API 吞吐量上不去;
- Netty 本身就有工作線程池了,再增加 API 的線程池,導(dǎo)致某些需要 ThreadLocal 特性的編程變得困難。
二是用信號(hào)量隔離,直接復(fù)用 Netty 的工作線程,上面線程池隔離提到的 3 個(gè)缺點(diǎn)都可以基本避免, 建議設(shè)置單個(gè) API 的信號(hào)量個(gè)數(shù)小于等于 Netty 工作線程池?cái)?shù)量的 1/3,這樣既兼顧了單個(gè) API 的性能又不至于單個(gè) API 的問(wèn)題導(dǎo)致整個(gè)網(wǎng)關(guān)堵塞。
具體實(shí)現(xiàn)可以考慮直接引用成熟的開(kāi)源框架,推薦 Hystrix,可以同時(shí)解決超時(shí)控制和熔斷。
參考配置如下:
Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey(groupKey)).andCommandKey(HystrixCommandKey.Factory.asKey(commandKey )).andCommandPropertiesDefaults(HystrixCommandProperties.Setter()// 艙壁隔離策略 - 信號(hào)量.withExecutionIsolationStrategy(HystrixCommandProperties.ExecutionIsolationStrategy.SEMAPHORE)// 設(shè)置每組 command 可以申請(qǐng)的信號(hào)量最大數(shù).withExecutionIsolationSemaphoreMaxConcurrentRequests(CmptInvoker.maxSemaphore)/* 開(kāi)啟超時(shí)設(shè)置 */.withExecutionIsolationThreadInterruptOnTimeout(true)/* 超時(shí)時(shí)間設(shè)置 */.withExecutionIsolationThreadTimeoutInMilliseconds(timeout).withCircuitBreakerEnabled(true)// 開(kāi)啟熔斷.withCircuitBreakerSleepWindowInMilliseconds(Constants.DEFAULT_CIRCUIT_BREAKER_SLEEP_WINDOW_IN_MILLISECONDS)// 5 秒后會(huì)嘗試閉合回路2、超時(shí)管控
API 的超時(shí)控制是必須要做的,否則上游服務(wù)即便是間歇性響應(yīng)緩慢也會(huì)堵塞大量線程(雖然通過(guò)信號(hào)量隔離后不會(huì)導(dǎo)致整個(gè)網(wǎng)關(guān)線程堵塞)。
其次,每個(gè) API 最好可以單獨(dú)配置超時(shí)時(shí)間,但不建議可以讓用戶隨意設(shè)置,還是要有個(gè)最大閾值。(API 網(wǎng)關(guān)不適合需要長(zhǎng)時(shí)間傳輸數(shù)據(jù)的場(chǎng)景,比如大文件上傳或者下載、DB 數(shù)據(jù)同步等)
實(shí)現(xiàn)上可以直接復(fù)用開(kāi)源組件的功能,比如:HttpClient 可以直接設(shè)置獲取連接和 Socket 響應(yīng)的超時(shí)時(shí)間,Hystrix 可以對(duì)整個(gè)調(diào)用進(jìn)行超時(shí)控制等。
3、熔斷
熔斷類似電路中的保險(xiǎn)絲,當(dāng)超過(guò)負(fù)荷或者電阻被擊穿的時(shí)候自動(dòng)斷開(kāi)對(duì)設(shè)備起到保護(hù)作用。在 API 網(wǎng)關(guān)中設(shè)置熔斷的目的是快速響應(yīng)請(qǐng)求,避免不必要的等待,比如某個(gè) API 后端服務(wù)正常情況下 1s 以內(nèi)響應(yīng),但現(xiàn)在因?yàn)楦鞣N原因出現(xiàn)堵塞大部分請(qǐng)求 20s 才能響應(yīng),雖然設(shè)置了 10s 的超時(shí)控制,但讓請(qǐng)求線程等待 10s 超時(shí)不僅沒(méi)有意義,反而會(huì)增加服務(wù)提供方的負(fù)擔(dān)。
為此我們可以設(shè)置單位時(shí)間內(nèi)超過(guò)多少比例的請(qǐng)求超時(shí)或者異常,則直接熔斷鏈路,等待一段時(shí)間后再次嘗試恢復(fù)鏈路。
實(shí)現(xiàn)層面可以直接復(fù)用 Hystrix。
運(yùn)行時(shí)配置更新機(jī)制
前面章節(jié)提到過(guò)出于性能考慮網(wǎng)關(guān)在運(yùn)行時(shí)要盡可能減小對(duì) DB 的訪問(wèn),設(shè)計(jì)上可以將 API、組件等關(guān)鍵內(nèi)容進(jìn)行緩存,這樣一來(lái)性能是提升了,但也帶來(lái)了新的問(wèn)題,比如 Admin 對(duì) API 或者組件進(jìn)行配置調(diào)整后如何及時(shí)更新到集群的各個(gè)網(wǎng)關(guān)節(jié)點(diǎn)。
解決方案很多,比如引入消息中間件,當(dāng) Admin 調(diào)整配置后就往消息中心發(fā)布一條消息,各網(wǎng)關(guān)節(jié)點(diǎn)訂閱消息,收到消息后刷新緩存數(shù)據(jù)。
我們?cè)诰唧w實(shí)現(xiàn)過(guò)程中采用的是 Zookeeper 集群數(shù)據(jù)同步機(jī)制,其實(shí)現(xiàn)原理跟消息中間件很類似,只不過(guò)網(wǎng)關(guān)在啟動(dòng)的時(shí)候就會(huì)向 ZK 節(jié)點(diǎn)進(jìn)行注冊(cè),也是被動(dòng)更新機(jī)制。
性能考慮
性能是網(wǎng)關(guān)一項(xiàng)非常重要的衡量指標(biāo),尤其是響應(yīng)時(shí)間,客戶端本來(lái)可以直連服務(wù)端的,現(xiàn)在增加了一個(gè)網(wǎng)關(guān)層,對(duì)于一個(gè)本身耗時(shí)幾百毫秒的服務(wù)接入網(wǎng)關(guān)后增加幾毫秒,影響倒是可以忽略不計(jì);但如果服務(wù)本身只需要幾毫秒,因?yàn)榻尤刖W(wǎng)關(guān)再增加一倍的延時(shí),用戶感受就會(huì)比較明顯。
建議在設(shè)計(jì)上需要遵循如下原則:
附 -HttpClient 連接池設(shè)置:
PoolingHttpClientConnectionManager cmOfHttp = new PoolingHttpClientConnectionManager(); cmOfHttp.setMaxTotal(maxConn); cmOfHttp.setDefaultMaxPerRoute(maxPerRoute); httpClient = HttpClients.custom().setConnectionManager(cmOfHttp).setConnectionManagerShared(true).build();說(shuō)明:
httpClient 對(duì)象可以作為類的成員變量長(zhǎng)期駐留內(nèi)存,這個(gè)是連接池復(fù)用的前提。
結(jié)語(yǔ)
API 網(wǎng)關(guān)作為企業(yè) API 服務(wù)的匯聚中心,其良好的性能、穩(wěn)定性和可擴(kuò)展性是基礎(chǔ),只有這個(gè)基礎(chǔ)打扎實(shí)了,我們才能在上面擴(kuò)展更多的特性。
這篇文章主要介紹網(wǎng)關(guān)的總體架構(gòu)設(shè)計(jì), 后面的篇幅在詳細(xì)探討下各種組件的具體設(shè)計(jì)和實(shí)現(xiàn)。
轉(zhuǎn)載于:https://www.cnblogs.com/davidwang456/articles/8743421.html
總結(jié)
以上是生活随笔為你收集整理的一个简单可参考的API网关架构设计的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 通过Zookeeper动态感知服务器上下
- 下一篇: 区块链基础设施纵览:基本框架原则——揭示