【spring cloud】spring cloud zuul 路由網關

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

版本介紹:java

<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>

 

====================================================mysql

參考地址:http://www.javashuo.com/article/p-tnehirch-dh.htmlgit

====================================================github

一.簡單介紹spring

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

爲何須要Zuul數據庫

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

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

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

相關文章
相關標籤/搜索