Spring Cloud Ribbon:負載均衡的服務調用

SpringBoot實戰電商項目mall(20k+star)地址: https://github.com/macrozheng/mall

摘要

Spring Cloud Ribbon 是Spring Cloud Netflix 子項目的核心組件之一,主要給服務間調用及API網關轉發提供負載均衡的功能,本文將對其用法進行詳細介紹。java

Ribbon簡介

在微服務架構中,不少服務都會部署多個,其餘服務去調用該服務的時候,如何保證負載均衡是個不得不去考慮的問題。負載均衡能夠增長系統的可用性和擴展性,當咱們使用RestTemplate來調用其餘服務時,Ribbon能夠很方便的實現負載均衡功能。git

RestTemplate的使用

RestTemplate是一個HTTP客戶端,使用它咱們能夠方便的調用HTTP接口,支持GET、POST、PUT、DELETE等方法。

GET請求方法

<T> T getForObject(String url, Class<T> responseType, Object... uriVariables);

<T> T getForObject(String url, Class<T> responseType, Map<String, ?> uriVariables);

<T> T getForObject(URI url, Class<T> responseType);

<T> ResponseEntity<T> getForEntity(String url, Class<T> responseType, Object... uriVariables);

<T> ResponseEntity<T> getForEntity(String url, Class<T> responseType, Map<String, ?> uriVariables);

<T> ResponseEntity<T> getForEntity(URI var1, Class<T> responseType);

getForObject方法

返回對象爲響應體中數據轉化成的對象,舉例以下:github

@GetMapping("/{id}")
public CommonResult getUser(@PathVariable Long id) {
    return restTemplate.getForObject(userServiceUrl + "/user/{1}", CommonResult.class, id);
}

getForEntity方法

返回對象爲ResponseEntity對象,包含了響應中的一些重要信息,好比響應頭、響應狀態碼、響應體等,舉例以下:web

@GetMapping("/getEntityByUsername")
public CommonResult getEntityByUsername(@RequestParam String username) {
    ResponseEntity<CommonResult> entity = restTemplate.getForEntity(userServiceUrl + "/user/getByUsername?username={1}", CommonResult.class, username);
    if (entity.getStatusCode().is2xxSuccessful()) {
        return entity.getBody();
    } else {
        return new CommonResult("操做失敗", 500);
    }
}

POST請求方法

<T> T postForObject(String url, @Nullable Object request, Class<T> responseType, Object... uriVariables);

<T> T postForObject(String url, @Nullable Object request, Class<T> responseType, Map<String, ?> uriVariables);

<T> T postForObject(URI url, @Nullable Object request, Class<T> responseType);

<T> ResponseEntity<T> postForEntity(String url, @Nullable Object request, Class<T> responseType, Object... uriVariables);

<T> ResponseEntity<T> postForEntity(String url, @Nullable Object request, Class<T> responseType, Map<String, ?> uriVariables);

<T> ResponseEntity<T> postForEntity(URI url, @Nullable Object request, Class<T> responseType);

postForObject示例

@PostMapping("/create")
public CommonResult create(@RequestBody User user) {
    return restTemplate.postForObject(userServiceUrl + "/user/create", user, CommonResult.class);
}

postForEntity示例

@PostMapping("/create")
public CommonResult create(@RequestBody User user) {
    return restTemplate.postForEntity(userServiceUrl + "/user/create", user, CommonResult.class).getBody();
}

PUT請求方法

void put(String url, @Nullable Object request, Object... uriVariables);

void put(String url, @Nullable Object request, Map<String, ?> uriVariables);

void put(URI url, @Nullable Object request);

PUT請求示例

@PutMapping("/update")
public CommonResult update(@RequestBody User user) {
    restTemplate.put(userServiceUrl + "/user/update", user);
    return new CommonResult("操做成功",200);
}

DELETE請求方法

void delete(String url, Object... uriVariables);

void delete(String url, Map<String, ?> uriVariables);

void delete(URI url);

DELETE請求示例

