微服務:SpringCloud zuul網關

一、存在的問題

在之前的文章中,微服務架構已經初具雛形。但還有一些問題:不同的微服務一般會有不同的網 絡地址,客戶端在訪問這些微服務時必須記住幾十甚至幾百個地址,這對於客戶端方來說太複雜也難以維護。如下圖:
 
前端應用程序調用我們消費者提供的接口會有以下幾個問題:
  • 客戶端會請求多個不同的服務,需要維護不同的請求地址,增加開發難度 (各個消費者ip不同)
  • 在某些場景下存在跨域請求的問題
  • 加大身份認證的難度,每個微服務需要獨立認證(關於微服務認證,可以看我之前的文章,深入的講解了分佈式的授權與認證)
那我們想,能否用同一個ip來解決呢?這樣就出現了我們網關的姿勢!
 
通過網關,前端應用程序,只需要調用網關的ip就可以了。在網關就可以找到對應的微服務。
 

二、網關的概念

什麼是微服務網關:
 
API網關是一個服務器,是系統對外的唯一入口。API網關封裝了系統內部架構,爲每個客戶端提供一個定製的APIAPI網關方式的核心要點是,所有的客戶端和消費端都通過統一的網關接入微服務,在網關層處理所有的非業務功能。通常,網關也是提供REST/HTTP的訪問API。服務端通過API-GW註冊和管理服務。
 
作用和應用場景:
 
網關具有的職責,如身份驗證、監控、負載均衡、緩存、請求分片與管理、靜態響應處理。當然,最主
要的職責還是與外界聯繫
 
常見的API網關實現方式
  • Kong:基於Nginx+Lua開發,性能高,穩定,有多個可用的插件(限流、鑑權等等)可以開箱即用。問題:只支持Http協議;二次開發,自由擴展困難;提供管理API,缺乏更易用的管控、配置方式。
  •  
    Zuul:Netflflix開源,功能豐富,使用JAVA開發,易於二次開發;需要運行在web容器中,如Tomcat。 問題:缺乏管控,無法動態配置;依賴組件較多;處理Http請求依賴的是Web容器,性能不如Nginx;
  •  
    Traefifik:Go語言開發;輕量易用;提供大多數的功能:服務路由,負載均衡等等;提供WebUI。問題:二進制文件部署,二次開發難度大;UI更多的是監控,缺乏配置、管理能力;
  •  
    Spring Cloud Gateway:SpringCloud提供的網關服務
  •  
    Nginx+lua實現:使用Nginx的反向代理和負載均衡可實現對api服務器的負載均衡及高可用問題:自注冊的問題和網關本身的擴展性。
     

三、zuul實現網關

1、Zuul簡介

ZUULNetflflix開源的微服務網關,它可以和EurekaRibbonHystrix等組件配合使用,Zuul組件的核心是一系列的過濾器,這些過濾器可以完成以下功能:
  • 動態路由:動態將請求路由到不同後端集羣
  • 壓力測試:逐漸增加指向集羣的流量,以瞭解性能
  • 負載分配:爲每一種負載類型分配對應容量,並棄用超出限定值的請求
  • 靜態響應處理:邊緣位置進行響應,避免轉發到內部集羣 
  • 身份認證和安全: 識別每一個資源的驗證要求,並拒絕那些不符的請求。Spring CloudZuul進行了整合和增強。

2、搭建一個Zuul網關服務器

(1)一個基礎工程

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>SpringCloudDemo</artifactId>
        <groupId>com.springcloud.demo</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>zuul_server</artifactId>

    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-zuul</artifactId>
        </dependency>
    </dependencies>
</project>
server:
  port: 8080 #端口

spring:
  application:
    name: api-zuul-server
package com.springcloud.demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.zuul.EnableZuulProxy;

@SpringBootApplication
@EnableZuulProxy
public class ZuulApplication {

    public static void main(String[] args) {
        SpringApplication.run(ZuulApplication.class,args);
    }
}

其他服務目錄:

person-sevice是9002端口,order-service是9001端口,這兩個是對外提供的接口。product-service是9010端口,是微服務。

(2)路由配置

