微服務網關解決方案調研和使用總結 專題

一.什麼是網關

1.1 什麼是網關

API Gateway(APIGW / API 網關),顧名思義,是出如今系統邊界上的一個面向API的、串行集中式的強管控服務,這裏的邊界是企業IT系統的邊界,能夠理解爲企業級應用防火牆,主要起到隔離外部訪問與內部系統的做用。在微服務概念的流行以前,API網關就已經誕生了,例如銀行、證券等領域常見的前置機系統,它也是解決訪問認證、報文轉換、訪問統計等問題的。前端

API網關的流行,源於近幾年來,移動應用與企業間互聯需求的興起。移動應用、企業互聯,使得後臺服務支持的對象,從之前單一的Web應用,擴展到多種使用場景,且每種使用場景對後臺服務的要求都不盡相同。這不只增長了後臺服務的響應量,還增長了後臺服務的複雜性。隨着微服務架構概念的提出,API網關成爲了微服務架構的一個標配組件java

1.2 網關應該具備的功能

如上圖所示:網關該具有的最基本的四大功能:統一接入,流量管控,協議適配轉發,安全防禦。mysql

 

網關的技術選型nginx

SpringCloud-Zuul :git

社區活躍,基於 SrpingCloud 完整生態, 是構建微服務體系前置網關服務的最佳選型.github

Kong : 基於OpenResty的 API 網關服務和網關服務管理層.web

