欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

SpringCloud(五):客户端负载均衡器—Ribbon

程序员文章站 2022-04-12 09:40:21
...


 (编写不易,转载请注明:http://shihlei.iteye.com/blog/2404997)

一 概述

负载均衡,简单说即将 “负载” 按照一定策略分摊到不同的执行单元中执行。

 

 服务的负载均衡目前主要实现策略:

1)服务器端反向代理:硬负载 F5, 软负载 LVS,Nginx;可以透明接入,但要独立搭建高可用架构。

2)客户单端负载均衡器:lib形式提供,可以根据需要灵活控制策略,但需要客户端集成。

 

Ribbon 属于客户端负载均衡器,主要应用于服务消费者,支持RestTemplate,Feign等请求技术集成,支持从配置文件或从Eureka重拉取Server实例列表,其他需要原生开发。

 

负载均衡算法:

随机,轮询,响应时间加权等等

 

git:https://github.com/Netflix/ribbon

 

本文规划:

1)Ribbon 原生使用及自定义rule。

2)SpringCloud RestClient集成Ribbon及简要说明实现机制。

 

二 Ribbon demo

1) 原生demo

(1)添加依赖

<dependency>
            <groupId>com.netflix.ribbon</groupId>
            <artifactId>ribbon-loadbalancer</artifactId>
            <version>2.2.5</version>
</dependency>

 

(2)demo

package x.demo.netflix.ribbon;

import java.util.Arrays;
import java.util.List;

import com.netflix.loadbalancer.ILoadBalancer;
import com.netflix.loadbalancer.IPing;
import com.netflix.loadbalancer.IRule;
import com.netflix.loadbalancer.LoadBalancerBuilder;
import com.netflix.loadbalancer.NoOpPing;
import com.netflix.loadbalancer.RoundRobinRule;
import com.netflix.loadbalancer.Server;

/**
 * LoadBalancerDemo
 */
public class LoadBalancerDemo {

    public static void main(String[] args) {
        // 负载均衡规则
        IRule rule = new RoundRobinRule();
        // ping 规则
        IPing ping = new NoOpPing();

        // 添加Server 列表
        List<Server> serverList = Arrays.asList(
                new Server("server1"),
                new Server("server2"),
                new Server("server3"),
                new Server("server4"),
                new Server("server5")
        );

        //初始化LoadBalancer
        ILoadBalancer loadBalancer =
                LoadBalancerBuilder
                        .newBuilder()
                        .withRule(rule)
                        .withPing(ping)
                        .buildFixedServerListLoadBalancer(serverList);

        // 简单查看效果
        for (int i = 0; i < 10; i++) {
            System.out.println(loadBalancer.chooseServer(null));
        }
    }
}

 

2)Ribbon 结果简单分析

Ribbon 源码比较简单,结构也比较清晰,很方便看,主要体系:LoadBalancer,Rule,Ping 可以简单看看。


SpringCloud(五):客户端负载均衡器—Ribbon
            
    
    博客分类: SpringCloud 负载均衡SpringCloud客户端负载均衡RibbonNetflix Ribbon 

 

(1)LoadBalancer 体系:

【1】 接口级别:

a)ILoadBalancer:定义LoadBalancer的基本行为,主要是从ServerList中根据策略选择一个Server实例进行请求。

核心方法:

  • addServers:向LoadBalancer增加Server实例。
  • chooseServer:通过某种策略,从ServerList选择一个Server实例。
  • markServerDown:通知LoadBalancer某个Server实例已Down,下次请求可以忽略。
  • getReachableServers:获取可用的Server实例列表。
  • getAllServers:获取所有Server实例列表。

 

b)AbstractLoadBalancer:抽象LoadBalancer的一般操作。

 

【2】实现级别:

c)BaseLoadBalancer:基础实现,把实现依赖抽象成如下几大块,做用户定制和扩展,