# 路由選擇配置
zuul:
  routes:
    service-order:
      path: /order-service/**
      url: http://127.0.0.1:9001
    service-person:
      path: /person-service/**
      url: http://127.0.0.1:9002
只需要在application.yml文件中配置路由規則即可:
  • product-service:配置路由id,可以隨意取名
  • url:映射路徑對應的實際url地址 
  • path:配置映射路徑,這裏將所有請求前綴爲/product-service/的請求,轉發到http://127.0.0.1:9002處理

都是可以的,他們都調用了一個微服務。

(3)面向服務的路由配置(從註冊中心拿到服務)

添加註冊中心依賴:

<dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
        </dependency>
添加註冊中心配置
 
eureka:
  client:
    service-url:
      defaultZone: http://localhost:9000/eureka/
  instance:
    prefer-ip-address: true #使用ip地址註冊
# 路由選擇配置
zuul:
  routes:
    service-order:
      path: /order-service/**
#      url: http://127.0.0.1:9001
      serviceId: service-order
    service-person:
      path: /person-service/**
#      url: http://127.0.0.1:9002
      serviceId: service-person

 

依然是沒有任何問題的!!!
 
簡化配置
zuul:
  routes:
     service-order: /service-order/**

3、過濾器

zuul有兩個核心功能:請求過濾

ZuulFilter簡介
 
Zuul 中的過濾器跟我們之前使用的 javax.servlet.Filter 不一樣,javax.servlet.Filter 只有一種類型,可
以通過配置 urlPatterns 來攔截對應的請求。而 Zuul 中的過濾器總共有 4 種類型,且每種類型都有對
應的使用場景。
 
  • PRE:這種過濾器在請求被路由之前調用。我們可利用這種過濾器實現身份驗證、在集羣中選擇請
    求的微服務、記錄調試信息等。
  •  
    ROUTING:這種過濾器將請求路由到微服務。這種過濾器用於構建發送給微服務的請求,並使用
    Apache HttpClientNetfifilx Ribbon請求微服務。
  •  
    POST:這種過濾器在路由到微服務以後執行。這種過濾器可用來爲響應添加標準的HTTP
    Header、收集統計信息和指標、將響應從微服務發送給客戶端等。
  •  
    ERROR:在其他階段發生錯誤時執行該過濾器。
Zuul提供了自定義過濾器的功能實現起來也十分簡單,只需要編寫一個類去實現zuul提供的接口
 
public abstract ZuulFilter implements IZuulFilter{ 
    abstract public String filterType(); 

    abstract public int filterOrder(); 

    boolean shouldFilter();// 來自IZuulFilter 
    
    Object run() throws ZuulException;// IZuulFilter 
}
ZuulFilter是過濾器的頂級父類。在這裏我們看一下其中定義的4個最重要的方法。
 
  • shouldFilter :返回一個 Boolean 值,判斷該過濾器是否需要執行。返回true執行,返回false不執行。
  •  
    run :過濾器的具體業務邏輯。
  •  
    filterType :返回字符串,代表過濾器的類型。包含以下4種:
    •  
      pre :請求在被路由之前執行
    •  
      routing :在路由請求時調用
    • post :在routingerrror過濾器之後調用
    • error :處理請求時發生錯誤調用
  • filterOrder :通過返回的int值來定義過濾器的執行順序,數字越小優先級越高。

生命週期:

 

4、自定義過濾器

package com.springcloud.demo.filter;

import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;

/**
 * @ClassName LoginFilter
 * @Description
 * @Author 戴書博
 * @Date 2020/5/28 16:24
 * @Version 1.0
 **/
@Component
public class LoginFilter extends ZuulFilter {

    /**
     * 類型:
     *      pre
     *      routing
     *      post
     *      error
     */
    @Override
    public String filterType() {
        return "pre";
    }

    /**
     * 指定過濾器的執行順序
     * 返回值越小,執行順序越靠前
     */
    @Override
    public int filterOrder() {
        return 0;
    }

    /**
     * 當前過濾器是否生效
     * true:使用此過濾器
     * false:不使用此過濾器
     */
    @Override
    public boolean shouldFilter() {
        return true;
    }

    /**
     * 執行過濾器的方法
     */
    @Override
    public Object run() throws ZuulException {
        System.out.println("================進入過濾器=================");
        //獲取上下文對象
        RequestContext ctx = RequestContext.getCurrentContext();
        //獲取request對象
        HttpServletRequest req = ctx.getRequest();
        //從請求中獲取token
        String token = req.getParameter("access-token");
        //判斷
        if(token == null || token.equals("")){
            // 沒有token,登錄校驗失敗,攔截
            ctx.setSendZuulResponse(false);
            // 返回401狀態碼。也可以考慮重定向到登錄頁。
            ctx.setResponseStatusCode(HttpStatus.UNAUTHORIZED.value());
        }
        // 校驗通過,可以考慮把用戶信息放入上下文,繼續向後執行
        return null;
    }
}

在我們之前寫的授權與認證中,使用了一個微服務認證。這裏簡單寫一下。

zuul出現的問題:

  • 性能問題 Zuul1x版本本質上就是一個同步Servlet,採用多線程阻塞模型進行請求轉發。簡單講,每一個請求,Servlet容器要爲該請求分配一個線程專門負責處理這個請求,直到響應返回客戶端這個線程纔會被釋放返回容器線程池。如果後臺服務調用比較耗時,那麼這個線程就會被阻塞,阻塞期間線程資源被佔用,不能幹其它事情。我們知道Servlet容器線程池的大小是有限制的,當前端請求量大,而後臺慢服務比較多時,很容易耗盡容器線程池內的線程,造成容器無法接受新的請求。
  • 不支持任何長連接,如websocket

源碼:[email protected]:Zesystem/springclouddedemowangguan.git

 

 

 

 

 

 

 

 

 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章