網(wǎng)上很多資源都把API網(wǎng)關(guān)凝化,是什么,能做什么解釋得非常清楚俊鱼,但是對于初學(xué)者來說我覺得是不夠友好的,Zuul就是SpringCloud微服務(wù)中的網(wǎng)關(guān)畅买。
對于初學(xué)者入門來說并闲,只需要知道Zuul就是當(dāng)服務(wù)增多之后,就要對API進(jìn)行一個統(tǒng)一的管理谷羞,某個類型的API就會調(diào)用某個類型的服務(wù)帝火,除此之外還能對請求過來的API進(jìn)行一個過濾。更進(jìn)一步才是Zuul其它作用湃缎,具體有哪些作用如圖所示:
本文重點講解的是路由轉(zhuǎn)發(fā) 和過濾器 犀填。
1 如何引入Zuul
一樣的,建立一個Zuul模塊嗓违,本例中沒有什么消費端九巡,所以就沒有采取之前建立空父模塊再建立具體子模塊的方法。然后往Zuul中的pom文件中添加依賴:
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-zuul</artifactId>
</dependency>
</dependencies>
到現(xiàn)在整個項目的目錄結(jié)構(gòu)如圖所示:
2 主啟動類和配置文件
因為不涉及服務(wù)消費等蹂季,只是做api的處理冕广,所以主啟動類還是比較簡單的
@SpringBootApplication
@EnableZuulProxy //開啟Zuul
@EnableEurekaClient
public class ZuulMain9401 {
public static void main(String[] args) {
SpringApplication.run(ZuulMain9401.class, args);
}
}
配置文件的話和常規(guī)的Eureka客戶端是一樣的
spring:
application:
name: zuul9401
server:
port: 9401
eureka:
client:
fetch-registry: true
register-with-eureka: true
service-url:
defaultZone: http://localhost:8001/eureka/
instance:
instance-id: zuul9401
3 路由轉(zhuǎn)發(fā)
路由轉(zhuǎn)發(fā)主要是通過配置文件來修改,往上面配置文件中增加內(nèi)容 偿洁,下面會講3種方式的轉(zhuǎn)發(fā)路由撒汉。
設(shè)置注冊Eureka的服務(wù)id
增加第一波配置文件,是在原有的配置文件上增加了以下內(nèi)容涕滋。
zuul:
routes:
user-a:
path: /api-a/**
serviceId: eureka-provide
user-a
是隨便定義 神凑,path
是外部訪問的路徑,serviceId是微服務(wù)配置文件的spring.application.name
的值何吝。
所以上面增加的配置文件整體意思是,當(dāng)外部訪問/api-a/
相關(guān)路徑時候鹃唯,會轉(zhuǎn)發(fā)給名字為eureka-provid
的服務(wù)提供服務(wù)爱榕。
開啟Eureka服務(wù)注冊中心EurekaServer8001
,服務(wù)提供者EurekaProvide7001/2/3
坡慌,API網(wǎng)關(guān)ZuulMain9401
:
接著訪問http://localhost:9401/api-a/eureka/provide黔酥,按照分析,應(yīng)該會被轉(zhuǎn)發(fā)到eureka-provide
服務(wù)里的eureka/provide路徑。
為了防止有點混跪者,貼一次第一個項目的代碼棵帽,詳情請看本系列的第一篇文章。
@SpringBootApplication
@RestController
@EnableEurekaClient
public class EurekaProvide7001 {
@Value("${server.port}")
int port;
@GetMapping("/eureka/provide")
public String getInfo() {
return "hello, i am eureka provide, the provide service. My port: " + port;
}
@GetMapping("/eureka/delayProvide")
public String delayGetInfo() throws InterruptedException {
Thread.sleep(3000);
return "hello, delay to do something";
}
public static void main(String[] args) {
SpringApplication.run(EurekaProvide7001.class, args);
}
}
可以看到能夠成功轉(zhuǎn)發(fā)路由
設(shè)置URL
增加第二波配置文件
zuul:
routes:
# user-a:
# path: /api-a/**
# serviceId: eureka-provide
user-b:
path: /api-b/**
url: http://localhost:7101/
其它如上渣玲,url
需要轉(zhuǎn)發(fā)到哪個服務(wù)
通過Edit Configurations
更改端口以及服務(wù)名以模擬新的服務(wù)逗概,具體操作同樣在第一篇文章中有清晰的gif圖。
其它服務(wù)不用關(guān)閉忘衍,繼續(xù)開啟剛新建的Provide7101
逾苫,重啟ZuulMain9401
服務(wù),訪問http://localhost:9401/api-b/eureka/provide枚钓,同樣能過夠看到成功轉(zhuǎn)發(fā)
設(shè)置非注冊Eureka的服務(wù)id
之前在學(xué)習(xí)Ribbon的時候也說過铅搓,我們可以通過Ribbon設(shè)置訪問一些沒有注冊進(jìn)Eureka的服務(wù),同樣在API網(wǎng)關(guān)也能通過配置文件設(shè)置Ribbon來達(dá)到一樣的效果搀捷。
增加第三波配置文件
zuul:
routes:
# user-a:
# path: /api-a/**
# serviceId: eureka-provide
# user-b:
# path: /api-b/**
# url: http://localhost:7101/
user-c:
path: /api-c/**
serviceId: provide-without-eureka
#一定需要這個才行
ribbon:
eureka:
enabled: false
provide-without-eureka:
ribbon:
ServerListClassName: com.netflix.loadbalancer.ConfigurationBasedServerList
listOfServers: localhost:7201, localhost:7202
ConnectTimeout: 1000
ReadTimeout: 3000
MaxTotalHttpConnections: 500
MaxConnectionsPerHost: 100
如果取消
user-a
的相關(guān)注釋星掰,此時訪問user-a是無法轉(zhuǎn)發(fā)的,會報500的錯誤嫩舟,我猜是因為設(shè)置了ribbon.eureka.enabled = false
的緣故氢烘。
serviceId同樣是微服務(wù)的名稱,然后對這個微服務(wù)設(shè)置至壤,所以是設(shè)置微服務(wù)名[provid-without-eureka].ribbon
威始,其它屬性都是相關(guān)屬性,最重要的同樣是listOfServers
像街,表示這個訪問這個服務(wù)名會在這些服務(wù)列表中進(jìn)行分配黎棠。
為了簡單還是用同一個服務(wù),用上面的方法修改配置文件即可镰绎,修改端口號7201脓斩,修改eureka.client.register.with.eureka = false
來模擬沒有注冊進(jìn)Eureka的服務(wù)。
接著再復(fù)制一份配置畴栖,其它都不遍随静,把端口號改成7202,總共是新建了端口為7201吗讶,7202的兩個服務(wù)燎猛。
其它服務(wù)不用關(guān),開啟ProvideWithoutEureka7201/2
服務(wù)照皆,重啟ZuulMain9401
服務(wù)重绷,此時所有的服務(wù)開啟如下
訪問http://localhost:9401/api-c/eureka/provide,服務(wù)還是一樣能進(jìn)行成功轉(zhuǎn)發(fā)
4 查看路由狀態(tài)
順便簡單說下查看路由狀態(tài)膜毁,首先還是需要增加配置文件昭卓,是一定要增加
management:
endpoints:
web:
exposure:
include: "*"
endpoint:
health:
show-details: ALWAYS
接著訪問http://localhost:9401/actuator/routes愤钾,正常情況會出現(xiàn)以下
如果想得到詳細(xì)信息,那么只需要訪問http://localhost:9401/actuator/routes/details
5 轉(zhuǎn)發(fā)路由時的Fallback
和Hystrix候醒,當(dāng)轉(zhuǎn)發(fā)路由發(fā)現(xiàn)服務(wù)不能夠正常提供服務(wù)的時候能颁,就可以fallback。
新建一個類MyFallbackProvider
實現(xiàn)FallbackProvider
接口
@Component
public class MyFallbackProvider implements FallbackProvider {
@Override
public String getRoute() {
//為所有的路由提供回退
return "*";
}
@Override
public ClientHttpResponse fallbackResponse(String route, Throwable throwable) {
return new ClientHttpResponse() {
@Override
public HttpStatus getStatusCode() throws IOException {
return HttpStatus.OK;
}
@Override
public int getRawStatusCode() throws IOException {
return 200;
}
@Override
public String getStatusText() throws IOException {
return "OK";
}
@Override
public void close() {
}
@Override
public InputStream getBody() throws IOException {
//回退后顯示出來
return new ByteArrayInputStream("something wrong, fallback now".getBytes());
}
@Override
public HttpHeaders getHeaders() {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
return headers;
}
};
}
}
現(xiàn)在來手動關(guān)閉ProvideWithoutEureka7201/2
服務(wù)模擬服務(wù)宕機倒淫,來看看是否能回退
6 過濾器
之所以Zuul能完成驗證伙菊、授權(quán)、靜態(tài)資源處理等昌简,就是得益于下面要講的過濾器占业,但是主要是講最基本的過濾,以后可能以后進(jìn)階的時候可能再深入講纯赎。
創(chuàng)建過濾器
首先創(chuàng)建filter包谦疾,然后創(chuàng)建一個過濾器類MyPreFilter
,需要實現(xiàn)ZuulFilter
接口
public class MyPreFilter extends ZuulFilter {
@Override
public String filterType() { //過濾器類型
return FilterConstants.PRE_TYPE; //請求前處理
}
@Override
public int filterOrder() { //過濾器順序犬金,越小越優(yōu)先
return 0;
}
@Override
public boolean shouldFilter() { //是否開啟過濾
return true;
}
@Override
public Object run() throws ZuulException { //執(zhí)行邏輯
RequestContext currentContext = RequestContext.getCurrentContext();
HttpServletRequest request = currentContext.getRequest();
System.out.println("[ PreFilter" + " ]" + String.format("send %s request to %s",request.getMethod(),request.getRequestURL()));
return null;
}
}
在FilterConstants
類中定義了一系列常量念恍,其中對于過濾器就是以下幾種
public static final String ERROR_TYPE = "error"; //出錯時執(zhí)行
public static final String POST_TYPE = "post"; //請求后請求
public static final String PRE_TYPE = "pre"; //請求前請求
public static final String ROUTE_TYPE = "route"; //處理目標(biāo)請求
同時再建立一個后置請求
public class MyPostFilter extends ZuulFilter {
@Override
public String filterType() {
return FilterConstants.POST_TYPE;
}
@Override
public int filterOrder() {
return 0;
}
@Override
public boolean shouldFilter() {
return true;
}
@Override
public Object run() throws ZuulException {
RequestContext currentContext = RequestContext.getCurrentContext();
HttpServletRequest request = currentContext.getRequest();
//這里把PreFilter改為PostFilter
System.out.println("[ PostFilter" + " ]" + String.format("send %s request to %s",request.getMethod(),request.getRequestURL()));
return null;
}
}
注入容器中
新建一個config包,在包下創(chuàng)建一個類ZuulConfiguration
@Configuration
public class ZuulConfiguration {
@Bean
public MyPreFilter getZuulPreFilterBean() {
return new MyPreFilter();
}
@Bean
public MyPostFilter getZuulPostFilterBean() {
return new MyPostFilter();
}
}
此時Zuul模塊的目錄結(jié)構(gòu)如下
注意晚顷,這里有個坑峰伙,就是當(dāng)開啟了過濾器后,會發(fā)現(xiàn)前一小節(jié)的fallback失效该默。
重啟ZuulMain9401
服務(wù)瞳氓,并且清空idea輸出控制臺
如果是接著上面一節(jié)的內(nèi)容,那么此時應(yīng)該是轉(zhuǎn)發(fā)的是非注冊進(jìn)Eureka服務(wù)的路由
訪問http://localhost:9401/api-c/eureka/provide栓袖,查看控制臺輸出
創(chuàng)作不易匣摘,如果對你有幫助,歡迎點贊裹刮,收藏和分享啦音榜!