天天看點

SpringCloud實戰2-Ribbon用戶端負載均衡

前面我們已經完成了注冊中心和服務提供者兩個基礎元件。接着介紹使用Spring Cloud Ribbon在用戶端負載均衡的調用服務。

ribbon 是一個用戶端負載均衡器,可以簡單的了解成類似于 nginx的負載均衡子產品的功能。

主流的LB方案可分成兩類:

  一種是集中式LB, 即在服務的消費方和提供方之間使用獨立的LB設施(可以是硬體,如F5, 也可以是軟體,如nginx), 由該設施負責把通路請求通過某種政策轉發至服務的提供方;

  另一種是程序内LB,将LB邏輯內建到消費方,消費方從服務注冊中心獲知有哪些位址可用,然後自己再從這些位址中選擇出一個合适的伺服器。Ribbon就屬于後者,它隻是一個類庫,內建于消費方程序,消費方通過它來擷取到服務提供方的位址。

  Ribbon的架構圖:如下:

SpringCloud實戰2-Ribbon用戶端負載均衡
1.首先我們先在原來的基礎上建立一個Ribbon子產品,如下圖:
SpringCloud實戰2-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

SpringCloud實戰2-Ribbon用戶端負載均衡

在這裡給普及一下有哪些負載均衡算法:

  

  1:簡單輪詢負載均衡(RoundRobin)

       以輪詢的方式依次将請求排程不同的伺服器,即每次排程執行i = (i + 1) mod n,并選出第i台伺服器。

   2:随機負載均衡 (Random)

       随機選擇狀态為UP的Server

   3:權重響應時間負載均衡 (WeightedResponseTime)

     根據相應時間配置設定一個weight,相應時間越長,weight越小,被選中的可能性越低。

   4:區域感覺輪詢負載均衡(ZoneAvoidanceRule)

       複合判斷server所在區域的性能和server的可用性選擇server

SpringCloud實戰2-Ribbon用戶端負載均衡
SpringCloud實戰2-Ribbon用戶端負載均衡

有興趣的還可以看一下我在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儀表盤,如下:

SpringCloud實戰2-Ribbon用戶端負載均衡

可以看到多了RIBBON-CONSUMER服務

接着我們繼續在已經運作的Ribbon子產品上,在浏覽器輸入localhost:8082,運作結果如下:

  hello1或者hello2,

可以看到hello1 ,hello2輪詢方式出現,因為預設就是輪詢方式

到這裡我們還發現Ribbon還是單點故障的,這裡我來解釋一下:

  因為這裡我是單獨建立一個SpringBoot的Ribbon子產品,實際使用并不是這樣用的,Ribbon是用戶端的負載均衡,是跟用戶端綁定在一起的,我們實際運用的時候往往會在服務裡面引入一個用戶端負載均衡去連接配接到Eureka客戶中心,

這樣我們還存在Ribbon單點故障嗎?不存在了,因為我們服務提供者就是高可用的,這樣還是個單點嗎?這裡讀者的思考必須轉過彎來,這一篇随筆我隻是為了示範Ribbon,實際使用并不是這樣用的。