自建網關服務: 如 談談基於 OpenResty 的接口網關設計[https://www.zybuluo.com/yishuailuo/note/844059?utm_source=tool.lu]redis

網關的設計要素算法

系統級別spring

高可用性
均衡負載: 容錯,防止雪崩.
併發控制 : 錯峯流控
動態路由制定和修改
應用級別

監控統計
版本控制
認證 鑑權
數據安全: 防篡改,參數脫敏…
協議轉換: 如 HTTP => RPC協議.
其餘(我的 YY)

基於機器學習, 預測流量高峯.

 

網關(API Gateway)技術選型

zuul
kong
nginx+lua
網關(API Gateway)的設計要素

限流:實現微服務訪問流量計算,基於流量計算分析進行限流,能夠定義多種限流規則。
緩存:數據緩存。
日誌:日誌記錄。
監控:記錄請求響應數據,api耗時分析,性能監控。
鑑權:權限身份認證。
灰度:線上灰度部署,能夠減少風險。
路由:路由是API網關很核心的模塊功能,此模塊實現根據請求,鎖定目標微服務並將請求進行轉發。
簡單介紹下你的網關實施方案

開發語言:java + groovy,groovy的好處是網關服務不須要重啓就能夠動態的添加filter來實現一些功能;
微服務基礎框架:springboot;
網關基礎組件:netflix zuul;
服務註冊中心:consul;
權限校驗:jwt;
API監控:prometheus + grafana;
API統一日誌收集:logback + ELK;
壓力測試:Jmeter;


你好,請教一下: 1.爲何網關須要數據緩存,是由於須要鑑權,因此須要緩存用戶數據?還有其餘數據須要緩存麼? 2.網關路由方面,只須要作路由的轉發,仍是須要作服務的聚合? 謝謝
好比限流 你須要緩存一些限流的策略,主要是緩存網關功能用到的一些數據,不涉及業務數據。 路由主要是作轉發

 

目前,咱們業務代碼是多語言的環境,網關則是用go寫的,目前主要是作到了對於HTTP和Thrift的業務服務的轉發(HTTP利用了fasthttp,Thrift用的網關啓動客戶端調用業務服務端的形式)過濾器是環繞的,系通通一的過濾和針對API級別的過濾。雖然用了go比較輕巧,可是目前功能還很值得完善

go語言體積小,性能高。大家這個設計方案不錯,又支持多語言,對技術棧沒有限制,很值得借鑑。

 

設計要素:
#1,高可用很是重要;
#2,網關須要支持動態修改路由規則;
#3,與服務註冊中心整合,經過註冊中心實現路由轉發;
#4,過濾器鏈適配不一樣的路由。
以上是我的愚見

 

選型

所使用的網關架構必須靈活,由於咱們可能須要不少與咱們業務相關的定製話的東西
有平臺背書,獲取有足夠的證據證實他是一個能抗的住咱們需求的併發的性能
根據需求選擇最好的方案
設計要素

結構必須靈活,方便擴展
基礎的功能應該由框架提供或者抽象,好比動態路由,權限校驗,限流
個人

咱們使用zuul做爲網關並對他進行了必定定製化的開發,由於咱們使用springcloud技術棧,同時zuul基於filter來處理一切的結構也是很是靈活的,而且由netflix背書。咱們在網關利用filter加入權限校驗,統一訪問日誌記錄,訪問異常請求記錄,聚合請求處理器等相關功能

負載均衡能夠經過在以前加入一個nginx或者dns解析來作,高可用能夠經過keepalived加虛擬ip與nginx結合或者直接與zuul結合來作


1.能處理一些公共的邏輯,好比獲取token
2.能支持動態的修改路由規則
3.對各服務結果和異常進行統一處理後返給調用方
目前實施了幾套方案,本身封裝的gateway層,準備用zuul進行替代


首先是穩定且性能好,能支持海量請求,這次是安全,最後是功能完善且易於擴展。
網關智能路由,請求過濾,日誌記錄,流量控制,權限審查,負載均衡,實時監控,多協議支持等是一個好的網關方案應具有的。
目前公司採用zuul做爲網關的,作了路由轉發,權限控制,再加上自定義日誌記錄功能跟蹤後端服務執行狀況。
zuul多開,前端再加上nginx作端口轉發和靜態緩存,待解決高可用和偶爾超時問題。

在跟蹤後端執行狀況上增長了請求執行時間,後端服務地址,當前用戶,請求參數,錯誤截取等內容,以便後續跟蹤問題。
而後經過logback發給elk進行記錄分析監控


網關的技術選型

1. SpringCloud-Zuul :社區活躍,基於 SrpingCloud 完整生態, 是構建微服務體系前置網關服務的最佳選型.
2. Kong : 基於OpenResty的 API 網關服務和網關服務管理層.
3. Nginx+Lua:成熟度也算能夠
4. 自建網關:成本較高
網關(API Gateway)的設計要素(高可用,安全)

* 性能:API高可用,負載均衡,容錯機制。
* 安全:權限身份認證、脫敏,流量清洗,後端簽名(保證全鏈路可信調用),黑名單(非法調用的限制)。
* 日誌:日誌記錄(spainid,traceid)一旦涉及分佈式,全鏈路跟蹤必不可少。
* 緩存:數據緩存。
* 監控:記錄請求響應數據,api耗時分析,性能監控。
* 限流:流量控制,錯峯流控,目前有漏桶算法、令牌桶算法也能夠定製限流規則。
* 灰度:線上灰度部署,能夠減少風險。
* 路由:動態路由規則。
* 靜態:代理
簡單介紹下你的網關實施方案

* 微服務基礎框架:springboot;
* 網關基礎組件:zuul;
* 服務註冊中心:consul;
* API監控:prometheus + grafana or 自建;
* API統一日誌收集:時序db + ELK;
* 壓力測試:Jmeter,AB,阿里壓測;


網關(API Gateway)技術選型

zuul
nginx
網關(API Gateway)的設計要素

高可用
灰度
負載
限流
反向代理
簡單介紹下你的網關實施方案

Spring Cloud Zuul
開發環境使用Spring cloud 技術棧 使用zuul方便
在網關上作限流 負載 限流。網關做爲整個系統的入口不該該作不少事 身份認證 權限認證等都放在下面的聚合層
(Low逼的見解 大佬別噴)


首先,網關做爲微服務的入口,其併發壓力都在網關,當網關的併發能力沒法支撐用戶量的時候咱們就須要部署多個網關,而後在網關的前面加一個負載均衡服務器,可是在負載均衡服務器的選擇上不能簡單的選擇nginx,由於若是選擇nginx的話,客戶端鏈接nginx,nginx再鏈接網關,網關再鏈接後端的微服務,這樣下來光是TCP鏈接的三次握手就比較耗時,因此咱們應該在網關的前面選用支持四層負載均衡的服務器,譬如Haproxy或者lvs,使用他們的在tcp第一次握手的時候修改源IP地址和目的地址的模式,這樣客戶端至關於直連網關,能夠作到性能最大化。


網關(API Gateway)技術選型
咱們本身寫了一個,利用反射技術,鏈接同一個zk
網關(API Gateway)的設計要素
在庫裏邊配置相應的信息便可
group varchar(255) NOT NULL,
service varchar(255) NOT NULL,
method varchar(255) DEFAULT NULL,
parameter varchar(255) NOT NULL,
value varchar(255) NOT NULL,
is_deleted tinyint(4) NOT NULL DEFAULT ‘0’,
簡單介紹下你的網關實施方案
自有的devops 部署

1.首先部署多臺gateway的網關 固然都是zuul的,好比部署3臺,端口分別爲 90,91,92.
2.使用ngnix代理80端口,而後配置權重把請求分發到90,91,92端口的網關,api網關本身再路由

首先服務網關 = 路由轉發 + 過濾器
一、路由轉發:接收一切外界請求,轉發到後端的微服務上去;
二、過濾器:在服務網關中能夠完成一系列的橫切功能,例如權限校驗、限流以及監控等,這些均可以經過過濾器完成(其實路由轉發也是經過過濾器實現的)。
三、增長了網關,多了一層轉發(本來用戶請求直接訪問open-service便可),性能會降低一些(可是降低不大,一般,網關機器性能會很好,並且網關與open-service的訪問一般是內網訪問,速度很快);
四、 網關的單點問題:在整個網絡調用過程當中,必定會有一個單點,多是網關、nginx、dns服務器等。防止網關單點,能夠在網關層前邊再掛一臺nginx,nginx的性能極高,基本不會掛,這樣以後,網關服務就能夠不斷的添加機器。可是這樣一個請求就轉發了兩次,因此最好的方式是網關單點服務部署在一臺牛逼的機器上),並且nginx與zuul的性能比較其實相差不大,zuul是netflix開源的一個用來作網關的開源框架;
五、網關要儘可能輕。

