# 第2章 SpringCloud
今日內容介紹

- SpringCloud Gateway 微服務網關
- Nacos 服務注冊中心
- Nacos 服務配置中心
1 微服務網關
在SpringCloud中網關的實現包括兩種:
- gateway
- zuul
Zuul是基于Servlet的實現,功能不強,性能較低,是阻塞式 。
SpringCloudGateway則是基于Spring5中提供的WebFlux,屬于響應式編程的實現,具備更好的性能。
Spring Cloud Gateway 是 Spring Cloud 的一個全新項目,該項目是基于 Spring 5.0,Spring Boot 2.0 和 Project Reactor 等響應式編程和事件流技術開發的網關,它旨在為微服務架構提供一種簡單有效的統一的 API 路由管理方式。
1.1 為什么需要網關【面試】
Gateway網關是我們服務的守門神,所有微服務的統一入口。
網關的核心功能特性:
- 請求路由
- 權限控制
- 限流
權限控制:網關作為微服務入口,需要校驗用戶是是否有請求資格,如果沒有則進行攔截。
路由和負載均衡:一切請求都必須先經過gateway,但網關不處理業務,而是根據某種規則,把請求轉發到某個微服務,這個過程叫做路由。當然路由的目標服務有多個時,還需要做負載均衡。
限流:當請求流量過高時,在網關中按照下流的微服務能夠接受的速度來放行請求,避免服務壓力過大。
1.2.gateway快速入門【掌握】
下面,我們就演示下網關的基本路由功能?;静襟E如下:
- 創建SpringBoot工程gateway,引入網關依賴
- 編寫啟動類
- 編寫基礎配置和路由規則
- 啟動網關服務進行測試
1)創建微服務網關并引入依賴
<?xml version="1.0" encoding="UTF-8"?>
<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">
<parent>
<artifactId>springcloud-parent</artifactId>
<groupId>com.itheima</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>itheima-gateway</artifactId>
<dependencies>
<!--網關-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<!-- Eureka客戶端 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
</dependencies>
</project>
2)配置文件application.yml
server:
# 網關端口
port: 7001
spring:
application:
name: gateway
cloud:
gateway:
routes: # 網關路由配置
- id: user-service # 路由id,自定義,只要唯一即可
# uri: http://127.0.0.1:18081 # 路由的目標地址 http就是固定地址
uri: lb://itheima-user # 路由的目標地址 lb就是負載均衡,后面跟服務名稱
predicates: # 路由斷言,也就是判斷請求是否符合路由規則的條件
- Path=/user/** # 這個是按照路徑匹配,只要以/user/開頭就符合要求
eureka:
client:
service-url:
# EurekaServer的地址
defaultZone: http://localhost:8001/eureka
instance:
prefer-ip-address: true
instance-id: ${spring.application.name}:${server.port}:@project.version@
lease-renewal-interval-in-seconds: 30 #心跳周期,默認是30秒
lease-expiration-duration-in-seconds: 90 #心跳失敗最長超時間,默認90秒
我們將符合Path 規則的一切請求,都代理到 uri參數指定的地址。
本例中,我們將 /user/**開頭的請求,代理到lb://itheima-user,lb是負載均衡,根據服務名拉取服務列表,實現負載均衡。
3)創建啟動類com.itheima.GatewayApplication
package com.itheima;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class GatewayApplication {
public static void main(String[] args) {
SpringApplication.run(GatewayApplication.class, args);
}
}
4)路由測試
重啟網關,訪問http://localhost:7001/user/1時,符合/user/**規則,請求轉發到uri:http://user/user/1,得到了結果:

思考:
比如所有以/order開始的請求交給itheima-order服務,如何配置itheima-order服務的路由請求?
1.3 斷言工廠
我們在配置文件中寫的斷言規則只是字符串,這些字符串會被Predicate Factory讀取并處理,轉變為路由判斷的條件
例如Path=/user/**是按照路徑匹配,這個規則是由
org.springframework.cloud.gateway.handler.predicate.PathRoutePredicateFactory類來
處理的,像這樣的斷言工廠在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 | 權重處理 |
我們只需要掌握Path這種路由工程就可以了。
上面使用案例如下:
spring:
application:
name: gateway
cloud:
gateway:
routes: # 網關路由配置
- id: user-service # 路由id,自定義,只要唯一即可
# uri: http://127.0.0.1:18081 # 路由的目標地址 http就是固定地址
uri: lb://user # 路由的目標地址 lb就是負載均衡,后面跟服務名稱
predicates: # 路由斷言,也就是判斷請求是否符合路由規則的條件
- Path=/user/** # 這個是按照路徑匹配,只要以/user/開頭就符合要求
- id: order-service
uri: lb://order
predicates:
- Path=/order/**
- After=2020-12-30T23:59:59.789+08:00[Asia/Shanghai]
- Before=2022-12-30T23:59:59.789+08:00[Asia/Shanghai]
- Between=2020-12-30T23:59:59.789+08:00[Asia/Shanghai],2022-12-30T23:59:59.789+08:00[Asia/Shanghai]
- Cookie=uname, itheima
- Header=X-Request-Id, \d+
- Host=localhost:7001
- Method=GET,POST
- Query=uname,zhangsan
- RemoteAddr=127.0.0.1/16
1.4.過濾器工廠【掌握】
GatewayFilter是網關中提供的一種過濾器,可以對進入網關的請求和微服務返回的響應做處理:
1.4.1.路由過濾器的種類
Spring提供了31種不同的路由過濾器工廠。例如:
| 名稱 | 說明 |
|---|---|
| AddRequestHeader | 給當前請求添加一個請求頭 |
| RemoveRequestHeader | 移除請求中的一個請求頭 |
| AddResponseHeader | 給響應結果中添加一個響應頭 |
| RemoveResponseHeader | 從響應結果中移除有一個響應頭 |
| RequestRateLimiter | 限制請求的流量 |
1.4.2.請求頭過濾器
下面我們以AddRequestHeader 為例來講解。
需求:給所有進入itheima-order的請求添加一個請求頭:Heima=szheima119 nb!
1)只需要修改gateway服務的application.yml文件,添加路由過濾即可:
spring:
cloud:
gateway:
routes: # 網關路由配置
- id: order-service # 路由id,自定義,只要唯一即可
#uri: http://127.0.0.1:18082 # 路由的目標地址 http就是固定地址
uri: lb://itheima-order # 路由的目標地址 lb就是負載均衡,后面跟服務名稱
filters: # 過濾器
- AddRequestHeader=Heima,szheima119 nb! # 添加請求頭
predicates: # 路由斷言,也就是判斷請求是否符合路由規則的條件
- Path=/order/** # 這個是按照路徑匹配,只要以/user/開頭就符合要求
注意:當前過濾器寫在order-service路由下,因此僅僅對訪問order-service的請求有效。
2)修改itheima-order中OrderController
/**
* 查詢訂單信息
*/
@GetMapping(value = "/{id}")
public OrderInfo one(@PathVariable(value = "id") Long id, @RequestHeader("Heima") String head){
System.out.println("*********head********"+head);
return orderService.findById(id);
}
1.4.3.默認過濾器
1)如果要對所有的路由都生效,則可以將過濾器工廠寫到default下。格式如下:
spring:
cloud:
gateway:
default-filters: # 默認過濾項
- AddRequestHeader= Heima,szheima119 nb!
routes: # 網關路由配置
- id: user-service # 路由id,自定義,只要唯一即可
#uri: http://127.0.0.1:18081 # 路由的目標地址 http就是固定地址
uri: lb://itheima-user # 路由的目標地址 lb就是負載均衡,后面跟服務名稱
predicates: # 路由斷言,也就是判斷請求是否符合路由規則的條件
- Path=/user/** # 這個是按照路徑匹配,只要以/user/開頭就符合要求
- id: order-service # 路由id,自定義,只要唯一即可
#uri: http://127.0.0.1:18082 # 路由的目標地址 http就是固定地址
uri: lb://itheima-order # 路由的目標地址 lb就是負載均衡,后面跟服務名稱
# filters: # 過濾器
# - AddRequestHeader=Heima,szheima119 nb! # 添加請求頭
predicates: # 路由斷言,也就是判斷請求是否符合路由規則的條件
- Path=/order/** # 這個是按照路徑匹配,只要以/user/開頭就符合要求
1.4.4.總結
網關過濾器的作用是什么?
① 對路由的請求或響應做加工處理,比如添加請求頭
② 配置在路由下的過濾器只對當前路由的請求生效
③default-filters的作用是什么? 對所有路由都生效的過濾器
1.5.全局過濾器【掌握】
上一節學習的過濾器,網關提供了31種,但每一種過濾器的作用都是固定的。如果我們希望攔截請求,做自己的業務邏輯則沒辦法實現。
1.5.1.全局過濾器作用
全局過濾器的作用也是處理一切進入網關的請求和微服務響應,與GatewayFilter的作用一樣。區別在于GatewayFilter通過配置定義,處理邏輯是固定的;而GlobalFilter的邏輯需要自己寫代碼實現。
定義方式是實現GlobalFilter接口。
public interface GlobalFilter {
/**
* 處理當前請求,有必要的話通過{@link GatewayFilterChain}將請求交給下一個過濾器處理
*
* @param exchange 請求上下文,里面可以獲取Request、Response等信息
* @param chain 用來把請求委托給下一個過濾器
* @return {@code Mono<Void>} 返回標示當前過濾器業務結束
*/
Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain);
}
在filter中編寫自定義邏輯,可以實現下列功能:
- 登錄狀態判斷
- 權限校驗
- 請求限流等
1.5.2.自定義全局過濾器
需求:定義全局過濾器,攔截請求,判斷請求的參數是否滿足下面條件:
- 參數中是否有authorization,
- authorization參數值是否為admin
如果同時滿足則放行,否則攔截
實現:在gateway中定義一個過濾器:
package com.itheima.gateway.filters;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.util.MultiValueMap;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import java.nio.charset.Charset;
/**
* 自定義全局過濾器
*/
@Component
public class AuthorizeFilter implements GlobalFilter {
/**
* -參數中是否有authorization,
* - authorization參數值是否為admin
* @param exchange
* @param chain
* @return
*/
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpResponse response = exchange.getResponse();
//1 獲取請求頭中authorization
MultiValueMap<String, String> queryParams = exchange.getRequest().getQueryParams();
//2 校驗是否為admin
String authorization = queryParams.getFirst("authorization");
//3 如果是則放行
if("admin".equals(authorization)){
return chain.filter(exchange);
}
//4 如果不是則 禁止訪問
//4.1 設置狀態碼
response.setStatusCode(HttpStatus.UNAUTHORIZED);
//4.2 設置請求已經完成
return response.setComplete();
}
}
響應提示信息:
response.setStatusCode(HttpStatus.UNAUTHORIZED);
response.getHeaders().setContentType(MediaType.APPLICATION_JSON);
return response.writeWith(Flux.just(response.bufferFactory().wrap("請您先登錄!".getBytes(Charset.forName("UTF-8")))));
測試:
http://localhost:7001/order/101?authorization=admin
http://localhost:7001/user/1?authorization=admin


