【spring cloud】spring cloud zuul 路由網關


GitHub源碼地址:https://github.com/AngelSXD/springcloud

版本介紹:

<properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
        <spring-boot.version>2.0.4.RELEASE</spring-boot.version>
        <spring-cloud.version>Finchley.SR1</spring-cloud.version>
        <lcn.last.version>4.2.1</lcn.last.version>
    </properties>

 

====================================================

參考地址:https://www.cnblogs.com/cralor/p/9234697.html

====================================================

一.簡單介紹

 Zuul作為微服務系統的網關組件,用於構建邊界服務,致力於動態路由、過濾、監控、彈性伸縮和安全。

為什么需要Zuul

Zuul、Ribbon以及Eureka結合可以實現智能路由和負載均衡的功能;網關將所有服務的API接口統一聚合,統一對外暴露。外界調用API接口時,不需要知道微服務系統中各服務相互調用的復雜性,保護了內部微服務單元的API接口;網關可以做用戶身份認證和權限認證,防止非法請求操作API接口;網關可以實現監控功能,實時日志輸出,對請求進行記錄;網關可以實現流量監控,在高流量的情況下,對服務降級;API接口從內部服務分離出來,方便做測試。

Zuul通過Servlet來實現,通過自定義的ZuulServlet來對請求進行控制。核心是一系列過濾器,可以在Http請求的發起和響應返回期間執行一系列過濾器。Zuul采取了動態讀取、編譯和運行這些過濾器。過濾器之間不能直接通信,而是通過RequestContext對象來共享數據,每個請求都會創建一個RequestContext對象。

Zuul生命周期如下圖。 當一個客戶端Request請求進入Zuul網關服務時,網關先進入”pre filter“,進行一系列的驗證、操作或者判斷。然后交給”routing filter“進行路由轉發,轉發到具體的服務實例進行邏輯處理、返回數據。當具體的服務處理完成后,最后由”post filter“進行處理,該類型的處理器處理完成之后,將Request信息返回客戶端。 

 

 

二.配置過程

1.pom依賴

    <!--熔斷器 健康檢查-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
    <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
        </dependency>    
    <!--zuul核心依賴-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-zuul</artifactId>
        </dependency>

 

2.配置application.properties

spring.application.name=springcloud-ms-gateway
server.port=8001
eureka.client.service-url.defaultZone=http://127.0.0.1:8000/eureka/



#因為parent的pom.xml中  添加了連接數據庫的依賴,所以 需要配置數據庫連接相關配置
spring.datasource.continue-on-error=false 
spring.datasource.url=jdbc:mysql://localhost:3306/springcloudtest?useSSL=false&useUnicode=true&characterEncoding=UTF-8
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.username=root
spring.datasource.password=root






zuul.host.socket-timeout-millis=10000
#網關最大超時時間10s
zuul.host.connect-timeout-millis=10000
#網關最大連接數 默認200
zuul.host.max-total-connections=200

#給要路由的API請求加前綴 可加可不加
zuul.prefix=/v1
#需要忽略的服務 ,號分隔  配置后將不會被路由
zuul.ignored-services=spring-cloud-ms-eureka

#配置了zuul之后,那么整個分布式系統,對外則只暴露http://zuul-IP:zuul-port/v1/服務自定義地址/具體API請求地址

