SpringCloud(六):服務網關zuul-API網關(服務降級和過濾)


什么是API網關:

在微服務架構中,通常會有多個服務提供者。設想一個電商系統,可能會有商品、訂單、支付、用戶等多個類型的服務,而每個類型的服務數量也會隨着整個系統體量的增大也會隨之增長和變更。作為UI端,在展示頁面時可能需要從多個微服務中聚合數據,而且服務的划分位置結構可能會有所改變。網關就可以對外暴露聚合API,屏蔽內部微服務的微小變動,保持整個系統的穩定性。

Zuul是Spring Cloud全家桶中的微服務API網關。

主要功能是對請求的路由和過濾。

1.整體資源不夠的情況下,需要關掉一些服務,待穩定了,再重啟。2.校驗token的情況。

所有從設備或網站來的請求都會經過Zuul到達后端的Netflix應用程序。作為一個邊界性質的應用程序,Zuul提供了動態路由、監控、彈性負載和安全功能。Zuul底層利用各種filter實現如下功能:

  • 認證和安全   識別每個需要認證的資源,拒絕不符合要求的請求。
  • 性能監測    在服務邊界追蹤並統計數據,提供精確的生產視圖。
  • 動態路由    根據需要將請求動態路由到后端集群。
  • 壓力測試    逐漸增加對集群的流量以了解其性能。
  • 負載卸載    預先為每種類型的請求分配容量,當請求超過容量時自動丟棄。
  • 靜態資源處理   直接在邊界返回某些響應。

在Spring Cloud微服務系統中,一種常見的負載均衡方式是,客戶端的請求首先經過負載均衡(Ngnix),再到達服務網關(zuul集群),然后再到具體的服務。服務統一注冊到高可用的服務注冊中心集群(eureka, consul),服務的所有的配置文件由配置服務管理,配置服務的配置文件放在git倉庫,方便開發人員隨時改配置。
————————————————

二、動態路由

 

 

 pom.xml:

<dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-eureka</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-zuul</artifactId>
        </dependency>
    </dependencies>

application.properties:

spring.application.name=service-zuul server.port=8061 ## 注冊服務中心的配置 eureka.client.service-url.defaultZone=http://localhost:8001/eureka/
#zuul.routes.<route>.path配置攔截請求的路徑 #zuul.routes.<route>.serviceId配置攔截之后路由到的指定的eureka服務 #這里除了能結合eureka服務,指定serviceId使用,還可以指定為一個url地址,比如zuul.routes.hello-service.path=http://localhost:8011
zuul.routes.user-service.path=/user-service/** zuul.routes.user-service.serviceId=USER-SERVICE # 注釋上面,改成user-service.url直接訪問百度 #zuul.routes.user-service.url=http://pay.weixin.qq.com/partner/public/home #zuul.routes.user-service.url=http://localhost:8011

zuul.routes.<route>.path與zuul.routes.<route>.serviceId分別配置zuul攔截請求的路徑,以及攔截之后路由到的指定的eureka服務

這里除了能結合eureka服務,指定serviceId使用,還可以指定為一個url地址,比如zuul.routes.hello-service.path=http://localhost:8011

application.yml:

 

##timeout config hystrix: command: default: execution: timeout: enabled: true isolation: thread: timeoutInMilliseconds: 60000 ribbon: ReadTimeout: 60000 ConnectTimeout: 60000 MaxAutoRetries: 0 MaxAutoRetriesNextServer: 1 eureka: enabled: true zuul: max: host: connections: 500 host: socket-timeout-millis: 60000 connect-timeout-millis: 60000

啟動類 Application.java:

package cn.demo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.netflix.eureka.EnableEurekaClient; import org.springframework.cloud.netflix.zuul.EnableZuulProxy; @SpringBootApplication @EnableZuulProxy @EnableEurekaClient public class ServiceZuulApplication { public static void main(String[] args) { SpringApplication.run(ServiceZuulApplication.class, args); } }

這里使用@EnableZuulProxy表示開啟zuul網關。

@EnableEurekaClient為了結合eureka,調用注冊在eureka中的服務,所以zuul這里也是作為eureka的客戶端。當然這里也可以使用@EnableDiscoveryClient,可以發現@EnableEurekaClient注解實現包含了@EnableDiscoveryClient,這里只用來調用eureka服務的話,兩個都可以使用,如果要使用其他的,比如consul,那就只能用@EnableDiscoveryClient了。

測試:

啟動eureka:8001, hello-service:8011,8012,zuul-service:8061

我們訪問:http://localhost:8061/user-service/hello?name=zuul

 

 

表示路由成功。而且重復訪問還可以發現默認使用了ribbon負載均衡。

接下來我們改成:

zuul.routes.user-service.path=/user-service/**
zuul.routes.user-service.url=http://localhost:8011

同樣的,訪問:http://localhost:8061/hello-service/hello?name=zuul

 

 

當然如果我們把連接改成百度網址,那么就直接跳轉到百度去了。  

既然在SpringCloud生態體系使用zuul,那么最好結合eureka ribbon使用。

三、網關過濾--(登錄,權限校驗)
如果在整個體系中,每個微服務都自己去管理用戶狀態,那顯然是不可取的,所以一般都是放在服務網關中的。那么我們就需要在服務網關中統一處理用戶登錄狀態,是否放行用戶請求。

這里我們來實現zuul網關過濾器,實現每個接口獲取參數中的access_token, 判斷是否合法,合法則放行,不合法則攔截並提示錯誤。

package cn.demo.filter; import com.netflix.zuul.ZuulFilter; import com.netflix.zuul.context.RequestContext; import org.apache.commons.lang.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; import javax.servlet.http.HttpServletRequest; /** * 服務請求過濾器 */ @Component public class AccessFilter extends ZuulFilter { private static Logger log = LoggerFactory.getLogger(AccessFilter.class); //路由之前
 @Override public String filterType() { return "pre"; } //過濾的順序
 @Override public int filterOrder() { return 0; } //這里可以寫邏輯判斷,是否要過濾,本文true,永遠過濾
 @Override public boolean shouldFilter() { return true; } @Override public Object run() { RequestContext requestContext = RequestContext.getCurrentContext(); HttpServletRequest request = requestContext.getRequest(); log.info("{} >>> {}", request.getMethod(), request.getRequestURL().toString()); String access_token = request.getParameter("access_token"); if(StringUtils.isBlank(access_token) || !"test".equals(access_token)){ // zuul過濾該請求
            requestContext.setSendZuulResponse(false);//表示不繼續轉發該請求。
            requestContext.setResponseStatusCode(401);//返回的狀態碼,這里為401
            requestContext.setResponseBody("token invide無效");//返回的內容,可以指定為一串json
            log.info("the request {} is fail, the token is invide無效", request.getRequestURL().toString()); } else { log.info("the request {} is ok", request.getRequestURL().toString()); } return null; } }

