一、Spring Cloud Ribbon简介

在微服务架构中,很多服务都会部署多个,其他服务去调用该服务的时候,如何保证负载均衡是个不得不去考虑的问题。负载均衡可以增加系统的可用性和扩展性,当我们使用RestTemplate来调用其他服务时,Ribbon可以很方便的实现负载均衡功能。

Spring Cloud Ribbon 是基于Netflix Ribbon实现的一套客户端负载均衡的工具。它是一个基于HTTP和TCP的客户端负载均衡器。它可以通过在客户端中配置ribbonServerList来设置服务端列表去轮询访问以达到均衡负载的作用。

当Ribbon与Eureka联合使用时,ribbonServerList会被DiscoveryEnabledNIWSServerList重写,扩展成从Eureka注册中心中获取服务实例列表。同时它也会用NIWSDiscoveryPing来取代IPing,它将职责委托给Eureka来确定服务端是否已经启动。

而当Ribbon与Consul联合使用时,ribbonServerList会被ConsulServerList来扩展成从Consul获取服务实例列表。同时由ConsulPing来作为IPing接口的实现。

我们在使用Spring Cloud Ribbon的时候,不论是与Eureka还是Consul结合,都会在引入Spring Cloud Eureka或Spring Cloud Consul依赖的时候通过自动化配置来加载上述所说的配置内容,所以我们可以快速在Spring Cloud中实现服务间调用的负载均衡。

二、准备工作

我们下面做个Eureka+Ribbon+RestTemplate的案例,端口规划如下:

  • 1台Eureka服务器:8001
  • 3台Ribbon客户端:8101 8102 8103
  • 1台Ribbon服务器:8201

先创建Eureka服务器和客户端,参考上一节:📃 Eureka 服务注册与发现

在Eureka客户端(同时也扮演了Ribbon客户端的角色)中创建控制器,以便之后测试:

  1. import org.slf4j.Logger;
  2. import org.slf4j.LoggerFactory;
  3. import org.springframework.web.bind.annotation.*;
  4. import java.util.HashMap;
  5. import java.util.Map;
  6. @RestController
  7. @RequestMapping("/test")
  8. public class TestController {
  9. private Logger LOGGER = LoggerFactory.getLogger(this.getClass());
  10. @GetMapping("/test")
  11. @ResponseBody
  12. public Object test() {
  13. Map<String, Object> map = new HashMap<>();
  14. map.put("code", "1");
  15. map.put("message", "ok");
  16. map.put("data", "");
  17. LOGGER.info("服务已被调用");
  18. return map;
  19. }
  20. }

启动Eureka服务器和客户端:
Snipaste_2021-01-24_20-54-51.png

在Eureka服务端管理界面看到,3个客户端已注册成功:
Snipaste_2021-01-24_20-58-37.png

访问客户端试试:
Snipaste_2021-01-24_21-00-06.pngSnipaste_2021-01-24_21-00-19.png
Snipaste_2021-01-24_20-59-56.png

都能访问成功,准备工作已经完毕。

三、Ribbon服务

创建一个ribbon-server模块,添加以下依赖:

  1. <dependency>
  2. <groupId>org.springframework.cloud</groupId>
  3. <artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
  4. </dependency>
  5. <dependency>
  6. <groupId>org.springframework.cloud</groupId>
  7. <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
  8. </dependency>
  9. <dependency>
  10. <groupId>org.springframework.boot</groupId>
  11. <artifactId>spring-boot-starter-web</artifactId>
  12. </dependency>

配置文件:

server:
  port: 8201
spring:
  application:
    name: ribbon-service
eureka:
  client:
    register-with-eureka: true
    fetch-registry: true
    service-url:
      defaultZone: http://localhost:8001/eureka/

添加一个Ribbon的配置类,只需给RestTemplate添加一个@LoadBalanced注解,表明restTemplate使用LoadBalancerClient(负载均衡客户端)执行请求:

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

在控制器中使用:

@RestController
@RequestMapping("/test")
public class TestController {
    @Autowired
    private RestTemplate restTemplate;

    @GetMapping("/test")
    @ResponseBody
    public Object test() {
        return restTemplate.getForObject("http://eureka-client/test/{1}", Object.class, "test");
    }
}

其中 http://eureka-client 指向Ribbon客户端的内网地址(即Ribbon客户端的服务名称,我们前面创建的3个Eureka客户端就充当了Ribbon客户端的角色)

启动Ribbon服务器,访问:http://localhost:8201/test/test
Snipaste_2021-01-24_21-14-39.png
多访问几次,负载均衡服务器将在多个Ribbon客户端之间切换:
Snipaste_2021-01-24_21-20-48.pngSnipaste_2021-01-24_21-21-03.pngSnipaste_2021-01-24_21-21-12.png

四、Ribbon常用配置

ribbon:
  ConnectTimeout: 1000 #服务请求连接超时时间(毫秒)
  ReadTimeout: 3000 #服务请求处理超时时间(毫秒)
  OkToRetryOnAllOperations: true #对超时请求启用重试机制
  MaxAutoRetriesNextServer: 1 #切换重试实例的最大个数
  MaxAutoRetries: 1 # 切换实例后重试最大次数
  NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule #修改负载均衡算法

五、RestTemplate

上面调用远程服务,使用到了RestTemplate。、

RestTemplate是一个HTTP客户端,使用它我们可以方便的调用HTTP接口,支持GET、POST、PUT、DELETE等方法。

常用的调用方法:

  • GET: getForObjectgetForEntity
  • POST: postForObjectpostForEntity
  • PUT: put
  • DELETE: delete

GET

getForObject:返回对象为响应体中数据转化成的对象。
定义:

<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);

举例:

@GetMapping("/{id}")
public CommonResult getUser(@PathVariable Long id) {
    return restTemplate.getForObject("http://eureka-client/user/{1}", CommonResult.class, id);
}

getForEntity:返回对象为ResponseEntity对象,包含了响应中的一些重要信息,比如响应头、响应状态码、响应体等。
定义:

<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);

举例:

@GetMapping("/getEntityByUsername")
public CommonResult getEntityByUsername(@RequestParam String username) {
    ResponseEntity<CommonResult> entity = restTemplate.getForEntity("http://eureka-client/user/getByUsername?username={1}", CommonResult.class, username);
    if (entity.getStatusCode().is2xxSuccessful()) {
        return entity.getBody();
    } else {
        return new CommonResult("操作失败", 500);
    }
}

POST

postForObject
定义:

<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);

举例:

@PostMapping("/create")
public CommonResult create(@RequestBody User user) {
    return restTemplate.postForObject("http://eureka-client/user/create", user, CommonResult.class);
}

postForEntity
定义:

<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);

举例:

@PostMapping("/create")
public CommonResult create(@RequestBody User user) {
    return restTemplate.postForEntity("http://eureka-client/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);

示例:

@PutMapping("/update")
public CommonResult update(@RequestBody User user) {
    restTemplate.put("http://eureka-client/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);

示例:

@DeleteMapping("/delete/{id}")
public CommonResult delete(@PathVariable Long id) {
   restTemplate.delete("http://eureka-client/user/delete/{1}", null, id);
   return new CommonResult("操作成功",200);
}

参考资料