http://www.spring4all.com/question/62

 

二.目前網關解決方案

2.1 Nginx+ Lua

Nginx是由IgorSysoev爲俄羅斯訪問量第二的Rambler.ru站點開發的,一個高性能的HTTP和反向代理服務器。Ngnix一方面能夠作反向代理,另一方面作能夠作靜態資源服務器。

可是準確的來講,在我看來,這種方案不是真正意義上的網關,並且即便自研網關的目標也是幹掉Ngnix。

2.2 Kong

Kong是Mashape提供的一款API管理軟件,它自己是基於Ngnix+lua的,但比nginx提供了更簡單的配置方式,數據採用了 ApacheCassandra/PostgreSQL存儲,而且提供了一些優秀的插件,好比驗證,日誌,調用頻次限制等。
Kong的一個很是誘人的地方就是提供了大量的插件來擴展應用,經過設置不一樣的插件能夠爲服務提供各類加強的功能。Kong默認插件插件包括:

  • 身份認證:Kong提供了Basic Authentication、Key authentication、OAuth2.0authentication、HMAC authentication、JWT、LDAP authentication認證明現。
  • 安全:ACL(訪問控制)、CORS(跨域資源共享)、動態SSL、IP限制、爬蟲檢測實現。
  • 流量控制:請求限流(基於請求計數限流)、上游響應限流(根據upstream響應計數限流)、請求大小限制。限流支持本地、Redis和集羣限流模式。
  • 分析監控:Galileo(記錄請求和響應數據,實現API分析)、Datadog(記錄API Metric如請求次數、請求大小、響應狀態和延遲,可視化API Metric)、Runscope(記錄請求和響應數據,實現API性能測試和監控)。
  • 轉換:請求轉換、響應轉換

優勢:Kong自己也是基於Nginx的,因此在性能和穩定性上都沒有問題。Kong做爲一款商業軟件,在Nginx上作了很擴展工做,並且還有不少付費的商業插件。Kong自己也有付費的企業版,其中包括技術支持、使用培訓服務以及API 分析插件。


缺點:Kong的缺點就是,若是你使用Spring Cloud,Kong如何結合目前已有的服務治理體系?

2.3 Spring Cloud Zuul

Zuul 是Netflix公司開源的一個API網關組件,Spring Cloud對其進行二次基於Spring Boot的註解式封裝作到開箱即用。目前來講,結合Sring Cloud提供的服務治理體系,能夠作到請求轉發,根據配置的或者默認的路由規則進行路由和Load Balance,集成Hystrix。詳細能夠參考Spring Cloud Zuul的URL轉發和路由規則

Spring Cloud Zuul處理每一個請求的方式是針對每一個請求是用一個線程來處理。PS,根據統計數據目前Zuul最多能達到(1000-2000)QPS。使用過Netty的都知道,通常都會使用Boos組和work組,一般狀況下,爲了提升性能,全部請求會被放處處理隊列中,從線程池中選取空閒線程來處理該請求。

