快速入門
新建zuul-gateway工程藻丢,引入pom依賴
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-zuul</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
引導(dǎo)類標(biāo)注
@EnableZuulProxy
@EnableDiscoveryClient
@SpringBootApplication
public class ZuulGatewayApplication {
public static void main(String[] args) {
SpringApplication.run(ZuulGatewayApplication.class, args);
}
}
基于服務(wù)發(fā)現(xiàn)配置路由規(guī)則
zuul.routes.feign.path=/feign/**
zuul.routes.feign.service-id=feign-consumer
zuul.routes.hello.path=/hello/**
zuul.routes.hello.service-id=hello-service
請求過濾
實(shí)現(xiàn)ZuulFilter接口剪撬,如果請求內(nèi)容中沒有token,則返回401
@Slf4j
public class AccessFilter extends ZuulFilter {
@Override
public String filterType() { //過濾器類型悠反,這里為pre残黑,標(biāo)識會在請求被路由之前執(zhí)行
return "pre";
}
@Override
public int filterOrder() { //過濾器的執(zhí)行順序
return 0;
}
@Override
public boolean shouldFilter() { //過濾是否會被執(zhí)行,在應(yīng)用中可以根據(jù)業(yè)務(wù)需求自定義實(shí)現(xiàn)
return true;
}
@Override
public Object run() throws ZuulException { //過濾器具體邏輯
RequestContext ctx = RequestContext.getCurrentContext();
HttpServletRequest request = ctx.getRequest();
log.info("filter..........");
Object t = request.getParameter("token");
if(Objects.isNull(t)) {
ctx.setSendZuulResponse(false);
ctx.setResponseStatusCode(HttpStatus.UNAUTHORIZED.value());
}
return null;
}
}
注入filter實(shí)例
@Bean
ZuulFilter filter() {
return new AccessFilter();
}
路由詳解
不依賴于服務(wù)治理斋否,單實(shí)例配置梨水,通過參數(shù)對配置
zuul.routes.<route>.path=/feign/**
zuul.routes.<route>.service-id=http://localhost:8080
多實(shí)例配置
zuul.routes.<route>.path=/feign/**
zuul.routes.<route>.service-id=MyService
#默認(rèn)依賴服務(wù)發(fā)現(xiàn)機(jī)制獲取服務(wù)名對應(yīng)的實(shí)例清單,沒有整合eureka茵臭,所以需要設(shè)置為false
ribbon.eureka.enabled=false
MyService.ribbon.listOfServers=http://localhost:8080,http://localhost:8081
服務(wù)路由的默認(rèn)規(guī)則
在實(shí)際應(yīng)用中疫诽,大部分的路由配置幾乎都會采用服務(wù)名作為請求前綴,對于這種有規(guī)則的配置內(nèi)容旦委,在引入eureka后奇徒,zuul會自動創(chuàng)建一個默認(rèn)路由規(guī)則,這個默認(rèn)規(guī)則的path會使用serviceId作為請求前綴
在配置文件中注釋掉自定義路由規(guī)則
#zuul.routes.feign.path=/feign/**
#zuul.routes.feign.service-id=feign-consumer
直接使用serviceId作為請求前綴發(fā)起請求缨硝,也可以實(shí)現(xiàn)正常訪問
http://localhost:8084/<serviceId>/ask1?name=sdfsdf&token=sdfsdf
自定義路由規(guī)則
可以使用正則表達(dá)式自定義路由規(guī)則
@Bean
PatternServiceRouteMapper patternServiceRouteMapper() {
return new PatternServiceRouteMapper("","");
}
- 參數(shù)一檢查服務(wù)名稱是否匹配正則表達(dá)式
- 參數(shù)二根據(jù)服務(wù)名定義的內(nèi)容轉(zhuǎn)換出的路徑表達(dá)式
路徑匹配
采用ant表達(dá)式風(fēng)格逼龟,即?//*,這里需要注意的是,路由的保存是有序的追葡,而通過properties文件加載是無序的,可以通過yaml文件來實(shí)現(xiàn)有序的路由規(guī)則
zuul:
routes:
feign:
path: /feign/**
service-id: feign-consumer
hello:
path: /hello/**
service-id: hello-service
忽略表達(dá)式
通過ignored-patterns可以設(shè)置不希望被網(wǎng)關(guān)路由的url表達(dá)式
zuul.ignored-patterns=/**/hello/*
路由前綴
為網(wǎng)關(guān)路由規(guī)則增加/api前綴
zuul.prefix=/api
本地跳轉(zhuǎn)
zuul支持forward形式的服務(wù)端跳轉(zhuǎn)配置奕短,下面的配置實(shí)現(xiàn)了hello跳轉(zhuǎn)到網(wǎng)關(guān)/local/hello的功能
zuul.routes.hello.path=/hello/**
zuul.routes.hello.service-id=forward:/local
Cookie和頭信息
默認(rèn)情況下宜肉,zuul在請求路由時,會過濾掉http請求頭中的一些敏感信息翎碑,防止被傳遞到下游服務(wù)器谬返,如Cookie,Authorization,Set-Cookie日杈,但是如果使用了spring security遣铝,shrio等安全框架時,需要使用這些敏感信息莉擒,可以通過以下兩種方式進(jìn)行配置
全局配置
zuul.sensitive-headers=
指定路由配置
設(shè)置敏感頭為空
zuul.routes.<route>.sensitive-headers=
或開啟自定義敏感頭
zuul.routes.<route>.custom-sensitive-headers=true
在使用安全框架時酿炸,登錄成功后,會返回302涨冀,location指向的是具體的服務(wù)地址填硕,而不是網(wǎng)關(guān),可以設(shè)置
zuul.add-host-header=true
這樣,使得網(wǎng)關(guān)在進(jìn)行路由轉(zhuǎn)發(fā)錢設(shè)置Host頭信息扁眯,標(biāo)識最初的服務(wù)器請求地址
Hystrix和ribbon支持
在spring-cloud-starter-netflix-zuul中已經(jīng)引入了hystrix和ribbon支持,可以設(shè)置ribbon及hystrix的相關(guān)配置
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
<version>2.2.2.RELEASE</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
<version>2.2.2.RELEASE</version>
<scope>compile</scope>
</dependency>
過濾器詳解
zuul中過濾器是實(shí)現(xiàn)請求轉(zhuǎn)發(fā)最關(guān)鍵的核心部件壮莹,每一個進(jìn)入zuul的http請求都會經(jīng)過一些列的過濾器處理鏈得到請求響應(yīng)并返回給客戶端
zuul中實(shí)現(xiàn)的過濾器包含四個基本特征,過濾類型姻檀,執(zhí)行順序命满,執(zhí)行條件,具體操作绣版,實(shí)際上就是ZuulFilter中定義的四個抽象方法
public class AccessFilter extends ZuulFilter {
@Override
public String filterType() { //過濾器類型胶台,這里為pre,標(biāo)識會在請求被路由之前執(zhí)行
return "pre";
}
@Override
public int filterOrder() { //過濾器的執(zhí)行順序
return 0;
}
@Override
public boolean shouldFilter() { //過濾是否會被執(zhí)行僵娃,在應(yīng)用中可以根據(jù)業(yè)務(wù)需求自定義實(shí)現(xiàn)
return true;
}
@Override
public Object run() throws ZuulException { //過濾器具體邏輯
}
}
zuul中概作,過濾器類型的執(zhí)行順序為
pre(customer) -> routing -> post -> error
其中error只有在前序三種過濾器發(fā)生錯誤時才會執(zhí)行
核心過濾器
zuul中定義了一批核心過濾器位于org.springframework.cloud.netflix.zuul.filters包下
pre階段
- ServletDetectionFilter:用來檢查是通過spring的dispatcherServlet處理運(yùn)行的還是通過ZuulServlet來處理運(yùn)行的,它的檢測結(jié)果會保存到當(dāng)前請求上下文的isDispaterServletRequest參數(shù)中
- Servlet30WrapperFilter:執(zhí)行順序-2默怨,將原始的httpservletequest包裝成servelt30requestwrapper對象
- FormBodyWrapperFilter:執(zhí)行順序-1讯榕,僅對兩類請求生效,application/x-www-form-urlencoded和multipart/form-data匙睹,將該兩類請求包裝成FormBodyRequestWrapper
- DebugFilter:執(zhí)行順序1愚屁,根據(jù)zuul.debug.request和請求中的debug參數(shù)決定是否執(zhí)行過濾器中操作。具體操作則是把debugRouting和debugRequest參數(shù)設(shè)置為true痕檬,在同一個請求的不同生命周期中都可以訪問這兩個參數(shù)霎槐,當(dāng)環(huán)境出現(xiàn)問題時,可以通過參數(shù)請求方式激活debug分析信息
- PreDecorationFilter:執(zhí)行順序5梦谜,pre階段最后執(zhí)行的過濾器丘跌,為當(dāng)前請求設(shè)置做預(yù)處理
route過濾器
- RibbonRoutingFilter:執(zhí)行順序10,只對配置serviceId路由規(guī)則的請求生效唁桩,通過ribbon和hystrix來向服務(wù)實(shí)例發(fā)起請求
- SimpleHostRoutingFilter:執(zhí)行順序100闭树,只對配置url路由規(guī)則的請求生效
- SendForwardFilter:執(zhí)行順序500,處理路由配置中forward本地跳轉(zhuǎn)
post過濾器
- SendErrorFilter:執(zhí)行順序0荒澡,將請求上下文中的錯誤信息來組成一個forward到api網(wǎng)關(guān)/error錯誤端點(diǎn)產(chǎn)生錯誤響應(yīng)
- SendResponseFilter:執(zhí)行順序1000报辱,利用請求上下文組織需要發(fā)送會客戶端的響應(yīng)內(nèi)容
異常處理
使用errorfilter自定義異常處理,流轉(zhuǎn)到SendErrorFilter
@Slf4j
public class ErrorFilter extends ZuulFilter {
@Override
public String filterType() { //過濾器類型单山,這里為pre碍现,標(biāo)識會在請求被路由之前執(zhí)行
return "error";
}
@Override
public int filterOrder() { //過濾器的執(zhí)行順序
return 10;
}
@Override
public boolean shouldFilter() { //過濾是否會被執(zhí)行,在應(yīng)用中可以根據(jù)業(yè)務(wù)需求自定義實(shí)現(xiàn)
return true;
}
@Override
public Object run() throws ZuulException { //過濾器具體邏輯
RequestContext ctx = RequestContext.getCurrentContext();
Throwable t = ctx.getThrowable();
log.info("filter..........");
ctx.set("error.status_code",HttpStatus.BAD_GATEWAY.value());
ctx.set("error.exception",t.getCause());
return null;
}
}