在微服務架構中,一般會有多個服務提供者。設想一個電商系統,可能會有商品、訂單、支付、用戶等多個類型的服務,而每一個類型的服務數量也會隨着整個系統體量的增大也會隨之增加和變動。做爲UI端,在展現頁面時可能須要從多個微服務中聚合數據,並且服務的劃分位置結構可能會有所改變。網關就能夠對外暴露聚合API,屏蔽內部微服務的微小變更,保持整個系統的穩定性。java
Zuul是Spring Cloud全家桶中的微服務API網關。git
全部從設備或網站來的請求都會通過Zuul到達後端的Netflix應用程序。做爲一個邊界性質的應用程序,Zuul提供了動態路由、監控、彈性負載和安全功能。Zuul底層利用各類filter實現以下功能:web
在Spring Cloud微服務系統中,一種常見的負載均衡方式是,客戶端的請求首先通過負載均衡(Ngnix),再到達服務網關(zuul集羣),而後再到具體的服務。服務統一註冊到高可用的服務註冊中心集羣(eureka, consul),服務的全部的配置文件由配置服務管理,配置服務的配置文件放在git倉庫,方便開發人員隨時改配置。
————————————————spring
2、動態路由sql
pom.xml:apache
<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:json
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使用。
3、網關過濾--(登陸,權限校驗)
若是在整個體系中,每一個微服務都本身去管理用戶狀態,那顯然是不可取的,因此通常都是放在服務網關中的。那麼咱們就須要在服務網關中統一處理用戶登陸狀態,是否放行用戶請求。
這裏咱們來實現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過濾器將最終結果返回給請求客戶端