spring cloud 使用Zuul 實(shí)現(xiàn)API網(wǎng)關(guān)服務(wù)問(wèn)題
通過(guò)前面幾次的分享,我們了解了微服務(wù)架構(gòu)的幾個(gè)核心設(shè)施,通過(guò)這些組件我們可以搭建簡(jiǎn)單的微服務(wù)架構(gòu)系統(tǒng)。比如通過(guò)Spring Cloud Eureka搭建高可用的服務(wù)注冊(cè)中心并實(shí)現(xiàn)服務(wù)的注冊(cè)和發(fā)現(xiàn);
通過(guò)Spring Cloud Ribbon或Feign進(jìn)行負(fù)載均衡;通過(guò)Spring Cloud Hystrix進(jìn)行服務(wù)容錯(cuò)保護(hù)以避免故障蔓延。微服務(wù)搭建好了之后我們肯定會(huì)提供給外部系統(tǒng)一些統(tǒng)一的RESTFul API服務(wù)接口進(jìn)行調(diào)用,
但是當(dāng)外部系統(tǒng)調(diào)用我們的RESTful API的時(shí)候,怎么確定它需要的功能具體是哪個(gè)服務(wù)提供的呢?這個(gè)就涉及到一個(gè)路由規(guī)則和服務(wù)實(shí)例列表的維護(hù)問(wèn)題。
這就引入了我們今天的主角--Spring Cloud Zuul,它是基于Netflix Zuul實(shí)現(xiàn)的API網(wǎng)關(guān)組件。它可以解決兩個(gè)大問(wèn)題:
- 就是我們上面提到的路由規(guī)則和服務(wù)實(shí)例的維護(hù)問(wèn)題
- 對(duì)于一些校驗(yàn)(比如登錄校驗(yàn)等)冗余問(wèn)題。 按照我們的習(xí)慣的做法,是在每個(gè)服務(wù)中都需要加入這些校驗(yàn),但是這樣會(huì)導(dǎo)致代碼冗余并且維護(hù)也比較麻煩,有了Spring Cloud Zuul這個(gè)網(wǎng)關(guān)服務(wù)之后,我們可以將這些共通的校驗(yàn)放到網(wǎng)關(guān)里面統(tǒng)一維護(hù)。
好,接下來(lái)我們就來(lái)看下怎么實(shí)現(xiàn)這個(gè)網(wǎng)關(guān)服務(wù)。
一、構(gòu)建網(wǎng)關(guān),配置路由
這里我們還是需要使用到前面的hello-service和feign-consumer服務(wù)。我們之前把feign-consumer作為服務(wù)消費(fèi)者,但是別忘了在eureka體系里面,每個(gè)服務(wù)既是服務(wù)提供者又是服務(wù)消費(fèi)者,所以feign-consumer也是一個(gè)服務(wù)提供者,并且http://localhost:9001/feign-consumer等接口就是它提供的服務(wù)。
接下來(lái)我們構(gòu)建一個(gè)網(wǎng)關(guān)服務(wù),代碼結(jié)構(gòu)如下:
代碼實(shí)現(xiàn)步驟:
新建maven工程api-gateway
修改POM文件
<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> <groupId>com.sam</groupId> <artifactId>api-gateway</artifactId> <version>0.0.1-SNAPSHOT</version> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.5.1.RELEASE</version> </parent> <properties> <javaVersion>1.8</javaVersion> </properties> <!-- 使用dependencyManagement進(jìn)行版本管理 --> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>Camden.SR6</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <dependencies> <!-- 引入zuul依賴 , 它依賴了spring-boot-starter-actuator/spring-boot-starter-hystrix/spring-boot-starter-ribbon--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-zuul</artifactId> </dependency> </dependencies> </project>
新建啟動(dòng)類
/** * @EnableZuulProxy 開(kāi)啟Zuul 的API網(wǎng)關(guān)服務(wù)功能 * */ @EnableZuulProxy @SpringCloudApplication public class GateWayApp { public static void main(String[] args) { SpringApplication.run(GateWayApp.class, args); } }
新建application.properties
server.port=5555 spring.application.name=api-gateway #增加路由規(guī)則的配置 #通過(guò)zuul.routes.<route>.path和zuul.routes.<route>.url進(jìn)行配置,<route>為路由的名字,可以任意指定,但是一組path和url的路由名要相同 #如下面的例子:所有滿足/api-a/** 規(guī)則的訪問(wèn)都會(huì)被路由轉(zhuǎn)發(fā)到//localhost:9001的地址 #也就是說(shuō),我們?cè)L問(wèn)http://localhost:5555/api-a/hello的時(shí)候,API網(wǎng)關(guān)服務(wù)就會(huì)將該請(qǐng)#求路由到 http://localhost:9001/hello提供的微服務(wù)接口上 zuul.routes.api-a.path=/api-a/** zuul.routes.api-a.url=http://localhost:9001 zuul.routes.api-b.path=/api-b/** zuul.routes.api-b.url=http://localhost:9090
測(cè)試,啟動(dòng)eureka、hello-service、feign-consumer以及本次新加的api-gateway服務(wù),然后訪問(wèn)http://localhost:5555/api-a/feign-consumer
成功訪問(wèn)到了feign-consumer的服務(wù)接口--feign-consonsumer。
以上步驟實(shí)現(xiàn)了傳統(tǒng)路由的配置,這種配置有個(gè)大的缺點(diǎn),就是需要手工在application.properties文件中進(jìn)行路由規(guī)則的配置,當(dāng)服務(wù)很多的時(shí)候,維護(hù)工作量就會(huì)很大。為了減小維護(hù)成本,還有另外一種路由--面向服務(wù)的路由。
二、面向服務(wù)的路由
Spring Cloud Zuul和Eureka進(jìn)行整合,我們可以讓路由的path不是映射具體的url,而是具體的某個(gè)服務(wù),而服務(wù)的url則交給Eureka服務(wù)發(fā)現(xiàn)機(jī)制自動(dòng)維護(hù),這類路由就是面向服務(wù)的路由。具體代碼配置如下:
修改POM文件,引入Eureka依賴
<!-- 引入eureka依賴 --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-eureka</artifactId> </dependency>
修改application.properties配置文件
server.port=5555 spring.application.name=api-gateway zuul.routes.api-a.path=/api-a/** #這里用serviceId代替url,用服務(wù)名代替ip+端口號(hào) zuul.routes.api-a.serviceId=hello-service eureka.client.service-url.defaultZone=http://localhost:1111/eureka
注意:zuul.routes.api-a.url=hello-service也能實(shí)現(xiàn)功能,但是它不能進(jìn)行正常的負(fù)載均衡和容錯(cuò)保護(hù)。
測(cè)試,訪問(wèn)http://localhost:5555/api-a/hello
訪問(wèn)成功。
三、服務(wù)路由的默認(rèn)規(guī)則
在面向服務(wù)的路由中,由于<route>名字是隨意起的,那么是不是可以這樣:
zuul.routes.hello-service.path=/hello-service/** zuul.routes.hello-service.serviceId=hello-service
<route>名字就是服務(wù)名,其實(shí)在實(shí)際的應(yīng)用中,我們往往就是這樣命名的。如果有這樣的規(guī)則的話,那Zuul就可以幫我們默認(rèn)實(shí)現(xiàn)這樣的功能,進(jìn)一步省去了配置的麻煩。
我們來(lái)做個(gè)實(shí)驗(yàn),將配置文件改為:
server.port=5555 spring.application.name=api-gateway eureka.client.service-url.defaultZone=http://localhost:1111/eureka
然后頁(yè)面訪問(wèn)驗(yàn)證
訪問(wèn)成功。
但是由于默認(rèn)情況下,Eureka上的服務(wù)都會(huì)被Zuul創(chuàng)建默認(rèn)的映射關(guān)系來(lái)進(jìn)行路由,使得我們不想對(duì)外開(kāi)放的服務(wù)也被外部訪問(wèn)到,這個(gè)時(shí)候可以通過(guò)配置zuul.ignored-services來(lái)進(jìn)行配置不需要自動(dòng)創(chuàng)建路由的規(guī)則。當(dāng)zuul.ignored-services=*的時(shí)候,所有的服務(wù)都不會(huì)自動(dòng)創(chuàng)建路由規(guī)則,這個(gè)時(shí)候需要通過(guò)前面的配置進(jìn)行相關(guān)路由配置了。
================華麗的分割線===================
前面說(shuō)了那么多都是圍繞一個(gè)問(wèn)題展開(kāi)的:路由規(guī)則和服務(wù)實(shí)例的維護(hù)問(wèn)題,那么怎么解決第二個(gè)問(wèn)題(校驗(yàn)冗余問(wèn)題)呢?
四、請(qǐng)求過(guò)濾
為了在API網(wǎng)關(guān)中實(shí)現(xiàn)對(duì)客戶端請(qǐng)求的校驗(yàn),我們可以通過(guò)過(guò)濾器來(lái)實(shí)現(xiàn)對(duì)請(qǐng)求的攔截和過(guò)濾,實(shí)現(xiàn)方法比較簡(jiǎn)單,只需要繼承ZuulFilter抽象類并實(shí)現(xiàn)其四個(gè)方法就行了。
修改api-gateway:
新增過(guò)濾器類
/** * 繼承ZuulFilter,并且實(shí)現(xiàn)其4個(gè)接口 * * 用來(lái)進(jìn)行請(qǐng)求過(guò)濾 * */ public class AccessFilter extends ZuulFilter { Logger logger = LoggerFactory.getLogger(AccessFilter.class); /* * shouldFilter 判斷該過(guò)濾器是否需要被執(zhí)行 * * 這里直接返回true,表示該過(guò)濾器對(duì)所有請(qǐng)求都會(huì)生效。 * 實(shí)際運(yùn)用中我們可以利用該函數(shù)指定過(guò)濾器的有效范圍 */ @Override public boolean shouldFilter() { return true; } /* * 過(guò)濾器的具體邏輯 * * 這里我們通過(guò)ctx.setSendZuulResponse(false)讓zuul過(guò)來(lái)請(qǐng)求,不對(duì)其進(jìn)行路由 * 然后通過(guò)ctx.setResponseStatusCode(401)設(shè)置了返回的錯(cuò)誤碼 * */ @Override public Object run() { RequestContext context = RequestContext.getCurrentContext(); HttpServletRequest request = context.getRequest(); Object accessToken = request.getParameter("accessToken"); logger.info("send {} request to {}", request.getMethod(),request.getRequestURL().toString()); if(accessToken == null) { context.setSendZuulResponse(false); context.setResponseStatusCode(401); } return null; } /* filterType 返回過(guò)濾器類型 * 他決定了過(guò)濾器在請(qǐng)求的哪個(gè)生命周期中執(zhí)行。這里定義為pre,代表會(huì)在請(qǐng)求被路由前執(zhí)行。 * * pre:請(qǐng)求執(zhí)行之前filter * route: 處理請(qǐng)求,進(jìn)行路由 * post: 請(qǐng)求處理完成后執(zhí)行的filter * error:出現(xiàn)錯(cuò)誤時(shí)執(zhí)行的filter */ @Override public String filterType() { return "pre"; } /* * filterOrder 返回過(guò)濾器的執(zhí)行順序 * * 當(dāng)請(qǐng)求在一個(gè)階段有多個(gè)過(guò)濾器是,需要根據(jù)該方法的返回值來(lái)一次執(zhí)行 * */ @Override public int filterOrder() { return 0; } }
修改啟動(dòng)類
/** * @EnableZuulProxy 開(kāi)啟Zuul 的API網(wǎng)關(guān)服務(wù)功能 * */ @EnableZuulProxy @SpringCloudApplication public class GateWayApp { //追加bean的是實(shí)現(xiàn) @Bean public AccessFilter accessFilter() { return new AccessFilter(); } public static void main(String[] args) { SpringApplication.run(GateWayApp.class, args); } }
測(cè)試
)訪問(wèn)http://localhost:5555/hello-service/hello,訪問(wèn)失敗
)訪問(wèn)http://localhost:5555/hello-service/hello?accessToken=token,正常訪問(wèn)
修改后的代碼結(jié)構(gòu):
五、拓展延伸
其實(shí)路由功能在真正運(yùn)行時(shí),他的路由映射和請(qǐng)求轉(zhuǎn)發(fā)都是由幾個(gè)不同的過(guò)濾器完成的。
路由映射主要通過(guò)pre類型的過(guò)濾器完成,他將請(qǐng)求路徑與配置的路由規(guī)則進(jìn)行匹配,找到需要轉(zhuǎn)發(fā)的目標(biāo)地址。
而請(qǐng)求轉(zhuǎn)發(fā)的部分則是由route類型的過(guò)濾器完成的,對(duì)pre類型過(guò)濾器獲取的路由地址進(jìn)行轉(zhuǎn)發(fā)。
所以,過(guò)濾器可以說(shuō)是Zuul實(shí)現(xiàn)API網(wǎng)關(guān)功能最為核心的部件,每一個(gè)進(jìn)入Zuul的HTTP請(qǐng)求都會(huì)經(jīng)過(guò)一系列的過(guò)濾器處理鏈得到請(qǐng)求響應(yīng)并返回給客戶端。
總結(jié)
以上所述是小編給大家介紹的spring cloud 使用Zuul 實(shí)現(xiàn)API網(wǎng)關(guān)服務(wù)問(wèn)題,希望對(duì)大家有所幫助,如果大家有任何疑問(wèn)請(qǐng)給我留言,小編會(huì)及時(shí)回復(fù)大家的。在此也非常感謝大家對(duì)腳本之家網(wǎng)站的支持!
相關(guān)文章
JAVA 16位ID生成工具類含16位不重復(fù)的隨機(jī)數(shù)數(shù)字+大小寫
這篇文章主要介紹了JAVA 16位ID生成工具類含16位不重復(fù)的隨機(jī)數(shù)數(shù)字+大小寫,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-02-02MybatisPlus lambdaQueryWrapper中常用方法的使用
本文主要介紹了MybatisPlus lambdaQueryWrapper中常用方法的使用,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2023-07-07Java之Spring簡(jiǎn)單的讀取和存儲(chǔ)對(duì)象
這篇文章主要介紹了Spring的讀取和存儲(chǔ)對(duì)象,獲取 bean 對(duì)象也叫做對(duì)象裝配,是把對(duì)象取出來(lái)放到某個(gè)類中,有時(shí)候也叫對(duì)象注?,想進(jìn)一步了解的同學(xué)可以參考本文2023-04-04SpringBoot使用MockMvc測(cè)試get和post接口的示例代碼
Spring Boot MockMvc是一個(gè)用于單元測(cè)試的模塊,它是Spring框架的一部分,專注于簡(jiǎn)化Web應(yīng)用程序的測(cè)試,MockMvc主要用來(lái)模擬一個(gè)完整的HTTP請(qǐng)求-響應(yīng)生命周期,本文給大家介紹了SpringBoot使用MockMvc測(cè)試get和post接口,需要的朋友可以參考下2024-06-06Java中l(wèi)ist集合的clear方法及空字符串的區(qū)別
這篇文章主要介紹了Java中l(wèi)ist集合的clear方法及空字符串的區(qū)別,在使用list?結(jié)合的時(shí)候習(xí)慣了?list=null?;在創(chuàng)建這樣的方式,但是發(fā)現(xiàn)使用list的clear?方法很不錯(cuò),尤其是有大量循環(huán)的時(shí)候<BR>list.clear()與list?=?null?區(qū)別,需要的朋友可以參考下2023-08-08