微服務——網關、網關登錄校驗、OpenFeign傳遞共享信息、Nacos共享配置以及熱更新、動態路由

之前學習了Nacos,用于發現并注冊、管理項目里所有的微服務,而OpenFeign簡化微服務之間的通信,而為了使得前端可以使用微服務項目里的每一個微服務的接口,就應該將所有微服務的接口管理起來方便前端調用,所以有了網關。

前端調用后端微服務項目的接口時,不需要指定每一個接口具體的地址,只需要將請求發送到后端的網關即可。

網關介紹

網關是網絡的關口,負責請求的路由、轉發、身份校驗 。

網關模塊的配置

1、新建一個maven空模塊,配置一下依賴

<dependencies>
......<!-- 其它依賴 --><!--網關-->  
<dependency>  <groupId>org.springframework.cloud</groupId>  <artifactId>spring-cloud-starter-gateway</artifactId>  
</dependency> <!--nocos discovery-->  
<dependency>  <groupId>com.alibaba.cloud</groupId>  <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>  
</dependency>  <!--負載均衡-->  
<dependency>  <groupId>org.springframework.cloud</groupId>  <artifactId>spring-cloud-starter-loadbalancer</artifactId>  
</dependency>  </dependencies>  
<build>  <finalName>${project.artifactId}</finalName>  <plugins>  <plugin>  <groupId>org.springframework.boot</groupId>  <artifactId>spring-boot-maven-plugin</artifactId>  </plugin>  </plugins>  
</build>  

2、創建com.<XXX項目名稱>.gateway包,報下名新建配置類

@SpringBootApplication
public class GatewayApplication {public static void main(String[] args) {SpringApplication.run(GatewayApplication.class, args);}
}

3、在靜態資源目錄下新建application.yaml文件,配置網關相關屬性

server:port: 8080  # 網關服務的端口號,指定網關運行在 8080 端口
spring:application:name: gateway  # 應用名稱,注冊到 Nacos 的服務名稱cloud:nacos:server-addr: 192.168.52.128:8848  # Nacos 服務器地址,配置 Nacos 注冊中心地址gateway:routes:  # 路由配置- id: item-service  # 路由 ID,唯一標識,可以隨便命名uri: lb://item-service  # 目標服務地址,即從注冊中心獲取 item-service 的地址predicates:  # 斷言,即路由轉發的規則- Path=/items/**,/search/**  # 匹配 /items/ 開頭的和 /search/ 開頭的請求到 item-service 服務獲取響應- id: user-service  uri: lb://user-service  predicates:  - Path=/items/**,/search/** 

4、最后啟動整個項目的時候也要把網關啟動

由下圖可見網關的效果有了

?網關的登錄校驗

網關過濾器有兩種,分別是:

  • GatewayFilter: 路由過濾器,作用于任意指定的路由;默認不生效,要配置到路由后生效。
  • GlobalFilter: 全局過濾器,作用范圍是所有路由;聲明后自動生效。

網關加公共依賴XXX-common實現請求的校驗

1、網關過濾器過濾請求(Filters文件夾)

@Component // 將該類標記為Spring組件,使其成為Spring容器管理的Bean
@RequiredArgsConstructor // Lombok注解,自動生成一個包含所有final字段的構造函數
public class AuthGlobalFilter implements GlobalFilter, Ordered {// 依賴注入JwtTool,用于JWT的解析和驗證private final JwtTool jwtTool;// 依賴注入AuthProperties,包含認證相關的配置信息,如排除路徑等private final AuthProperties authProperties;// AntPathMatcher用于路徑匹配,判斷請求路徑是否在排除路徑中private final AntPathMatcher antPathMatcher = new AntPathMatcher();@Overridepublic Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {// 1. 獲取當前請求對象ServerHttpRequest 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 (headers != null && !headers.isEmpty()) {token = headers.get(0); // 獲取第一個authorization頭,通常為Bearer Token}// 4. 校驗并解析tokenLong userId = null;try {// 使用JwtTool解析token,獲取用戶IDuserId = jwtTool.parseToken(token);} catch (UnauthorizedException e) {// 如果token無效或解析失敗,攔截請求并返回401 Unauthorized狀態碼ServerHttpResponse response = exchange.getResponse();response.setStatusCode(HttpStatus.UNAUTHORIZED);return response.setComplete(); // 結束請求處理}// 打印用戶ID(通常用于調試,生產環境中不建議直接打印敏感信息)System.out.println("userId = " + userId);String userInfo = userId.toString();// 將用戶信息存入請求頭ServerWebExchange swe = exchange.mutate().request(builder -> builder.header("user-info", userInfo)).build();// 5. 如果token有效,繼續執行后續的過濾器鏈return chain.filter(swe);}// 判斷請求路徑是否在排除路徑列表中private boolean isExclude(String path) {for (String pathPattern : authProperties.getExcludePaths()) {// 使用AntPathMatcher進行路徑匹配if (antPathMatcher.match(pathPattern, path)) {return true; // 如果匹配到排除路徑,返回true}}return false; // 否則返回false}@Overridepublic int getOrder() {// 返回過濾器的執行順序,0表示最高優先級return 0;}
}

