網關路由SpringCloudGateway、nacos配置管理(熱更新、動態路由)

文章目錄

  • 前言
  • 一、網關路由
  • 二、SpringCloudGateway
    • 1. 路由過濾
    • 2. 網關登錄校驗
      • 2.1 鑒權
      • 2.2 網關過濾器
      • 2.3 登錄校驗
        • 2.3.1 JWT
        • 2.3.2 登錄校驗過濾器
    • 3. 微服務從網關獲取用戶
    • 4. 微服務之間用戶信息傳遞
  • 三、nacos配置管理
    • 問題引入
    • 3.1 配置共享
      • 3.1.1 在Nacos中添加共享配置
      • 3.1.2 拉取共享配置
    • 3.2 配置熱更新
      • 問題引入
      • 實現步驟
    • 3.3 動態路由
      • 問題引入
      • 3.3.1 監聽nacos配置變更,更新路由表
      • 3.3.2 實現動態路由


前言

前端請求不能直接訪問微服務,而是要請求網關(SpringCloudGateway)
網關干什么?路由過濾,登錄校驗。
nacos既是注冊中心也可用作配置管理,用來解決各個微服務塊配置文件中相同的配置冗余,配置熱更新屬性,動態路由等。

一、網關路由

數據在網絡間傳輸,從一個網絡傳輸到另一網絡時就需要經過網關來做數據的路由和轉發以及數據安全的校驗。
在這里插入圖片描述
微服務中的網關作用:前端請求不能直接訪問微服務,而是要請求網關。

  • 網關可以做安全控制,也就是登錄身份校驗,校驗通過才放行
  • 通過認證后,網關再根據請求判斷應該訪問哪個微服務,將請求轉發過去

二、SpringCloudGateway

網關本身也是一個獨立的微服務,所以也需要單獨建立一個模塊。實現步驟:

  • 創建網關微服務模塊
  • 引入SpringCloudGateway、NacosDiscovery依賴
  • 編寫啟動類
  • 配置網關路由

1. 路由過濾

spring:cloud:gateway:routes:- id: itemuri: lb://item-servicepredicates:- Path=/items/**,/search/**   #請求路徑必須符合指定規則

RouteDefinition就是具體的路由規則定義。

  • id:路由的唯一標示
  • predicates:路由斷言,其實就是匹配條件
  • filters:路由過濾條件
  • uri:路由目標地址,lb://代表負載均衡,從注冊中心獲取目標微服務的實例列表,并且負載均衡選擇一個訪問。

2. 網關登錄校驗

2.1 鑒權

在網關中保存密鑰開發登錄校驗功能
在這里插入圖片描述
問題
1、怎么在轉發之前做登錄校驗
2、校驗JWT之后,怎么把用戶信息傳遞給微服務
3、微服務之間怎么傳遞用戶信息

2.2 網關過濾器

在這里插入圖片描述
實現原理

  1. 客戶端請求進入網關后由HandlerMapping對請求做判斷,找到與當前請求匹配的路由規則(Route),然后將請求交給WebHandler去處理。
  2. WebHandler則會加載當前路由下需要執行的過濾器鏈(Filter chain),然后按照順序逐一執行過濾器(后面稱為Filter)。
  3. 圖中Filter被虛線分為左右兩部分,是因為Filter內部的邏輯分為pre和post兩部分,分別會在請求路由到微服務之前和之后被執行。
  4. 只有所有Filter的pre邏輯都依次順序執行通過后,請求才會被路由到微服務。
  5. 微服務返回結果后,再倒序執行Filter的post邏輯。
  6. 最終把響應結果返回。

定義一個過濾器,在其中實現登錄校驗邏輯,并且將過濾器執行順序定義到NettyRoutingFilter之前?