#這一組配置后,訪問http://localhost:8001/v1/ms-member/member/showMember 即相當於直接訪問會員服務http://localhost:9000/member/showMember
#zuul.routes.xx  xx隨便寫,zuul中唯一即可
#zuul.routes.xx.serviceId=eureka中注冊的服務名 即各個服務配置文件中的spring.application.name
zuul.routes.member.serviceId=springcloud-ms-member
#zuul.routes.xx.path=/自定義的地址    /**表示下級也可以訪問
zuul.routes.member.path=/ms-member/**

#這一組配置后,所有訪問積分服務的請求 即可直接訪問網關地址http://localhost:8001/v1/ms-integral/具體接口地址   ,由zuul進行路由轉發和負載均衡
zuul.routes.integral.serviceId=springcloud-ms-integral
zuul.routes.integral.path=/ms-integral/**

#這一組配置后,所有訪問商品服務的請求 即可直接訪問網關地址http://localhost:8001/v1/ms-goods/具體接口地址  ,由zuul進行路由轉發和負載均衡
zuul.routes.goods.serviceId=springcloud-ms-goods
zuul.routes.goods.path=/ms-goods/**


#txmanager地址 必填
tm.manager.url=http://127.0.0.1:7000/tx/manager/

 

 

 

3.啟動類注解

核心注解@EnableZuulProxy

同時需要添加注解,服務發現,注冊eureka

package com.swapping.springcloud.ms.gateway;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.netflix.hystrix.dashboard.EnableHystrixDashboard;
import org.springframework.cloud.netflix.zuul.EnableZuulProxy;
import org.springframework.cloud.openfeign.EnableFeignClients;

@EnableCircuitBreaker    //Hystrix Dashboard必須加
@EnableHystrixDashboard//展示熔斷器儀表盤

@EnableZuulProxy    //網關映射 注解
@EnableFeignClients    //feign調用注解
@EnableDiscoveryClient
@SpringBootApplication(scanBasePackages = "com.swapping")
public class SpringcloudMsGatewayApplication {

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

 

 >>>>>>>>>>>>>路由轉發

4.啟動網關服務 

springcloud-ms-gateway  端口8001

啟動ms-member服務,端口9000

那么現在訪問

http://localhost:9000/member/showMember
即直接訪問ms-member服務,的/member/showMember這個API

現在可以訪問網關,通過zuul進行動態路由

http://localhost:8001/v1/ms-member/member/showMember
統一訪問網關地址,由網關進行統一路由轉發

至此,zuul實現路由轉發! 

  >>>>>>>>>>>>>路由攔截

 5.使用zuul進行路由攔截

現在既然所有的訪問接口都是由zuul網關暴露出去的,那所有的請求都來找網關,這樣的話在網關的過濾器中就可以做很多的事情。

Filter是Zuul的核心,用來實現對外服務的控制。Filter的生命周期有4個,分別是“PRE”、“ROUTING”、“POST”、“ERROR”,整個生命周期可以用下圖來表示

 

 

5.1 Zuul大部分功能都是通過過濾器來實現的,這些過濾器類型對應於請求的典型生命周期。

  • PRE: 這種過濾器在請求被路由之前調用。我們可利用這種過濾器實現身份驗證、在集群中選擇請求的微服務、記錄調試信息等。
  • ROUTING:這種過濾器將請求路由到微服務。這種過濾器用於構建發送給微服務的請求,並使用Apache HttpClient或Netfilx Ribbon請求微服務。
  • POST:這種過濾器在路由到微服務以后執行。這種過濾器可用來為響應添加標准的HTTP Header、收集統計信息和指標、將響應從微服務發送給客戶端等。
  • ERROR:在其他階段發生錯誤時執行該過濾器。 除了默認的過濾器類型,Zuul還允許我們創建自定義的過濾器類型。例如,我們可以定制一種STATIC類型的過濾器,直接在Zuul中生成響應,而不將請求轉發到后端的微服務。

 5.2 在zuul中默認已經實現的filter有:

類型 順序 過濾器 功能
pre -3 ServletDetectionFilter 標記處理Servlet的類型
pre -2 Servlet30WrapperFilter 包裝HttpServletRequest請求
pre -1 FormBodyWrapperFilter 包裝請求體
route 1 DebugFilter 標記調試標志
route 5 PreDecorationFilter 處理請求上下文供后續使用
route 10 RibbonRoutingFilter serviceId請求轉發
route 100 SimpleHostRoutingFilter url請求轉發
route 500 SendForwardFilter forward請求轉發
post 0 SendErrorFilter 處理有錯誤的請求響應
post 1000 SendResponseFilter 處理正常的請求響應

5.3 zuul還提供了一類特殊的過濾器,分別為:StaticResponseFilter和SurgicalDebugFilter

StaticResponseFilter:StaticResponseFilter允許從Zuul本身生成響應,而不是將請求轉發到源。

SurgicalDebugFilter:SurgicalDebugFilter允許將特定請求路由到分隔的調試集群或主機

 

5.4 在zuul中定義自定義的過濾器

  例如:自定義一個請求被路由之前的過濾器,用於驗證請求中是否攜帶auth,如果攜帶安全驗證,則成功路由;否則統一終止路由,返回響應

自定義filter需要繼承ZuulFilter【注意,自定義Filter需要注入spring 使用注解@Component】

package com.swapping.springcloud.ms.gateway.filter;

import com.alibaba.fastjson.JSON;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import com.swapping.springcloud.ms.core.response.UniVerResponse;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;

/**
 * 路由攔截
 *
 *
 * >>>>>zuul的filter過濾器的生命周期有一下四個:
 *
 *  PRE: 這種過濾器在請求被路由之前調用。我們可利用這種過濾器實現身份驗證、在集群中選擇請求的微服務、記錄調試信息等。
 *  ROUTING:這種過濾器將請求路由到微服務。這種過濾器用於構建發送給微服務的請求,並使用Apache HttpClient或Netfilx Ribbon請求微服務。
 *  POST:這種過濾器在路由到微服務以后執行。這種過濾器可用來為響應添加標准的HTTP Header、收集統計信息和指標、將響應從微服務發送給客戶端等。
 *  ERROR:在其他階段發生錯誤時執行該過濾器。 除了默認的過濾器類型,Zuul還允許我們創建自定義的過濾器類型。例如,我們可以定制一種STATIC類型的過濾器,直接在Zuul中生成響應,而不將請求轉發到后端的微服務。
 *
 *
 *  Zuul中默認實現了很多Filter,也可以自己自定義過濾器
 *
 *  下面是自己自定義過濾器
 *  實際使用中我們可以結合shiro、oauth2.0等技術去做鑒權、驗證
 *
 */