?過濾器里涉及的一些依賴

// jwt校驗工具
@Component
public class JwtTool {private final JWTSigner jwtSigner;public JwtTool(KeyPair keyPair) {this.jwtSigner = JWTSignerUtil.createSigner("rs256", keyPair);}public String createToken(Long userId, Duration ttl) {// 1.生成jwsreturn JWT.create().setPayload("user", userId).setExpiresAt(new Date(System.currentTimeMillis() + ttl.toMillis())).setSigner(jwtSigner).sign();}/*** 解析token** @param token token* @return 解析刷新token得到的用戶信息*/public Long parseToken(String token) {// 1.校驗token是否為空if (token == null) {throw new UnauthorizedException("未登錄");}// 2.校驗并解析jwtJWT jwt;try {jwt = JWT.of(token).setSigner(jwtSigner);} catch (Exception e) {throw new UnauthorizedException("無效的token", e);}// 2.校驗jwt是否有效if (!jwt.verify()) {// 驗證失敗throw new UnauthorizedException("無效的token");}// 3.校驗是否過期try {JWTValidator.of(jwt).validateDate();} catch (ValidateException e) {throw new UnauthorizedException("token已經過期");}// 4.數據格式校驗Object userPayload = jwt.getPayload("user");if (userPayload == null) {// 數據為空throw new UnauthorizedException("無效的token");}// 5.數據解析try {return Long.valueOf(userPayload.toString());} catch (RuntimeException e) {// 數據格式有誤throw new UnauthorizedException("無效的token");}}
}// 攔截器攔截
@Data
@Component
@ConfigurationProperties(prefix = "hm.auth")
public class AuthProperties {private List<String> includePaths;private List<String> excludePaths;
}

2、網關的yaml文件里配置不需要校驗直接放行的請求?

hm:jwt: #解析jwt密鑰文件location: classpath:hmall.jksalias: hmallpassword: hmall123tokenTTL: 30mauth:excludePaths:- /search/**- /users/login- /items/**- /hi

3、由于每一個微服務都導入了XX-common模塊的依賴,所以在XX-common模塊里配置并注冊攔截器,攔截所有發送到每個微服務里的請求,用于將請求頭里用戶信息存入線程池。

public class UserInfoInterceptor implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {// 1. 獲取登錄用戶信息String userInfo = request.getHeader("user-info"); // 從請求頭里獲取// 2. 判斷是否獲取了用戶,如果有,存入ThreadLocalif (StrUtil.isNotBlank(userInfo)) {UserContext.setUser(Long.valueOf(userInfo));}// 3. 放行return true;}@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {// 清理用戶UserContext.removeUser();}
}

?4、注冊XX-common模塊里的攔截器

@Configuration
@ConditionalOnClass(DispatcherServlet.class) // 使得網關不去生效改攔截器
public class MvcConfig implements WebMvcConfigurer {@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(new UserInfoInterceptor());// 默認攔截所有的請求,目的是為了將每一個請求里包含的用戶信息存入線程池}
}

5、配置靜態資源文件夾下的spring.factories文件,取保每個微服務可以讀取到XX-common模塊里的攔截器

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\com.hmall.common.config.MyBatisConfig,\com.hmall.common.config.MvcConfig,\com.hmall.common.config.JsonConfig

OpenFeign傳遞用戶信息

使用OpenFeign時,一個微服務發送給另一個微服務的請求也要攜帶用戶信息到請求頭里,要和網關發送給微服務的請求一樣。所有要在公共api模塊里加攔截器,使得每一個請求的請求頭里添加用戶信息。

寫到OpenFeign的配置類里,且微服務的啟動類加上@EnableFeignClients(basePackages = "com.hmall.api.client",?defaultConfiguration = DefaultFeignConfig.class)的注解

