客户端负载均衡-Ribbon 基础篇

客户端负载均衡-Ribbon 基础篇

文章目录

    • 客户端负载均衡-Ribbon 基础篇
    • 前言
    • 项目环境
    • 1.负载均衡简介
      • 1.1 集中式负载均衡
      • 1.2 客户端负载均衡
    • 2.什么是 Ribbon?
    • 3.RestTemplate 简介
    • 4.原生 Ribbon 示例
    • 5.RestTemplate & Ribbon 示例
    • 6.参考

前言

本章主要介绍 Ribbon 、RestTemplate 的一些相关的基本概念和用法,以及 RestTemplate 和 Ribbon 如何结合使用,对 Ribbon 有一个基本的印象,下一章我们再对其进行相关的源码分析。

项目环境

  • Java 8
  • Spring Cloud Finchley
  • 项目地址:https://github.com/huajiexiewenfeng/deep-in-spring-cloud-netflix

1.负载均衡简介

负载均衡是一种基础的网络服务,核心原理是按照指定的负载均衡算法,将请求分配到后端服务集群上,从而为系统提供并行处理和高可用的能力。

负载均衡一般分为以下两种:

  • 集中式负载均衡,在消费者和服务提供方中间使用独立的代理方式进行负载,有硬件的负载均衡器,比如 F5,也有软件,比如 Nginx。

    • F5,四层负载均衡通过虚拟 IP + 端口接收请求,然后再分配到真实的服务器
    • Nginx,七层负载均衡通过虚拟的 URL 或主机名接收请求,然后再分配到真实的服务器。
  • 客户端负载均衡,客户端根据自己的请求情况做负载,Ribbon 就属于客户端自己做负载均衡的框架。

1.1 集中式负载均衡

客户端负载均衡-Ribbon 基础篇_第1张图片

如上图所示,负债均衡器维护了需要负载的服务器实例相关信息,比如:服务1:192.168.1.100,服务2:192.168.1.100 两个实例。

客户端发送请求到负债均衡器,负载均衡器根据相关的负载均衡算法(随机、轮询、加权)选择其中一台服务器,将请求转发到服务器上。

1.2 客户端负载均衡

客户端负载均衡-Ribbon 基础篇_第2张图片

如上图所示,和集中式负载均衡的不同是,客户端负载均衡器需要自己维护服务实例的信息,然后通过相关的负载均衡算法(随机、轮询、加权)从实例中选取一个实例,直接进行访问。

2.什么是 Ribbon?

Ribbon 是 Netflix 公司开源的一个负载均衡的组件,它属于上述的第二种方式,将负载均衡逻辑封装在客户端中,并且运行在客户端的进程里。Ribbon 是一个经过了云端测试的 IPC 库,可以很好的控制 HTTP 和 TCP 客户端的负债均衡行为。

在 Spring Cloud 体系中,Ribbon 作为服务端消费者的负载均衡器,有三种使用方式:

  • 和 RestTemplate 结合使用
  • 和 Fegin 结合使用,Fegin 默认集成了 Ribbon,在 Fegin 的博客中,我们再进行讨论
  • 和 Zuul 结合使用,后续博客中进行讨论

3.RestTemplate 简介

RestTemplate 是 Spring Resources 中一个访问第三方 RESTful API 接口的网络请求框架。RestTemplate 的设计原则和 Spring 中其他 Template (例如 JdbcTemplate、JmsTemplate)类似,都是为了执行复杂任务提供了一个具有默认行为的模板方法。

示例

@RestController
public class RestTestController {

    @Autowired
    @Qualifier("restTemplate")
    private RestTemplate restTemplate;

    @GetMapping("/testRest")
    public String testRest() {
        String html = restTemplate.getForObject("http://www.baidu.com", String.class);
        return html;
    }
}

测试浏览器输入 http://127.0.0.1:8180/testRest

客户端负载均衡-Ribbon 基础篇_第3张图片

可以看到返回了该网页的 Html 字符串。

4.原生 Ribbon 示例

  • 首先构建一个服务实例列表,当然也可以通过服务名从注册中心获取
  • 构建负债均衡器,并设置负载策略为随机策略
  • 最后调用,Ribbon 会随机选择一个服务实例返回
public class RibbonNativeClientDemo {
    public static void main(String[] args) {
        // 服务实例列表
        List<Server> serverList = Arrays.asList(new Server("localhost", 8081),
                new Server("localhost", 8083),
                new Server("localhost", 8084),
                new Server("localhost", 8085));
        // 构建 LoadBalancer
        BaseLoadBalancer baseLoadBalancer = LoadBalancerBuilder.newBuilder().buildFixedServerListLoadBalancer(serverList);
        // 设置负债均衡策略
        baseLoadBalancer.setRule(new RandomRule());// 随机
        // 测试
        for (int i = 0; i < 10; i++) {
            test(baseLoadBalancer);
        }
    }

    private static void test(BaseLoadBalancer baseLoadBalancer) {
        String result = LoadBalancerCommand.<String>builder().withLoadBalancer(baseLoadBalancer).build()
                .submit(server -> {
                    try {
                        String addr = "http://" + server.getHost() + ":" + server.getPort();
                        System.out.println("调用地址:" + addr);
                        return Observable.just("success");
                    } catch (Exception e) {
                        return Observable.error(e);
                    }
                }).toBlocking().first();
        System.out.println("调用结果:" + result);
    }
}

测试结果:

调用地址:http://localhost:8085
调用结果:success
15:41:34.944 [main] DEBUG com.netflix.loadbalancer.LoadBalancerContext - default using LB returned Server: localhost:8081 for request null
调用地址:http://localhost:8081
调用结果:success
15:41:34.945 [main] DEBUG com.netflix.loadbalancer.LoadBalancerContext - default using LB returned Server: localhost:8081 for request null
调用地址:http://localhost:8081
调用结果:success
15:41:34.945 [main] DEBUG com.netflix.loadbalancer.LoadBalancerContext - default using LB returned Server: localhost:8085 for request null
调用地址:http://localhost:8085
调用结果:success
15:41:34.945 [main] DEBUG com.netflix.loadbalancer.LoadBalancerContext - default using LB returned Server: localhost:8083 for request null
调用地址:http://localhost:8083
...

可以看到每次返回的服务器实例信息是随机的。

5.RestTemplate & Ribbon 示例

完整的代码请参考 github 上的项目,这里只展示核心的代码

我们先配置两个 RestTemplate,注入到 IoC 容器中

  • 一个是普通的 RestTemplate
  • 一个是增加了 @LoadBalanced 注解的 RestTemplate,也就是集成 Ribbon
@Configuration
public class RestTemplateConfig {

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

    @Bean
    @LoadBalanced
    @Qualifier("loadBalancedRestTemplate")
    public RestTemplate loadBalancedRestTemplate() {
        return new RestTemplate();
    }

}

对应的 Controller 我们同样写两个方法,使用 @Qualifier 分别依赖注入两个 RestTemplate

  • getUser 使用普通的 restTemplate
  • getUserRibbon 使用集成了 Ribbon 的 loadBalancedRestTemplate
@RestController
public class RibbonController {

    @Autowired
    @Qualifier("restTemplate")
    private RestTemplate restTemplate;

    @Autowired
    @Qualifier("loadBalancedRestTemplate")
    private RestTemplate loadBalancedRestTemplate;

    @GetMapping("/getUser")
    public User getUser() {
        User user = restTemplate.getForObject("http://localhost:8181/getUser", User.class);
        return user;
    }

    @GetMapping("/ribbon/getUser")
    public User getUserRibbon() {
        User user = loadBalancedRestTemplate.getForObject("http://user-service/getUser", User.class);
        return user;
    }

}

启动&测试

这里我们需要起两个服务 user-service 的服务注册到 Eureka 注册中心上。

UserController 代码如下:

@RestController
public class UserController {

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

    @GetMapping("getUser")
    public User getUser() {
        User user = new User();
        user.setId(1L);
        user.setName("小仙 port:" + port);
        return user;
    }

}

全部启动完成之后的效果如下:

客户端负载均衡-Ribbon 基础篇_第4张图片

注册中心

客户端负载均衡-Ribbon 基础篇_第5张图片

测试普通接口,浏览器输入 http://127.0.0.1:8180/getUser,每次刷新返回的结果都一样,如下图所示:

客户端负载均衡-Ribbon 基础篇_第6张图片

测试负载均衡结果,浏览器输入 http://127.0.0.1:8180/ribbon/getUser

第一次刷新,结果如下图所示:

客户端负载均衡-Ribbon 基础篇_第7张图片

第二次刷新,结果如下图所示:

客户端负载均衡-Ribbon 基础篇_第8张图片

多次刷新,每次的返回结果的 port 端口号都不一样,表示请求每次调用的服务提供者都不一样,这说明已经完成负债均衡。

6.参考

  • 《深入理解 Spring Cloud 与微服务架构》 方志朋

  • 《300分钟搞懂 Spring Cloud》尹吉欢

你可能感兴趣的:(Spring,Cloud,系列,负载均衡,Ribbon)