1. 程式人生 > WINDOWS開發 >API閘道器

API閘道器

API閘道器一般伴隨著微服務架構出現,不同的微服務一般會有不同的網路地址,特別是每個微服務還會部署多份服務例項,外部客戶端如果呼叫時一是如果需要三個微服務組合才能完成一次呼叫,客戶端需要知道這三個微服務的地址,埠號等.有可能會出現如下問題:

  • 客戶端多次請求不同的微服務,增加了客戶端的複雜性
  • 存在跨域請求,在某些場景下處理相對複雜
  • 認證複雜及重複,每個微服務都需要獨立認證
  • 重構困難,隨著專案的迭代和產品的發展,可能需要重新劃分微服務,如將多個微服務合併為一個或將一個微服務分拆為多個,此時客戶端呼叫微服務時成本又會增加
  • 有一些微服務可能放置於防火牆後或其他訪問限制,直接訪問會不可達

    所以出現了API閘道器,它是介於客戶端和服務端之間的橋樑,所有外部請求都會先給過閘道器層再路由到相應的微服務進行處理,根據這一特性我們一般會在閘道器層用來處理安全,效能,監控等通用的功能等,而不會用來處理業務邏輯.

    技術分享圖片

閘道器的優勢及選型

  • 易於監控,認證
  • 減少客戶端與各個微服務之間的互動,直接和閘道器互動

閘道器技術選型

  • 自研
  • Nginx衍生的Kong
  • Netflex Zuul:zuul1,zuul2(閉源)
  • Spring Cloud Gateway

下面我們主要以spring cloud gateway為例來講閘道器.
Spring cloud gateway包含spring 5,spring boot 2,project reactor,它提供一種簡單有效的路由轉發請求,並提供橫切關注點,如安全性,監控/指標和彈性等,它的特性如下:

  • 基於Java 8 編碼
  • 支援Spring framework 5
  • 支援spring boot 2
  • 支援動態路由
  • 支援基於HTTP請求的路由匹配(path,method,header,host etc)
  • 過濾器作用於匹配的路由,修改下游HTTP請求和HTTP響應(增加/修改頭部,增加/修改請求引數,改寫請求路徑)
  • 支援spring cloud discovery client,與服務發現和註冊配合使用
  • 支援websocket,使用非阻塞API(主要與zuul1比較,zuul1是基於Servlet實現,基於阻塞I/O,不支援任何長連線,每次I/O操作都是從工作執行緒中選擇一個執行,request-per-thread)
  • 基於Filter鏈提供了身份驗證,監控,負載均衡,限流,降級與應用檢測等功能

spring cloud gateway重要概念

  • 路由:路由資訊由ID,目標URL,一組斷言,一組filter.若斷言為真,則請求URL和配置匹配.
  • Filter:分兩類gateway filter和globalfilter,過濾器將會對請求和響應進行修改處理.
  • 斷言:自定義匹配來自http request中的任務資訊如請求頭和引數

實踐:
application.yaml

server:
  port: ${port:8180}
  ipAddr: ${ipAddr:127.0.0.1}

spring:
  zipkin:
    base-url: http://127.0.0.1:9411
    discovery-client-enabled: false #
  sleuth:
    sampler:
      probability: 1.0 #取樣百分比 range[0.1~1.0]

  application:
    name: service-gateway
  cloud:
    gateway:
      discovery:
        locator:
          enabled: true
          lowerCaseServiceId: true
      #          lower-case-service-id: true
      routes:
        - id: service_product
          uri: lb://service-product
          order: 1 #數字越小,越先執行,即優先順序越高
          predicates:
            - Path=/api/product/**
          filters:
            - StripPrefix=1
        - id: service_order
          uri: lb://service-order
          order: 1 #數字越小,即優先順序越高
          predicates:
            - Path=/api/order/**
          filters:
            - StripPrefix=1
      globalcors:
        cors-configurations:
          ‘[/**]‘:
              allow-credentials: true #允許攜帶認證資訊
              allowed-origins: #允許跨域的源(域名和IP),設定*為全部
                - ‘*‘
              allowed-headers: ‘*‘ # 允許跨域請求裡的header欄位,*為全部
              allowed-methods: #允許跨域的方法
                - GET
                - POST
                - OPTIONS
              max-age: 3600 # 跨域允許的有效期
    nacos:
      discovery:
        server-addr: ${server.ipAddr}:8848
        namespace: 31885c53-49eb-4031-9450-89e78cfb48fa

logging:
  level:
    root: info
#  pattern:
#    console:  ‘[%-5level] %clr(%d{HH:mm:ss}){cyan} %logger - %clr(%msg){green} [%clr(%thread{20}){cyan}]%n‘

上述配置檔案包括應用名稱,埠號以及註冊中心的配置和配置中心的配置,跨域請求配置.日誌列印級別及控制檯列印格式,還可以指定日誌檔案儲存等,最核心的是路由的配置.注意uri是使用lb:// 這裡使用了負載均衡.

我們還會在程式碼中實現一個token過濾器,程式碼片段如下:

    @Value("${auth.skip.urls:skip urls not config yet}")
    private String[] skipAuthUrls; //忽略驗證的urls

    @Override
    public Mono<Void> filter(ServerWebExchange exchange,GatewayFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
        String path = request.getURI().getPath().trim();
        Assert.notEmpty(skipAuthUrls,"skip auth urls is not null");
        //如果當前路徑不需要驗證就放行
        if (Stream.of(skipAuthUrls).anyMatch(url -> url.equalsIgnoreCase(path.trim()))) {
            log.debug("{} 不需要鑑權,its order {}",path.trim(),getOrder());
            return chain.filter(exchange);
        }
        //TODO 獲取客戶端 傳來的token資訊進行安全鑑定
        //TODO 比如使用token去redis裡檢視是否過期,是否在黑名單 中等
        //TODO 驗證通過後走過濾器鏈即可
        //TODO 驗證未通過 直接返回客戶端 相應的提示資訊

        return chain.filter(exchange);
    }

    /**
     * Get the order value of this object.
     * <p>Higher values are interpreted as lower priority. As a consequence,* the object with the lowest value has the highest priority (somewhat
     * analogous to Servlet {@code load-on-startup} values).
     * <p>Same order values will result in arbitrary sort positions for the
     * affected objects.
     *
     * @return the order value
     * @see #HIGHEST_PRECEDENCE
     * @see #LOWEST_PRECEDENCE
     */
    @Override
    public int getOrder() {
        return -100;
    }

以上程式碼片段處理了需要跳過鑑權的url,比如 註冊,登入等
還通過Order介面指定了執行的順序,這裡的Order,數字越小,優先順序越高.
以及鑑權過程,這裡為了脫敏只寫了TODO 提示.
完整程式碼 請參照github.com/gisonwin