前面我們已經完成了注冊中心和服務提供者兩個基礎組件。接着介紹使用Spring Cloud Ribbon在客戶端負載均衡的調用服務。
ribbon 是一個客戶端負載均衡器,可以簡單的理解成類似於 nginx的負載均衡模塊的功能。
主流的LB方案可分成兩類:
一種是集中式LB, 即在服務的消費方和提供方之間使用獨立的LB設施(可以是硬件,如F5, 也可以是軟件,如nginx), 由該設施負責把訪問請求通過某種策略轉發至服務的提供方;
另一種是進程內LB,將LB邏輯集成到消費方,消費方從服務注冊中心獲知有哪些地址可用,然后自己再從這些地址中選擇出一個合適的服務器。Ribbon就屬於后者,它只是一個類庫,集成於消費方進程,消費方通過它來獲取到服務提供方的地址。
Ribbon的架構圖:如下:

1.首先我們先在原來的基礎上新建一個Ribbon模塊,如下圖:

現在我們單獨使用ribbon,在Ribbon模塊下添加依賴,如下圖所示:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-ribbon</artifactId>
<version>1.4.0.RELEASE</version>
</dependency>
修改application.yml文件,如下所示:
server: port: 8082 spring: application: name: Ribbon-Consumer #providers這個是自己命名的,ribbon,listOfServer這兩個是規定的 providers: ribbon: listOfServers: localhost:8080,localhost:8081
在Ribbon模塊下新建一個測試類如下代碼 * Created by cong on 2018/5/8. */
@RestController public class ConsumerController {
//注入負載均衡客戶端 @Autowired
private LoadBalancerClient loadBalancerClient; @RequestMapping("/consumer") public String helloConsumer() throws ExecutionException, InterruptedException {
//這里是根據配置文件的那個providers屬性取的 ServiceInstance serviceInstance = loadBalancerClient.choose("providers");
//負載均衡算法默認是輪詢,輪詢取得服務 URI uri = URI.create(String.format("http://%s:%s", serviceInstance.getHost(), serviceInstance.getPort())); return uri.toString();
}
運行結果如下:
會輪詢的獲取到兩個服務的URL 訪問第一次,瀏覽器出現http://localhost:8080 訪問第二次就會出現http://localhost:8081

在這里給普及一下有哪些負載均衡算法:
1:簡單輪詢負載均衡(RoundRobin)
以輪詢的方式依次將請求調度不同的服務器,即每次調度執行i = (i + 1) mod n,並選出第i台服務器。
2:隨機負載均衡 (Random)
隨機選擇狀態為UP的Server
3:加權響應時間負載均衡 (WeightedResponseTime)
根據相應時間分配一個weight,相應時間越長,weight越小,被選中的可能性越低。
4:區域感知輪詢負載均衡(ZoneAvoidanceRule)
復合判斷server所在區域的性能和server的可用性選擇server


有興趣的還可以看一下我在Ngnix的隨筆文章中列出的負載均衡算法實現:http://www.cnblogs.com/huangjuncong/p/8319182.html
如果想配置其他輪詢算法在yml配置文件中配置,如下配置一個隨機算法所示:
server: port: 8082 spring: application: name: Ribbon-Consumer #providers這個是自己命名的,ribbon,listOfServer這兩個是規定的 providers: ribbon: listOfServers: localhost:8080,localhost:8081 ##如果不想選用默認的輪詢的負載均衡算法,在這里做如下配置 NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule
接着在啟動類動一下手腳讓我們配置的隨機算法的負載均衡生效,只需要實現一個實現了IRule接口的Bean即可,如下:
package hjc; import com.netflix.loadbalancer.IRule; import com.netflix.loadbalancer.RandomRule; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; import org.springframework.cloud.client.loadbalancer.LoadBalanced; import org.springframework.context.annotation.Bean; import org.springframework.web.client.RestTemplate; @SpringBootApplication public class RibbonApplication { public static void main(String[] args) { SpringApplication.run(RibbonApplication.class, args); } @Bean public IRule ribbonRule(){ return new RandomRule(); } }
因此重新啟動Ribbon啟動類,得到的結果是隨機的,如下所示:
瀏覽器隨機出現http://localhost:8080 或者http://localhost:8081
那么問題來了,服務的地址是寫死在配置文件中,如果某個服務掛了,那么還會把請求轉發到掛掉的服務中,因此,解決的辦法是,跟Eureka對接,結合一起用。就可以依靠Eureka動態的獲取一個可用的服務列表,隔一段時間我就更新一次,
或者Eureka設置一個監聽端口,某一個服務掛了,Eureka通知我,我會知道,變更服務列表,這樣不久形成一個閉環了嗎?這樣就不存在高可用性問題了。跟Eureka配合一起用同時解決了的Ribbon的單點故障問題。
第一步,毫無疑問就是修改Ribbon模塊的pom.xml文件,加入如下依賴:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
<version>1.3.5.RELEASE</version>
</dependency>
第二步,修改yml配置文件如下:
server: port: 8082 spring: application: name: Ribbon-Consumer eureka: #客戶端 client: #注冊中心地址 service-url: defaultZone: http://localhost:8888/eureka/,http://localhost:8889/eureka/
這樣就可以跟Eureka結合,這樣Ribbon就可以通過Eureka動態的獲取服務列表
接着在啟動類加上服務發現注解,如下:
@EnableDiscoveryClient
啟動類接着聲明一個負載均衡的請求器@LoadBalanced,還有請求發起的工具RestTemplate
如下代碼:
@SpringBootApplication @EnableDiscoveryClient public class RibbonApplication { public static void main(String[] args) { SpringApplication.run(RibbonApplication.class, args); } @Bean @LoadBalanced public RestTemplate restTemplate(){ return new RestTemplate(); } }
接着我們在上一節文章中的兩個provider1,provider2模塊添加一下測試代碼:如下:
provider1:
package hjc.hello; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; /** * Created by cong on 2018/5/8. */ @RestController public class HelloController { @RequestMapping("/hello") public String hello(){ return "hello1"; } }
provider2代碼如下:
package hjc.hello; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; /** * Created by cong on 2018/5/8. */ @RestController public class HelloController { @RequestMapping("/hello") public String hello(){ return "hello2"; } }
接着我們用RestTemplate進行面向服務調用,不再面向IP調用。
如下代碼:
/** * Created by cong on 2018/5/8. */ @RestController public class ConsumerController { @Autowired private RestTemplate restTemplate; @RequestMapping("/consumer") public String helloConsumer() throws ExecutionException, InterruptedException { return restTemplate.getForEntity("http://HELLO-SERVICE/hello",String.class).getBody();
}
接着啟動Ribbon模塊,我們看一下Eureka儀表盤,如下:

可以看到多了RIBBON-CONSUMER服務
接着我們繼續在已經運行的Ribbon模塊上,在瀏覽器輸入localhost:8082,運行結果如下:
hello1或者hello2,
可以看到hello1 ,hello2輪詢方式出現,因為默認就是輪詢方式
到這里我們還發現Ribbon還是單點故障的,這里我來解釋一下:
因為這里我是單獨建立一個SpringBoot的Ribbon模塊,實際使用並不是這樣用的,Ribbon是客戶端的負載均衡,是跟客戶端綁定在一起的,我們實際運用的時候往往會在服務里面引入一個客戶端負載均衡去連接到Eureka客戶中心,
這樣我們還存在Ribbon單點故障嗎?不存在了,因為我們服務提供者就是高可用的,這樣還是個單點嗎?這里讀者的思考必須轉過彎來,這一篇隨筆我只是為了演示Ribbon,實際使用並不是這樣用的。