主要依赖:

 

  • IClientConfig:提供配置信息,如ping 超时时间等。
  • IRule:提供Server实例选择策略。
  • IPing:提供验证实例是否可用策略。

d)DynamicServerListLoadBalancer:可动态添加ServerList(yml文件等),并提供Filter过滤能力

e) ZoneAwareLoadBalancer:提供Zone 负载均衡

 

 

(2)Rule体系

 
SpringCloud(五):客户端负载均衡器—Ribbon
            
    
    博客分类: SpringCloud 负载均衡SpringCloud客户端负载均衡RibbonNetflix Ribbon 
 

【1】接口级别:

a)IRule:提供Server选择策略

核心方法:Server choose(in Object key)

b)AbstractLoadBalancerRule:抽象Rule的一般操作,主要维护一个ILoadBalancer实例,提供get,set方法,完成绑定,以便获得SeverList供选择使用

 

【2】实现级别:

d)RandomRule

e)RoundRobinRule(及ClientConfigEnabledRoundRobinRule),

f)WeightedResponseTimeRule(返回时间权重规则)

g)BestAvailableRule(最低并发规则)

 

【3】其他包装类:

h)RetryRule:支持重试的规则

 

(3)Ping体系

SpringCloud(五):客户端负载均衡器—Ribbon
            
    
    博客分类: SpringCloud 负载均衡SpringCloud客户端负载均衡RibbonNetflix Ribbon 
 

【1】接口级别:

a)IPing:提供判读Sever实例是否可用的策略

核心方法:boolean isAlive(Server server)

b)AbstractLoadBalancerPing:主要维护一个ILoadBalancer实例,提供get,set方法,完成绑定,以便获得SeverList以便尝试时使用

 

【2】实现级别:

c)NoOpPing:无任何操作,永远返回true,有效。

d)DummyPing:也是永远返回true。

e)NIWSDiscoveryPing:基于发现的Ping规则

 

3)自定义一个Rule实现只使用第一个服务器

 

package x.demo.springcloud.webfront.service.impl.ribbon;

import java.util.List;

import com.netflix.client.config.IClientConfig;
import com.netflix.loadbalancer.AbstractLoadBalancerRule;
import com.netflix.loadbalancer.Server;

/**
 * 只请求第一台server
 *
 */
public class FirstServerRule extends AbstractLoadBalancerRule {

    @Override
    public Server choose(Object key) {
        List<Server> servers = getLoadBalancer().getReachableServers();
        return servers.get(0);
    }

    @Override
    public void initWithNiwsConfig(IClientConfig clientConfig) {
    
    }
}
 

三 SpringCloud Ribben + RestClient

1)概述

SpringCloud 对 LoadBalancer 进行了高层抽象,用于多种Http客户端获得负载均衡的能力。Ribbon作为一种基础实现,通过SpringCloud AutoConfiguration机制集成。

 

注:demo 默认使用《SpringCloud文章系列》的 “microservice-time” 时间微服务,作为服务提供者,为简化项目复杂度,禁用Eureka 服务发现能力。

 

具体项目环境可参见《SpringCloud(一): SpringBoot 创建简单的微服务》

 

本文主要改造微服务客户端项目:

spring-cloud-webfront:服务调用者,调用“时间”微服务,返回当前时间。

 

 

2)集成使用Demo

(1)添加依赖

 

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

 

(2)RestClient  集成 Ribbon

只需要通过在@Configuration中添加创建RestTemplate的Bean,并使用@LoadBalanced进行注解即可

 

package x.demo.springcloud.webfront.service.impl.ribbon;

import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;

/**
 * RestTemplateRibbon
 *
 */
@Configuration
public class RestTemplateRibbonConfiguration {

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

 

(3)配置服务器信息

a)yml 文件配置方式 

 

spring:
  profiles: Standalone
  application:
    name: webfront

server:
  port: 20001

# 自定义配置
timeMisroService:
  v1:
    uri: http://microservice-time/time/v1

