spring cloud 之ribbon組件的學習與使用

Ribbon是什麼

ribbon是一種可以實現客戶端負載均衡的組件,它和服務端負載均衡有什麼區別呢?

客戶端負載均衡:

 原理:部署在客戶端,客戶端獲取所有的服務器列表,然後可以根據各自不同客戶端的負載均衡算法,找到要訪問的服務器

優勢:

1.更自由的爲不同客戶端實現不同的負載均衡算法

2. 穩定性好,就算掛了一臺客戶端或服務器,也不會對其他可用的客戶端的負載均衡有任何影響

服務端負載均衡

原理:當有請求時,攔截請求,然後使用比如nginx的反向代理的方式去獲取到要訪問的服務器

優勢:

1. 統一維護,成本低,因爲都放在類似nginx這個統一的地方進行處理了

不足:如果nginx宕機了,那麼所有服務都用不了了

1.Ribbon使用前

服務提供者與服務消費者之間的調用方式一般就是通過RestTemplate,或者HttpClient等請求方式直接請求接口獲取數據

1.1 服務提供者

1.1.1 application.yml:

server:
  port: 8085
spring:
  application:
    name: ribbon-service-provider

1.1.2 controller

@RestController
public class ServiceProviderController {

    @Value("${server.port}")
    private Integer port;

    @PostMapping("/greeting")
    public String greeting(@RequestBody User user) {
        return "Greeting , " + user + " on port : " + port;
    }
}

然後使用postman請求一下,發現返回正常,那麼服務提供方是沒有問題了

1.2 服務消費者

1.2.1 啓動類

package cn.tanfp.cloudstudy.ribbondemo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;

@SpringBootApplication
public class RibbonDemoApplication {

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

    @Bean
    public RestTemplate restTemplate(){
        return new RestTemplate();
    }
}

1.2.2 application.yml

server:
  port: 8086
spring:
  application:
    name: ribbon-demo

serivce-provider:
  host: localhost
  port: 8085

1.2.3 controller

package cn.tanfp.cloudstudy.ribbondemo.web.controller;

import cn.tanfp.cloudstudy.ribbondemo.domain.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

@RestController
public class ClientController {

    @Autowired
    private RestTemplate restTemplate;

    @Value("${serivce-provider.host}")
    private String serviceProviderHost;

    @Value("${serivce-provider.port}")
    private Integer serviceProviderPort;


    @GetMapping("/a")
    public String index() {
        User user = new User();
        user.setId(1L);
        user.setName("tanfp");
        return restTemplate.postForObject("http://" +
                        serviceProviderHost + ":" + serviceProviderPort +
                        "/greeting",
                user, String.class);

    }

}

1.2.4 測試

調用http://localhost:8086/a發現正常返回結果,說明使用restTemplate直接調用服務提供者的url是可以的。這種方式很直接,但是有幾個問題:

1. 用不了客戶端負載均衡(服務端負載均衡是可以的,但是使用服務端的負載均衡配置量會很大,而且每有新的客戶端服務器都需要配置一下負載均衡的配置,不能動態生成)

2. 寫起來麻煩

2. 使用ribbon(不集成eureka的情況)

2.1 引入依賴

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

2.2 啓動類

@SpringBootApplication
public class RibbonDemoApplication {

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

    //聲明 RestTemplate
    // 啓用ribbon
    @LoadBalanced
    @Bean
    public RestTemplate restTemplate(){
        return new RestTemplate();
    }
}

2.3 application.yml

server:
  port: 8086
spring:
  application:
    name: ribbon-demo

serivce-provider:
  host: localhost
  port: 8085
  name: ribbon-service-provider

ribbon-service-provider:
  ribbon:
    listOfServers: http://${serivce-provider.host}:${serivce-provider.port}

定義ribbon的服務列表格式:服務提供方的應用名.ribbon.listOfServers,多個用逗號隔開

2.4 controller

package cn.tanfp.cloudstudy.ribbondemo.web.controller;

import cn.tanfp.cloudstudy.ribbondemo.domain.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

/**
 * Client {@link RestController}
 *
 * @author <a href="mailto:[email protected]">Mercy</a>
 * @since TODO
 */
@RestController
public class ClientController {