@Component
public class AuthFilter extends ZuulFilter{


    @Override
    public String filterType() {
        return "pre";//可以在請求被路由之前調用
    }

    @Override
    public int filterOrder() {
        return 0;//filter執行順序,通過數字指定 ,優先級為0,數字越大,優先級越低
    }

    @Override
    public boolean shouldFilter() {
        return true;// 是否執行該過濾器,此處為true,說明需要過濾
    }

    /**
     *  filter需要執行的具體操作
     *
     * 例如:本filter實際執行的邏輯 是驗證所有的訪問請求中,是否包含安全信息auth
     * @return
     * @throws ZuulException
     */
    @Override
    public Object run() throws ZuulException {

        RequestContext ctx = RequestContext.getCurrentContext();
        HttpServletRequest request = ctx.getRequest();

        String auth = request.getParameter("auth");
        //TODO 此處可以做日志記錄
        System.out.println("zuul攔截--請求前驗證---auth:"+auth);

        //成功的情況
        if (StringUtils.isNotBlank(auth)){
            ctx.setSendZuulResponse(true); //對請求進行路由
            ctx.setResponseStatusCode(200);
            ctx.set("isSuccess", true);
        }else {
            //失敗的情況
            UniVerResponse res = new UniVerResponse();
            res.beFalse3("zuul攔截--請求前驗證---沒有auth登錄驗證",UniVerResponse.ERROR_BUSINESS);

            ctx.setSendZuulResponse(false); //不對請求進行路由
            ctx.setResponseStatusCode(res.getCode());//設置返回狀態碼
            ctx.setResponseBody(JSON.toJSONString(res));//設置返回響應體
            ctx.set("isSuccess", false);
            ctx.getResponse().setContentType("application/json;charset=UTF-8");//設置返回響應體格式,可能會亂碼

        }

        return null;
    }

}
View Code

 

5.5 測試自定義攔截

啟動網關服務 ms-gateway 端口:8001

啟動服務注冊中心eureka

啟動要被路由的服務ms-member 端口:9000

@RestController
@RequestMapping("/member")
public class MemberController {


    @Autowired
    MemberService memberService;

    @RequestMapping(value = "/showMember")
    public String showMember(){
        return "會員服務正常,會員服務是服務消費者,也就是服務調用者,會調用商品服務進行商品購買,減少庫存,增加銷量;\n\r同時調用積分服務,增加積分";
    }
View Code

 

訪問地址:攜帶auth驗證

http://localhost:8001/v1/ms-member/member/showMember?auth=111

 

訪問地址:不攜帶auth

 

 

5.6 如果你想禁用 zuul中的filter,可以這么做:

如果你想禁用一個,只需設置zuul.<SimpleClassName>.<filterType> .disable = true。按照慣例,過濾器后面的包是Zuul過濾器類型。例如,要禁用org.springframework.cloud.netflix.zuul.filters.post.SendResponseFilter 需設置 zuul.SendResponseFilter.post.disable = true

 

 

 >>>>>>>>>>>>>路由熔斷

 6.使用zuul進行路由熔斷

 類似與服務之間的feign調用熔斷器設定,網關路由各個服務的請求也可以做路由熔斷,在不能成功路由到具體服務上的請求時,進行降級處理,定制返回內容【也就是設置統一響應體】

目前路由熔斷僅能 支持服務級別的熔斷,不支持具體到某個URL進行熔斷

 自定義熔斷類需要實現FallbackProvider接口【注意,自定義熔斷類需要注入spring 使用注解@Component】

package com.swapping.springcloud.ms.gateway.fallback;

import com.alibaba.fastjson.JSON;
import com.swapping.springcloud.ms.core.response.UniVerResponse;
import org.springframework.cloud.netflix.zuul.filters.route.FallbackProvider;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.stereotype.Component;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;

/**
 * 路由熔斷
 *
 * 類似與服務之間的feign調用熔斷器設定,網關路由各個服務的請求也可以做路由熔斷,
 * 在不能成功路由到具體服務上的請求時,進行降級處理,定制返回內容【也就是設置統一響應體】
 *
 * 目前路由熔斷僅能 支持服務級別的熔斷,不支持具體到某個URL進行熔斷
 *
 */
@Component
public class UniVerFallback implements FallbackProvider {