# Ribbon 负载均衡配置
# 取消eureka 依赖
ribbon:
  eureka:
    enabled: false
# microservice-time 相对于虚拟主机,RestTemplate 必须使用microservice-time 访问 才能走复杂均衡策略
microservice-time:
  ribbon:
    # LoadBalancer
    NFLoadBalancerClassName:  com.netflix.loadbalancer.DynamicServerListLoadBalancer
    # 由于禁用了Ribbon Eureka获取服务ip端口,需要手动提供
    listOfServers: 127.0.0.1:10001,127.0.0.1:10002
    # 负载均衡策略
    NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RoundRobinRule
    # ping策略
    NFLoadBalancerPingClassName: com.netflix.loadbalancer.NoOpPing

注:

      microservice-time.ribbon 是针对 microservice-time 的 ribbon配置,microservice-time 相当于虚拟主机,使用时host需要使用 microservice-time 作为虚拟主机,如地址http://microservice-time/time/v1,Ribbon根据策略获取实际的IP和端口,这里是通过microservice-time.ribbon.listOfServers获取的

 

其他定制项:

 

  • NFLoadBalancerClassName: 默认 DefaultClientConfigImpl
  • NFLoadBalancerPingClassName: 默认 DummyPing
  • NFLoadBalancerRuleClassName: 默认 ZoneAvoidanceRule
  • NIWSServerListClassName: 默认 ConfigurationBasedServerList
  • NIWSServerListFilterClassName: 默认 ZonePreferenceServerListFilter 

 

b)程序配置方式:
使用 @RibbonClient 配置RibbonClinet,通过 configuration 属性执行配置Bean;注:这个Bean不能添加@Configuration 否则配置全局生效
package x.demo.springcloud.webfront.service.impl.ribbon;

import java.util.Arrays;
import java.util.List;

import com.netflix.loadbalancer.IPing;
import com.netflix.loadbalancer.IRule;
import com.netflix.loadbalancer.NoOpPing;
import com.netflix.loadbalancer.RandomRule;
import com.netflix.loadbalancer.Server;
import com.netflix.loadbalancer.ServerList;
import org.springframework.cloud.netflix.ribbon.RibbonClient;
import org.springframework.context.annotation.Bean;

/**
 * Ribbon 负载均衡配置
 * <p>
 * 特别注意:
 * 这里是针对 name = "microservice-time" 的服务进行的配置,所以不能 添加 @Configuration ,否则接入容器,会全局生效
 *
 */
@RibbonClient(name = "microservice-time", configuration = TimeMicroServiceConfiguration.class)
public class TimeMicroServiceConfiguration {

    @Bean
    public IRule rule() {
        return new RandomRule();
    }

    @Bean
    public IPing ping() {
        return new NoOpPing();
    }

    @Bean
    public ServerList<Server> serverList() {
        List<Server> servers = Arrays.asList(new Server("127.0.0.1", 10001), new Server("127.0.0.1", 10002));

        ServerList<Server> serverList = new ServerList<Server>() {
            @Override
            public List<Server> getInitialListOfServers() {
                return servers;
            }

            @Override
            public List<Server> getUpdatedListOfServers() {
                return servers;
            }
        };

        return serverList;
    }
}

(4)service 层

 

package x.demo.springcloud.webfront.service;

public interface TimeService {
    /**
     * 获取当前时间
     * @return 当前时间,格式:yyyy-MM-dd HH:mm:ss
     */
    String now();
}


package x.demo.springcloud.webfront.service.impl;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import x.demo.springcloud.webfront.service.TimeService;

@Service
public class TimeServiceRestClientImpl implements TimeService {

    @Value("${timeMisroService.v1.uri}")
    private String timeMicroServiceV1Uri;

    @Autowired
    private RestTemplate restTemplate;

    /**
     * 获取当前时间
     *
     * @return 当前时间,格式:yyyy-MM-dd HH:mm:ss
     */
    @Override
    public String now() {
        String url = timeMicroServiceV1Uri + "/now";
        ProtocolResult<String> result = restTemplate.getForObject(url, ProtocolResult.class);
        return result.getBody();
    }
}

