Spring Cloud Gateway組件的三種使用方式實(shí)例詳解
??前言
Spring Cloud Gateway是 Spring 官方基于 Spring5.0 、 SpringBoot2.0 和 Project Reactor 等技術(shù)開發(fā)的網(wǎng)關(guān)旨在為微服務(wù)框架提供一種簡(jiǎn)單而有效的統(tǒng)一的API 路由管理方式,統(tǒng)一訪問(wèn)接口。
Spring Cloud Gateway作為 Spring Cloud 生態(tài)體系中的網(wǎng)關(guān),目標(biāo)是替代 Netflix 的 Zuul ,其不僅提供統(tǒng) 一的路由方式,并且基于Filter 鏈的方式提供了網(wǎng)關(guān)基本的功能,例如:安全、監(jiān)控 / 埋點(diǎn)和限流等等,而且它是基于Netty 的響應(yīng)式開發(fā)模式。
?創(chuàng)建模塊
我們的Gatewa既然是負(fù)責(zé)網(wǎng)絡(luò)路由的,那么它首先肯定是個(gè)模塊,所以我們的第一步就是需要?jiǎng)?chuàng)建一個(gè)模塊。
修改該模塊的pom.xml文件,讓其繼承父模塊及導(dǎo)入相應(yīng)的依賴
<parent> <groupId>org.example</groupId> <artifactId>Cloud</artifactId> <version>0.0.1-SNAPSHOT</version> </parent> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-webflux</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-gateway</artifactId> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.35</version> </dependency> </dependencies>
因?yàn)樗械姆?wù)都要接入網(wǎng)關(guān),而服務(wù)都在注冊(cè)中心有記錄,所以我們網(wǎng)關(guān)要與Nacos關(guān)聯(lián),首先需要在配置文件中與Nacos做配置并且在啟動(dòng)類開啟Nacos
@EnableDiscoveryClient(啟動(dòng)類打上該注解)
server: port: 8083 spring: cloud: nacos: server-addr: localhost:8848 application: name: geteway
將全部服務(wù)啟動(dòng)就可以看到我們的gateway也被注冊(cè)到Nacos中啦
?使用Gateway的三種方式
??方法一
gateway: discovery: locator: #是否與服務(wù)發(fā)現(xiàn)組件進(jìn)行結(jié)合,通過(guò)service-id(必須設(shè)置成大寫)轉(zhuǎn)發(fā)到具體的服務(wù)實(shí)例。默認(rèn) false #為true代表開啟基于服務(wù)發(fā)現(xiàn)的路由規(guī)則。 enabled: true #配置之后訪問(wèn)時(shí)service-id無(wú)需大寫 lower-case-service-id: true
注意:我們的gateway是與spring下的nacos的標(biāo)簽同級(jí)別可不要將它寫在nacos下面
前面是通過(guò)服務(wù)直接訪問(wèn)的,后面則是通過(guò)網(wǎng)關(guān)向服務(wù)發(fā)起的請(qǐng)求,此時(shí)我們的請(qǐng)求已經(jīng)被官網(wǎng)處理了。
??方法二
第二種方式是為了防止使用第一種方式的時(shí)候服務(wù)名字太長(zhǎng)而導(dǎo)致的麻煩,這種方式類似于為服務(wù)名又起了一個(gè)別名。
gateway: routes: # 路由標(biāo)識(shí)(id:標(biāo)識(shí),具有唯一性) - id: user-consumer-api #目標(biāo)服務(wù)地址(uri:地址,請(qǐng)求轉(zhuǎn)發(fā)后的地址),會(huì)自動(dòng)從注冊(cè)中心獲得服務(wù)的IP,不需要手動(dòng)寫死 uri: lb://consume #優(yōu)先級(jí),越小越優(yōu)先 predicates: # 路徑匹配, - Path=/aa/** filters: #前綴過(guò)濾,請(qǐng)求地址:http://localhost:8084/usr/hello #此處配置去掉1個(gè)路徑前綴,再配置上面的 Path=/usr/**,就將**轉(zhuǎn)發(fā)到指定的微服務(wù) - StripPrefix=1
這里如果使用的是http://localhost:8083/aa/test01就會(huì)映射到consume這個(gè)服務(wù),并且通過(guò)前綴過(guò)濾會(huì)過(guò)濾掉aa拿到test01就會(huì)映射到consume/test01上,得到如下結(jié)果????
??方法三
第三種方式是結(jié)合配置讀取類進(jìn)行的,我們將對(duì)服務(wù)的網(wǎng)關(guān)配置寫在Nacos,然后再用這個(gè)讀取類去進(jìn)行讀取,獲取我們想要的路由信息。
gateway: nacos: server-addr: ${spring.cloud.nacos.server-addr} data-id: dynamic-routing.json group: DEFAULT_GROUP
首先需要新建一個(gè)POJO包將以下類進(jìn)行創(chuàng)建
package org.example.geteway.pojo; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; import lombok.experimental.Accessors; import java.util.LinkedHashMap; import java.util.Map; /** * @author hgh */ @SuppressWarnings("all") @Data @NoArgsConstructor @AllArgsConstructor @Accessors(chain = true) public class FilterEntity { //過(guò)濾器對(duì)應(yīng)的Name private String name; //路由規(guī)則 private Map<String, String> args = new LinkedHashMap<>(); }
package org.example.geteway.pojo; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; import lombok.experimental.Accessors; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.stereotype.Component; @SuppressWarnings("all") @Data @NoArgsConstructor @AllArgsConstructor @Accessors(chain = true) @ConfigurationProperties(prefix = "gateway.nacos") @Component public class GatewayNacosProperties { private String serverAddr; private String dataId; private String namespace; private String group; }
package org.example.geteway.pojo; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; import lombok.experimental.Accessors; import java.util.LinkedHashMap; import java.util.Map; /** * @author hgh */ @SuppressWarnings("all") @Data @NoArgsConstructor @AllArgsConstructor @Accessors(chain = true) public class PredicateEntity { //斷言對(duì)應(yīng)的Name private String name; //斷言規(guī)則 private Map<String, String> args = new LinkedHashMap<>(); }
package org.example.geteway.pojo; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; import lombok.experimental.Accessors; import java.util.ArrayList; import java.util.List; /** * @author hgh */ @SuppressWarnings("all") @Data @NoArgsConstructor @AllArgsConstructor @Accessors(chain = true) public class RouteEntity { //路由id private String id; //路由斷言集合 private List<PredicateEntity> predicates = new ArrayList<>(); //路由過(guò)濾器集合 private List<FilterEntity> filters = new ArrayList<>(); //路由轉(zhuǎn)發(fā)的目標(biāo)uri private String uri; //路由執(zhí)行的順序 private int order = 0; }
最后創(chuàng)建一個(gè)配置信息讀取類,這個(gè)類是一個(gè)基于 Spring Cloud Gateway 和 Nacos 的動(dòng)態(tài)路由配置類,它實(shí)現(xiàn)了一個(gè) Spring 提供的事件推送接口 ApplicationEventPublisherAware。它的功能主要包括監(jiān)聽 Nacos 的配置變化、解析從 Nacos 讀取的路由配置信息(json格式)、路由更新、以及組裝和定義路由信息等。通過(guò)這個(gè)類,可以實(shí)現(xiàn)路由信息的動(dòng)態(tài)管理和更新。
package org.example.geteway; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import com.alibaba.nacos.api.NacosFactory; import com.alibaba.nacos.api.PropertyKeyConst; import com.alibaba.nacos.api.config.ConfigService; import com.alibaba.nacos.api.config.listener.Listener; import com.alibaba.nacos.api.exception.NacosException; import com.fasterxml.jackson.databind.ObjectMapper; import lombok.extern.slf4j.Slf4j; import org.example.geteway.pojo.FilterEntity; import org.example.geteway.pojo.GatewayNacosProperties; import org.example.geteway.pojo.PredicateEntity; import org.example.geteway.pojo.RouteEntity; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cloud.gateway.event.RefreshRoutesEvent; import org.springframework.cloud.gateway.filter.FilterDefinition; import org.springframework.cloud.gateway.handler.predicate.PredicateDefinition; import org.springframework.cloud.gateway.route.RouteDefinition; import org.springframework.cloud.gateway.route.RouteDefinitionWriter; import org.springframework.context.ApplicationEventPublisher; import org.springframework.context.ApplicationEventPublisherAware; import org.springframework.context.annotation.Bean; import org.springframework.stereotype.Component; import org.springframework.web.util.UriComponentsBuilder; import reactor.core.publisher.Mono; import java.net.URI; import java.util.ArrayList; import java.util.List; import java.util.Properties; import java.util.concurrent.Executor; /** * 此類實(shí)現(xiàn)了Spring Cloud Gateway + nacos 的動(dòng)態(tài)路由, * 它實(shí)現(xiàn)一個(gè)Spring提供的事件推送接口ApplicationEventPublisherAware */ @SuppressWarnings("all") @Slf4j @Component public class DynamicRoutingConfig implements ApplicationEventPublisherAware { @Autowired private RouteDefinitionWriter routeDefinitionWriter; @Autowired private GatewayNacosProperties gatewayProperties; @Autowired private ObjectMapper mapper; private ApplicationEventPublisher applicationEventPublisher; @Override public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) { this.applicationEventPublisher = applicationEventPublisher; } /** * 這個(gè)方法主要負(fù)責(zé)監(jiān)聽Nacos的配置變化,這里先使用參數(shù)構(gòu)建一個(gè)ConfigService, * 再使用ConfigService開啟一個(gè)監(jiān)聽, * 并且在監(jiān)聽的方法中刷新路由信息。 */ @Bean public void refreshRouting() throws NacosException { //創(chuàng)建Properties配置類 Properties properties = new Properties(); System.out.println(gatewayProperties); //設(shè)置nacos的服務(wù)器地址,從配置類GatewayProperties中獲取 properties.put(PropertyKeyConst.SERVER_ADDR, gatewayProperties.getServerAddr()); //設(shè)置nacos的命名空間,表示從具體的命名空間中獲取配置信息,不填代表默認(rèn)從public獲得 if (gatewayProperties.getNamespace() != null) { properties.put(PropertyKeyConst.NAMESPACE, gatewayProperties.getNamespace()); } //根據(jù)Properties配置創(chuàng)建ConfigService類 ConfigService configService = NacosFactory.createConfigService(properties); //獲得nacos中已有的路由配置 String json = configService.getConfig(gatewayProperties.getDataId(), gatewayProperties.getGroup(), 5000); this.parseJson(json); //添加監(jiān)聽器,監(jiān)聽nacos中的數(shù)據(jù)修改事件 configService.addListener(gatewayProperties.getDataId(), gatewayProperties.getGroup(), new Listener() { @Override public Executor getExecutor() { return null; } /** * 用于接收遠(yuǎn)端nacos中數(shù)據(jù)修改后的回調(diào)方法 */ @Override public void receiveConfigInfo(String configInfo) { log.warn(configInfo); //獲取nacos中修改的數(shù)據(jù)并進(jìn)行轉(zhuǎn)換 parseJson(configInfo); } }); } /** * 解析從nacos讀取的路由配置信息(json格式) */ public void parseJson(String json) { log.warn("從Nacos返回的路由配置(JSON格式):" + json); boolean refreshGatewayRoute = JSONObject.parseObject(json).getBoolean("refreshGatewayRoute"); if (refreshGatewayRoute) { List<RouteEntity> list = JSON.parseArray(JSONObject.parseObject(json).getString("routeList")).toJavaList(RouteEntity.class); for (RouteEntity route : list) { update(assembleRouteDefinition(route)); } } else { log.warn("路由未發(fā)生變更"); } } /** * 路由更新 */ public void update(RouteDefinition routeDefinition) { try { this.routeDefinitionWriter.delete(Mono.just(routeDefinition.getId())); log.warn("路由刪除成功:" + routeDefinition.getId()); } catch (Exception e) { log.error(e.getMessage(), e); } try { routeDefinitionWriter.save(Mono.just(routeDefinition)).subscribe(); this.applicationEventPublisher.publishEvent(new RefreshRoutesEvent(this)); log.warn("路由更新成功:" + routeDefinition.getId()); } catch (Exception e) { log.error(e.getMessage(), e); } } /** * 路由定義 */ public RouteDefinition assembleRouteDefinition(RouteEntity routeEntity) { RouteDefinition definition = new RouteDefinition(); // ID definition.setId(routeEntity.getId()); // Predicates List<PredicateDefinition> pdList = new ArrayList<>(); for (PredicateEntity predicateEntity : routeEntity.getPredicates()) { PredicateDefinition predicateDefinition = new PredicateDefinition(); predicateDefinition.setArgs(predicateEntity.getArgs()); predicateDefinition.setName(predicateEntity.getName()); pdList.add(predicateDefinition); } definition.setPredicates(pdList); // Filters List<FilterDefinition> fdList = new ArrayList<>(); for (FilterEntity filterEntity : routeEntity.getFilters()) { FilterDefinition filterDefinition = new FilterDefinition(); filterDefinition.setArgs(filterEntity.getArgs()); filterDefinition.setName(filterEntity.getName()); fdList.add(filterDefinition); } definition.setFilters(fdList); // URI URI uri = UriComponentsBuilder.fromUriString(routeEntity.getUri()).build().toUri(); definition.setUri(uri); return definition; } }
現(xiàn)在需要我們?nèi)acos創(chuàng)建對(duì)應(yīng)的配置 dynamic-routing.json
{ "refreshGatewayRoute": true, "routeList": [ { "id": "consumer-api", "predicates": [ { "name": "Path", "args": { "_genkey_0": "/cum/**" } } ], "filters": [ { "name": "StripPrefix", "args": { "_genkey_0": "1" } } ], "uri": "lb://consumer", "order": 0 }, { "id": "provider-api", "predicates": [ { "name": "Path", "args": { "_genkey_0": "/pvr/**" } } ], "filters": [ { "name": "StripPrefix", "args": { "_genkey_0": "1" } } ], "uri": "lb://provider", "order": 0 } ] }
現(xiàn)在我們就可以通過(guò)獲取Nacos所配置的路由進(jìn)行訪問(wèn)了
如果你更改了Nacos的信息也會(huì)實(shí)時(shí)發(fā)送改變不用重啟服務(wù)器
到此這篇關(guān)于Spring Cloud Gateway組件的三種使用方式的文章就介紹到這了,更多相關(guān)Spring Cloud Gateway組件內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- Spring Cloud Gateway層限流實(shí)現(xiàn)過(guò)程
- Spring Cloud Gateway內(nèi)置的斷言和過(guò)濾器作用說(shuō)明
- Spring Cloud Gateway 緩存區(qū)異常問(wèn)題及解決方案
- spring cloud gateway中redis一直打印重連日志問(wèn)題及解決
- Spring?Cloud?Gateway?2.x跨域時(shí)出現(xiàn)重復(fù)Origin的BUG問(wèn)題
- SpringCloud-Gateway網(wǎng)關(guān)的使用實(shí)例教程
- 使用SpringCloud Gateway解決跨域問(wèn)題
- 一文掌握spring cloud gateway(總結(jié)篇)
相關(guān)文章
Ribbon負(fù)載均衡服務(wù)調(diào)用的示例詳解
Rbbo其實(shí)就是一個(gè)軟負(fù)載均衡的客戶端組件,他可以和其他所需請(qǐng)求的客戶端結(jié)合使用,這篇文章主要介紹了Ribbon負(fù)載均衡服務(wù)調(diào)用案例代碼,需要的朋友可以參考下2023-01-01Java數(shù)據(jù)結(jié)構(gòu)之HashMap源碼深入分析
Java HashMap是一種基于哈希表實(shí)現(xiàn)的鍵值對(duì)存儲(chǔ)結(jié)構(gòu),可以實(shí)現(xiàn)快速的數(shù)據(jù)查找和存儲(chǔ)。它是線程不安全的,但在單線程環(huán)境中運(yùn)行效率高,被廣泛應(yīng)用于Java開發(fā)中2023-04-04淺談Java序列化和反序列化為何要實(shí)現(xiàn)Serializable接口
這篇文章主要介紹了淺談Java序列化和反序列化為何要實(shí)現(xiàn)Serializable接口,序列化最重要的作用是在傳遞和保存對(duì)象時(shí).保證對(duì)象的完整性和可傳遞性,對(duì)象轉(zhuǎn)換為有序字節(jié)流,以便在網(wǎng)絡(luò)上傳輸或者保存在本地文件中,需要的朋友可以參考下2023-12-12Spring-基于Spring使用自定義注解及Aspect實(shí)現(xiàn)數(shù)據(jù)庫(kù)切換操作
這篇文章主要介紹了Spring-基于Spring使用自定義注解及Aspect實(shí)現(xiàn)數(shù)據(jù)庫(kù)切換操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-09-09VS?Code中運(yùn)行Java?SpringBoot的項(xiàng)目詳細(xì)步驟
這篇文章主要介紹了VS?Code中運(yùn)行Java?SpringBoot項(xiàng)目的相關(guān)資料,文中涵蓋了安裝必要的擴(kuò)展、配置環(huán)境、創(chuàng)建或?qū)腠?xiàng)目、配置調(diào)試環(huán)境、運(yùn)行和調(diào)試項(xiàng)目、使用Spring?Boot?Actuator以及配置任務(wù)自動(dòng)化等步驟,需要的朋友可以參考下2024-12-12java 實(shí)現(xiàn)將Object類型轉(zhuǎn)換為int類型
這篇文章主要介紹了java 實(shí)現(xiàn)將Object類型轉(zhuǎn)換為int類型的操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-07-07詳談Java中net.sf.json包關(guān)于JSON與對(duì)象互轉(zhuǎn)的坑
下面小編就為大家分享一篇Java中net.sf.json包關(guān)于JSON與對(duì)象互轉(zhuǎn)的坑,具有很好的參考價(jià)值,希望對(duì)大家有所幫助2017-12-12詳解eclipse創(chuàng)建maven項(xiàng)目實(shí)現(xiàn)動(dòng)態(tài)web工程完整示例
這篇文章主要介紹了詳解eclipse創(chuàng)建maven項(xiàng)目實(shí)現(xiàn)動(dòng)態(tài)web工程完整示例,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-12-12