基于黑馬教程——微服務架構解析(二)

本篇文章基于黑馬程序員的微服務課程內容,結合個人學習過程中的理解與思考進行整理。本節將圍繞以下幾個問題展開:什么是網關和配置管理

前面那篇文章,我們了解如何把一個單體的項目拆成分布式微服務項目,并且講解一下各個服務之間是如何通信的。發現一些問題:

  1. 每個微服務都有不同的地址和端口,那么前端需要調用微服務的功能時,就會出現以下問題:
    • 前端需要調用不同微服務的時候,地址和端口太多,不易維護
    • 前端無法調用Nacos,當后端端口發送改變的時候,前端察覺不到
  2. 單體項目需要做登錄、權限校驗,還有為了方便用戶信息傳遞登錄時保存用戶信息,那么拆分成微服務就會出現以下問題:
    • 每個微服務都需要編寫登錄校驗、用戶信息保存
    • 當微服務之間的調用的時候,該如何傳遞用戶信息?

這篇文章就是解決這些問題的,也是這篇文章的主題——網關:

  • 網關路由:解決前端請求路口統一的問題
  • 網關鑒權:解決統一登錄校驗和用戶信息獲取問題
  • 統一配置管理:解決微服務,配置文件重復的問題和配置熱更新的問題

1.網關路由

1.1 認識網關

網關(Gateway) 是微服務架構中的統一入口,它位于客戶端與服務端之間,接收所有外部請求,然后根據請求內容將其轉發到對應的后端微服務。

可以理解為:網關是微服務系統的“前門”。

在這里插入圖片描述

從圖中可以看出,網關作為后端的一部分,承擔了統一入口的作用。前端只需請求網關,不需要關心各個微服務的具體地址。網關接收到請求后,會通過訪問 Nacos 注冊中心獲取服務的最新信息,并根據配置的路由規則將請求轉發到對應服務,同時實現負載均衡和服務發現,簡化了前端調用邏輯,也增強了系統的靈活性與可維護性。

1.2 快速實現

  • 創建網關模塊
  • 引入網關的依賴
  • 編寫啟動類
  • 編寫配置文件

1.3 配置文件

這里主要講解一下配置文件

server:port: 8080  # 網關端口,給前端的端口spring:application:name: gateway-servicecloud:nacos:discovery:server-addr: localhost:8848  # Nacos注冊中心地址gateway:routes:# 訂單服務路由- id: order_routeuri: lb://order-service    # 負載均衡到訂單服務predicates:- Path=/order/**         # 匹配/order開頭的請求- Method=GET,POST        # 允許GET/POST請求filters:- StripPrefix=1           # 移除第一段路徑(/order)- AddRequestHeader=Gateway,true  # 添加請求頭# 用戶服務路由- id: user_routeuri: lb://user-servicepredicates:- Path=/user/**           # 匹配/user路徑- After=2025-01-01T00:00:00.000+08:00  # 時間生效范圍filters:- PrefixPath=/api         # 添加前綴 /api/user/**

這里我們重點關注predicates,也就是路由斷言。SpringCloudGateway中支持的斷言類型有很多:

名稱說明示例
After是某個時間點后的請求- After=2037-01-20T17:42:47.789-07:00[America/Denver]
Before是某個時間點之前的請求- Before=2031-04-13T15:14:47.433+08:00[Asia/Shanghai]
Between是某兩個時間點之前的請求- Between=2037-01-20T17:42:47.789-07:00[America/Denver], 2037-01-21T17:42:47.789-07:00[America/Denver]
Cookie請求必須包含某些cookie- Cookie=chocolate, ch.p
Header請求必須包含某些header- Header=X-Request-Id, \d+
Host請求必須是訪問某個host(域名)- Host=.somehost.org,.anotherhost.org
Method請求方式必須是指定方式- Method=GET,POST
Path請求路徑必須符合指定規則- Path=/red/{segment},/blue/**
Query請求參數必須包含指定參數- Query=name, Jack或者- Query=name
RemoteAddr請求者的ip必須是指定范圍- RemoteAddr=192.168.1.1/24
weight權重處理

2.網關登錄校驗

2.1 思路分析(JWT為例)

在這里插入圖片描述

我們看到這張圖網關需要完成的工作為:

  • JWT信息校驗
  • 將用戶信息傳遞到下游
  • 然后微服務之間傳遞用戶信息

2.2 網關過濾器

首先,登錄校驗一定要放在轉發路由之前去完成,因此我們需要先了解網關是如何進行工作的。

在這里插入圖片描述

  1. 接受前端的請求,HandlerMapper,進行路由匹配
  2. 然后經過一系列的過濾器鏈
    1. 路徑處理過濾器
    2. 添加信息頭處理器
    3. 自定義處理器
    4. 等等
  3. 路由轉發到相應的微服務中
  4. 微服務返回結果再次經過過處理器鏈

我們需要做的就是在轉發到微服務之前進行登錄校驗,就是自定義一個網關過濾器。

網關過濾器鏈中的過濾器有兩種:

  • GatewayFilter: 路由過濾器,作用范圍比較靈活,可以是任意指定的路由Route
  • GlobalFilter:全局過濾器,作用范圍是所有路由,不可配置。

如何理解這個不可配置是什么意思? 意思就是不可以通過配置文件動態配置

2.2.1 基于全局過濾器實現登錄校驗

我們就自定義一個基于全局過濾器的登錄校驗過濾器

1. 創建自定義全局過濾器

我們創建一個LoginCheckGlobalFilter類,實現GlobalFilterOrdered接口:

import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;@Component
public class LoginCheckGlobalFilter implements GlobalFilter, Ordered {// 白名單路徑(不需要登錄校驗的路徑)private static final List<String> WHITE_LIST = Arrays.asList("/api/user/login","/api/user/register","/api/doc.html","/api/swagger-ui.html");@Overridepublic Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {// 1. 獲取請求路徑String path = exchange.getRequest().getPath().toString();// 2. 檢查是否在白名單中if (WHITE_LIST.contains(path)) {return chain.filter(exchange); // 直接放行}// 3. 獲取tokenString token = exchange.getRequest().getHeaders().getFirst("Authorization");// 4. 校驗tokenif (StringUtils.isBlank(token) || !validateToken(token)) {// 未登錄或token無效exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);return exchange.getResponse().setComplete();}// 5. token有效,放行請求return chain.filter(exchange);}@Overridepublic int getOrder() {return -1; // 設置過濾器執行順序(數值越小優先級越高)}// 模擬token校驗方法(實際項目中應調用認證服務)private boolean validateToken(String token) {// 這里簡單模擬校驗邏輯return token != null && token.startsWith("Bearer ");}
}
2. 過濾器邏輯說明
  1. 白名單檢查:放行登錄、注冊等不需要認證的接口
  2. Token獲取:從請求頭Authorization中獲取JWT token
  3. Token校驗
    • 無效token:返回401 Unauthorized
    • 有效token:放行請求

2.3 微服務直接如何傳遞用戶信息

現在,網關已經可以完成登錄校驗并獲取登錄用戶身份信息。但是當網關將請求轉發到微服務時,微服務又該如何獲取用戶身份呢?

由于網關發送請求到微服務依然采用的是Http請求,因此我們可以將用戶信息以請求頭的方式傳遞到下游微服務。然后微服務可以從請求頭中獲取登錄用戶信息。考慮到微服務內部可能很多地方都需要用到登錄用戶信息,因此我們可以利用SpringMVC的攔截器來實現登錄用戶信息獲取,并存入ThreadLocal,方便后續使用。

大致流程

首先每一次請求可以看做一個線程,之前在單體項目的時候。經過登錄校驗的時候,把信息放入線程的上下文中。我微服務項目也可以根據這個方法,進行改造一下:

  1. 網關 → 微服務:通過 HTTP請求頭 傳遞用戶信息(如JWT解析后的用戶ID)。
  2. 微服務內部:通過 SpringMVC攔截器 + ThreadLocal 存儲用戶信息,避免重復解析。
  3. 微服務間調用(OpenFeign):通過 Feign攔截器 透傳用戶信息,確保鏈路完整。
具體步驟

1. 網關傳遞用戶信息到微服務

網關(如Spring Cloud Gateway):在登錄校驗后,將用戶信息(如userIdusername)添加到請求頭中,轉發到下游微服務。

# 網關配置示例(Spring Cloud Gateway)
spring:cloud:gateway:default-filters:- name: AddRequestHeaderargs:name: X-User-Idvalue: "#{@userContext.getUserId()}" # 從JWT解析后注入

2. 微服務接收并存儲用戶信息

2.1 定義ThreadLocal上下文
public class UserContext {private static final ThreadLocal<Long> userIdHolder = new ThreadLocal<>();public static void setUserId(Long userId) {userIdHolder.set(userId);}public static Long getUserId() {return userIdHolder.get();}public static void clear() {userIdHolder.remove();}
}
2.2 攔截器解析請求頭
@Component
public class UserInterceptor implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {String userId = request.getHeader("X-User-Id");if (userId != null) {UserContext.setUserId(Long.valueOf(userId));}return true;}@Overridepublic void afterCompletion(...) {UserContext.clear(); // 避免內存泄漏}
}
2.3 注冊攔截器
@Configuration
public class WebConfig implements WebMvcConfigurer {@Autowiredprivate UserInterceptor userInterceptor;@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(userInterceptor);}
}

3. OpenFeign透傳用戶信息

微服務間調用時,需通過Feign攔截器將用戶信息附加到請求頭,確保鏈路透明。

3.1 Feign攔截器實現

java

復制

@Component
public class FeignUserInterceptor implements RequestInterceptor {@Overridepublic void apply(RequestTemplate template) {Long userId = UserContext.getUserId();if (userId != null) {template.header("X-User-Id", String.valueOf(userId));}}
}
3.2 啟用Feign攔截器

確保Feign客戶端掃描到該攔截器(通常已自動注入)。


4. 完整流程總結

  1. 用戶請求 → 網關:攜帶JWT令牌。
  2. 網關:解析JWT → 將userId放入請求頭 → 轉發到微服務A。
  3. 微服務A
    • 攔截器讀取X-User-Id → 存入UserContext(ThreadLocal)。
    • 業務代碼通過UserContext.getUserId()獲取用戶信息。
  4. 微服務A → 微服務B(通過OpenFeign):
    • Feign攔截器自動附加X-User-Id到請求頭。
  5. 微服務B:重復步驟3的流程。

3. 配置管理

我們已經解決了微服務間的通信、注冊發現、路由、登錄鑒權等核心問題,但還有三大痛點懸而未決:

  • 網關路由硬編碼:路由規則寫在application.yml里,改個路徑就得重啟網關
  • 業務配置寫死:數據庫連接、業務開關、限流閾值全在代碼里,調個參數就得重新打包部署
  • 重復配置爆炸:每個微服務都復制粘貼Redis、MySQL、日志配置,一改全改,維護成本飆升?
解決方案:統一配置中心

Nacos/Apollo等配置中心,把所有配置集中管理,實現:

  1. 動態路由:網關路由規則放到配置中心,熱更新無需重啟。

    # Nacos中動態配置網關路由
    spring:cloud:gateway:routes:- id: user-serviceuri: lb://user-servicepredicates:- Path=/user/**  # 修改后立即生效
    
  2. 業務配置動態化:用@RefreshScope注解,運行時刷新配置

    @Component
    @RefreshScope
    public class DynamicConfig {@Value("${order.timeout:30}") // 配置中心修改后自動更新private Long timeout;
    }
    
  3. 共享配置模板

    • 通用配置(如Redis、MySQL)抽成shared-config.yml,所有微服務復用
    • 差異化配置(如端口、服務名)獨立維護,避免冗余。

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

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

相關文章

Text2SQL智能問答系統開發(一)

開發一個面向企業的chatBI工作流 已完成 基礎 Text2SQL 功能實現 實現用戶輸入自然語言問題后&#xff0c;系統能夠自動生成 SQL 并執行返回結果。用戶交互優化 支持用戶通過補充信息對查詢進行調整&#xff0c;提升易用性。模糊時間處理機制 對“最近”“近期”等模糊時間關…

Python HTML模塊詳解:從基礎到實戰

一、模塊體系全景圖 Python生態中處理HTML的工具可分為三大層級&#xff1a; 標準庫基礎層&#xff1a;html模塊 html.parser第三方增強層&#xff1a;BeautifulSoup&#xff08;搭配解析器&#xff09;專業級工具層&#xff1a;lxml requests-html 二、標準庫核心模塊詳解…

PyTorch常用Tensor形狀變換函數詳解

PyTorch常用Tensor形狀變換函數詳解 在PyTorch中&#xff0c;對張量&#xff08;Tensor&#xff09;進行形狀變換是深度學習模型構建中不可或缺的一環。無論是為了匹配網絡層的輸入要求&#xff0c;還是為了進行數據預處理和維度調整&#xff0c;都需要靈活運用各種形狀變換函數…

自主智能Agent如何重塑工作流自動化:技術、經濟與未來展望

自主智能Agent的崛起與工作流自動化的范式革命2025年7月&#xff0c;當OpenAI向付費用戶推出具備網頁瀏覽和代碼執行能力的ChatGPT Agent時&#xff0c;工作流自動化領域迎來了一場靜默但徹底的革命。這款不再滿足于簡單問答的智能體&#xff0c;在一個安全的虛擬計算機環境中運…

技術架構、行業應用、工具鏈整合、挑戰應對及未來趨勢五大模塊,引用多個權威來源數據與開源項目實現細節。

以下是一份關于AI技術落地的實戰經驗總結報告&#xff0c;結合代碼示例、可視化圖表與行業案例&#xff0c;內容分為技術架構、行業應用、工具鏈整合、挑戰應對及未來趨勢五大模塊&#xff0c;引用多個權威來源數據與開源項目實現細節。AI技術落地實戰指南&#xff1a;從架構設…

第 9 篇:神經網絡初探——當AI擁有了“大腦”,世界從此不同

《人工智能AI之機器學習基石》系列⑨ 專欄核心理念: 用通俗語言講清楚機器學習的核心原理,強調“洞察 + 技術理解 + 應用連接”,構建一個完整的、富有啟發性的知識體系。

音頻焦點 Android Audio Focus 進階

舊焦點處理 示例調用鏈: requestAudioFocus() → propagateFocusLossFromGain_syncAf() → handleFocusLossFromGain()。 系統事件(如來電)→ 強制焦點變化 → handleFocusLossFromGain()。 函數 propagateFocusLossFromGain_syncAf 焦點持有者發生的焦點丟失通知 主要功能…

MFC UI對話框

文章目錄對話框模態對話框創建銷毀關閉CDialog::OnCancel()EndDialog()CDialog::DestroyWindow()非模態對話框創建銷毀關閉delete this對話框 模態對話框 ??阻塞父窗口??&#xff0c;強制用戶先處理對話框。關閉前父窗口無法響應事件。 創建 推薦&#xff1a;非指針方式…

RabbitMQ--@RabbitListener及@RabbitHandle

兩者區別 在 Spring AMQP 中&#xff0c;RabbitListener 和 RabbitHandler 是處理 RabbitMQ 消息的核心注解&#xff0c;但職責和使用場景完全不同。以下從 定義、區別、場景、示例 逐層解析&#xff1a;一、核心定義1. RabbitListener作用&#xff1a;標記 方法或類 為 Ra…

【基于CKF的IMM】MATLAB例程,CV和CT兩個模型下的IMM,二維,濾波使用CKF(容積卡爾曼濾波),附下載鏈接

本程序實現了基于交互多模型&#xff08;IMM&#xff09;容積卡爾曼濾波&#xff08;CKF&#xff09;的多模型融合定位方法&#xff0c;并與純CV?CKFCV-CKFCV?CKF&#xff08;勻速模型&#xff09;和CT?CKFCT-CKFCT?CKF&#xff08;勻角速度轉彎模型&#xff09;方法對比。…

AI資訊日報 - 2025年07月28日

AI資訊日報 | 2025年07月28日 周一 今日核心要點 精華提煉 技術突破 ? 騰訊混元&#xff1a;開源發布、3D技術 ? 書生Intern&#xff1a;開源發布、多模態 企業動態 ? AI工具集&#xff1a;協同創作、視頻生成 數據概覽分類數量重點關注技術突破2 條開源發布、3D技術企業動態…

大語言模型 LLM 通過 Excel 知識庫 增強日志分析,根因分析能力的技術方案(1):總體介紹

文章大綱 1. 核心目標 2. 系統總體架構 3. Google Cloud 端到端方案(含無 RAG & RAG 雙模式) 3.1 無 RAG:Function-Calling 查表模式 3.2 RAG:托管式向量檢索 4. 開源輕量級方案 5. 數字孿生聯合驗證(實驗性) 6. 知識圖譜增強(Neo4j) 7. 監控與持續優化(CometLLM)…

Deepseek + browser-use 輕松實現瀏覽器自動化

在數字化時代&#xff0c;瀏覽器應用廣泛&#xff0c;瀏覽器自動化可大幅提升效率。Deepseek 是強大的智能語言模型&#xff0c;能精準解析復雜指令&#xff0c;browser - use 是專注瀏覽器操作的工具&#xff0c;提供豐富 API 接口&#xff0c;支持主流瀏覽器的各類自動化操作…

開疆智能ModbusTCP轉Profient網關連接西門子PLC與川崎機器人配置案例

本案例是西門子PLC與川崎機器人通過Profient轉ModbusTCP網關進行通訊轉換的配置案例&#xff0c;西門子作為profinet主站&#xff0c;機器人作為ModbusTCP服務器。配置過程&#xff1a;機器人配置川崎機器人控制器提供了RS232、以太網的通信接口&#xff0c;同時也可通過加裝選…

Docker多主機網絡連接:實現跨主機通信

Docker 是一種流行的容器化平臺&#xff0c;它可以幫助開發人員更方便地構建、發布和管理應用程序。在 Docker 中&#xff0c;容器是獨立運行的應用程序包裝&#xff0c;包含了運行所需的所有文件、庫和環境變量。Docker 提供了多種網絡連接方式&#xff0c;使得容器之間可以進…

OSPF筆記

一、OSPF基礎1、技術背景&#xff08;RIP中存在的問題&#xff09;RIP中存在最大跳數為15的限制&#xff0c;不能適應大規模組網周期性發送全部路由信息&#xff0c;占用大量的帶寬資源路由收斂速度慢以跳數作為度量值存在路由環路可能性每隔30秒更新2、OSPF協議特點沒有跳數限…

kotlin基礎【3】

Kotlin Playground: Edit, Run, Share Kotlin Code Online 資料&#xff1a;kotlin實戰 第一章 data class Person(val name: String,val age:Int?null)//允許接受以age為空&#xff0c;當為空將它賦值為null,如果不這么寫直接寫age:Int?是否可以fun main(args:Array<St…

Java-數構二叉樹

1.樹 1.1概念 樹是一種非線性的數據結構&#xff0c;它是由n個有限節點組成一個具有層次關系。這種結構有以下特點&#xff1a; 一個特殊的結點&#xff0c;稱為根節點&#xff0c;根節點沒有前驅節點除根節點以外&#xff0c;其余節點分成M個互不相交的集合。每個集合又是一…

編程中水合的理解

在編程中&#xff0c;水合&#xff08;Hydration&#xff09; 是一個常見概念&#xff0c;尤其在 前端開發 和 服務端渲染&#xff08;SSR&#xff09; 場景中頻繁出現。它的核心含義是&#xff1a;將靜態內容“激活”為交互式動態內容。1. 水合的本質簡單理解&#xff1a;水合…

使用ffmpeg轉碼h265后mac默認播放器不支持問題

由于mac自帶錄屏是mov并且文件特別大&#xff0c;我使用ffmpeg轉碼視頻為h265使用如下命令ffmpeg_command [ffmpeg_path,"-i", input_path,"-c:v", "libx265","-preset", "veryslow","-map_metadata", "0&q…