程序员

Spring Cloud — Ribbon 负载均衡

2020-03-16  本文已影响0人  俞大仙_

版本控制

介绍

一般来说,为了服务的高可用,在生产环境中,每个微服务通常都会部署多个实例。因此服务消费者需要将请求合理的分摊到多个服务提供者实力上。Spring cloud Ribbon是基于Netflix Ribbon实现的一套客户端负载均衡工具

Nginx & Ribbon

Nginx

Nginx是服务端负载均衡。Nginx存在于服务的前端。客户端所有的请求都会交给Nginx服务器,然后由Nginx实现请求的分发。属于服务端负载均衡。

Ribbon

Ribbon是客户端负载均衡。它可以存在于多个客户端中。在SpringCloud中,Ribbon可以配合Eureka使用,在调用接口时,会从Eureka Server中获取服务列表,并基于指定的负载策略请求其中的一个实例。

LoadBalancerClient

LoadBalancerClientRibbon的核心组件。它是定义客户端负载均衡的接口。

可以调用choose方法来获取服务实例:

ServiceInstance instance = loadBalancerClient.choose("serviceId");

RibbonLoadBalancerClientchoose()方法中,通过负载均衡器ILoadBalancer去获取实例信息:

    protected Server getServer(ILoadBalancer loadBalancer, Object hint) {
        if (loadBalancer == null) {
            return null;
        }
        // 获取服务实例
        return loadBalancer.chooseServer(hint != null ? hint : "default");
    }

ILoadBalancer

定义负载均衡具体操作的接口。维护服务实例信息;

public interface ILoadBalancer {

    /**
     * 初始化服务列表
     */
    public void addServers(List<Server> newServers);
    
    /**
     * 从负载均衡中获取一个服务实例
     */
    public Server chooseServer(Object key);
    
    /**
     * 标记指定服务已下线
     */
    public void markServerDown(Server server);

    /**
     * 获取可用的服务列表
     */
    public List<Server> getReachableServers();

    /**
     * 获取所有已知服务器,包括可用和不可用的
     */
    public List<Server> getAllServers();
}

IRule

具体定义负载均衡策略的接口。ILoadBalancer会根据IRule的规则去选择一个符合条件的服务实例

public interface IRule{

    public Server choose(Object key);
    
    public void setLoadBalancer(ILoadBalancer lb);
    
    public ILoadBalancer getLoadBalancer();    
}

IRule默认实现了一些策略,具体可以查看接口实现类:

如果有特殊的需要可以自定义负载策略,只需要实现IRule`接口

捋一捋

整理一下负载均衡获取实例的流程:

实战

Ribbon的引入很简单,这里我们通过RestTemplate + Ribbon来实现服务的负载均衡。一个访问第三方RESTful API接口的网络请求框架,封装了常用的请求。首先创建一个Ribbon服务

pom

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

yml文件

server:
  port: 8081
spring:
  application:
    name: ribbon
eureka:
  instance:
    # 以IP地址注册到服务中心,相互注册使用IP地址
    prefer-ip-address: true
    instance-id: ${spring.cloud.client.ipaddress}:${server.port}
  client:
    serviceUrl:
      defaultZone: http://localhost:8761/eureka/
# 配置 Ribbon 饥饿加载
ribbon:
  eager-load:
    enabled: true
    clients: api

Ribbon默认是懒加载的,只有当第一次请求时才回去创建ILoadBalancer,这就导致第一次请求时消耗更多时间,可能会出现请求超时的情况。通过ribbon.eager-load.enabled=trueribbon.eager-load.clients=server1,server2的形式配置饥饿加载模式,在项目启动时就创建对应的ILoadBalancer

config

@Configuration
public class RibbonConfig {

    /**
     * 通过RestTemplate来进行通信
     * 注解:@LoadBalanced 开启负载均衡
     */
    @Bean
    @LoadBalanced
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }
    
    @Bean
    public IRule myRule() {
        // 随机方式,默认轮询:RoundRobinRule;也可以使用自己实现的
        return new RandomRule();
    }
}

通过@LoadBalanced注解表示在RestTemplate中使用Ribbon负载均衡;

默认的负载策略为轮询,如果需要实现其他的负载策略,只需要引入对应的IRule实例;

访问接口

@RestController
@RequestMapping("/ribbon")
public class RibbonController {

    private final RestTemplate restTemplate;

    private final LoadBalancerClient loadBalancerClient;

    public RibbonController(RestTemplate restTemplate, LoadBalancerClient loadBalancerClient) {
        this.restTemplate = restTemplate;
        this.loadBalancerClient = loadBalancerClient;
    }

    /**
     * LoadBalancerClient 从 Eureka Client 获取服务注册列表信息的,并进行缓存
     * 调用choose()方法时,会根据负载策略选择一个实例
     */
    @GetMapping("/loadBalancer")
    public String loadBalancerTest() {
        ServiceInstance instance = loadBalancerClient.choose("api");
        return instance.getHost() + ":" + instance.getPort();
    }

    /**
     * 整合Eureka后可以直接通过微服务名称进行访问
     */
    @GetMapping("/restTemplate/{msg}")
    public String restTemplateTest(@PathVariable String msg) {
        return restTemplate.getForObject("http://api/api/restTemplate/" + msg, String.class);
    }
}

API服务

新建一个API服务,注册到Eureka服务,创建对外接口类:

@RestController
@RequestMapping("/api")
public class AipController {

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

    @GetMapping("/restTemplate/{msg}")
    public String forRestTemplate(@PathVariable String msg) {
        System.out.printf("get message %s with restTemplate\n", msg);
        return "[restTemplate] from port: " + port;
    }
}

测试

启动Eureka服务,启动Ribbon服务,通过修改API服务端口号,多次启动服务。调用Ribbon服务的接口查看返回数据

访问源码

所有代码均上传至Github

>>>>>> Ribbon 负载均衡 <<<<<<

日常求赞

创作不易,如果各位觉得有帮助,求点赞 支持


求关注

微信公众号: 俞大仙

俞大仙
上一篇下一篇

猜你喜欢

热点阅读