(5)启动类

 

package x.demo.springcloud.webfront;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class SpringCloudWebfrontApplication {

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

(6)查看结果(略)

 

(7)SpringCloud Ribbon + FeignClien:

FeignClent 默认集成了Ribbon支持,只要依赖添加完成就可完成集成工作。
如果需要定制某个FeignClient的RibbonClient ,FeignClinet 的name使用服务的虚拟主机名,采用 上面
二 SpringCloud Ribben + RestClient 》 2)集成demo 》(3)配置服务器信息 即可完Ribbon的配置

 

四 SpringCloud LoadBalancer 源码分析

1)概述

SpringCloud LoadBalancer 进行了高层抽象,针对RestClent的扩展主要使用了RestTemplate的 ClientHttpRequestInterceptor 机制在请求时拦截走LoadBalancer的实现。
其实现结构如下:
SpringCloud(五):客户端负载均衡器—Ribbon
            
    
    博客分类: SpringCloud 负载均衡SpringCloud客户端负载均衡RibbonNetflix Ribbon 
 

(1)LoadBalancerClient体系

a)ServiceInstanceChooser:Service实例选择器接口

核心方法:ServiceInstance choose(String serviceId) :根据serviceId 选择一个 Service 实例。

 

b)LoadBalancerClient:负载均衡客户端接口,具有选择服务实例,执行请求的能力,继承自 ServiceInstanceChooser;

核心方法:

执行请求方法:

T execute(String serviceId, LoadBalancerRequest<T> request) 

T execute(String serviceId, ServiceInstance serviceInstance, LoadBalancerRequest<T> request)

重建请求链接方法:

URI reconstructURI(ServiceInstance instance, URI original);

 

c)RibbonLoadBalancerClient:基于Ribbon实现的LoadBalancerClient,内部依赖 Ribbon的ILoadBalancer体系

 

(2) ClientHttpRequestInterceptor体系:

a)RestTemplate:Rest请求客户端,提供Rest服务访问能力

 

b)ClientHttpRequestInterceptor:RestTemplate提供的拦截器,可以在请求之根据需要进行拦截,一般用实现Header头改写,添加Token等通用的面向切面编程。

 

c)RetryLoadBalancerInterceptor:实现了ClientHttpRequestInterceptor,拦截Http请求,依赖 LoadBalancerClient 选择Service实例,重新发起请求。实现负载均衡。

 

 

(3)AutoConfiguration体系:

a)LoadBalancerAutoConfiguration:配置负载均衡客户端(di主要通过这个进行配置)如果是 RestTemplate 的形式,则在restTemplate 中通过添加RetryLoadBalancerInterceptor; 用于拦截请求。

 

b)RibbonAutoConfiguration:查看spring.factories文件,拉起Ribbon的默认配置及组装点

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\org.springframework.cloud.netflix.ribbon.RibbonAutoConfiguration

 
  • SpringCloud(五):客户端负载均衡器—Ribbon
            
    
    博客分类: SpringCloud 负载均衡SpringCloud客户端负载均衡RibbonNetflix Ribbon 
  • 大小: 549.7 KB
  • SpringCloud(五):客户端负载均衡器—Ribbon
            
    
    博客分类: SpringCloud 负载均衡SpringCloud客户端负载均衡RibbonNetflix Ribbon 
  • 大小: 284.4 KB
  • SpringCloud(五):客户端负载均衡器—Ribbon
            
    
    博客分类: SpringCloud 负载均衡SpringCloud客户端负载均衡RibbonNetflix Ribbon 
  • 大小: 170.8 KB
  • SpringCloud(五):客户端负载均衡器—Ribbon
            
    
    博客分类: SpringCloud 负载均衡SpringCloud客户端负载均衡RibbonNetflix Ribbon 
  • 大小: 425.9 KB