Spring Cloud Zuul須要作一些灰度,降級,標籤路由,限流,WAF封禁,須要自定義Filter去或者作一些定製化實現。詳細文章能夠參考在Spring Cloud中實現降級之權重路由和標籤路由

雖然能夠經過自定義Filter實現,咱們想要的功能,可是因爲Zuul自己的設計和基於單線程的接收請求和轉發處理,在我看來目前來看Zuul 就顯得很雞肋,隨着Zuul2一直跳票,Spring Cloud推出本身的Spring Cloud Gateway.

The API Gateway is Dead! Long Live the API Gateway!

大意:Zuul已死,Spring Cloud Gateway永生。

2.4 Spring Cloud Gateway

A Gateway built on Spring Framework 5.0 and Spring Boot 2.0 providing routing and more。

Spring Cloud Gateway是基於Spring 框架5.0版本和Spring Boot 2.0的版本構建,提供路由等功能。

Spring Cloud GateWay具備如下特徵

  • Java 8/Spring 5/Boot 2
  • WebFlux/Reactor
  • HTTP/2 and Websockets
  • Finchley Release Train (Q4 2017)

因爲Spring 5.0支持Netty,Http2,而Spring Boot 2.0支持Spring 5.0,所以Spring Cloud Gateway支持Netty和Http2瓜熟蒂落。至於2017年Q4季度是否發佈完整的Spring Cloud Gateway咱們拭目以待,可是至於最終落地看最終使用狀況

詳細信息能夠參考:Spring Cloud Gateway離開孵化器的變化

2.5 Kong+Zuul的網關方案

 

如上圖所示:Kong+Zuul實現的網關方案,在加上阿里雲的SLB,整個調用鏈路多了好幾層,爲何要這麼作呢?發揮Kong+Spring Cloud Zuul各自的優勢造成「聚合網關」。我的不建議這樣使用網關,所以自研網關中間件,顯得尤爲重要。

三.基於Spring Cloud Zuul構建網關

用Spring Cloud Zuul構建網關其實至關雞肋,好比動態Filter,好比標籤路由,降級,好比動態Filter,好比帶管控審計流程,易操做的UI界面等。

zuul是netfix的api 網關,主要特點有:filter的PRPE(pre,route,post,error)模型、groovy的fitler機制,其中spring cloud對其有比較好的擴展,可是spring cloud對其的擴展感受不是很完美,存在路由規則沒法只能是經過配置文件來存儲,而沒法動態配置的目的,其中有一我的寫了一個starter插件來解決路由規則配置到Cassandra的問題,詳細請看:將路由規則配置到KV分佈式存儲系統Cassandra

3.1 定義本身的Filter機制

這裏主要是作了流控及協議轉化的工做,這裏主要是http->grpc的轉換;
LimitAccessFilter:利用redis令牌桶算法進行流控
GrpcRemoteRouteFilter:http轉化爲grpc的協議轉換

3.2 路由數據變動基於事件通知路由規則刷新

實現動態路由有兩種實現方式:
1.第一是DiscoveryClientRouteLocator的從新覆蓋,推薦是,Spring Cloud整合GRPC,REST協議適配轉發爲內部GRPC服務時採用此種方法擴展修改。

2.第二是實現了RefreshableRouteLocator接口,可以實現動態刷新,能夠參考 spring cloud Zuul動態路由

3.2.1 基於事件更新源碼分析

爲何要基於事件更新,原理以下所示:
在org.springframework.cloud.netflix.zuul.ZuulConfiguration.java中228-250行

