:服務(wù)注冊與配置管理詳解)
1. 環(huán)境準(zhǔn)備與版本選型Nacos中和Spring Cloud Alibaba版本搭配1.1 Nacos Server部署在動手寫業(yè)務(wù)代碼之前先把Nacos Server跑起來。這一步看著簡單但版本坑最多。我用的是Nacos 2.2.0版本部署方式選的是單機模式官方文檔里寫的是需要MySQL數(shù)據(jù)庫支持2.x版本默認(rèn)使用內(nèi)嵌數(shù)據(jù)庫Derby但生產(chǎn)環(huán)境強烈建議切換成MySQL。安裝部署具體步驟如下去Nacos官方GitHub下載2.2.0版本的壓縮包解壓到指定目錄這里以Linux服務(wù)器為例目錄結(jié)構(gòu)為/opt/nacos。修改配置文件conf/application.properties把數(shù)據(jù)源改成MySQL需要新建數(shù)據(jù)庫庫名可以叫nacos_config然后執(zhí)行conf/mysql-schema.sql初始化腳本。單機模式啟動命令sh startup.sh -m standalone這里的-m參數(shù)指定模式standalone表示單機。啟動后訪問http://127.0.0.1:8848/nacos默認(rèn)賬號密碼都是nacos首次登錄后建議立刻修改。1024是新手第一次接觸Nacos最容易犯的錯誤就是直接用官方默認(rèn)包啟動后發(fā)現(xiàn)控制臺打不開或者是登錄密碼不對。這里補充說明Nacos 2.2.0的登錄邏輯和1.x版本不一樣默認(rèn)的用戶名密碼是nacos/nacos這個很多老文章沒講清楚。還有一點啟動前檢查一下磁盤空間Nacos的日志增長很快尤其是登錄失敗時會產(chǎn)生大量的鑒權(quán)日志磁盤被寫滿導(dǎo)致服務(wù)掛掉的案例我見過不止一次。1.2 Spring Cloud Alibaba版本對應(yīng)關(guān)系Spring Cloud Alibaba和各組件版本對應(yīng)關(guān)系這塊是整合踩坑的重災(zāi)區(qū)建議直接對照官方Wiki的版本說明。我自己項目里最終確定的版本組合是Spring Boot 2.6.11加Spring Cloud 2021.0.3加Spring Cloud Alibaba 2021.0.5.0這幾個版本是經(jīng)過了多次驗證的搭配起來問題最少。版本選擇背后的邏輯值得展開講講。Spring Cloud Alibaba是在2020版本之后把版本號改為以年份命名的2021.0.5.0對應(yīng)的是Spring Cloud 2021.0.x系列的版本而Nacos客戶端則是由Spring Cloud Alibaba自己維護的。如果Spring Boot版本低于2.4或者Spring Cloud版本低于2020.0.0很多動態(tài)刷新和配置綁定的功能支持就不完整比如bootstrap.yml的加載機制在Spring Boot 2.4之后默認(rèn)是關(guān)閉的需要單獨引入依賴或者配置開啟。當(dāng)前項目接口用到了Web功能所以引入的starter選擇spring-cloud-starter-alibaba-nacos-discovery和spring-cloud-starter-alibaba-nacos-config這兩個核心依賴。這段選型是整個項目能否順利跑起來的關(guān)鍵。我見過太多人隨便選了一個版本然后出現(xiàn)注冊不上、配置讀取不到等問題最后排查半天發(fā)現(xiàn)是版本不兼容。所以如果你跟我一樣用的是Spring Boot 2.6.x建議直接復(fù)制我這份組合不要自己亂配版本號。2. 服務(wù)注冊與發(fā)現(xiàn)實現(xiàn)搭建Provider和Consumer2.1 搭建服務(wù)提供者Provider服務(wù)注冊的代碼看起來簡單但工程結(jié)構(gòu)布局是課堂教學(xué)里不會強調(diào)的事情。我習(xí)慣把服務(wù)拆成nacos-provider和nacos-consumer兩個獨立的Spring Boot工程。Provider對外提供接口Consumer通過Nacos發(fā)現(xiàn)Provider并調(diào)用接口。先看Provider的具體實現(xiàn)。新建一個Spring Boot項目引入依賴核心依賴如下dependency groupIdcom.alibaba.cloud/groupId artifactIdspring-cloud-starter-alibaba-nacos-discovery/artifactId /dependency dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency配置文件application.yml里最關(guān)鍵的是注冊中心地址這段配置要注意spring: application: name: nacos-provider cloud: nacos: discovery: server-addr: 127.0.0.1:8848 register-enabled: true service: ${spring.application.name} server: port: 8081這里解釋幾個關(guān)鍵配置項的含義。spring.application.name必須配置這是服務(wù)注冊到Nacos之后的服務(wù)名如果沒配置注冊中心的列表里會看不到服務(wù)。server-addr是Nacos服務(wù)端的地址端口默認(rèn)是8848。register-enabled默認(rèn)就是true其實可以不寫但寫出來比較直觀方便后續(xù)做灰度發(fā)布時設(shè)置為false。service這個參數(shù)是服務(wù)注冊名默認(rèn)取${spring.application.name}顯式配置是為了防止有些場景下取錯。Provider核心接口代碼RestController RequestMapping(/provider) public class ProviderController { GetMapping(/hello) public String hello(RequestParam String name) { return Hello, name , this is nacos-provider!; } }2.2 搭建服務(wù)消費者ConsumerConsumer同樣引入nacos-discovery依賴配置文件里服務(wù)名改成nacos-consumer端口改成8082。Consumer調(diào)用Provider的方式有兩種一種是RestTemplate配合LoadBalanced注解另一種是OpenFeign。我推薦用OpenFeign因為代碼更優(yōu)雅聲明式接口很好維護。OpenFeign需要額外引入依賴dependency groupIdorg.springframework.cloud/groupId artifactIdspring-cloud-starter-openfeign/artifactId /dependencyConsumer啟動類增加EnableFeignClients注解然后定義一個FeignClient接口FeignClient(name nacos-provider) public interface ProviderClient { GetMapping(/provider/hello) String hello(RequestParam(name) String name); }Controller里直接注入這個ClientRestController RequestMapping(/consumer) public class ConsumerController { Autowired private ProviderClient providerClient; GetMapping(/call) public String call(RequestParam String name) { return providerClient.hello(name); } }2.3 驗證與原理分析啟動Nacos再依次啟動Provider和Consumer打開Nacos控制臺在服務(wù)列表里可以看到兩個服務(wù)服務(wù)名是nacos-provider和nacos-consumer集群數(shù)默認(rèn)是單機實例數(shù)是各一個。服務(wù)注冊的原理值得聊一聊。Nacos客戶端啟動后會通過心跳機制向注冊中心發(fā)送注冊請求默認(rèn)是每5秒發(fā)送一次心跳。Nacos 2.x版本的通信協(xié)議改用gRPC之后長連接維護比以前更好在控制臺能看到健康狀態(tài)顯示為“上線”。如果你在控制臺看不到服務(wù)先不要懷疑代碼寫錯了用curl命令檢查注冊中心是否正常curl -X GET http://127.0.0.1:8848/nacos/v1/ns/service/list?pageNo1pageSize10返回結(jié)果里如果能看到服務(wù)名說明注冊中心沒問題問題出在客戶端。再通過curl -X GET查看實例詳情curl -X GET http://127.0.0.1:8848/nacos/v1/ns/instance/list?serviceNamenacos-provider這里要說一下負(fù)載均衡的處理。Feign調(diào)用默認(rèn)集成了Ribbon的負(fù)載均衡能力因為Spring Cloud 2021.0.x移除了Ribbon的自動配置Spring Cloud LoadBalancer已經(jīng)替代了它LoadBalanced和FeignClient會自動適配不需要額外引入Ribbon依賴。如果要用用戶自定義的負(fù)載均衡策略可以通過實現(xiàn)ServiceInstanceListSupplier接口來定制這個后續(xù)文章可以展開講。3. 配置管理實戰(zhàn)Nacos Config動態(tài)刷新與共享配置3.1 引入配置中心依賴服務(wù)注冊搞定之后接下來做配置管理。這是Nacos比較核心的能力。Spring Cloud Alibaba整合Nacos配置管理需要額外引入依賴dependency groupIdcom.alibaba.cloud/groupId artifactIdspring-cloud-starter-alibaba-nacos-config/artifactId /dependency3.2 bootstrap配置與動態(tài)刷新機制環(huán)境配置最頭疼的是Spring Boot 2.4版本以后的加載順序變化。Spring Boot 2.4之前bootstrap.yml是自動加載的Spring Boot 2.4之后默認(rèn)不再加載bootstrap.yml了必須通過在pom.xml里引入依賴或者在配置文件手動開啟。我很推薦在pom.xml里引入spring-cloud-starter-bootstrap依賴來開啟dependency groupIdorg.springframework.cloud/groupId artifactIdspring-cloud-starter-bootstrap/artifactId /dependency然后新建bootstrap.yml配置文件spring: application: name: nacos-provider cloud: nacos: config: server-addr: 127.0.0.1:8848 file-extension: yaml group: DEFAULT_GROUP這里重點說兩個細(xì)節(jié)。第一file-extension支持properties和yaml兩種格式在Nacos控制臺新建配置時Data ID要寫成${spring.application.name}.yaml這種格式比如nacos-provider.yaml只有這樣客戶端才知道去哪里找對應(yīng)文件。第二group默認(rèn)就是DEFAULT_GROUP如果沒有特殊需求可以先不配但在真正的多環(huán)境管理時需要區(qū)分。Nacos配置管理最吸引人的地方是動態(tài)刷新不用重啟服務(wù)就能修改配置。做法如下在Nacos控制臺的配置管理界面新建配置nacos-provider.yaml內(nèi)容里放一個測試配置項比如user.nametest-user-from-nacos。在代碼里用Value讀取這個配置RestController RequestMapping(/config) public class ConfigController { Value(${user.name}) private String userName; GetMapping(/get) public String get() { return userName; } }啟動服務(wù)后第一次訪問/config/get返回的是Nacos里的值然后去控制臺修改配置內(nèi)容再刷新接口發(fā)現(xiàn)返回的是新值根本沒重啟應(yīng)用。這個機制背后的原理是Nacos客戶端會基于長輪詢機制默認(rèn)每30秒向服務(wù)端發(fā)一次請求但這個請求不是立即返回的服務(wù)端會hold住30秒左右如果在這個時間段內(nèi)配置發(fā)生了變化服務(wù)端立刻返回變化的通知客戶端收到通知后立即更新本地配置整個響應(yīng)時間大概在1到3秒之間不需要等30秒。這其實是一種很高效的長輪詢方案。3.3 多環(huán)境與共享配置實現(xiàn)動態(tài)刷新做通了就要面對更實際的場景多環(huán)境配置。開發(fā)環(huán)境、測試環(huán)境、生產(chǎn)環(huán)境的數(shù)據(jù)庫地址、日志級別肯定不一樣Nacos解決這個問題提供了三種途徑。第一種是通過spring.profiles.active來區(qū)分。啟動時加上--spring.profiles.activedevNacos客戶端就會去加載nacos-provider-dev.yaml這個配置。前提是Data ID要提前準(zhǔn)備好。好處是配置隔離非常清晰比如nacos-provider-dev.yaml、nacos-provider-test.yaml、nacos-provider-prod.yaml三個文件各管各的環(huán)境。第二種是通過group區(qū)分。同一個Data ID可以有不同的group通過bootstrap.yml里的group: DEV切換。這種方式通常用于按業(yè)務(wù)域或團隊來隔離配置的場景。第三種是extension-configs用于加載公共的共享配置。比如多個服務(wù)都要使用同一個Redis配置或數(shù)據(jù)源配置就可以把這部分配置單獨放在一個common.yaml文件里然后在每個服務(wù)的bootstrap.yml中引用spring: cloud: nacos: config: extension-configs: ->