1.5.3.過濾器執行順序
請求進入網關會碰到三類過濾器:當前路由的過濾器、DefaultFilter、GlobalFilter
請求路由后,會將當前路由過濾器和DefaultFilter、GlobalFilter,合并到一個過濾器鏈(集合)中,排序后依次執行每個過濾器:
排序的規則是什么呢?
- 每一個過濾器都必須指定一個int類型的order值,order值越小,優先級越高,執行順序越靠前。
- GlobalFilter通過實現Ordered接口,或者添加@Order注解來指定order值,由我們自己指定
- 路由過濾器和defaultFilter的order由Spring指定,默認是按照聲明順序從1遞增。
- 當過濾器的order值一樣時,會按照 defaultFilter > 路由過濾器 > GlobalFilter的順序執行。
詳細內容,可以查看源碼:
org.springframework.cloud.gateway.route.RouteDefinitionRouteLocator#getFilters()方法是先加載defaultFilters,然后再加載某個route的filters,然后合并。
org.springframework.cloud.gateway.handler.FilteringWebHandler#handle()方法會加載全局過濾器,與前面的過濾器合并后根據order排序,組織過濾器鏈

1.6.跨域問題【了解】
1.6.1.什么是跨域問題
跨域:域名不一致就是跨域,主要包括:
- 域名不同: www.taobao.com 和 www.taobao.org 和 www.jd.com 和 miaosha.jd.com
- 域名相同,端口不同:localhost:8080和localhost8081
跨域問題:瀏覽器禁止請求的發起者與服務端發生跨域ajax請求,請求被瀏覽器攔截的問題
1.6.2.模擬跨域問題
找到資料nginx-1.18.0跨域測試.7z 解壓放到沒有空格和中文目錄下,啟動并訪問。
可以在瀏覽器控制臺看到下面的錯誤:

從localhost:8090訪問localhost:7001,端口不同,顯然是跨域的請求。
1.6.3.解決跨域問題
在gateway服務的application.yml文件中,添加下面的配置:
spring:
cloud:
gateway:
# 。。。
globalcors: # 全局的跨域處理
add-to-simple-url-handler-mapping: true # 解決options請求被攔截問題
corsConfigurations:
'[/**]':
allowedOrigins: # 允許所有跨域請求
- "*"
allowedMethods: # 允許的跨域ajax的請求方式
- "GET"
- "POST"
- "DELETE"
- "PUT"
- "OPTIONS"
allowedHeaders: "*" # 允許在請求中攜帶的頭信息
allowCredentials: true # 是否允許攜帶cookie
maxAge: 360000 # 這次跨域檢測的有效期
1.7 小結
1.為什么需要網關?
- 請求路由
- 權限控制
- 限流
- 統一入口
- 跨域問題
- 請求 響應處理
2.掌握gateway網關搭建
會配置網關路由過濾器
會配置網關全局過濾器
會解決跨域問題
2 Nacos 服務注冊
國內公司一般都推崇阿里巴巴的技術,比如注冊中心,SpringCloudAlibaba也推出了一個名為Nacos的注冊中心。
2.1.認識和安裝Nacos
Nacos是阿里巴巴的產品,現在是SpringCloud中的一個組件。相比Eureka功能更加豐富,在國內受歡迎程度較高。
安裝方式可以參考課前資料《Nacos安裝指南.md》
nacos訪問地址:http://localhost:8848/nacos
控制臺賬號:nacos 密碼:nacos
2.2.服務注冊到nacos
Nacos是SpringCloudAlibaba的組件,而SpringCloudAlibaba也遵循SpringCloud中定義的服務注冊、服務發現規范。因此使用Nacos和使用Eureka對于微服務來說,并沒有太大區別。
主要差異在于:
- 依賴不同
- 服務地址不同
1)引入依賴
在springcloud-parent父工程的pom文件中的<dependencyManagement>中引入SpringCloudAlibaba的依賴:
<dependencyManagement>
<dependencies>
.......
<!--引入SpringCloudAlibaba的依賴-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>2.2.6.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
然后在itheima-user和itheima-order中的pom文件中引入nacos-discovery依賴:
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
注意:不要忘了注釋掉eureka的依賴。
2)配置nacos地址
在user-service和order-service的application.yml中添加nacos地址:
spring:
cloud:
nacos:
server-addr: localhost:8848
注意:不要忘了注釋掉eureka的地址
3)itheima-order修改OrderController
去掉請求頭參數
package com.itheima.controller;
import com.itheima.order.pojo.OrderInfo;
import com.itheima.service.OrderService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping(value = "/order")
public class OrderController {
@Autowired
private OrderService orderService;
/**
* 查詢訂單信息
*/
@GetMapping(value = "/{id}")
public OrderInfo one(@PathVariable(value = "id") Long id){
return orderService.findById(id);
}
}
4)重啟
重啟微服務后,登錄nacos管理頁面,可以看到微服務信息:

5)測試
訪問:http://localhost:18082/order/101 http://localhost:18081/user/1
2.3.服務分級存儲模型【掌握】
一個服務可以有多個實例,例如我們的itheima-user,可以有:
- 127.0.0.1:18081
- 127.0.0.1:28081
- 127.0.0.1:38081
假如這些實例分布于全國各地的不同機房,例如:
- 127.0.0.1:18081,在深圳機房
- 127.0.0.1:28081,在深圳機房
- 127.0.0.1:38081,在杭州機房
Nacos就將同一機房內的實例 劃分為一個集群。
也就是說,itheima-user是服務,一個服務可以包含多個集群,如深圳、杭州,每個集群下可以有多個實例,形成分級模型,如圖:

微服務互相訪問時,應該盡可能訪問同集群實例,因為本地訪問速度更快。當本集群內不可用時,才訪問其它集群。例如:

深圳機房內的itheima-order應該優先訪問同機房的itheima-user。
2.3.1.給itheima-user配置集群
修改itheima-user的application.yml文件,添加集群配置:
spring:
cloud:
nacos:
server-addr: localhost:8848
discovery:
cluster-name: SZ # 集群名稱
重啟兩個user-service實例后,我們可以在nacos控制臺看到下面結果:

我們再次復制一個itheima-user啟動配置,添加屬性:
-Dserver.port=38081 -Dspring.cloud.nacos.discovery.cluster-name=HZ
配置如圖所示:

啟動UserApp3后再次查看nacos控制臺:

2.3.2.同集群優先的負載均衡
默認的ZoneAvoidanceRule并不能實現根據同集群優先來實現負載均衡。
因此Nacos中提供了一個NacosRule的實現,可以優先從同集群中挑選實例。
1)給order-service配置集群信息
修改order-service的application.yml文件,添加集群配置:
spring:
cloud:
nacos:
server-addr: localhost:8848
discovery:
cluster-name: SZ # 集群名稱
2)修改負載均衡規則
修改itheima-order的application.yml文件,修改負載均衡規則:
user:
ribbon:
NFLoadBalancerRuleClassName: com.alibaba.cloud.nacos.ribbon.NacosRule # 負載均衡規則
2.4.權重配置【掌握】
實際部署中會出現這樣的場景:
服務器設備性能有差異,部分實例所在機器性能較好,另一些較差,我們希望性能好的機器承擔更多的用戶請求。
但默認情況下NacosRule是同集群內隨機挑選,不會考慮機器的性能問題。
因此,Nacos提供了權重配置來控制訪問頻率,權重越大則訪問頻率越高。
在nacos控制臺,找到user-service的實例列表,點擊編輯,即可修改權重:
在彈出的編輯窗口,修改權重:
注意:如果權重修改為0,則該實例永遠不會被訪問
2.5.環境隔離【掌握】
Nacos提供了namespace來實現環境隔離功能。
- nacos中可以有多個namespace(環境隔離:test dev pro)
- namespace下有group(項目隔離 探花項目 頭條項目)、service(實例隔離tanhua-server tanhua-service)等
- 不同namespace之間相互隔離,例如不同namespace的服務互相不可見
2.5.1.創建namespace
默認情況下,所有service、data、group都在同一個namespace,名為public:
我們可以點擊頁面新增按鈕,添加一個namespace:
然后,填寫表單:
就能在頁面看到一個新的namespace:
2.5.2.給微服務配置namespace
給微服務配置namespace只能通過修改配置來實現。
例如,修改itheima-order的application.yml文件:
spring:
cloud:
nacos:
server-addr: localhost:8848
discovery:
cluster-name: SZ
namespace: devnamespace # 命名空間,填ID
重啟itheima-order后,訪問控制臺,可以看到下面的結果:

此時訪問itheima-order,因為namespace不同,會導致找不到user,控制臺會報錯:

在itheima-user修改配置:
-Dspring.cloud.nacos.discovery.namespace=devnamespace

重啟itheima-user后,訪問控制臺,可以看到下面的結果:

此時訪問itheima-user,因為namespace相同,找到itheima-user:

2.6.Nacos與Eureka的區別【面試】
Nacos的服務實例分為兩種l類型:
- 臨時實例:如果實例宕機超過一定時間,會從服務列表剔除,默認的類型。
- 非臨時實例:如果實例宕機,不會從服務列表剔除,也可以叫永久實例。
配置一個服務實例為永久實例:
spring:
cloud:
nacos:
discovery:
ephemeral: false # 設置為非臨時實例
Nacos和Eureka整體結構類似,服務注冊、服務拉取、心跳等待,但是也存在一些差異:

- Nacos與eureka的共同點
- 都支持服務注冊和服務拉取
- 都支持服務提供者心跳方式做健康檢測
- Nacos與Eureka的區別
- Nacos支持服務端主動檢測提供者狀態:臨時實例采用心跳模式,非臨時實例采用主動檢測模式
- 臨時實例心跳不正常會被剔除,非臨時實例則不會被剔除
- Nacos支持服務列表變更的消息推送模式,服務列表更新更及時
- Nacos集群默認采用AP方式,當集群中存在非臨時實例時,采用CP模式;Eureka采用AP方式(CAP理論:C一致性,A高可用,P分區容錯性)
- Nacos使用的netty和服務進行連接,屬于長連接。eureka使用定時發送和服務進行連接,屬于短連接
3.Nacos配置管理
Nacos除了可以做注冊中心,同樣可以做配置管理來使用。
3.1.統一配置管理【掌握】
當微服務部署的實例越來越多,達到數十、數百時,逐個修改微服務配置就會讓人抓狂,而且很容易出錯。我們需要一種統一配置管理方案,可以集中管理所有實例的配置。

Nacos一方面可以將配置集中管理,另一方可以在配置變更時,及時通知微服務,實現配置的熱更新。
3.1.1.在nacos中添加配置文件
如何在nacos中管理配置呢?

然后在彈出的表單中,填寫配置信息:

配置內容:

注意:項目的核心配置,需要熱更新的配置才有放到nacos管理的必要?;静粫兏囊恍┡渲眠€是保存在微服務本地比較好。
3.1.2.微服務從配置中心拉取配置
微服務要拉取nacos中管理的配置,并且與本地的application.yml配置合并,才能完成項目啟動。
但如果尚未讀取application.yml,又如何得知nacos地址呢?
因此spring引入了一種新的配置文件:bootstrap.yaml文件,會在application.yml之前被讀取,流程如下:

1)引入nacos-config依賴
首先,在user-service服務中,引入nacos-config的客戶端依賴:
<!--nacos配置管理依賴-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
2)修改application.yaml
將application.yaml 修改為 bootstrap.yaml,并修改配置完整內容如下:
server:
port: 18081
spring:
application:
name: itheima-user
profiles:
active: dev #開發環境,這里是dev
datasource:
driverClassName: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/cloud_user?characterEncoding=UTF8&&serverTimezone=Asia/Shanghai
username: root
password: admin
cloud:
nacos:
server-addr: localhost:8848
config:
file-extension: yaml # 文件后綴名
server-addr: localhost:8848 #nacos配置中心地址
namespace: devnamespace
discovery:
cluster-name: SZ
#ephemeral: false # 設置為非臨時實例
namespace: devnamespace
這里會根據spring.cloud.nacos.server-addr獲取nacos地址,再根據
${spring.application.name}-${spring.profiles.active}.${spring.cloud.nacos.config.file-extension}作為文件id,來讀取配置itheima-user-dev.yaml:

3)讀取nacos配置
在user-service中的UserController中,讀取uname配置:
@Value("${user.uname}")
private String uname;
@GetMapping("name")
public String now(){
return "********"+uname+"*********";
}
- 在頁面訪問,可以看到效果:

itheima-user-dev.yaml > itheima-user.yaml > application.yml >bootstrap.yml
3.2.配置熱更新【掌握】
我們最終的目的,是修改nacos中的配置后,微服務中無需重啟即可讓配置生效,也就是配置熱更新。
要實現配置熱更新,可以使用兩種方式:
3.2.1.方式一
在@Value注入的變量所在類上添加注解@RefreshScope:
@RestController
@RequestMapping(value = "/user")
@RefreshScope //刷新配置
public class UserController {
3.2.2.方式二
使用@ConfigurationProperties注解代替@Value注解。
在itheima-user服務中處理如下:
@RestController
@RequestMapping(value = "/user")
//@RefreshScope //注釋掉
@ConfigurationProperties(prefix = "user") //配置讀取注解,并未uname添加set方法
@Setter
public class UserController {
@Autowired
private UserService userService;
//@Value("${user.uname}") //去掉@Value("${xx}")
private String uname;
@GetMapping("name")
public String now(){
return "********"+uname+"*********";
}
........
}
3.3.配置共享【掌握】
其實微服務啟動時,會去nacos讀取多個配置文件,例如:
[spring.application.name]-[spring.profiles.active].yaml,例如:itheima-user-dev.yaml[spring.application.name].yaml,例如:itheima-user.yaml
而[spring.application.name].yaml不包含環境,因此可以被多個環境共享。
下面我們通過案例來測試配置共享
1)添加一個環境共享配置
我們在nacos中添加一個itheima-user.yaml文件:

2)在user-service中讀取共享配置
在user-service服務中,修改UserController
@RestController
@RequestMapping(value = "/user")
//@RefreshScope //注釋掉
@ConfigurationProperties(prefix = "user")
@Setter
public class UserController {
@Autowired
private UserService userService;
//@Value("${user.uname}")
private String uname;
private String sysname;
@GetMapping("name")
public String now(){
return "********"+uname+"*********"+sysname+"*********";
}
............
}
3)運行兩個UserApp,使用不同的profile
UserApp(18081)使用的profile是dev,UserApp2(28081)使用的profile是test。
修改UserApp2這個啟動項,改變其profile值:

啟動UserApp和UserApp2,訪問http://localhost:18081/user/name,結果:
訪問http://localhost:28081/user/name,結果:
可以看出來,不管是dev,還是test環境,都讀取到了sysname這個屬性的值。
4)配置共享的優先級
當nacos、服務本地同時出現相同屬性時,優先級有高低之分:
itheima-user-dev.yaml(當前環境配置) > itheima-user.yaml(共享配置) > application.yml(本地配置)>bootstrap.yml(本地配置)
3.4.搭建Nacos集群【了解】
Nacos生產環境下一定要部署為集群狀態,部署方式參考課前資料中的文檔:
浙公網安備 33010602011771號