@Configuration
@EnableConfigurationProperties({ ZuulProperties.class })
@ConditionalOnClass(ZuulServlet.class)
// Make sure to get the ServerProperties from the same place as a normal web app would
@Import(ServerPropertiesAutoConfiguration.class)
public class ZuulConfiguration {
   //zuul的配置信息,對應了application.properties或yml中的配置信息
    @Autowired
    protected ZuulProperties zuulProperties;
    @Autowired
    protected ServerProperties server;
    @Autowired(required = false)
    private ErrorController errorController;
    @Bean
    public HasFeatures zuulFeature() {
        return HasFeatures.namedFeature("Zuul (Simple)", ZuulConfiguration.class);
    }
    @Bean
    @ConditionalOnMissingBean(RouteLocator.class)
    public RouteLocator routeLocator() {
       //默認配置的實現是SimpleRouteLocator.class
        return new SimpleRouteLocator(this.server.getServletPrefix(),
                this.zuulProperties);
    }
    @Bean
    public ZuulController zuulController() {
        return new ZuulController();
    }
    @Bean
    public ZuulHandlerMapping zuulHandlerMapping(RouteLocator routes) {
        ZuulHandlerMapping mapping = new ZuulHandlerMapping(routes, zuulController());
        mapping.setErrorController(this.errorController);
        return mapping;
    }
  //註冊了一個路由刷新監聽器,默認實現是ZuulRefreshListener.class
    @Bean
    public ApplicationListener<ApplicationEvent> zuulRefreshRoutesListener() {
        return new ZuulRefreshListener();
    }
    @Bean
    @ConditionalOnMissingBean(name = "zuulServlet")
    public ServletRegistrationBean zuulServlet() {
        ServletRegistrationBean servlet = new ServletRegistrationBean(new ZuulServlet(),
                this.zuulProperties.getServletPattern());
        // The whole point of exposing this servlet is to provide a route that doesn't
        // buffer requests.
        servlet.addInitParameter("buffer-requests", "false");
        return servlet;
    }
    // pre filters
    @Bean
    public ServletDetectionFilter servletDetectionFilter() {
        return new ServletDetectionFilter();
    }
    @Bean
    public FormBodyWrapperFilter formBodyWrapperFilter() {
        return new FormBodyWrapperFilter();
    }
    @Bean
    public DebugFilter debugFilter() {
        return new DebugFilter();
    }
    @Bean
    public Servlet30WrapperFilter servlet30WrapperFilter() {
        return new Servlet30WrapperFilter();
    }
    // post filters
    @Bean
    public SendResponseFilter sendResponseFilter() {
        return new SendResponseFilter();
    }
    @Bean
    public SendErrorFilter sendErrorFilter() {
        return new SendErrorFilter();
    }
    @Bean
    public SendForwardFilter sendForwardFilter() {
        return new SendForwardFilter();
    }
    @Configuration
    protected static class ZuulFilterConfiguration {
        @Autowired
        private Map<String, ZuulFilter> filters;
        @Bean
        public ZuulFilterInitializer zuulFilterInitializer() {
            return new ZuulFilterInitializer(this.filters);
        }
    }
    private static class ZuulRefreshListener
            implements ApplicationListener<ApplicationEvent> {
        @Autowired
        private ZuulHandlerMapping zuulHandlerMapping;
        private HeartbeatMonitor heartbeatMonitor = new HeartbeatMonitor();
        @Override
        public void onApplicationEvent(ApplicationEvent event) {
            if (event instanceof ContextRefreshedEvent
                    || event instanceof RefreshScopeRefreshedEvent
                    || event instanceof RoutesRefreshedEvent) {
                this.zuulHandlerMapping.setDirty(true);
            }
            else if (event instanceof HeartbeatEvent) {
                if (this.heartbeatMonitor.update(((HeartbeatEvent) event).getValue())) {
                    this.zuulHandlerMapping.setDirty(true);
                }
            }
        }
    }
}

如上所示,當使用ApplicationEventPublisher發送的Event爲ContextRefreshedEvent,RefreshScopeRefreshedEvent,RoutesRefreshedEvent纔會通知Zuul去刷新路由。

3.3 基於事件更新實現方式處理方式-DiscoveryClientRouteLocator

3.3.1 處理思路

此插件針對的spring cloud zuul版本比較老,所以須要對其進行改進,將路由配置能夠配置到mysql這樣的關係型數據庫中,詳細請看Zuul的改動點

3.3.2 對DiscoveryClientRouteLocator的從新覆蓋

對DiscoveryClientRouteLocator的從新覆蓋,該類的做用就是從yml或屬性文件中讀取路由規則;
具體參看源碼org.springframework.cloud.netflix.zuul.filters.discovery.DiscoveryClientRouteLocator,主要方法以下,淺顯易懂,就不作多餘解釋。

