springcloud(七)------zuul 網關

網關是什麼

是一個網絡整體系統中的前置門戶入口。請求首先通過網關,進行路徑的路由,定位到具體的服務節點上。是進入服務的統一大門。

網關分類

api開放網關

開放api(openApi) 企業需要將自身數據、能力等作爲開發平臺向外開放,通常會以rest的方式向外提供,最好的例子就是淘寶開放平臺、騰訊公司的QQ開發平臺、微信開放平臺。 Open API開放平臺必然涉及到客戶應用的接入、API權限的管理、調用次數管理等,必然會有一個統一的入口進行管理,這正是API網關可以發揮作用的時候。

微服務網關

微服務的概念最早在2012年提出,在Martin Fowler的大力推廣下,微服務在2014年後得到了大力發展。 在微服務架構中,有一個組件可以說是必不可少的,那就是微服務網關,微服務網關處理了負載均衡,緩存,路由,訪問控制,服務代理,監控,日誌等。API網關在微服務架構中正是以微服務網關的身份存在。

API服務管理平臺

上述的微服務架構對企業來說有可能實施上是困難的,企業有很多遺留系統,要全部抽取爲微服務器改動太大,對企業來說成本太高。但是由於不同系統間存在大量的API服務互相調用,因此需要對系統間服務調用進行管理,清晰地看到各系統調用關係,對系統間調用進行監控等。 API網關可以解決這些問題,我們可以認爲如果沒有大規模的實施微服務架構,那麼對企業來說微服務網關就是企業的API服務管理平臺。

網關作用

  • 統一入口:未全部爲服務提供一個唯一的入口,網關起到外部和內部隔離的作用,保障了後臺服務的安全性。
  • 鑑權校驗:識別每個請求的權限,拒絕不符合要求的請求。
  • 動態路由:動態的將請求路由到不同的後端集羣中。
  • 減少客戶端與服務端的耦合:服務可以獨立發展,通過網關層來做映射。
  • 限流控制等

網關與過濾器區別

網關是攔截所有服務器請求進行控制
過濾器攔截某單個服務器請求進行控制

Nginx與Zuul的區別

Nginx是採用服務器負載均衡進行轉發
Zuul依賴Ribbon和eureka實現本地負載均衡轉發
相對來說Nginx功能比Zuul功能更加強大,能夠整合其他語言比如lua腳本實現強大的功能,同時Nginx可以更好的抗高併發,Zuul網關適用於請求過濾和攔截等。

Zuul 網關的原理

springcloud 整合了 zuul,其實現是類似 過濾器和aop 的整合,一系列Filter展開的,這些Filter在整個HTTP請求過程中執行一連串的操作,zuul 整合了ribbon 和hystrix 提供服務。

Zuul 網關使用

  • 啓動 eureka 服務 : http://localhost:1008;
  • 啓動product 服務: http://localhost:3008
    product 服務是註冊到 eureka 的名稱
    product 服務接口: http://localhost:3008/product/test1
    @RequestMapping("/product/test1")
    public String test1(){
        System.out.println("------test1 接口被調用啦,正在執行");
        return "success return test1";
    }

作爲我們的測試服務,接下來是網關服務

maven 依賴

 <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-zuul</artifactId>
            <version>2.0.1.RELEASE</version>
        </dependency>
        <!-- SpringBoot整合eureka客戶端 -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
            <version>2.0.1.RELEASE</version>
        </dependency>
        <!-- zuul網關的重試機制,不是使用ribbon內置的重試機制
           是藉助spring-retry組件實現的重試
           開啓zuul網關重試機制需要增加下述依賴
         -->
        <dependency>
            <groupId>org.springframework.retry</groupId>
            <artifactId>spring-retry</artifactId>
        </dependency>
        <!-- 集成lombok 框架 -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>

啓動類

/**
 *
 * @EnableZuulProxy - 開啓Zuul網關。
 *  當前應用是一個Zuul微服務網關。會在Eureka註冊中心中註冊當前服務。並發現其他的服務,不用添加其他的eureka註解了
 */