 // 寫到OpenFeign的配置類里,且微服務的啟動類加上
// @EnableFeignClients(basePackages = "com.hmall.api.client",
//  defaultConfiguration = DefaultFeignConfig.class)的注解
@Bean // 聲明為一個Bean,可以被Spring容器管理public RequestInterceptor userInfoRequestInterceptor() {return new RequestInterceptor() {@Overridepublic void apply(RequestTemplate template) {// 獲取當前用戶的IDLong userId = UserContext.getUser(); // 導入XXX-common模塊里的線程池// 如果用戶ID不為空,則添加到請求頭中if (userId != null) { // 確保每一個微服務之間發送的請求也攜帶user-info到請求頭里// 將用戶ID添加到請求頭中,key為"user-info"System.out.println("將用戶ID添加到請求頭中,key為user-info,id為" + userId);template.header("user-info", userId.toString());}}};}

nacos共享配置

由于每一個微服務的yaml文件里有多個共同的配置信息,所有可以將其抽取出來的配置共同使用nacos注冊中心配置。

?每一個微服務里導入如下依賴即可實現。

<!-- 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>  

nacos里共同配置的信息()${XXX:YY}表示如果讀取不到XXX則默認為YY

# 數據庫和mybatis
spring:datasource:url: jdbc:mysql://${hm.db.host:192.168.52.128}:${hm.db.port:3306}/${hm.db.database}?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&serverTimezone=Asia/Shanghaidriver-class-name: com.mysql.cj.jdbc.Driverusername: ${hm.db.un:root}password: ${hm.db.pw:123}
mybatis-plus:configuration:default-enum-type-handler: com.baomidou.mybatisplus.core.handlers.MybatisEnumTypeHandlerglobal-config:db-config:update-strategy: not_nullid-type: auto# 日志記錄
logging:level:com.hmall: debugpattern:dateformat: HH:mm:ss:SSSfile:path: "logs/${spring.application.name}"# swagger配置
knife4j:enable: trueopenapi:title: ${hm.swagger.title:黑馬商城接口文檔}description: ${hm.swagger.desc:黑馬商城接口文檔}email: zhanghuyi@itcast.cnconcat: 虎哥url: https://www.itcast.cnversion: v1.0.0group:default:group-name: defaultapi-rule: packageapi-rule-resources:- ${hm.swagger.package}

拉取nacos里的配置文件到本地微服務(以下為bootstrap.yaml文件)

spring:  main:  additional-properties: --add-opens=java.base/java.lang.invoke=ALL-UNNAMED  application:  # 應用程序名稱,用于標識該服務,在Nacos或其他服務注冊中心中可用到  name: cart-service  cloud:  nacos:  # Nacos的服務地址,用于連接到Nacos服務器  server-addr: localhost:8848  # nacos地址  config:  # 配置文件的格式,這里指定為YAML格式  file-extension: yaml  # 定義共享配置文件列表,這些配置將從Nacos服務器加載  shared-configs:   # 一定對應好nacos里的Data ID- data-id: shared-jdbc.yaml  # JDBC共享配置文件  - data-id: shared-log.yaml    # 日志共享配置文件  - data-id: shared-swagger.yaml # Swagger共享配置文件  

nacos配置里的變量在本地微服務里配置好(以下為application.yaml文件)

server:port: 8082
feign:okhttp:enabled: true
hm:db:database: hm-cartswagger:title: "黑馬城市購物車服務接口文檔"package: com.hmall.cart.controller

配置熱更新

配置熱更新:修改配置文件里的配置的時候,不需要重新啟動微服務項目配置就可以生效配置。

具體應用實例

需求:購買車的限定數量目前是寫死在業務中的,將其改為讀取配置文件屬性,并將配置置交給Nacos管理,實現熱更新。

首先在nocas配置要限定數量所在的微服務的yaml文件

之后在對應的微服務里添加config文件?

@Data
@Component
@ConfigurationProperties(prefix = "hm.cart") // 對應yaml文件里的配置
public class CartProperties {private Integer maxItems;
}

最后在業務文件里面就可以去使用了

private final CartProperties cartProperties; // 導入依賴
......private void checkCartsFull(Long userId) {int count = lambdaQuery().eq(Cart::getUserId, userId).count();if (count >= cartProperties.getMaxItems()) {throw new BizIllegalException(StrUtil.format("用戶購物車課程不能超過{}", cartProperties.getMaxItems()));}}
......

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

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

相關文章

2025年3月11日(有限元牛頓迭代法:通俗講解與示例)

牛頓迭代法的正確流程解釋 是的&#xff0c;你的理解基本正確&#xff01;但需要更準確地描述內外力的關系和迭代邏輯。以下是更清晰的步驟說明&#xff1a; 核心流程&#xff08;修正版&#xff09; 假設已知 外力 ( F_{\text{ext}} )&#xff08;如2000 N&#xff09;&…

爬蟲的精準識別:基于 User-Agent 的正則實現

&#x1f9d1; 博主簡介&#xff1a;CSDN博客專家&#xff0c;歷代文學網&#xff08;PC端可以訪問&#xff1a;https://literature.sinhy.com/#/?__c1000&#xff0c;移動端可微信小程序搜索“歷代文學”&#xff09;總架構師&#xff0c;15年工作經驗&#xff0c;精通Java編…

【AI大模型】LLM訓練deepseek如何識別視頻

要讓像DeepSeek這樣的大語言模型&#xff08;LLM&#xff09;具備視頻識別能力&#xff0c;需要結合多模態學習技術&#xff0c;將視覺信息與文本語義進行融合。以下是實現這一目標的關鍵步驟和技術要點&#xff1a; --- 一、視頻識別的核心挑戰 1. 多模態數據&#xff1a;視頻…

【物聯網-以太網-W5500】

物聯網-以太網-W5500 ■ W5500-簡介■■■■ ■ W5500-簡介 ■ ■ ■ ■

centos linux安裝mysql8 重置密碼 遠程連接

1. 下載并安裝 MySQL Yum 倉庫 從 MySQL 官方網站下載并安裝 Yum 倉庫配置文件。 # 下載MySQL 8.0的Yum倉庫包 wget https://dev.mysql.com/get/mysql80-community-release-el7-5.noarch.rpm # 安裝Yum倉庫包 sudo rpm -ivh mysql80-community-release-el7-5.noarch.rpm2. 啟…

C++【類和對象】(超詳細!!!)

C【類和對象】 1.運算符重載2.賦值運算符重載3.日期類的實現 1.運算符重載 (1).C規定類類型運算符使用時&#xff0c;必須轉換成調用運算符重載。 (2).運算符重載是具有特殊名字的函數&#xff0c;名字等于operator加需要使用的運算符&#xff0c;具有返回類型和參數列表及函數…

【面試】Java 多線程

多線程 1、什么是線程和進程2、創建線程有幾種方式3、線程有幾種狀態4、什么是上下文切換5、什么是守護線程&#xff0c;和普通線程有什么區別6、什么是線程池&#xff0c;如何實現的7、Executor和Executors的區別8、線程池處理任務的流程9、線程數設定成多少更合適10、執行exe…

宿主機運行pyspark任務讀取docker hadoop容器上的數據

熬了四個大夜才搞明白&#xff0c;最晚一天熬到早上十點/(ㄒoㄒ)/~~&#xff0c;最后發現只要加個參數就解決了。。。抱頭痛哭 問題描述&#xff1a; Hadoop集群部署在docker容器中&#xff0c;宿主機執行pyspark程序讀取hive表 問題一&#xff1a;當master(local[*])時&…

《平凡的世界》:在泥土中尋找星辰的勇氣

“平凡不是平庸的代名詞&#xff0c;而是千萬人用脊梁扛起時代的勛章。”——路遙的《平凡的世界》用百萬字書寫了黃土地上孫少安、孫少平兩兄弟的掙扎與覺醒&#xff0c;撕開了“奮斗逆襲”的浪漫濾鏡&#xff0c;告訴你&#xff1a;真正的英雄主義&#xff0c;是在認清了生活…

【SpringMVC】深入解析使用 Postman 和瀏覽器模擬將單個與多個參數傳遞到后端和后端接收過程

SpringMVC—請求(Request) 訪問不同的路徑&#xff0c;就是發送不同的請求&#xff1b;在發送請求時&#xff0c;可能會帶一些參數&#xff0c;所以學習Spring的請求&#xff0c;主要是學習如何傳遞參數到后端以及后端如何接收&#xff1b; 我們主要是使用 瀏覽器 和 Postman …

04 | 初始化 fastgo 項目倉庫

提示&#xff1a; 所有體系課見專欄&#xff1a;Go 項目開發極速入門實戰課&#xff1b;歡迎加入我的訓練營&#xff1a;云原生AI實戰營&#xff0c;一個助力 Go 開發者在 AI 時代建立技術競爭力的實戰營&#xff1b;本節課最終源碼位于 fastgo 項目的 feature/s01 分支&#x…

Docker 安裝成功后,安裝 Dify 中文版本的步驟

Docker 安裝成功后&#xff0c;安裝 Dify 中文版本的步驟如下1&#xff1a; 克隆 Dify 代碼倉庫&#xff1a;在終端中執行以下命令&#xff0c;將 Dify 源代碼克隆至本地環境。 bash git clone https://github.com/langgenius/dify.git進入 Dify 的 docker 目錄&#xff1a; b…

RPC服務調用深度解析:從原理到Java實踐

一、RPC的核心原理與架構設計 1.1 RPC的本質 RPC&#xff08;Remote Procedure Call&#xff09;是一種分布式系統間通信協議&#xff0c;允許程序像調用本地方法一樣調用遠程服務。其核心目標是通過位置透明性和協議標準化隱藏網絡通信細節。RPC的調用流程可抽象為以下步驟&…

電腦的寫字板如何使用?

打開寫字板&#xff1a; 直接按一下鍵盤上的win R 鍵&#xff0c;然后輸入&#xff1a;write &#xff0c; 再按一下回車 , 即可打開寫字板 可以在里面寫文字 和 插入圖片等… &#xff0c; 如下所示&#xff1a; 保存寫字板內容&#xff1a; 當我們寫好了之后&#xff0c;…

醫療AI測試實戰:如何確保人工智能安全賦能醫療行業?

一、醫療AI測試的重要性 人工智能&#xff08;AI&#xff09;正廣泛應用于醫療行業&#xff0c;如疾病診斷、醫學影像分析、藥物研發、手術機器人和智能健康管理等領域。醫療AI技術的應用不僅提高了診斷效率&#xff0c;還能降低誤診率&#xff0c;改善患者治療效果。然而&…

AI日報 - 2025年3月12日

AI日報 - 2025年3月12日 &#x1f31f; 今日概覽&#xff08;60秒速覽&#xff09; ▎&#x1f916; AGI突破 | Anthropic CEO預測AI將主導代碼編寫 &#x1f52c; 自訓練技術顯著提升LLM思維清晰度 ▎&#x1f4bc; 商業動向 | OpenAI與CoreWeave達成119億美元基建協議 &…

跳表數據結構

跳表&#xff08;Skip List&#xff09;是一種支持高效插入、刪除和查找的鏈表結構&#xff0c;用于加速查找操作&#xff0c;特別適用于有序數據集合。它在Redis、LevelDB等系統中被用于**有序集合&#xff08;Sorted Set&#xff09;**的實現。 1. 跳表的結構 跳表的核心思…

系統會把原先的對話狀態堆棧從 [“assistant“] 更新為 [“assistant“, “update_flight“]這個更新的處理過程

這個更新主要是在 State 定義中通過 Annotated 來自動處理的。在 State 類型中&#xff0c;我們對 dialog_state 字段綁定了 update_dialog_stack 函數&#xff0c;如下所示&#xff1a; class State(TypedDict):messages: Annotated[list[AnyMessage], add_messages]user_inf…

HTTP發送POST請求的兩種方式

1、json String json HttpRequest.post(getUrl(method, "v1", url, userId, appKey)).header("Content-type", "application/json") // 設置請求頭為 JSON 格式.body(JSONUtil.toJsonStr(params)) // 請求體為 JSON 字符串.execute().body(); …

Windows 萬興恢復專家 Wondershare Recoverit-v13.5.7.9-[電腦數據恢復工具]

Windows 萬興恢復專家Wondershare_Recoverit 鏈接&#xff1a;https://pan.xunlei.com/s/VOL3z608vzAj_IYTvH-F1q7kA1?pwdiu89# 1. 打開Setup.exe進行安裝&#xff0c;安裝完不要打開軟件&#xff0c;記住安裝目錄 2. 將"Crack"文件夾內的所有文件復制到安裝目錄 …