@Override
    protected LinkedHashMap<String, ZuulRoute> locateRoutes() {
        LinkedHashMap<String, ZuulRoute> routesMap = new LinkedHashMap<String, ZuulRoute>();
        routesMap.putAll(super.locateRoutes());
        if (this.discovery != null) {
            Map<String, ZuulRoute> staticServices = new LinkedHashMap<String, ZuulRoute>();
            for (ZuulRoute route : routesMap.values()) {
                String serviceId = route.getServiceId();
                if (serviceId == null) {
                    serviceId = route.getId();
                }
                if (serviceId != null) {
                    staticServices.put(serviceId, route);
                }
            }
            // Add routes for discovery services by default
            List<String> services = this.discovery.getServices();
            String[] ignored = this.properties.getIgnoredServices()
                    .toArray(new String[0]);
            for (String serviceId : services) {
                // Ignore specifically ignored services and those that were manually
                // configured
                String key = "/" + mapRouteToService(serviceId) + "/**";
                if (staticServices.containsKey(serviceId)
                        && staticServices.get(serviceId).getUrl() == null) {
                    // Explicitly configured with no URL, cannot be ignored
                    // all static routes are already in routesMap
                    // Update location using serviceId if location is null
                    ZuulRoute staticRoute = staticServices.get(serviceId);
                    if (!StringUtils.hasText(staticRoute.getLocation())) {
                        staticRoute.setLocation(serviceId);
                    }
                }
                if (!PatternMatchUtils.simpleMatch(ignored, serviceId)
                        && !routesMap.containsKey(key)) {
                    // Not ignored
                    routesMap.put(key, new ZuulRoute(key, serviceId));
                }
            }
        }
        if (routesMap.get(DEFAULT_ROUTE) != null) {
            ZuulRoute defaultRoute = routesMap.get(DEFAULT_ROUTE);
            // Move the defaultServiceId to the end
            routesMap.remove(DEFAULT_ROUTE);
            routesMap.put(DEFAULT_ROUTE, defaultRoute);
        }
        LinkedHashMap<String, ZuulRoute> values = new LinkedHashMap<>();
        for (Entry<String, ZuulRoute> entry : routesMap.entrySet()) {
            String path = entry.getKey();
            // Prepend with slash if not already present.
            if (!path.startsWith("/")) {
                path = "/" + path;
            }
            if (StringUtils.hasText(this.properties.getPrefix())) {
                path = this.properties.getPrefix() + path;
                if (!path.startsWith("/")) {
                    path = "/" + path;
                }
            }
            values.put(path, entry.getValue());
        }
        return values;
    }

3.3.3 生產者產生事件通知

數據變動對網關的穩定性來講,也是一個很大的挑戰。當對路由信息進行CRUD操做以後,須要Spring Cloud Zuul從新刷新路由規則,實現方式經過spring的event來實現。

1.實現基於ApplicationEventPublisherAware的事件生產者的代碼片斷

private ApplicationEventPublisher publisher;
publisher.publishEvent(new InstanceRegisteredEvent<>(this, this.environment));

2.Spring Cloud netflix內部的事件消費者

org.springframework.cloud.netflix.zuul.RoutesRefreshedEvent

@SuppressWarnings("serial")
public class RoutesRefreshedEvent extends ApplicationEvent {
    private RouteLocator locator;
    public RoutesRefreshedEvent(RouteLocator locator) {
        super(locator);
        this.locator = locator;
    }
    public RouteLocator getLocator() {
        return this.locator;
    }
}

四.基於Spring Cloud Gateway構建網關

因爲Spring Cloud Gateway未徹底成熟,並且性能,穩定性等,如今無從考證,沒有使用案例,基於Spring Cloud Gateway方案構建本身的網關風險比較大,並且PS不知道到年末是否成熟可用。故在這裏不作過多說明。

五.基於Netty自研網關中間件

5.1 架構圖

能夠參考架構圖以下:

5.2 設計原則

  • 1.每一個Filter基於責任鏈,只作專注的一件事
  • 2.每一個Filter有各自獨立的數據
  • 3.損耗性能的Filter順序日後放
  • 4.啓動讀取配置順序,先遠端,若遠端失敗,則讀取本地。
  • 5.集羣網關,要注意數據的diff和灰度
  • 6.儘可能作到和服務治理框架解耦,易於接入,易於升級

六.參考文章

企業級API網關的設計
微服務與API 網關(上): 爲何須要API網關?
http://blog.csdn.net/u013815546/article/details/68944039
http://www.javashuo.com/article/p-ntexbnsy-gg.html

 

http://xujin.org/janus/gw-solution/?from=timeline

相關文章
相關標籤/搜索