    /**
     * 指定 可以熔斷攔截 哪些服務
     * @return
     */
    @Override
    public String getRoute() {
//        return "springcloud-ms-member";//指定 可熔斷某個服務 服務名是配置文件中配置的各服務的serviceId
        return "*"; //指定    可熔斷所有服務
    }

    /**
     * 指定  熔斷后返回的定制化內容
     * @param route
     * @param cause
     * @return
     */
    @Override
    public ClientHttpResponse fallbackResponse(String route, Throwable cause) {
        return new ClientHttpResponse() {
            @Override
            public HttpStatus getStatusCode() throws IOException {
                return HttpStatus.OK;
            }

            @Override
            public int getRawStatusCode() throws IOException {
                return 200;
            }

            @Override
            public String getStatusText() throws IOException {
                return "OK";
            }

            @Override
            public void close() {

            }

            /**
             * 設置響應體
             * @return
             * @throws IOException
             */
            @Override
            public InputStream getBody() throws IOException {
                //TODO 此處可以做日志記錄
                UniVerResponse uniVerResponse = new UniVerResponse();
                uniVerResponse.beFalse3(route+"服務涼涼了",UniVerResponse.ERROR_BUSINESS);
                return new ByteArrayInputStream(JSON.toJSONString(uniVerResponse).getBytes());
            }

            /**
             * 設置響應頭信息
             * @return
             */
            @Override
            public HttpHeaders getHeaders() {
                HttpHeaders headers = new HttpHeaders();
                headers.setContentType(MediaType.APPLICATION_JSON_UTF8);//指定響應體 格式+編碼方式
                return headers;
            }
        };
    }
}
View Code

 

保證eureka啟動,

然后重啟網關服務ms-gateway,端口:8001

關閉ms-member服務,端口:9000

 然后訪問地址:來訪問ms-member的接口

http://localhost:8001/v1/ms-member/member/showMember?auth=111

 

就可以對指定的服務進行熔斷!!!

 

 >>>>>>>>>>>>>路由重試

 7.使用zuul進行路由重試【慎用】

在服務暫時不可用的情況下,可能是服務重啟正被注冊中心掃描或者其他原因,我們想要對發起的請求進行重試,zuul結合Spring Retry實現路由重試

  7.1 網關服務添加依賴

<!--路由重試 依賴-->
        <dependency>
            <groupId>org.springframework.retry</groupId>
            <artifactId>spring-retry</artifactId>
        </dependency>

  7.2 application.properties添加配置

#路由重試
#是否開啟路由重試,針對於 查詢接口可以使用,但是對於非冪等的新增或更新接口,使用路由重試會出現很大的問題,應該注意

#是否開啟重試功能
zuul.retryable=true
#對當前服務的重試次數[不包含首次訪問],也就是說實際訪問接口的次數是3+1次
ribbon.MaxAutoRetries=3
#切換相同Server的次數
ribbon.MaxAutoRetriesNextServer=0

  7.3 重啟網關服務,更改一下ms-member的測試接口

@RequestMapping(value = "/showMember")
    public String showMember(){
        System.out.println("請求到達!!!!");

        try {
            Thread.sleep(10000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return "會員服務正常,會員服務是服務消費者,也就是服務調用者,會調用商品服務進行商品購買,減少庫存,增加銷量;\n\r同時調用積分服務,增加積分";
    }

可以看到,要訪問的接口 休眠10s,而網關配置的超時時間設置為2s

結合上面的重試次數的配置,請求到達的次數應該是3+1次。

 

啟動ms-member服務,訪問地址:

http://localhost:8001/v1/ms-member/member/showMember?auth=111

 

可以看到,請求總共到達了4次,首次+重試3次

請求結果可以看到,最終服務被熔斷。

  

   7.4 路由重試注意

  

是否開啟路由重試,針對於 查詢接口可以使用,但是對於非冪等的新增或更新接口,使用路由重試會出現很大的問題,應該注意
開啟重試在某些情況下是有問題的,比如當壓力過大,一個實例停止響應時,路由將流量轉到另一個實例,很有可能導致最終所有的實例全被壓垮。說到底,斷路器的其中一個作用就是防止故障或者壓力擴散。用了retry,斷路器就只有在該服務的所有實例都無法運作的情況下才能起作用。這種時候,斷路器的形式更像是提供一種友好的錯誤信息,或者假裝服務正常運行的假象給使用者。 不用retry,僅使用負載均衡和熔斷,就必須考慮到是否能夠接受單個服務實例關閉和eureka刷新服務列表之間帶來的短時間的熔斷。如果可以接受,就無需使用retry。

 

 

=============結束================

參考:http://www.ityouknow.com/springcloud/2018/01/20/spring-cloud-zuul.html

參考:https://www.cnblogs.com/cralor/p/9234697.html

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM