歡迎來到菜鳥SpringCloud實戰入門系列(SpringCloudForNoob),該系列經過層層遞進的實戰視角,來一步步學習和理解SpringCloud。html
本系列適合有必定Java以及SpringBoot基礎的同窗閱讀。java
每篇文章末尾都附有本文對應的Github源代碼,方便同窗調試。git
Github倉庫地址:github
你能夠經過如下兩種途徑查看菜鳥SpringCloud實戰入門系列:spring
前文回顧:apache
[菜鳥SpringCloud實戰入門]第九章:服務網關Zuul體驗後端
外部的應用如何來訪問內部各類各樣的微服務呢?在微服務架構中,後端服務每每不直接開放給調用端,而是經過一個API網關根據請求的url,路由到相應的服務。當添加API網關後,在第三方調用端和服務提供方之間就建立了一面牆,這面牆直接與調用方通訊進行權限控制,後將請求均衡分發給後臺服務端。安全
這個API網關即是Spring Cloud Zuulbash
Zuul提供動態路由,監控,彈性,安全等的邊緣服務。Zuul是Netflix出品的一個基於JVM路由和服務端的負載均衡器。
Zuul與Ribbon以及Eureka配合:
Zuul、Ribbon以及Eureka結合能夠實現智能路由和負載均衡的功能;網關將全部服務的API接口統一聚合,統一對外暴露。外界調用API接口時,不須要知道微服務系統中各服務相互調用的複雜性,保護了內部微服務單元的API接口;網關能夠作用戶身份認證和權限認證,防止非法請求操做API接口;網關能夠實現監控功能,實時日誌輸出,對請求進行記錄;網關能夠實現流量監控,在高流量的狀況下,對服務降級;API接口從內部服務分離出來,方便作測試。
Zuul經過Servlet來實現,經過自定義的ZuulServlet來對請求進行控制。核心是一系列過濾器,能夠在Http請求的發起和響應返回期間執行一系列過濾器。Zuul採起了動態讀取、編譯和運行這些過濾器。過濾器之間不能直接通訊,而是經過RequestContext對象來共享數據,每一個請求都會建立一個RequestContext對象。
Zuul生命週期以下圖。
Zuul大部分功能都是經過過濾器來實現的,這些過濾器類型對應於請求的典型生命週期。
Zuul中默認實現的Filter:
圖片來自:
www.ityouknow.com/springcloud…
在yml中能夠禁用指定的Filter:
zuul:
FormBodyWrapperFilter:
pre:
disable: true
複製代碼
本章須要用到以前章節的的模塊:
咱們新建子模塊(新建模塊若有疑惑請看教程第一章),名爲zuul
修改pom.xml:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<artifactId>zuul</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>zuul</name>
<packaging>jar</packaging>
<description>Demo project for Spring Boot</description>
<!--父工程的依賴-->
<parent>
<groupId>com.pricemonitor</groupId>
<artifactId>springcloud</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-zuul</artifactId>
</dependency>
</dependencies>
</project>
複製代碼
在主類中加上@EnableZuulProxy:
@SpringBootApplication
@EnableZuulProxy
public class ZuulApplication {
public static void main(String[] args) {
SpringApplication.run(ZuulApplication.class, args);
}
}
複製代碼
修改yml文件:
spring:
application:
name: gateway-service-zuul
server:
port: 8773
eureka:
client:
serviceUrl:
defaultZone: http://localhost:8761/eureka/
zuul:
routes:
hello:
path: /producer/**
serviceId: service-hi
複製代碼
前後運行eureka,eureka-hi和zuul
使用官網來訪問8763上eureka-hi提供的服務:
http://localhost:8773/producer/hi/rude3knife
獲得:
說明網關正確轉發了請求
爲了更好的模擬服務集羣,咱們再新開一個eureka-hi,端口號爲8764,只須要修改yml的配置文件,而後從新運行便可。
你會看到來自8763和8764隨機提供的服務:
Zuul其實已經代理全部註冊到Eureka Server的微服務,而且Zuul的路由規則以下:http://ZUUL_HOST:ZUUL_PORT/
微服務在Eureka上的serviceId/**會被轉發到serviceId對應的微服務。
咱們試驗一下, http://localhost:8773/service-hi/hi/rude3knife :
最上面的簡介中咱們提到,Zuul中有默認實現的Filter,固然也能夠實現本身的自定義Filter,用來完成一些鑑權、流量轉發、請求統計等工做。咱們來實現一個自定義filter。
咱們假設有這樣一個場景,由於服務網關應對的是外部的全部請求,爲了不產生安全隱患,咱們須要對請求作必定的限制,好比請求中含有Token便讓請求繼續往下走,若是請求不帶Token就直接返回並給出提示。
新建TokenFilter類:
package com.pricemonitor.zuul;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.servlet.http.HttpServletRequest;
public class TokenFilter extends ZuulFilter {
private final Logger logger = LoggerFactory.getLogger(TokenFilter.class);
@Override
public String filterType() {
// 能夠在請求被路由以前調用
return "pre";
}
@Override
public int filterOrder() {
// filter執行順序,經過數字指定 ,優先級爲0,數字越大,優先級越低
return 0;
}
@Override
public boolean shouldFilter() {
// 是否執行該過濾器,此處爲true,說明須要過濾
return true;
}
@Override
public Object run() {
RequestContext ctx = RequestContext.getCurrentContext();
HttpServletRequest request = ctx.getRequest();
logger.info("--->>> TokenFilter {},{}", request.getMethod(), request.getRequestURL().toString());
String token = request.getParameter("token");
if (StringUtils.isNotBlank(token)) {
ctx.setSendZuulResponse(true); //對請求進行路由
ctx.setResponseStatusCode(200);
ctx.set("isSuccess", true);
return null;
} else {
ctx.setSendZuulResponse(false); //不對其進行路由
ctx.setResponseStatusCode(400);
ctx.setResponseBody("token is empty");
ctx.set("isSuccess", false);
return null;
}
}
}
複製代碼
隨後,在啓動類注入這個bean
@SpringBootApplication
@EnableZuulProxy
public class ZuulApplication {
public static void main(String[] args) {
SpringApplication.run(ZuulApplication.class, args);
}
@Bean
public TokenFilter tokenFilter() {
return new TokenFilter();
}
}
複製代碼
運行eureka註冊中心,eureka-hi服務提供者,zuul網關三個子模塊。
不帶token訪問:
http://localhost:8773/producer/hi/rude3knife
帶着token訪問:
http://localhost:8773/producer/hi/rude3knife?token=dsa
經過上面這例子咱們能夠看出,咱們可使用「PRE」類型的Filter作不少的驗證工做,在實際使用中咱們能夠結合shiro、oauth2.0等技術去作鑑權、驗證。
以前咱們學過hystrix的熔斷,主要針對的是內部服務互相調用的熔斷,而zuul的熔斷則主要服務於外部接口調用。
注意:Zuul 目前只支持服務級別的熔斷,不支持具體到某個URL進行熔斷。
實現:主要是經過繼承FallbackProvider(以前繼承的是ZuulFallbackProvider:默認有兩個方法,一個用來指明熔斷攔截哪一個服務,一個定製返回內容。)
能夠配合Spring Retry,來對暫時不可用的服務進行重試。
注意: 開啓重試在某些狀況下是有問題的,好比當壓力過大,一個實例中止響應時,路由將流量轉到另外一個實例,頗有可能致使最終全部的實例全被壓垮。說到底,斷路器的其中一個做用就是防止故障或者壓力擴散。用了retry,斷路器就只有在該服務的全部實例都沒法運做的狀況下才能起做用。這種時候,斷路器的形式更像是提供一種友好的錯誤信息,或者僞裝服務正常運行的假象給使用者。
不用retry,僅使用負載均衡和熔斷,就必須考慮到是否可以接受單個服務實例關閉和eureka刷新服務列表之間帶來的短期的熔斷。若是能夠接受,就無需使用retry。
www.ityouknow.com/springcloud…
www.ityouknow.com/springcloud…
菜鳥SpringCloud實戰入門專欄全導航:經過如下兩種途徑查看
我是蠻三刀把刀,後端開發。主要關注後端開發,數據安全,爬蟲等方向。微信:yangzd1102
Github:@qqxx6661
我的博客:
若是文章對你有幫助,不妨收藏起來並轉發給您的朋友們~