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