Spring Cloud入門教程-Ribbon實現客戶端負載均衡

簡介

結構

咱們繼續以以前博客的代碼爲基礎,增長Ribbon組件來提供客戶端負載均衡。負載均衡是實現高併發、高性能、可伸縮服務的重要組成部分,它能夠把請求分散到一個集羣中不一樣的服務器中,以減輕每一個服務器的負擔。客戶端負載均衡是運行在客戶端程序中的,如咱們的web項目,而後經過獲取集羣的IP地址列表,隨機選擇一個server發送請求。相對於服務端負載均衡來講,它不須要消耗服務器的資源。git

基礎環境

  • JDK 1.8
  • Maven 3.3.9
  • IntelliJ 2018.1
  • Git

項目源碼

Gitee碼雲web

更新配置

咱們此次須要在本地啓動兩個產品服務程序,用來驗證負載均衡,因此須要爲第二個程序提供不一樣的端口。Spring Cloud配置服務中心的配置默認會覆蓋本地系統環境變量,而咱們須要經過系統環境變量來設置產品服務的端口,因此須要在配置中心git倉庫中修改產品服務的配置文件product-service.ymlspring

server:
  port: 8081
spring:
  cloud:
    config:
      allow-override: true
      override-system-properties: false

allow-override的默認值即爲true,寫出它來是想做說明,它的意思是容許遠程配置中心的配置項覆蓋本地的配置,並非說容許本地的配置去覆蓋遠程的配置。固然咱們能夠把它設置成false,可是爲了提供更精確的覆蓋規則,這裏保留了默認值。 咱們添加了override-system-properties=false,即雖然遠程配置中心的配置文件能夠覆蓋本地的配置,可是不要覆蓋本地系統變量。修改完成後提交到git倉庫。shell

另外,在productService項目的ProductController中添加一些log,用來驗證負載均衡是否生效:編程

package cn.zxuqian.controllers;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;


@RestController
public class ProductController {

    private static Logger log = LoggerFactory.getLogger(ProductController.class);

    @RequestMapping("/products")
    public String productList() {
        log.info("Access to /products endpoint");
        return "外套,夾克,毛衣,T恤";
    }
}

爲web配置Ribbon

首先在pom.xml中添加Ribbon的依賴:服務器

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
</dependency>

而後修改Application類,添加以下代碼:併發

@EnableCircuitBreaker
@EnableDiscoveryClient
@RibbonClient(name = "product-service")
@SpringBootApplication
public class Application {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }

    @Bean
    @LoadBalanced
    public RestTemplate rest(RestTemplateBuilder builder) {
        return builder.build();
    }
}

這裏用到了@RibbonClient(name = "product-service")註解,用來標記此項目爲Ribbon負載均衡的客戶端,它須要選擇產品服務集羣中其中的一臺來訪問所須要的服務,這裏的name屬性對應於productService項目中配置的spring.application.name屬性。 @LoadBalanced註解標明瞭RestTemplate會被配置爲自動使用Ribbon的LoadBalancerClient來選擇服務的uri併發送請求。app

在咱們在ProductService類中添加以下代碼:負載均衡

@Service
public class ProductService {

    private final RestTemplate restTemplate;

    @Autowired
    private DiscoveryClient discoveryClient;

    public ProductService(RestTemplate restTemplate) {
        this.restTemplate = restTemplate;
    }

    @HystrixCommand(fallbackMethod = "backupProductList")
    public String productList() {
        List<ServiceInstance> instances = this.discoveryClient.getInstances("product-service");
        if(instances != null && instances.size() > 0) {
            return this.restTemplate.getForObject(instances.get(0).getUri() + "/products", String.class);
        }

        return "";
    }

    public String backupProductList() {
        return "夾克,毛衣";
    }


    public String productListLoadBalanced() {
        return this.restTemplate.getForObject("http://product-service/products", String.class);
    }
}

這裏新添加了一個productListLoadBalanced方法,跟以前的productList方法訪問的是同一服務,只不過是用Ribbon Client去作了負載均衡,這裏的uri的host變成了product-service即要訪問的服務的名字,跟@RibbonClient中配置的name屬性保持一致。最後在咱們的ProductController中添加下面的代碼:ide

@RestController
public class ProductController {

    @Autowired
    private ProductService productService;

    @RequestMapping("/products")
    public String productList() {
        return productService.productList();
    }

    @RequestMapping("/productslb")
    public String productListLoadBalanced() {
        return productService.productListLoadBalanced();
    }
}

來建立一個專門處理/productslb請求的方法,調用productServie提供負載均衡的方法。

到這裏咱們的代碼就完成了,代碼看似簡單,實際上是全部的配置都使用了默認值。Ribbon提供了編程式和配置式兩種方式來配置Ribbon Client。現簡單介紹下,後續深刻Ribbon時再和你們一塊兒看看如何修改它的配置。Ribbon提供以下配置(左邊是接口,右邊是默認實現):

  • IClientConfig ribbonClientConfig: DefaultClientConfigImpl
  • IRule ribbonRule: ZoneAvoidanceRule
  • IPing ribbonPing: DummyPing
  • ServerList<Server> ribbonServerList: ConfigurationBasedServerList
  • ServerListFilter<Server> ribbonServerListFilter: ZonePreferenceServerListFilter
  • ILoadBalancer ribbonLoadBalancer: ZoneAwareLoadBalancer
  • ServerListUpdater ribbonServerListUpdater: PollingServerListUpdater

由於咱們這個項目用了Eureka,因此有些配置項和默認實現有所不一樣,如Eureka使用DiscoveryEnabledNIWSServerList取代ribbonServerList來獲取在Eureka上註冊的服務的列表。下邊有一個簡單的Congiguration類,來自Spring官網:

public class SayHelloConfiguration {

  @Autowired
  IClientConfig ribbonClientConfig;

  @Bean
  public IPing ribbonPing(IClientConfig config) {
    return new PingUrl();
  }

  @Bean
  public IRule ribbonRule(IClientConfig config) {
    return new AvailabilityFilteringRule();
  }

}

Ribbon默認不會發送Ping檢查server的健康狀態,默認均正常,而後IRune默認實現爲ZoneAvoidanceRule用來避免AWS EC2問題較多的zone,這在本地測試環境來講是用不到的,而後替換成了AvailabilityFilteringRule,這個能夠開啓Ribbon自帶的斷路器功能,來過濾不正常工做的服務器。

測試

首先啓動咱們的configserver配置中心服務,而後啓動registry Eureka註冊與發現服務,而後啓動兩個productService,第一個咱們能夠正常使用spring-boot:run插件來啓動,第二個咱們須要給它提供一個新的端口,能夠用以下命令啓動:

$ SERVER_PORT=8082 mvn spring-boot:run

最後啓動咱們的web客戶端項目,訪問http://localhost:8080/productslb,而後刷新幾回,會看到運行着productService的兩個命令行窗口會隨機出現咱們的log:

Access to /products endpoint

歡迎訪問個人博客張旭乾的博客

相關文章
相關標籤/搜索