@DeleteMapping("/delete/{id}")
public CommonResult delete(@PathVariable Long id) {
   restTemplate.delete(userServiceUrl + "/user/delete/{1}", null, id);
   return new CommonResult("操做成功",200);
}

建立一個user-service模塊

首先咱們建立一個user-service,用於給Ribbon提供服務調用。

在pom.xml中添加相關依賴

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

在application.yml進行配置

主要是配置了端口和註冊中心地址。
server:
  port: 8201
spring:
  application:
    name: user-service
eureka:
  client:
    register-with-eureka: true
    fetch-registry: true
    service-url:
      defaultZone: http://localhost:8001/eureka/

添加UserController用於提供調用接口

UserController類定義了對User對象常見的CRUD接口。
/**
 * Created by macro on 2019/8/29.
 */
@RestController
@RequestMapping("/user")
public class UserController {

    private Logger LOGGER = LoggerFactory.getLogger(this.getClass());

    @Autowired
    private UserService userService;

    @PostMapping("/create")
    public CommonResult create(@RequestBody User user) {
        userService.create(user);
        return new CommonResult("操做成功", 200);
    }

    @GetMapping("/{id}")
    public CommonResult<User> getUser(@PathVariable Long id) {
        User user = userService.getUser(id);
        LOGGER.info("根據id獲取用戶信息,用戶名稱爲:{}",user.getUsername());
        return new CommonResult<>(user);
    }

    @GetMapping("/getUserByIds")
    public CommonResult<List<User>> getUserByIds(@RequestParam List<Long> ids) {
        List<User> userList= userService.getUserByIds(ids);
        LOGGER.info("根據ids獲取用戶信息,用戶列表爲:{}",userList);
        return new CommonResult<>(userList);
    }

    @GetMapping("/getByUsername")
    public CommonResult<User> getByUsername(@RequestParam String username) {
        User user = userService.getByUsername(username);
        return new CommonResult<>(user);
    }

    @PostMapping("/update")
    public CommonResult update(@RequestBody User user) {
        userService.update(user);
        return new CommonResult("操做成功", 200);
    }

    @PostMapping("/delete/{id}")
    public CommonResult delete(@PathVariable Long id) {
        userService.delete(id);
        return new CommonResult("操做成功", 200);
    }
}

建立一個ribbon-service模塊

這裏咱們建立一個ribbon-service模塊來調用user-service模塊演示負載均衡的服務調用。

在pom.xml中添加相關依賴

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<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>

在application.yml進行配置

主要是配置了端口、註冊中心地址及user-service的調用路徑。
server:
  port: 8301
spring:
  application:
    name: ribbon-service
eureka:
  client:
    register-with-eureka: true
    fetch-registry: true
    service-url:
      defaultZone: http://localhost:8001/eureka/
service-url:
  user-service: http://user-service

使用@LoadBalanced註解賦予RestTemplate負載均衡的能力

能夠看出使用Ribbon的負載均衡功能很是簡單,和直接使用RestTemplate沒什麼兩樣,只需給RestTemplate添加一個@LoadBalanced便可。
/**
 * Created by macro on 2019/8/29.
 */
@Configuration
public class RibbonConfig {

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

添加UserRibbonController類

注入RestTemplate,使用其調用user-service中提供的相關接口,這裏對GET和POST調用進行了演示,其餘方法調用都可參考。
/**
 * Created by macro on 2019/8/29.
 */
@RestController
@RequestMapping("/user")
public class UserRibbonController {
    @Autowired
    private RestTemplate restTemplate;
    @Value("${service-url.user-service}")
    private String userServiceUrl;

    @GetMapping("/{id}")
    public CommonResult getUser(@PathVariable Long id) {
        return restTemplate.getForObject(userServiceUrl + "/user/{1}", CommonResult.class, id);
    }

    @GetMapping("/getByUsername")
    public CommonResult getByUsername(@RequestParam String username) {
        return restTemplate.getForObject(userServiceUrl + "/user/getByUsername?username={1}", CommonResult.class, username);
    }