網關過濾器

  • GatewayFilter:路由過濾器,作用范圍比較靈活,可以是任意指定的路由Route.
  • GlobalFilter:全局過濾器,作用范圍是所有路由,不可配置。FilteringWebHandler在處理請求時,會將GlobalFilter裝飾為GatewayFilter,然后放到同一個過濾器鏈中,排序以后依次執行。
  • AddRequestHeaderGatewayFilterFacotry: 就是添加請求頭的過濾器,可以給請求添加一個請求頭并傳遞到下游微服務。直接在yaml文件中配置

1、自定義GatewayFilter
定義一個類,這個類的名稱一定要以GatewayFilterFactory為后綴,這個類繼承了AbstractGatewayFilterFactory
2、自定義GlobalFilter
定義一個類,直接實現GlobalFilter接口。

2.3 登錄校驗

2.3.1 JWT
  • AuthProperties:配置登錄校驗需要攔截的路徑,因為不是所有的路徑都需要登錄才能訪問
  • JwtProperties:定義與JWT工具有關的屬性,比如秘鑰文件位置
  • SecurityConfig:工具的自動裝配
  • JwtTool:JWT工具,其中包含了校驗和解析token的功能
  • hmall.jks:秘鑰文件
2.3.2 登錄校驗過濾器

定義一個登錄校驗的過濾器,實現GlobalFilter接口。

@Component
@RequiredArgsConstructor
@EnableConfigurationProperties(AuthProperties.class)
public class AuthGlobalFilter implements GlobalFilter, Ordered {private final JwtTool jwtTool;private final AuthProperties authProperties;private final AntPathMatcher antPathMatcher = new AntPathMatcher();@Overridepublic Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {// 1.獲取RequestServerHttpRequest request = exchange.getRequest();// 2.判斷是否不需要攔截if(isExclude(request.getPath().toString())){// 無需攔截,直接放行return chain.filter(exchange);}// 3.獲取請求頭中的tokenString token = null;List<String> headers = request.getHeaders().get("authorization");if (!CollUtils.isEmpty(headers)) {token = headers.get(0);}// 4.校驗并解析tokenLong userId = null;try {userId = jwtTool.parseToken(token);} catch (UnauthorizedException e) {// 如果無效,攔截ServerHttpResponse response = exchange.getResponse();response.setRawStatusCode(401);return response.setComplete();}// TODO 5.如果有效,傳遞用戶信息//保存用戶請求頭String userInfo = userId.toString();ServerWebExchange ex = exchange.mutate().request(b -> b.header("user-info",userInfo)).build();// 6.放行return chain.filter(exchange);}private boolean isExclude(String antPath) {for (String pathPattern : authProperties.getExcludePaths()) {if(antPathMatcher.match(pathPattern, antPath)){return true;}}return false;}@Overridepublic int getOrder() {return 0;}
}

3. 微服務從網關獲取用戶

將用戶信息以請求頭的方式傳遞到下游微服務,然后微服務可以從請求頭中獲取登錄用戶信息。考慮到微服務內部可能很多地方都需要用到登錄用戶信息,因此我們可以利用SpringMVC的攔截器來實現登錄用戶信息獲取,并存入ThreadLocal,方便后續使用。
在這里插入圖片描述
實現步驟:

  • 改造網關過濾器,在獲取用戶信息后保存到請求頭,轉發到下游微服務
  • 編寫微服務攔截器,攔截請求獲取用戶信息,保存到ThreadLocal后放行(因為每個微服務都執行獲取用戶信息的邏輯,因此統一攔截所有請求,進行處理(AOP的思想))

4. 微服務之間用戶信息傳遞

由于微服務獲取用戶信息是通過攔截器在請求頭中讀取,因此要想實現微服務之間的用戶信息傳遞,就必須在微服務發起調用時把用戶信息存入請求頭。
微服務之間調用是基于OpenFeign來實現的,并不是我們自己發送的請求。我們如何才能讓每一個由OpenFeign發起的請求自動攜帶登錄用戶信息呢?
Feign中提供的一個攔截器接口:feign.RequestInterceptor
實現方式
定義一個類,實現RequestInterceptor接口,實現接口中的apply方法,用RequestTemplate類來添加請求頭,將用戶信息保存到請求頭中。這樣以來,每次OpenFeign發起請求的時候都會調用該方法,傳遞用戶信息。

@Bean
public RequestInterceptor userInfoRequestInterceptor(){return new RequestInterceptor() {@Overridepublic void apply(RequestTemplate template) {// 獲取登錄用戶Long userId = UserContext.getUser();if(userId == null) {// 如果為空則直接跳過return;}// 如果不為空則放入請求頭中,傳遞給下游微服務template.header("user-info", userId.toString());}};
}

在這里插入圖片描述

三、nacos配置管理

問題引入

  • 網關路由在配置文件中寫死了,如果變更必須重啟微服務
  • 某些業務配置在配置文件中寫死了,每次修改都要重啟服務
  • 每個微服務都有很多重復的配置,維護成本高

nacos: 無需重啟即可生效,實現配置熱更新。實現動態路由功能,無需重啟網關即可修改路由配置。
在這里插入圖片描述

3.1 配置共享

3.1.1 在Nacos中添加共享配置

抽取配置文件yaml中重復的模塊,如jdbc相關配置(datasource、mybatis-plus)、日志配置(logging)、swagger(knife4j)以及OpenFeign的配置(feign)
在這里插入圖片描述
在這里插入圖片描述

3.1.2 拉取共享配置

如何去加載nacos中的配置文件?
在這里插入圖片描述
實現步驟
1、在模塊中引入依賴

  <!--nacos配置管理--><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId></dependency><!--讀取bootstrap文件--><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-bootstrap</artifactId></dependency>

2、新建bootstrap.yaml,配置nacos地址

spring:cloud:nacos:server-addr: 192.168.150.101 # nacos地址config:file-extension: yaml # 文件后綴名shared-configs: # 共享配置- dataId: shared-jdbc.yaml # 共享mybatis配置- dataId: shared-log.yaml # 共享日志配置- dataId: shared-swagger.yaml # 共享日志配置

3.2 配置熱更新

問題引入

業務相關參數,將來可能會根據實際情況臨時調整。例如購物車業務,購物車數量有一個上限,默認是10。

實現步驟

1、在nacos中添加配置屬性
2、新建一個屬性讀取類,在需要使用該屬性的業務類中注入。

3.3 動態路由

問題引入

網關的路由配置全部是在項目啟動的時候加載,并且一經加載就會緩存到內存中的路由表內(一個Map),不會改變,也不會監聽路由變更。

  • 如何監聽Nacos配置變更?
  • 如何把路由信息更新到路由表?

3.3.1 監聽nacos配置變更,更新路由表

使用 Nacos 動態監聽配置接口來實現。

public void addListener(String dataId, String group, Listener listener)

核心點:

  • 創建ConfigService,連接到Nacos(spring-cloud-starter-alibaba-nacos-config自動裝配,拿到NacosConfigManager就等于拿到了ConfigService)
  • 添加配置監聽器,編寫配置變更的通知處理邏輯
String serverAddr = "{serverAddr}";
String dataId = "{dataId}";
String group = "{group}";
// 1.創建ConfigService,連接Nacos
Properties properties = new Properties();
properties.put("serverAddr", serverAddr);
ConfigService configService = NacosFactory.createConfigService(properties);
// 2.讀取配置
String content = configService.getConfig(dataId, group, 5000);
// 3.添加配置監聽器
configService.addListener(dataId, group, new Listener() {@Overridepublic void receiveConfigInfo(String configInfo) {// 配置變更的通知處理System.out.println("recieve1:" + configInfo);}@Overridepublic Executor getExecutor() {return null;}
});

更新路由表

package org.springframework.cloud.gateway.route;
import reactor.core.publisher.Mono;
/*** @author Spencer Gibb*/
public interface RouteDefinitionWriter {/*** 更新路由到路由表,如果路由id重復,則會覆蓋舊的路由*/Mono<Void> save(Mono<RouteDefinition> route);/*** 根據路由id刪除某個路由*/Mono<Void> delete(Mono<String> routeId);
}

3.3.2 實現動態路由

1、網關gateway引入依賴

<!--統一配置管理-->
<dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
<!--加載bootstrap-->
<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-bootstrap</artifactId>
</dependency>

2、在網關gateway的resources目錄創建bootstrap.yaml文件

spring:application:name: gatewaycloud:nacos:server-addr: 192.168.150.101config:file-extension: yamlshared-configs:- dataId: shared-log.yaml # 共享日志配置

3、在gateway中定義配置監聽器

package com.hmall.gateway.route;import cn.hutool.json.JSONUtil;
import com.alibaba.cloud.nacos.NacosConfigManager;
import com.alibaba.nacos.api.config.listener.Listener;
import com.alibaba.nacos.api.exception.NacosException;
import com.hmall.common.utils.CollUtils;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.route.RouteDefinition;
import org.springframework.cloud.gateway.route.RouteDefinitionWriter;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Mono;import javax.annotation.PostConstruct;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.Executor;@Slf4j
@Component
@RequiredArgsConstructor
public class DynamicRouteLoader {private final RouteDefinitionWriter writer;private final NacosConfigManager nacosConfigManager;// 路由配置文件的id和分組private final String dataId = "gateway-routes.json";private final String group = "DEFAULT_GROUP";// 保存更新過的路由idprivate final Set<String> routeIds = new HashSet<>();@PostConstructpublic void initRouteConfigListener() throws NacosException {// 1.注冊監聽器并首次拉取配置String configInfo = nacosConfigManager.getConfigService().getConfigAndSignListener(dataId, group, 5000, new Listener() {@Overridepublic Executor getExecutor() {return null;}@Overridepublic void receiveConfigInfo(String configInfo) {updateConfigInfo(configInfo);}});// 2.首次啟動時,更新一次配置updateConfigInfo(configInfo);}private void updateConfigInfo(String configInfo) {log.debug("監聽到路由配置變更,{}", configInfo);// 1.反序列化List<RouteDefinition> routeDefinitions = JSONUtil.toList(configInfo, RouteDefinition.class);// 2.更新前先清空舊路由// 2.1.清除舊路由for (String routeId : routeIds) {writer.delete(Mono.just(routeId)).subscribe();}routeIds.clear();// 2.2.判斷是否有新的路由要更新if (CollUtils.isEmpty(routeDefinitions)) {// 無新路由配置,直接結束return;}// 3.更新路由routeDefinitions.forEach(routeDefinition -> {// 3.1.更新路由writer.save(Mono.just(routeDefinition)).subscribe();// 3.2.記錄路由id,方便將來刪除routeIds.add(routeDefinition.getId());});}
}

4、在nacos中配置路由信息,路由文件名為gateway-routes.json,類型為json

[{"id": "item","predicates": [{"name": "Path","args": {"_genkey_0":"/items/**", "_genkey_1":"/search/**"}}],"filters": [],"uri": "lb://item-service"},....

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/13454.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/13454.shtml
英文地址,請注明出處:http://en.pswp.cn/web/13454.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

【前端三劍客之HTML】詳解HTML

1. HTML(超文本標記語言) HTML意為超文本標記語言&#xff0c;其可以通過標簽把其他網頁/圖片/視頻等資源引入到當前網頁中&#xff0c;讓網頁最終呈現出來的效果超越了文本.HTML是一種標記語言&#xff0c;其是由一系列標簽組成的. 而且每個標簽都有特定的含義和確定的頁面顯…

Vue 3入門指南

title: Vue 3入門指南 date: 2024/5/23 19:37:34 updated: 2024/5/23 19:37:34 categories: 前端開發 tags: 框架對比環境搭建基礎語法組件開發響應式系統狀態管理路由配置 第1章&#xff1a;Vue 3簡介 1.1 Vue.js的歷史與發展 Vue.js由前谷歌工程師尤雨溪&#xff08;Eva…

Java分支結構詳解

Java分支結構詳解 前言一、if 語句基本語法表示一表示二表示三 代碼示例判定一個數字是奇數還是偶數判定一個數字是正數還是負數判定某一年份是否是閏年 注意要點懸垂 else 問題代碼風格問題分號問題 二、switch 語句基本語法代碼示例根據 day 的值輸出星期 注意事項break 不要…

深入了解 Pandas:對象的缺少值

目錄 前言 第一點&#xff1a;導入模塊 第二點 &#xff1a;發現對象的缺失值 第二點&#xff1a;剔除缺少值 第三點&#xff1a;填補缺失值 總結 前言 在數據處理中&#xff0c;經常會遇到數據中存在缺失值的情況。處理缺失值是數據清洗的一個重要環節&#xff0c;能夠確…

spring常用知識點

1、攔截器和過濾器區別 1. 原理不同&#xff1a; 攔截器是基于java的反射機制&#xff0c;而過濾器采用責任鏈模式是基于函數回調的。 2. 使用范圍不同&#xff1a; 過濾器Filter的使用依賴于Tomcat等容器&#xff0c;導致它只能在web程序中使用 攔截器是一個Sping組件&am…

abs(-2147483648) == 2147483648?

從數學意義上&#xff0c;這是對的。但是&#xff0c;就怕但是。 #include int main() {long long v;v abs(-2147483648);printf("%lld\n", v);return 0; } 輸出: -2147483648 我們從source code中一一解開. /* Return the absolute value of I. */ int abs (…

Mongodb介紹及springboot集成增刪改查

文章目錄 1. MongoDB相關概念1.1 業務應用場景1.2 MongoDB簡介1.3 體系結構1.4 數據模型1.5 MongoDB的特點 2. docker安裝mongodb3. springboot集成3.1 文件結構3.2 增刪改查3.2.1 增加insert3.2.2 保存save3.2.3 更新update3.2.4 查詢3.2.5 刪除 1. MongoDB相關概念 1.1 業務…

Docker-Android安卓模擬器本地部署并實現遠程開發測試

文章目錄 1. 虛擬化環境檢查2. Android 模擬器部署3. Ubuntu安裝Cpolar4. 配置公網地址5. 遠程訪問小結 6. 固定Cpolar公網地址7. 固定地址訪問 本文主要介紹如何在Ubuntu系統使用Docker部署docker-android安卓模擬器&#xff0c;并結合cpolar內網穿透工具實現公網遠程訪問本地…

51建模網AR虛擬試用,讓網購不再只靠想象!

在數字化的浪潮中&#xff0c;網購已成為現代人生活的一部分。然而&#xff0c;傳統的網購模式常常因為無法直接試穿、試用商品&#xff0c;導致買家在收到商品后感到失望&#xff0c;特別是面對大件家居產品時&#xff0c;僅憑屏幕上的圖片和尺寸描述&#xff0c;很難準確地把…

智能AI愈發強大,企業如何防范AI網絡釣魚攻擊

隨著AI技術的快速發展&#xff0c;如ChatGPT等智能化工具在各個領域得到了廣泛應用。然而&#xff0c;這些工具的普及也給網絡安全帶來了新的挑戰。AI模型的自然語言生成功能使得網絡釣魚攻擊更加智能化和隱蔽化&#xff0c;攻擊者能夠利用AI技術生成高度逼真的欺騙性郵件和其他…

深度學習之基于YoloV5人體姿態摔倒識別分析報警系統

歡迎大家點贊、收藏、關注、評論啦 &#xff0c;由于篇幅有限&#xff0c;只展示了部分核心代碼。 文章目錄 一項目簡介 二、功能三、系統四. 總結 一項目簡介 一、項目背景與意義 隨著人口老齡化的加劇和人們對健康安全的日益關注&#xff0c;摔倒事件在老年人、幼兒、體育運…

2024-05-23 服務器開發-windows-加載dll動態庫

摘要: 2024-05-23 服務器開發-windows-加載dll動態庫 使用 LoadLibrary HMODULE mdl ::LoadLibrary(L"mylib.dll");if (!mdl){auto err ::GetLastError();std::cout << "ERROR: load VxCfgClient fail, error: " << err << std::endl…

分布式文件系統minIo

分布式文件系統 什么是分布式文件系統 一個計算機無法存儲海量的文件&#xff0c;通過網絡將若干計算機組織起來共同去存儲海量的文件&#xff0c;去接收海量用戶的請求&#xff0c;這些組織起來的計算機通過網絡進行通信&#xff0c;如下圖&#xff1a; 好處&#xff1a; 1、…

怎么ai思維導圖自動生成?推薦這三個工具

怎么ai思維導圖自動生成&#xff1f;隨著人工智能&#xff08;AI&#xff09;技術的不斷發展&#xff0c;其在各個領域的應用也日益廣泛。在思維導圖這一領域&#xff0c;AI技術的引入不僅極大地提高了制圖效率&#xff0c;還為用戶提供了更多樣化、個性化的制圖體驗。本文將為…

JavaScript身份三要素認證API、身份證二要素實名認證接口

996這種工作模式&#xff0c;試問有多少人愿意接受&#xff1f;然而這種工作制度在程序員的圈子里早已成為不成文的“規定”。網絡段子也有不少調侃程序員的&#xff0c;比如&#xff1a;一程序員去面試&#xff0c;面試官問&#xff1a;“你畢業才兩年&#xff0c;這三年工作經…

基于消息中間件的異步通信機制在系統解耦中的優化與實現

??謝謝大家捧場&#xff0c;祝屏幕前的小伙伴們每天都有好運相伴左右&#xff0c;一定要天天開心哦&#xff01;?? &#x1f388;&#x1f388;作者主頁&#xff1a; 喔的嘛呀&#x1f388;&#x1f388; ?? 帥哥美女們&#xff0c;我們共同加油&#xff01;一起進步&am…

【只會for循環? 來看下, Nodejs中典型的5種循環方式】

Nodejs中的&#xff0c;除了經典的for循環 , 其實還有幾種好用的循環方式&#xff0c; 并有典型的使用場景。下面來一起看下&#x1f447;&#x1f3fb; 5種循環用法 For Loop&#xff1a;這是最常見的循環方式&#xff0c;適用于你知道循環次數的情況。 for (let i 0; i &…

GPT-SoVITS語音克隆部署與使用

GPT-SoVITS是一款強大的少量樣本語音轉換與語音合成開源工具。當前&#xff0c;GPT-SoVITS實現了如下幾個方面的功能&#xff1a; 由參考音頻的情感、音色、語速控制合成音頻的情感、音色、語速可以少量語音微調訓練&#xff0c;也可不訓練直接推理可以跨語種生成&#xff0c;…

【AI】試用 ai 提取文章內容嘗試

電梯產業面臨這樣一個問題&#xff0c;因為太多的品牌&#xff0c;將近 400 多個&#xff0c;甚至有寶馬&#xff0c;奧迪&#xff0c;你敢相信&#xff0c;一家造汽車的造過電梯?不過好像想想也是&#xff0c;電梯是第二大交通工具&#xff0c;電梯從某種意義上來說&#xff…

無網環境禁止 WPS 提示登錄,且基本功能按鈕可用

目前 WPS 升級后&#xff0c;每次打開都會提示你登錄 WPS&#xff0c;并且在未登錄之前所有基本功能按鈕是置灰狀態&#xff0c;無法使用。 如此一來&#xff0c;在內網或無網環境&#xff0c;我們無法登陸 WPS &#xff0c;就給我們的使用帶來了極大的不便&#xff0c;那么有沒…