@SpringBootApplication
@EnableZuulProxy
public class DemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }
}

配置路由

首先端口和eureka配置

server.port=4008
spring.application.name=zuul
eureka.client.serviceUrl.defaultZone = http://localhost:1008/eureka/

增加路由,先採用名稱路由

zuul.routes.webshop-product.path=/api-product/**
zuul.routes.webshop-product.service-id=product

訪問的方式是這樣
http://zuulHostIp:port/要訪問的服務名稱/服務中的URL,
例如: http://localhost:3008/product/test1
訪問: http://localhost:4008/api-product/product/test1

過濾器

  • 前置過濾:是請求進入Zuul之後,立刻執行的過濾邏輯。
  • 路由後過濾:是請求進入Zuul之後,並Zuul實現了請求路由後執行的過濾邏輯,
  • 路由後過濾,是在遠程服務調用之前過濾的邏輯。
  • 後置過濾:遠程服務調用結束後執行的過濾邏輯。
  • 異常過濾:是任意一個過濾器發生異常或遠程服務調用無結果反饋的時候執行的過濾邏輯。無結果反饋,就是遠程服務調用超時。

實現父類來定義,具體如下

@Component
@Slf4j
public class PreFilter extends ZuulFilter {
    /**
     * 過濾器的類型。可選值有:
     * pre - 前置過濾
     * route - 路由後過濾
     * error - 異常過濾
     * post - 遠程服務調用後過濾
     */
    @Override
    public String filterType() {
        return "pre";
    }
    /**
     * 同種類的過濾器的執行順序。
     * 按照返回值的自然升序執行。
     */
    @Override
    public int filterOrder() {
        return 0;
    }
    /**
     * 返回boolean類型。代表當前filter是否生效。
     * 默認值爲false。
     * 返回true代表開啓filter。
     */
    @Override
    public boolean shouldFilter() {
        return true;
    }
    /**
     * run方法就是過濾器的具體邏輯。
     * return 可以返回任意的對象,當前實現忽略。(spring-cloud-zuul官方解釋)
     * 直接返回null即可。
     *
     * 使用過濾器驗證客戶端是否有登陸
     */
    @Override
    public Object run() throws ZuulException {
        System.out.println("Pre 網關過濾執行");
        // 通過zuul,獲取請求上下文
        RequestContext rc = RequestContext.getCurrentContext();
        HttpServletRequest request = rc.getRequest();
        log.info("LogFilter1.....method={},url={}",
                request.getMethod(),request.getRequestURL().toString());
        // 可以記錄日誌、鑑權,給維護人員記錄提供定位協助、統計性能
        String userToken = request.getParameter("userToken");
//        if (StringUtils.isEmpty(userToken)) {
//            rc.setSendZuulResponse(false);
//            rc.setResponseStatusCode(401);
//            rc.setResponseBody("userToken is null");
//            return null;
//        }
        // 否則正常執行業務邏輯.....

        return null;
    }
}

現在啓動 zuul 服務。訪問 http://localhost:4008/api-product/product/test1,返回正常,已經路由到了product 服務。

容錯機制

路由到目標服務有時候會出現網絡不通等原因,造成失敗,需要注意的是,只要有返回,就不會觸發容錯機制,不論是不是異常。
跳轉 product服務,延時3秒返回