    @GetMapping("/getEntityByUsername")
    public CommonResult getEntityByUsername(@RequestParam String username) {
        ResponseEntity<CommonResult> entity = restTemplate.getForEntity(userServiceUrl + "/user/getByUsername?username={1}", CommonResult.class, username);
        if (entity.getStatusCode().is2xxSuccessful()) {
            return entity.getBody();
        } else {
            return new CommonResult("操做失敗", 500);
        }
    }

    @PostMapping("/create")
    public CommonResult create(@RequestBody User user) {
        return restTemplate.postForObject(userServiceUrl + "/user/create", user, CommonResult.class);
    }

    @PostMapping("/update")
    public CommonResult update(@RequestBody User user) {
        return restTemplate.postForObject(userServiceUrl + "/user/update", user, CommonResult.class);
    }

    @PostMapping("/delete/{id}")
    public CommonResult delete(@PathVariable Long id) {
        return restTemplate.postForObject(userServiceUrl + "/user/delete/{1}", null, CommonResult.class, id);
    }
}

負載均衡功能演示

  • 啓動eureka-server於8001端口;
  • 啓動user-service於8201端口;
  • 啓動另外一個user-service於8202端口,能夠經過修改IDEA中的SpringBoot的啓動配置實現:

  • 此時運行中的服務以下:

  • 能夠發現運行在8201和8202的user-service控制檯交替打印以下信息:

Ribbon的經常使用配置

全局配置

ribbon:
  ConnectTimeout: 1000 #服務請求鏈接超時時間(毫秒)
  ReadTimeout: 3000 #服務請求處理超時時間(毫秒)
  OkToRetryOnAllOperations: true #對超時請求啓用重試機制
  MaxAutoRetriesNextServer: 1 #切換重試實例的最大個數
  MaxAutoRetries: 1 # 切換實例後重試最大次數
  NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule #修改負載均衡算法

指定服務進行配置

與全局配置的區別就是ribbon節點掛在服務名稱下面,以下是對ribbon-service調用user-service時的單獨配置。
user-service:
  ribbon:
    ConnectTimeout: 1000 #服務請求鏈接超時時間(毫秒)
    ReadTimeout: 3000 #服務請求處理超時時間(毫秒)
    OkToRetryOnAllOperations: true #對超時請求啓用重試機制
    MaxAutoRetriesNextServer: 1 #切換重試實例的最大個數
    MaxAutoRetries: 1 # 切換實例後重試最大次數
    NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule #修改負載均衡算法

Ribbon的負載均衡策略

所謂的負載均衡策略,就是當A服務調用B服務時,此時B服務有多個實例,這時A服務以何種方式來選擇調用的B實例,ribbon能夠選擇如下幾種負載均衡策略。
  • com.netflix.loadbalancer.RandomRule:從提供服務的實例中以隨機的方式;
  • com.netflix.loadbalancer.RoundRobinRule:以線性輪詢的方式,就是維護一個計數器,從提供服務的實例中按順序選取,第一次選第一個,第二次選第二個,以此類推,到最後一個之後再從頭來過;
  • com.netflix.loadbalancer.RetryRule:在RoundRobinRule的基礎上添加劇試機制,即在指定的重試時間內,反覆使用線性輪詢策略來選擇可用實例;
  • com.netflix.loadbalancer.WeightedResponseTimeRule:對RoundRobinRule的擴展,響應速度越快的實例選擇權重越大,越容易被選擇;
  • com.netflix.loadbalancer.BestAvailableRule:選擇併發較小的實例;
  • com.netflix.loadbalancer.AvailabilityFilteringRule:先過濾掉故障實例,再選擇併發較小的實例;
  • com.netflix.loadbalancer.ZoneAwareLoadBalancer:採用雙重過濾,同時過濾不是同一區域的實例和故障實例,選擇併發較小的實例。

使用到的模塊

springcloud-learning
├── eureka-server -- eureka註冊中心
├── user-service -- 提供User對象CRUD接口的服務
└── ribbon-service -- ribbon服務調用測試服務

項目源碼地址

https://github.com/macrozheng/springcloud-learning算法

公衆號

mall項目全套學習教程連載中,關注公衆號第一時間獲取。spring

公衆號圖片

相關文章
相關標籤/搜索