乡下人产国偷v产偷v自拍,国产午夜片在线观看,婷婷成人亚洲综合国产麻豆,久久综合给合久久狠狠狠9

  • <output id="e9wm2"></output>
    <s id="e9wm2"><nobr id="e9wm2"><ins id="e9wm2"></ins></nobr></s>

    • 分享

      干貨: API 網(wǎng)關(guān)架構(gòu)設(shè)計

       陳振興xy3xt22o 2019-04-25

      網(wǎng)關(guān)一詞較早出現(xiàn)在網(wǎng)絡(luò)設(shè)備里面,比如兩個相互獨(dú)立的局域網(wǎng)段之間通過路由器或者橋接設(shè)備進(jìn)行通信, 這中間的路由或者橋接設(shè)備我們稱之為網(wǎng)關(guān)。

      相應(yīng)的 API 網(wǎng)關(guān)將各系統(tǒng)對外暴露的服務(wù)聚合起來,所有要調(diào)用這些服務(wù)的系統(tǒng)都需要通過 API 網(wǎng)關(guān)進(jìn)行訪問,基于這種方式網(wǎng)關(guān)可以對 API 進(jìn)行統(tǒng)一管控,例如:認(rèn)證、鑒權(quán)、流量控制、協(xié)議轉(zhuǎn)換、監(jiān)控等等。

      API 網(wǎng)關(guān)的流行得益于近幾年微服務(wù)架構(gòu)的興起,原本一個龐大的業(yè)務(wù)系統(tǒng)被拆分成許多粒度更小的系統(tǒng)進(jìn)行獨(dú)立部署和維護(hù),這種模式勢必會帶來更多的跨系統(tǒng)交互,企業(yè) API 的規(guī)模也會成倍增加,API 網(wǎng)關(guān)(或者微服務(wù)網(wǎng)關(guān))就逐漸成為了微服務(wù)架構(gòu)的標(biāo)配組件。

      如下是我們整理的 API 網(wǎng)關(guān)的幾種典型應(yīng)用場景:

      干貨: API 網(wǎng)關(guān)架構(gòu)設(shè)計

      1、面向 Web 或者移動 App

      這類場景,在物理形態(tài)上類似前后端分離,前端應(yīng)用通過 API 調(diào)用后端服務(wù),需要網(wǎng)關(guān)具有認(rèn)證、鑒權(quán)、緩存、服務(wù)編排、監(jiān)控告警等功能。

      2、面向合作伙伴開放 API

      這類場景,主要為了滿足業(yè)務(wù)形態(tài)對外開放,與企業(yè)外部合作伙伴建立生態(tài)圈,此時的 API 網(wǎng)關(guān)注重安全認(rèn)證、權(quán)限分級、流量管控、緩存等功能的建設(shè)。

      3、企業(yè)內(nèi)部系統(tǒng)互聯(lián)互通

      對于中大型的企業(yè)內(nèi)部往往有幾十、甚至上百個系統(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)、流量管控、超時熔斷、監(jiān)控告警管理,從而提高系統(tǒng)的穩(wěn)定性、降低重復(fù)建設(shè)、運(yùn)維管理等成本。

      設(shè)計目標(biāo)

      1. 純 Java 實現(xiàn);
      2. 支持插件化,方便開發(fā)人員自定義組件;
      3. 支持橫向擴(kuò)展,高性能;
      4. 避免單點(diǎn)故障,穩(wěn)定性要高,不能因為某個 API 故障導(dǎo)致整個網(wǎng)關(guān)停止服務(wù);
      5. 管理控制臺配置更新可自動生效,不需要重啟網(wǎng)關(guān);

      應(yīng)用架構(gòu)設(shè)計

      干貨: API 網(wǎng)關(guān)架構(gòu)設(shè)計

      整個平臺拆分成 3 個子系統(tǒng),Gateway-Core(核心子系統(tǒng))、Gateway-Admin(管理中心)、Gateway-Monitor(監(jiān)控中心)。

      • Gateway-Core 負(fù)責(zé)接收客戶端請求,調(diào)度、加載和執(zhí)行組件,將請求路由到上游服務(wù)端,處理上游服務(wù)端返回的結(jié)果等;
      • Gateway-Admin 提供統(tǒng)一的管理界面,用戶可在此進(jìn)行 API、組件、系統(tǒng)基礎(chǔ)信息的設(shè)置和維護(hù);
      • Gateway-Monitor 負(fù)責(zé)收集監(jiān)控日志、生成各種運(yùn)維管理報表、自動告警等;

      系統(tǒng)架構(gòu)設(shè)計

      干貨: API 網(wǎng)關(guān)架構(gòu)設(shè)計

      說明:

      1. 網(wǎng)關(guān)核心子系統(tǒng)通過 HAProxy 或者 Nginx 進(jìn)行負(fù)載均衡,為避免正好路由的 LB 節(jié)點(diǎn)服務(wù)不可用,可以考慮在此基礎(chǔ)上增加 Keepalived 來實現(xiàn) LB 的失效備援,當(dāng) LB Node1 停止服務(wù),Keepalived 會將虛擬 IP 自動飄移到 LB Node2,從而避免因為負(fù)載均衡器導(dǎo)致單點(diǎn)故障。DNS 可以直接指向 Keepalived 的虛擬 IP。
      2. 網(wǎng)關(guān)除了對性能要求很高外,對穩(wěn)定性也有很高的要求,引入 Zookeeper 及時將 Admin 對 API 的配置更改同步刷新到各網(wǎng)關(guān)節(jié)點(diǎn)。
      3. 管理中心和監(jiān)控中心可以采用類似網(wǎng)關(guān)子系統(tǒng)的高可用策略,如果嫌麻煩管理中心可以省去 Keepalived,相對來說管理中心沒有這么高的可用性要求。
      4. 理論上監(jiān)控中心需要承載很大的數(shù)據(jù)量,比如有 1000 個 API,平均每個 API 一天調(diào)用 10 萬次,對于很多互聯(lián)網(wǎng)公司單個 API 的量遠(yuǎn)遠(yuǎn)大于 10 萬,如果將每次調(diào)用的信息都存儲起來太浪費(fèi),也沒有太大的必要??梢钥紤]將 API 每分鐘的調(diào)用情況匯總后進(jìn)行存儲,比如 1 分鐘的平均響應(yīng)時間、調(diào)用次數(shù)、流量、正確率等等。
      5. 數(shù)據(jù)庫選型可以靈活考慮,原則上網(wǎng)關(guān)在運(yùn)行時要盡可能減少對 DB 的依賴,否則 IO 延時會嚴(yán)重影響網(wǎng)關(guān)性能??梢钥紤]首次訪問后將 API 配置信息緩存,Admin 對 API 配置更改后通過 Zookeeper 通知網(wǎng)關(guān)刷新,這樣一來 DB 的訪問量可以忽略不計,團(tuán)隊可根據(jù)自身偏好靈活選型。

      非阻塞式 HTTP 服務(wù)

      管理和監(jiān)控中心可以根據(jù)團(tuán)隊的情況采用自己熟悉的 Servlet 容器部署,網(wǎng)關(guān)核心子系統(tǒng)對性能的要求非常高,考慮采用 NIO 的網(wǎng)絡(luò)模型,實現(xiàn)純 HTTP 服務(wù)即可,不需要實現(xiàn) Servlet 容器,推薦 Netty 框架(設(shè)計優(yōu)雅,大名鼎鼎的 Spring Webflux 默認(rèn)都是使用的 Netty,更多的優(yōu)勢就不在此詳述了),內(nèi)部測試在相同的機(jī)器上分別通過 Tomcat 和 Netty 生成 UUID,Netty 的性能大約有 20% 的提升,如果后端服務(wù)響應(yīng)耗時較高的話吞吐量還有更大的提升。(補(bǔ)充:Netty4.x 的版本即可,不要采用 5 以上的版本,有嚴(yán)重的缺陷沒有解決)

      采用 Netty 作為 Http 容器首先需要解決的是 Http 協(xié)議的解析和封裝,好在 Netty 本身提供了這樣的 Handler,具體參考如下代碼:

      1、構(gòu)建一個單例的 HttpServer,在 SpringBoot 啟動的時候同時加載并啟動 Netty 服務(wù)

      復(fù)制代碼

      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

      復(fù)制代碼

      @Override protected 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 分別實現(xiàn) Http 協(xié)議的解析和封裝,Http Post 內(nèi)容超過一個數(shù)據(jù)包大小會自動分組,通過 HttpObjectAggregator 可以自動將這些數(shù)據(jù)粘合在一起,對于上層收到是一個完整的 Http 請求。

      3、通過 HttpServerInboundHandler 將網(wǎng)絡(luò)請求轉(zhuǎn)發(fā)給網(wǎng)關(guān)執(zhí)行器

      復(fù)制代碼

      @Override public 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è)計上建議將 Netty 接入層代碼跟網(wǎng)關(guān)核心邏輯代碼分離,不要將 Netty 收到 HttpRequest 和 HttpContent 直接給到網(wǎng)關(guān)執(zhí)行器,可以考慮做一層轉(zhuǎn)換封裝成自己的 Request 給到執(zhí)行器,方便后續(xù)可以很容易的將 Netty 替換成其它 Http 容器。(如上代碼所示,CmptRequest 即為自定義的 Http 請求封裝類,CmptResult 為網(wǎng)關(guān)執(zhí)行結(jié)果類)

      組件化及自定義組件支持

      組件是網(wǎng)關(guān)的核心,大部分功能特性都可以基于組件的形式提供,組件化可以有效提高網(wǎng)關(guān)的擴(kuò)展性。

      先來看一個簡單的微信認(rèn)證組件的例子:

      如下實現(xiàn)的功能是對 API 請求傳入的 Token 進(jìn)行校驗,其結(jié)果分別是認(rèn)證通過、Token 過期和無效 Token,認(rèn)證通過后再將微信 OpenID 攜帶給上游服務(wù)系統(tǒng)。

      復(fù)制代碼

      /** * 微信 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(); } @Override public CmptResult execute(CmptRequest request, Map<String, FieldDTO> config) { if (logger.isDebugEnabled()) { logger.debug('WeixinTokenCmpt ......'); } CmptResult cmptResult = null; //Token 認(rèn)證超時間 (傳入單位: 分) 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 過期, 請重新獲取 Token!'); break; } case INVALID: { cmptResult = buildCmptResult(RespErrCode.AUTH\_INVALID\_TOKEN, 'Token 無效!'); break; } } return cmptResult; } ... }

      上面例子看不懂沒關(guān)系,接下來會詳細(xì)闡述組件的設(shè)計思路。

      1、組件接口定義

      復(fù)制代碼

      public interface ICmpt { /** * 組件執(zhí)行入口 * * @param request * @param config,組件實例的參數(shù)配置 * @return */ CmptResult execute(CmptRequest request, Map<String, FieldDTO> config); /** * 銷毀組件持有的特殊資源,比如線程。 */ void destroy();}

      execute 是組件執(zhí)行的入口方法,request 前面提到過是 http 請求的封裝,config 是組件的特殊配置,比如上面例子提到的微信認(rèn)證組件就有一個自定義配置 -Token 的有效期,不同的 API 使用該組件可以設(shè)置不同的有效期。

      FieldDTO 定義如下:

      復(fù)制代碼

      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é)果,其定義如下:

      復(fù)制代碼

      public class CmptResult { RespErrMsg respErrMsg;// 組件返回錯誤信息 private boolean passed;// 組件過濾是否通過 private byte\[\] data;// 組件返回數(shù)據(jù) private Map<String, String> header = new HashMap<String, String>();// 透傳后端服務(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í)行的,但緩存類型的組件沒有命中才繼續(xù)往下執(zhí)行。當(dāng)然這樣設(shè)計存在一些缺陷,比如新增組件類型需要執(zhí)行器配合調(diào)整處理邏輯。(Kong 也提供了大量的功能組件,沒有研究過其網(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)先級設(shè)定

      執(zhí)行位置:Pre、Routing、After,分別代表后端服務(wù)調(diào)用前、后端服務(wù)調(diào)用中和后端服務(wù)調(diào)用完成后,相同位置的組件根據(jù)優(yōu)先級決定執(zhí)行的先后順序。

      4、組件發(fā)布形式

      組件打包成標(biāo)準(zhǔn)的 Jar 包,通過 Admin 管理界面上傳發(fā)布。

      附 - 組件可視化選擇 UI 設(shè)計

      干貨: API 網(wǎng)關(guān)架構(gòu)設(shè)計

      組件熱插拔設(shè)計和實現(xiàn)

      JVM 中 Class 是通過類加載器 + 全限定名來唯一標(biāo)識的,上面章節(jié)談到組件是以 Jar 包的形式發(fā)布的,但相同組件的多個版本的入口類名需要保持不變,因此要實現(xiàn)組件的熱插拔和多版本并存就需要自定義類加載器來實現(xiàn)。

      大致思路如下:

      網(wǎng)關(guān)接收到 API 調(diào)用請求后根據(jù)請求參數(shù)從緩存里拿到 API 配置的組件列表,然后再逐一參數(shù)從緩存里獲取組件對應(yīng)的類實例,如果找不到則嘗試通過自定義類加載器載入 Jar 包,并初始化組件實例及緩存。

      附 - 參考示例

      復(fù)制代碼

      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;}

      補(bǔ)充說明:

      自定義類加載器可直接需要繼承至 URLClassLoader,另外必須指定其父類加載器為執(zhí)行器的加載器,否則組件沒法引用網(wǎng)關(guān)的其它類。

      API 故障隔離及超時、熔斷處理

      在詳細(xì)闡述設(shè)計前先講個實際的案例,大概 12 年的時候某公司自研了一款 ESB 的中間件(企業(yè)服務(wù)總線跟 API 網(wǎng)關(guān)很類似,當(dāng)年 SOA 理念大行其道的時候都推崇的是 ESB,側(cè)重服務(wù)的編排和異構(gòu)系統(tǒng)的整合。),剛開始用的還行,但隨著接入系統(tǒng)的增多,突然某天運(yùn)維發(fā)現(xiàn)大量 API 出現(xiàn)緩慢甚至超時,初步檢查發(fā)現(xiàn) ESB 每個節(jié)點(diǎn)的線程幾乎消耗殆盡,起初判斷是資源不夠,緊急擴(kuò)容后還是很快線程占滿,最終導(dǎo)致上百個系統(tǒng)癱瘓。

      最終找到問題的癥結(jié)是某個業(yè)務(wù)系統(tǒng)自身的原因?qū)е路?wù)不可用,下游業(yè)務(wù)系統(tǒng)請求大量堆積到 ESB 中,從而導(dǎo)致大量線程堵塞。

      以上案例說明了一個在企業(yè)應(yīng)用架構(gòu)設(shè)計里面的經(jīng)典原則 - 故障隔離,由于所有的 API 請求都要經(jīng)過網(wǎng)關(guān),必須隔離 API 之間的相互影響,尤其是個別 API 故障導(dǎo)致整個網(wǎng)關(guān)集群服務(wù)中斷。

      接下來分別介紹故障隔離、超時管控、熔斷的實現(xiàn)思路。

      1、故障隔離

      有兩種方式可以實現(xiàn),一是為每個 API 創(chuàng)建一個線程池,每個線程分配 10~20 個線程,這也是常用的隔離策略,但這種方式有幾個明顯的缺點(diǎn):

      • 線程數(shù)會隨著 API 接入數(shù)量遞增,1000 個 API 就需要 2 萬個線程,光線程切換對 CPU 就是不小的開銷,而其線程還需要占用一定的內(nèi)存資源;
      • 平均分配線程池大小導(dǎo)致個別訪問量較大且響應(yīng)時間相對較長的 API 吞吐量上不去;
      • Netty 本身就有工作線程池了,再增加 API 的線程池,導(dǎo)致某些需要 ThreadLocal 特性的編程變得困難。

      二是用信號量隔離,直接復(fù)用 Netty 的工作線程,上面線程池隔離提到的 3 個缺點(diǎn)都可以基本避免, 建議設(shè)置單個 API 的信號量個數(shù)小于等于 Netty 工作線程池數(shù)量的 1/3,這樣既兼顧了單個 API 的性能又不至于單個 API 的問題導(dǎo)致整個網(wǎng)關(guān)堵塞。

      具體實現(xiàn)可以考慮直接引用成熟的開源框架,推薦 Hystrix,可以同時解決超時控制和熔斷。

      參考配置如下:

      復(fù)制代碼

      Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey(groupKey)) .andCommandKey(HystrixCommandKey.Factory.asKey(commandKey )) .andCommandPropertiesDefaults(HystrixCommandProperties.Setter() // 艙壁隔離策略 - 信號量 .withExecutionIsolationStrategy(HystrixCommandProperties.ExecutionIsolationStrategy.SEMAPHORE) // 設(shè)置每組 command 可以申請的信號量最大數(shù) .withExecutionIsolationSemaphoreMaxConcurrentRequests(CmptInvoker.maxSemaphore) /* 開啟超時設(shè)置 */ .withExecutionIsolationThreadInterruptOnTimeout(true) /* 超時時間設(shè)置 */ .withExecutionIsolationThreadTimeoutInMilliseconds(timeout) .withCircuitBreakerEnabled(true)// 開啟熔斷 .withCircuitBreakerSleepWindowInMilliseconds(Constants.DEFAULT_CIRCUIT_BREAKER_SLEEP_WINDOW_IN_MILLISECONDS)// 5 秒后會嘗試閉合回路

      2、超時管控

      API 的超時控制是必須要做的,否則上游服務(wù)即便是間歇性響應(yīng)緩慢也會堵塞大量線程(雖然通過信號量隔離后不會導(dǎo)致整個網(wǎng)關(guān)線程堵塞)。

      其次,每個 API 最好可以單獨(dú)配置超時時間,但不建議可以讓用戶隨意設(shè)置,還是要有個最大閾值。(API 網(wǎng)關(guān)不適合需要長時間傳輸數(shù)據(jù)的場景,比如大文件上傳或者下載、DB 數(shù)據(jù)同步等)

      實現(xiàn)上可以直接復(fù)用開源組件的功能,比如:HttpClient 可以直接設(shè)置獲取連接和 Socket 響應(yīng)的超時時間,Hystrix 可以對整個調(diào)用進(jìn)行超時控制等。

      3、熔斷

      熔斷類似電路中的保險絲,當(dāng)超過負(fù)荷或者電阻被擊穿的時候自動斷開對設(shè)備起到保護(hù)作用。在 API 網(wǎng)關(guān)中設(shè)置熔斷的目的是快速響應(yīng)請求,避免不必要的等待,比如某個 API 后端服務(wù)正常情況下 1s 以內(nèi)響應(yīng),但現(xiàn)在因為各種原因出現(xiàn)堵塞大部分請求 20s 才能響應(yīng),雖然設(shè)置了 10s 的超時控制,但讓請求線程等待 10s 超時不僅沒有意義,反而會增加服務(wù)提供方的負(fù)擔(dān)。

      為此我們可以設(shè)置單位時間內(nèi)超過多少比例的請求超時或者異常,則直接熔斷鏈路,等待一段時間后再次嘗試恢復(fù)鏈路。

      實現(xiàn)層面可以直接復(fù)用 Hystrix。

      運(yùn)行時配置更新機(jī)制

      前面章節(jié)提到過出于性能考慮網(wǎng)關(guān)在運(yùn)行時要盡可能減小對 DB 的訪問,設(shè)計上可以將 API、組件等關(guān)鍵內(nèi)容進(jìn)行緩存,這樣一來性能是提升了,但也帶來了新的問題,比如 Admin 對 API 或者組件進(jìn)行配置調(diào)整后如何及時更新到集群的各個網(wǎng)關(guān)節(jié)點(diǎn)。

      解決方案很多,比如引入消息中間件,當(dāng) Admin 調(diào)整配置后就往消息中心發(fā)布一條消息,各網(wǎng)關(guān)節(jié)點(diǎn)訂閱消息,收到消息后刷新緩存數(shù)據(jù)。

      我們在具體實現(xiàn)過程中采用的是 Zookeeper 集群數(shù)據(jù)同步機(jī)制,其實現(xiàn)原理跟消息中間件很類似,只不過網(wǎng)關(guān)在啟動的時候就會向 ZK 節(jié)點(diǎn)進(jìn)行注冊,也是被動更新機(jī)制。

      性能考慮

      性能是網(wǎng)關(guān)一項非常重要的衡量指標(biāo),尤其是響應(yīng)時間,客戶端本來可以直連服務(wù)端的,現(xiàn)在增加了一個網(wǎng)關(guān)層,對于一個本身耗時幾百毫秒的服務(wù)接入網(wǎng)關(guān)后增加幾毫秒,影響倒是可以忽略不計;但如果服務(wù)本身只需要幾毫秒,因為接入網(wǎng)關(guān)再增加一倍的延時,用戶感受就會比較明顯。

      建議在設(shè)計上需要遵循如下原則:

      1. 核心網(wǎng)關(guān)子系統(tǒng)必須是無狀態(tài)的,便于橫向擴(kuò)展。
      2. 運(yùn)行時不依賴本地存儲,盡量在內(nèi)存里面完成服務(wù)的處理和中轉(zhuǎn)。
      3. 減小對線程的依賴,采用非阻塞式 IO 和異步事件響應(yīng)機(jī)制。
      4. 后端服務(wù)如果是 HTTP 協(xié)議,盡量采用連接池或者 Http2,測試連接復(fù)用和不復(fù)用性能有幾倍的差距。(TCP 建立連接成本很高)

      附 -HttpClient 連接池設(shè)置:

      復(fù)制代碼

      PoolingHttpClientConnectionManager cmOfHttp = new PoolingHttpClientConnectionManager();cmOfHttp.setMaxTotal(maxConn);cmOfHttp.setDefaultMaxPerRoute(maxPerRoute);httpClient = HttpClients.custom() .setConnectionManager(cmOfHttp) .setConnectionManagerShared(true) .build();

      說明:

      httpClient 對象可以作為類的成員變量長期駐留內(nèi)存,這個是連接池復(fù)用的前提。

      結(jié)語

      API 網(wǎng)關(guān)作為企業(yè) API 服務(wù)的匯聚中心,其良好的性能、穩(wěn)定性和可擴(kuò)展性是基礎(chǔ),只有這個基礎(chǔ)打扎實了,我們才能在上面擴(kuò)展更多的特性。

        本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點(diǎn)擊一鍵舉報。
        轉(zhuǎn)藏 分享 獻(xiàn)花(0

        0條評論

        發(fā)表

        請遵守用戶 評論公約

        類似文章 更多