@RestController
public class TestController1 {
    @RequestMapping("/product/test1")
    public String test1(){
        try {
            Thread.sleep(3000); // 模擬故障,超時 3 秒
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("------test1 接口被調用啦,正在執行");
        return "success return test1";
    }

}

現在設置zuul 路由到product 時間是 2秒,增加配置

zuul.host.connect-timeout-millis=2000

增加容錯代碼

@Component
public class TestFallbackProvider implements FallbackProvider {
    /**
     * return - 返回fallback處理哪一個服務。返回的是服務的名稱,
     * 推薦 - 爲指定的服務定義特性化的fallback邏輯。
     * 推薦 - 提供一個處理所有服務的fallback邏輯。
     * 好處 - 服務某個服務發生超時,那麼指定的fallback邏輯執行。如果有新服務上線,未提供fallback邏輯,有一個通用的。
     * 可以使用通配符‘*’代表爲全部的服務提供容錯處理。
     */
    @Override
    public String getRoute() {
        return "product";
    }
    // 降級處理
    @Override
    public ClientHttpResponse fallbackResponse(String route, Throwable cause) {
        System.out.println("進入 fallbackResponse");
        // 設置降級給客戶端返回的信息
        return new ClientHttpResponse(){
            @Override
            public HttpHeaders getHeaders() {
                HttpHeaders header = new HttpHeaders();
                // 返回json數據
                MediaType mt = new MediaType("application","json", Charset.forName("utf-8"));
                header.setContentType(mt);
                return header;
            }
            // 設置響應的內容
            @Override
            public InputStream getBody() throws IOException {
                return new ByteArrayInputStream("product服務不可用,請於管理員聯繫!".getBytes("utf-8"));
            }

            @Override
            public HttpStatus getStatusCode() throws IOException {
                return HttpStatus.BAD_REQUEST;
            }

            @Override
            public int getRawStatusCode() throws IOException {
                return HttpStatus.BAD_REQUEST.value();
            }
            // 返回狀態碼
            @Override
            public String getStatusText() throws IOException {
                return HttpStatus.BAD_REQUEST.getReasonPhrase();
            }

            @Override
            public void close() {

            }
        };
    }
}

訪問 http://localhost:4008/api-product/product/test1,
前臺返回 友好提示

product服務不可用,請於管理員聯繫!

zuul 限流

首先簡單說一下 spring cloud zuul-ratelimit,他是外國人專門針對 zuul 編寫的限流庫,提供來4種限流策略,如下。

限流粒度/類型 說明
User 針對請求的用戶進行限流
Origin 針對請求的Origin進行限流
URL 針對請求的接口限流
ServerId 針對服務限流,默認的

多種粒度臨時變量儲存方式

存儲方式 說明
IN_MEMORY 基於本地內存,底層是ConcurrentHashMap,默認的
REDIS 基於redis存儲,使用時必須搭建redis
JPA spring data jpa,基於數據庫
BUKET4J 使用一個Java編寫的基於令牌桶算法的限流庫
CONSUL consul 的kv存儲

如果 zuul 需要多節點部署,那就不能用 IN_MEMORY 存儲方式,比較常用的就是用Redis。

增加依賴

 <dependency>
            <groupId>com.marcosbarbero.cloud</groupId>
            <artifactId>spring-cloud-zuul-ratelimit</artifactId>
            <version>1.3.4.RELEASE</version>
        </dependency>

配置文件增加

# 全侷限流配置
# 開啓限流保護
zuul.ratelimit.enabled=true
# 60s內請求超過3次,服務端就拋出異常,60s後可以恢復正常請求
zuul.ratelimit.default-policy.limit=3
zuul.ratelimit.default-policy.refresh-interval=60

簡單的設置了全局的限流
然後訪問服務,當第四次的時候,返回

Whitelabel Error Page
This application has no explicit mapping for /error, so you are seeing this as a fallback.

Sat May 09 15:35:08 CST 2020
There was an unexpected error (type=Too Many Requests, status=429).
429

這個地方,可以統一處理下異常,返回友好提示。

  • 補充:局部限流
    針對product 服務限流
# 開啓限流保護
zuul.ratelimit.enabled=true
# hystrix-application-client服務60s內請求超過3次,服務拋出異常。
zuul.ratelimit.policies.product.limit=3
zuul.ratelimit.policies.product.refresh-interval=60
# 針對IP限流。
zuul.ratelimit.policies.product.type=origin
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章