filterType:返回一個字符串代表過濾器的類型,在zuul中定義了四種不同生命周期的過濾器類型,具體如下:

pre:路由之前
routing:路由之時
post: 路由之后
error:發送錯誤調用
filterOrder:過濾的順序

shouldFilter:這里可以寫邏輯判斷,是否要過濾,本文true,永遠過濾。

run:過濾器的具體邏輯。可用很復雜,包括查sql,nosql去判斷該請求到底有沒有權限訪問。

上面指定filterType:pre表示在路由之前攔截請求,shouldFilter始終為true,表示永遠過濾,並執行run方法。

requestContext.setSendZuulResponse(false);表示不繼續轉發該請求。
requestContext.setResponseStatusCode(401);返回的狀態碼,這里為401
requestContext.setResponseBody("token is invalid");返回的內容,可以指定為一串json

測試
重新啟動 zuul-service:8061

訪問:http://localhost:8061/hello-service/hello?name=zuul

瀏覽器返回401

 

 

 console 控制台日志輸出:

 

 

 接下來我們訪問:http://localhost:8061/hello-service/hello?name=zuul&access_token=test

 

 

 表示校驗過濾,放行請求。

四、請求生命周期

從圖中,我們可以看到,當外部HTTP請求到達API網關服務的時候,首先它會進入第一個階段pre,在這里它會被pre類型的過濾器進行處理,該類型的過濾器主要目的是在進行請求路由之前做一些前置加工,比如請求的校驗等。在完成了pre類型的過濾器處理之后,請求進入第二個階段routing,也就是之前說的路由請求轉發階段,請求將會被routing類型過濾器處理,這里的具體處理內容就是將外部請求轉發到具體服務實例上去的過程,當服務實例將請求結果都返回之后,routing階段完成,請求進入第三個階段post,此時請求將會被post類型的過濾器進行處理,這些過濾器在處理的時候不僅可以獲取到請求信息,還能獲取到服務實例的返回信息,所以在post類型的過濾器中,我們可以對處理結果進行一些加工或轉換等內容。另外,還有一個特殊的階段error,該階段只有在上述三個階段中發生異常的時候才會觸發,但是它的最后流向還是post類型的過濾器,因為它需要通過post過濾器將最終結果返回給請求客戶端

 

 

 


免責聲明!

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



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