    @Autowired
    private RestTemplate restTemplate;

    @Value("${serivce-provider.name}")
    private String serviceProviderName;


    @GetMapping("/a")
    public String index() {
        User user = new User();
        user.setId(1L);
        user.setName("tanfp");

        return restTemplate.postForObject("http://" +
                        serviceProviderName +
                        "/greeting",
                user, String.class);
    }

}

這裏是使用服務名代替了上面的域名+端口的形式

2.5 測試是否可用

調用http://localhost:8086/a發現正常返回結果,說明使用restTemplate直接調用服務提供者的服務名是可以的

2.6 總結:使用ribbon這種方式相對於上面的傳統直接調用的方式優勢就在於,只需要服務名就能調用了,就算有多個服務提供者,也不需要關注他們的域名和端口,只需要在application.yml進行簡單配置即可,且實現了客戶端負載均衡,但是這樣還是有些問題,比如我又加了一臺服務提供者,那麼我需要在每個消費者的application.yml中維護一下listOfServers屬性,這是非常麻煩的,所以我們想到了將eureka+ribbon結合的方式來做,這樣我們就不需要管理那些服務提供者的具體實例了,只需要知道他們的應用名就行了

3. Ribbon+Eureka

高可用的服務註冊中心我就不寫了,網上很容易找,我的博客中也有

3.1 服務提供方

3.1.1 添加依賴

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

3.1.2 啓動類

@SpringBootApplication
@EnableDiscoveryClient
public class RibbonServiceProviderDemoApplication {

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

}

3.1.3 配置

server:
  port: 8085
spring:
  application:
    name: ribbon-service-provider

eureka:
  client:
    serviceUrl:
      #配置多個eureka註冊中心
      defaultZone: http://localhost:9094/eureka/,http://localhost:9093/eureka/
#關閉管理端actuator的安全,即/env /health端口完全開放
#management.security.enabled 在spring-boot2.0時過時,使用如下配置代替
management:
  endpoints:
    web:
      exposure:
        include: "*"

3.1.4 controller,這裏打印port是爲了測試負載均衡

@RestController
public class ServiceProviderController {

    @Value("${server.port}")
    private Integer port;

    @PostMapping("/greeting")
    public String greeting(@RequestBody User user) {
        return "Greeting , " + user + " on port : " + port;
    }

}

3.2 服務消費者

3.2.1 pom

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

3.2.2 啓動類

@SpringBootApplication
@EnableDiscoveryClient
public class RibbonDemoApplication {

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

    //聲明 RestTemplate
    // 啓用ribbon
    @LoadBalanced
    @Bean
    public RestTemplate restTemplate(){
        return new RestTemplate();
    }
}

3.2.3 配置

server:
  port: 8086
spring:
  application:
    name: ribbon-demo

serivce-provider:
  name: ribbon-service-provider

eureka:
  client:
    serviceUrl:
      #配置多個eureka註冊中心
      defaultZone: http://localhost:9094/eureka/,http://localhost:9093/eureka/

3.2.4 controller

@RestController
public class ClientController {

    @Autowired
    private RestTemplate restTemplate;

    @Value("${serivce-provider.name}")
    private String serviceProviderName;


    @GetMapping("/a")
    public String index() {
        User user = new User();
        user.setId(1L);
        user.setName("tanfp");

        return restTemplate.postForObject("http://" +
                        serviceProviderName +
                        "/greeting",
                user, String.class);
    }

}

3.2.5 測試是否可用

調用http://localhost:8086/a,結果返回正常就行了

3.2.6 測試負載均衡

爲了方便一點,我就不復制服務提供者項目了,直接在idea 中修改下啓動參數,然後開不同端口就好了。

選擇項目後,點擊編輯config,然後:

這裏一定要把allow parallel run 勾選上,意思是是否允許同一個項目可並行運行,第二步就是添加program 參數,

--server.port=8087,修改下端口,這樣等火啓動時不會報端口衝突錯誤。

保存後,你會發現之前已經運行的項目現在變成了可啓動的圖標,然後點擊即可

啓動後需要稍微一夥,讓該服務註冊到註冊中心上,然後請求http://localhost:8086/a,會發現,打印的端口會有變化,且時按輪詢的規則訪問的服務提供者

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章