Spring Cloud:第五章:Zuul服務(wù)網(wǎng)關(guān)
快速入門
定義user,order,pay服務(wù),定義zull服務(wù)網(wǎng)關(guān)服務(wù)都注冊(cè)到eureka服務(wù)上,
通過(guò)一下接口訪問(wèn)user,order,pay的服務(wù),
http://localhost:7070/pay/index
http://localhost:8080/user/index
http://localhost:9090/order/index
定義服務(wù)網(wǎng)關(guān)服務(wù)zuul,我們看看其相關(guān)配置,zuul-service加入依賴:
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zuul</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
</dependencies>
配置文件:
spring:
application:
name: zuul-service
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka
instance:
instance-id: ${spring.application.name}:${spring.cloud.client.ipAddress}:${spring.application.instance_id:${server.port}}
prefer-ip-address: true
server:
port: 6069
定義啟動(dòng)類:
package com.zhihao.miao;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.zuul.EnableZuulProxy;
//使用@EnableZuulProxy注解開啟zuul的api網(wǎng)關(guān)服務(wù)功能
@SpringBootApplication
@EnableZuulProxy
public class ZuulApplication {
public static void main(String[] args) {
SpringApplication.run(ZuulApplication.class,args);
}
}
然后可以通過(guò)服務(wù)網(wǎng)關(guān)進(jìn)行訪問(wèn)上面的三個(gè)服務(wù)接口,
http://localhost:6069/pay-service/pay/index
http://localhost:6069/user-service/user/index
http://localhost:6069/order-service/order/index
注意:
默認(rèn)的zuul結(jié)合eureka會(huì)將注冊(cè)到eureka的服務(wù)名作為訪問(wèn)的ContextPath。
也可以指定一些路由機(jī)制,application.yml中定義如下,
zuul:
routes:
user-service:
path: /users/**
serviceId: user-service
url符合/users/**規(guī)則的就被轉(zhuǎn)發(fā)到user-service實(shí)例中了。
http://localhost:6069/users/user/index
之前的通過(guò)服務(wù)實(shí)例名稱的也能訪問(wèn)
http://localhost:6069/user-service/user/index
也可以忽略所有的代理,只保留自己配置的即可。
zuul:
ignored-services: '*'
routes:
user-service: /users/**
此時(shí)只能訪問(wèn)user-service服務(wù),并且只能以u(píng)sers這種路由來(lái)訪問(wèn)。
不能訪問(wèn)
http://localhost:6069/pay-service/pay/index
http://localhost:6069/order-service/order/index
之前默認(rèn)的注冊(cè)到eureka上的服務(wù)(user-service)也不能訪問(wèn)。
不能訪問(wèn)
http://192.168.1.57:6069/user-service/user/index
可以訪問(wèn)
http://192.168.1.57:6069/users/user/index
以上的配置也可以這樣指定
zuul:
ignored-services: order-service,pay-service
routes:
user-service: /users/**
當(dāng)然此時(shí)除了http://192.168.1.57:6069/users/user/index能被代理訪問(wèn)到
http://192.168.1.57:6069/user-service/user/index也能訪問(wèn)了
請(qǐng)求過(guò)濾
在實(shí)現(xiàn)請(qǐng)求理由功能之后,我們的微服務(wù)應(yīng)用提供的接口就可以通過(guò)統(tǒng)一的api網(wǎng)關(guān)入口被客戶端訪問(wèn)到了。每個(gè)客戶端用戶請(qǐng)求服務(wù)應(yīng)用提供的接口時(shí),他們的訪問(wèn)權(quán)限往往都有一定的限制,系統(tǒng)并不會(huì)將所有的微服務(wù)接口對(duì)他們開放。然而,目前的服務(wù)路由并沒有限制權(quán)限這樣的功能,所有請(qǐng)求都會(huì)被毫無(wú)保留的轉(zhuǎn)發(fā)到具體的應(yīng)用并返回結(jié)果,為了實(shí)現(xiàn)對(duì)客戶端請(qǐng)求的安全校驗(yàn)和權(quán)限控制,最簡(jiǎn)單和粗暴的方法就是在每個(gè)微服務(wù)應(yīng)用都實(shí)現(xiàn)一套用于校驗(yàn)簽名和鑒別權(quán)限的過(guò)濾器或攔截器。這樣有個(gè)問(wèn)題就是功能實(shí)現(xiàn)太過(guò)冗余。比較好的做法就是將這些校驗(yàn)邏輯剝離出去,構(gòu)建一個(gè)獨(dú)立的鑒權(quán)服務(wù)。在完成剝離之后,直接在微服務(wù)應(yīng)用中通過(guò)調(diào)用鑒權(quán)系統(tǒng)服務(wù)來(lái)實(shí)現(xiàn)校驗(yàn),但是這樣僅僅只是解決了鑒權(quán)邏輯的分離,并沒有在本質(zhì)上將這部分不屬于冗余的邏輯從原有的微服務(wù)應(yīng)用中拆出去,冗余的攔截器或者過(guò)濾器依然會(huì)存在。
對(duì)于這樣的問(wèn)題,更好的做法是通過(guò)前置的網(wǎng)關(guān)服務(wù)來(lái)完成這些非業(yè)務(wù)性質(zhì)的校驗(yàn)。由于網(wǎng)關(guān)服務(wù)的加入,外部客戶端訪問(wèn)我們的系統(tǒng)已經(jīng)有了統(tǒng)一的入口,既然這些校驗(yàn)與具體的業(yè)務(wù)無(wú)關(guān),那何不在請(qǐng)求到達(dá)的時(shí)候就完成校驗(yàn)和過(guò)濾,微服務(wù)應(yīng)用端就可以去除各種復(fù)雜的過(guò)濾器和攔截器了,這使得微服務(wù)應(yīng)用接口的開發(fā)和測(cè)試復(fù)雜度也得到了相應(yīng)的降低。這就涉及到了zuul的另一個(gè)主要功能,請(qǐng)求過(guò)濾。
在zuul-service服務(wù)中定義一個(gè)過(guò)濾器,
public class AccessFilter extends ZuulFilter{
private Logger logger = LoggerFactory.getLogger(getClass());
//過(guò)濾器的類型,它決定過(guò)濾器在請(qǐng)求的哪個(gè)生命周期中執(zhí)行,這里定義為pre,代表會(huì)在請(qǐng)求被理由之前執(zhí)行。
@Override
public String filterType() {
return "pre";
}
//過(guò)濾器的執(zhí)行順序。當(dāng)請(qǐng)求在一個(gè)階段中存在多個(gè)過(guò)濾器時(shí),需要根據(jù)該方法返回的值來(lái)依次執(zhí)行
@Override
public int filterOrder() {
return 0;
}
//判斷該過(guò)濾器是否需要被執(zhí)行。這里我們直接返回了true,因此該過(guò)濾器對(duì)所有的請(qǐng)求都生效。實(shí)際運(yùn)行中我們可以利用該函數(shù)
//來(lái)指定過(guò)濾器的有效范圍。
@Override
public boolean shouldFilter() {
return true;
}
//過(guò)濾器的具體執(zhí)行邏輯。
@Override
public Object run() {
RequestContext ctx = RequestContext.getCurrentContext();
HttpServletRequest request = ctx.getRequest();
logger.info("send {} request to {}",request.getMethod(),request.getRequestURI().toString());
Object accessToken = request.getParameter("accessToken");
if(accessToken == null){
logger.warn("access token is empty");
ctx.setSendZuulResponse(false); //令zuul過(guò)濾該請(qǐng)求,不對(duì)其進(jìn)行路由
ctx.setResponseStatusCode(401); //設(shè)置返回的錯(cuò)誤碼
}
logger.info("access token ok");
return null;
}
}
ZuulFilter接口中定義四個(gè)方法:
filterType:過(guò)濾器的類型,它決定過(guò)濾器在請(qǐng)求的哪個(gè)生命周期中執(zhí)行,這里定義為pre,代表會(huì)在請(qǐng)求被理由之前執(zhí)行。
filterOrder:過(guò)濾器的執(zhí)行順序。當(dāng)請(qǐng)求在一個(gè)階段中存在多個(gè)過(guò)濾器時(shí),需要根據(jù)該方法返回的值來(lái)依次執(zhí)行。
shouldFilter:判斷該過(guò)濾器是否需要被執(zhí)行。這里我們直接返回了true,因此該過(guò)濾器對(duì)所有的請(qǐng)求都生效。實(shí)際運(yùn)行中我們可以利用該函數(shù)。
run:過(guò)濾器的具體執(zhí)行邏輯。
在將過(guò)濾器納入spring容器中,
@SpringBootApplication
@EnableZuulProxy
public class ZuulApplication {
public static void main(String[] args) {
SpringApplication.run(ZuulApplication.class,args);
}
@Bean
public AccessFilter accessFilter(){
return new AccessFilter();
}
}
訪問(wèn)服務(wù):
http://localhost:6069/users/user/index
狀態(tài)碼錯(cuò)誤為401,正確訪問(wèn)姿勢(shì)是
http://localhost:6069/users/user/index?accessToken=111
過(guò)濾器類型與請(qǐng)求生命周期
PRE:這種過(guò)濾器在請(qǐng)求被路由之前調(diào)用。我們可利用這種過(guò)濾器實(shí)現(xiàn)身份驗(yàn)證、在集群中選擇請(qǐng)求的微服務(wù)、記錄調(diào)試信息等。
ROUTING:這種過(guò)濾器將請(qǐng)求路由到微服務(wù)。這種過(guò)濾器用于構(gòu)建發(fā)送給微服務(wù)的請(qǐng)求,并使用Apache HttpClient或Netfilx Ribbon請(qǐng)求微服務(wù)。
POST:這種過(guò)濾器在路由到微服務(wù)以后執(zhí)行。這種過(guò)濾器可用來(lái)為響應(yīng)添加標(biāo)準(zhǔn)的HTTP Header、收集統(tǒng)計(jì)信息和指標(biāo)、將響應(yīng)從微服務(wù)發(fā)送給客戶端等。
ERROR:在其他階段發(fā)生錯(cuò)誤時(shí)執(zhí)行該過(guò)濾器。
除了默認(rèn)的過(guò)濾器類型,Zuul還允許我們創(chuàng)建自定義的過(guò)濾器類型。例如,我們可以定制一種STATIC類型的過(guò)濾器,直接在Zuul中生成響應(yīng),而不將請(qǐng)求轉(zhuǎn)發(fā)到后端的微服務(wù)。
總結(jié)
就目前掌握的api網(wǎng)關(guān)知識(shí),總結(jié)出四點(diǎn)如下
它作為系統(tǒng)的統(tǒng)一入口,屏蔽了系統(tǒng)內(nèi)部各個(gè)微服務(wù)的細(xì)節(jié)。
它可以與服務(wù)治理框架結(jié)合,實(shí)現(xiàn)自動(dòng)化的服務(wù)實(shí)例維護(hù)以及負(fù)載均衡的路由轉(zhuǎn)發(fā)。
它可以實(shí)現(xiàn)接口權(quán)限校驗(yàn)與微服務(wù)業(yè)務(wù)邏輯的解耦。
通過(guò)服務(wù)網(wǎng)關(guān)中的過(guò)濾器,在各個(gè)生命周期中去校驗(yàn)請(qǐng)求的內(nèi)容,將原本在對(duì)外服務(wù)層做的校驗(yàn)遷移,保證了微服務(wù)的無(wú)狀態(tài)性,同時(shí)降低了微服務(wù)的測(cè)試難度,讓服務(wù)本身更集中關(guān)注業(yè)務(wù)邏輯的處理。