
1. 引言在微服務架構中一個服務往往會被部署成多個實例以提升系統的可用性和吞吐量。當客戶端調用某個服務時如何從多個實例中選擇一個進行請求就成了一個關鍵問題。Spring Cloud Ribbon 正是為了解決這個問題而誕生的客戶端負載均衡組件。Ribbon 是 Netflix 開源的一款基于 HTTP 和 TCP 的客戶端負載均衡工具。它內嵌在服務消費方通過一系列內置的負載均衡策略幫助開發者輕松實現服務調用的負載均衡。雖然 Spring Cloud 官方在后續版本中推薦使用 Spring Cloud LoadBalancer 作為替代但 Ribbon 在大量存量項目中依然廣泛使用理解其原理對閱讀和維護老項目非常有幫助。本文將帶你從零開始手把手搭建一個基于 Spring Cloud Ribbon 的負載均衡示例并深入剖析其核心工作原理。2. 環境準備在開始編碼之前我們需要準備好以下環境JDK 1.8 及以上Maven 3.6 及以上IntelliJ IDEA 或其他主流 IDE一個可用的注冊中心本文以 Eureka 為例本文使用的 Spring Cloud 版本為 Hoxton.SR12對應的 Spring Boot 版本為 2.3.12.RELEASE。這兩個版本是 Ribbon 時代比較穩定且常用的組合。3. 創建服務提供者首先我們創建一個服務提供者模塊provider-service它負責對外提供具體的業務接口。3.1 引入依賴在pom.xml中引入必要的依賴dependenciesdependencygroupIdorg.springframework.cloud/groupIdartifactIdspring-cloud-starter-netflix-eureka-client/artifactId/dependencydependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-web/artifactId/dependency/dependencies3.2 編寫啟動類SpringBootApplicationEnableEurekaClientpublicclassProviderApplication{publicstaticvoidmain(String[]args){SpringApplication.run(ProviderApplication.class,args);}}3.3 編寫業務接口RestControllerpublicclassHelloController{Value(${server.port})privateStringport;GetMapping(/hello)publicStringhello(){returnHello from provider, port: port;}}3.4 配置文件server:port:8081spring:application:name:provider-serviceeureka:client:service-url:defaultZone:http://localhost:8761/eureka/為了讓負載均衡效果更明顯我們可以將服務提供者啟動兩個實例端口分別設置為 8081 和 8082。在 IDEA 中可以通過配置多個啟動項來實現也可以使用--server.port8082參數啟動第二個實例。4. 創建服務消費者接下來創建服務消費者模塊consumer-service它將通過 Ribbon 調用服務提供者。4.1 引入依賴dependenciesdependencygroupIdorg.springframework.cloud/groupIdartifactIdspring-cloud-starter-netflix-eureka-client/artifactId/dependencydependencygroupIdorg.springframework.cloud/groupIdartifactIdspring-cloud-starter-netflix-ribbon/artifactId/dependencydependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-web/artifactId/dependency/dependencies4.2 編寫啟動類SpringBootApplicationEnableEurekaClientpublicclassConsumerApplication{publicstaticvoidmain(String[]args){SpringApplication.run(ConsumerApplication.class,args);}}4.3 配置 RestTemplate在啟動類中注入一個帶有LoadBalanced注解的RestTemplateBeanBeanLoadBalancedpublicRestTemplaterestTemplate(){returnnewRestTemplate();}LoadBalanced是 Ribbon 的核心注解它會讓RestTemplate具備負載均衡的能力。加上這個注解后RestTemplate發出的請求會經過 Ribbon 的攔截器自動完成服務實例的選擇。4.4 編寫調用接口RestControllerpublicclassConsumerController{AutowiredprivateRestTemplaterestTemplate;GetMapping(/call)publicStringcall(){Stringurlhttp://provider-service/hello;returnrestTemplate.getForObject(url,String.class);}}注意這里的 URL 使用的是服務名稱provider-service而不是具體的 IP 和端口。Ribbon 會從注冊中心獲取該服務名對應的實例列表然后根據負載均衡策略選擇一個實例進行調用。4.5 配置文件server:port:8080spring:application:name:consumer-serviceeureka:client:service-url:defaultZone:http://localhost:8761/eureka/5. 啟動與測試6. 結合 OpenFeign 使用 Ribbon除了RestTemplateSpring Cloud 還提供了 OpenFeign 這一聲明式 HTTP 客戶端。OpenFeign 底層同樣依賴 Ribbon 實現負載均衡使用起來更加簡潔優雅。6.1 引入依賴在服務消費者的pom.xml中引入 OpenFeign 依賴dependenciesdependencygroupIdorg.springframework.cloud/groupIdartifactIdspring-cloud-starter-openfeign/artifactId/dependency/dependencies注意spring-cloud-starter-openfeign會自動傳遞引入 Ribbon 相關依賴因此無需再單獨添加spring-cloud-starter-netflix-ribbon。6.2 開啟 Feign 客戶端在啟動類上添加EnableFeignClients注解SpringBootApplicationEnableEurekaClientEnableFeignClientspublicclassConsumerApplication{publicstaticvoidmain(String[]args){SpringApplication.run(ConsumerApplication.class,args);}}6.3 編寫 Feign 接口創建一個 Feign 接口通過聲明式注解直接調用服務提供者FeignClient(nameprovider-service)publicinterfaceProviderClient{GetMapping(/hello)Stringhello();}FeignClient注解中的name屬性指定了要調用的服務名稱。Feign 會將該名稱交給 Ribbon由 Ribbon 從注冊中心獲取實例列表并完成負載均衡。6.4 在 Controller 中調用RestControllerpublicclassFeignConsumerController{AutowiredprivateProviderClientproviderClient;GetMapping(/feign/call)publicStringcall(){returnproviderClient.hello();}}啟動服務后多次訪問http://localhost:8080/feign/call同樣可以看到請求被輪流分發到 8081 和 8082 兩個實例上。6.5 配置 Ribbon 策略Feign 使用 Ribbon 時可以通過配置文件為指定服務設置負載均衡策略provider-service:ribbon:NFLoadBalancerRuleClassName:com.netflix.loadbalancer.RandomRule上面的配置將provider-service的負載均衡策略設置為隨機策略。也可以像第 6.4 節那樣通過配置類全局注入自定義的IRuleBean兩種方式對 Feign 同樣生效。6.6 Feign 與 RestTemplate 的適用場景方式優點適用場景RestTemplate靈活度高可動態拼接 URL適合簡單調用調用邏輯簡單、URL 需要動態變化的場景OpenFeign聲明式接口代碼簡潔支持注解配置易于維護服務間調用較多、接口固定的微服務場景簡單來說如果只是偶爾調用一兩個服務使用RestTemplate足夠如果服務間調用關系復雜、接口眾多推薦使用 OpenFeign它能讓代碼更加清晰、可維護性更高。按照以下步驟啟動所有服務啟動 Eureka 注冊中心默認端口 8761。啟動兩個服務提供者實例端口分別為 8081 和 8082。啟動服務消費者端口 8080。啟動完成后在瀏覽器中多次訪問http://localhost:8080/call觀察返回結果Hello from provider, port: 8081 Hello from provider, port: 8082 Hello from provider, port: 8081 Hello from provider, port: 8082可以看到請求被輪流分發到了兩個不同的服務實例上說明 Ribbon 的負載均衡已經生效。默認情況下Ribbon 使用的是輪詢策略所以兩個實例會交替被訪問。6. Ribbon 核心原理了解了基本用法之后我們來深入剖析 Ribbon 的工作原理。6.1 核心組件Ribbon 的核心組件包括ILoadBalancer負載均衡器負責維護服務實例列表并根據策略選擇實例。IRule負載均衡策略接口定義了如何從實例列表中選擇一個實例。IPing心跳檢測接口用于檢查服務實例是否存活。ServerList服務實例列表負責從注冊中心獲取實例信息。ServerListUpdater服務實例列表更新器定時刷新實例列表。6.2 工作流程Ribbon 的工作流程可以概括為以下幾個步驟服務消費者發起請求URL 中使用服務名稱。LoadBalanced注解的RestTemplate被 Ribbon 的攔截器攔截。攔截器將服務名稱交給ILoadBalancer處理。ILoadBalancer從ServerList中獲取可用的服務實例列表。IRule根據配置的負載均衡策略從實例列表中選擇一個實例。Ribbon 將請求轉發到選中的實例并返回結果。下面用一張流程圖來展示這個過程客戶端發起請求RestTemplate 攔截器ILoadBalancer 負載均衡器ServerList 獲取實例列表IRule 選擇實例轉發請求到目標實例返回響應結果6.3 負載均衡策略Ribbon 內置了多種負載均衡策略常用的有以下幾種策略類說明RoundRobinRule輪詢策略按順序依次選擇實例默認策略RandomRule隨機策略隨機選擇一個實例WeightedResponseTimeRule根據響應時間加權響應時間越短權重越大RetryRule帶重試的輪詢策略失敗后自動重試BestAvailableRule選擇并發量最小的實例AvailabilityFilteringRule先過濾掉故障實例再輪詢選擇6.4 自定義負載均衡策略如果內置策略不滿足需求我們可以自定義負載均衡策略。只需要實現IRule接口即可publicclassMyRuleextendsAbstractLoadBalancerRule{OverridepublicServerchoose(Objectkey){ListServerserversgetLoadBalancer().getAllServers();// 自定義選擇邏輯例如始終選擇第一個實例returnservers.get(0);}}然后在配置類中注入ConfigurationpublicclassRibbonConfig{BeanpublicIRuleribbonRule(){returnnewMyRule();}}7. 常見問題與注意事項在使用 Ribbon 的過程中有幾個常見問題需要特別注意。7.1 服務名大小寫問題Ribbon 在匹配服務名時是區分大小寫的。如果注冊中心中的服務名是provider-service那么調用時也必須使用完全一致的名稱否則會報No instances available錯誤。7.2 超時配置Ribbon 默認的連接超時和讀取超時時間可能不適合所有場景可以通過配置進行調整ribbon:ConnectTimeout:3000ReadTimeout:50007.3 饑餓加載默認情況下Ribbon 是在第一次調用時才去注冊中心拉取服務列表這會導致第一次請求較慢。可以通過開啟饑餓加載來解決ribbon:eager-load:enabled:trueclients:provider-service8. 總結本文從零開始完整演示了如何使用 Spring Cloud Ribbon 實現客戶端負載均衡。我們首先搭建了服務提供者和服務消費者然后通過LoadBalanced注解讓RestTemplate具備負載均衡能力最后深入剖析了 Ribbon 的核心組件和工作原理。Ribbon 作為 Spring Cloud Netflix 時代的核心組件雖然官方已進入維護模式但理解它的設計思想對于學習 Spring Cloud LoadBalancer 以及閱讀存量項目代碼仍然非常有幫助。希望本文能幫助你更好地掌握 Ribbon 的使用和原理。