客户端负载均衡Ribbon - Spring Cloud系列(二)

本文章基于spring-boot-starter-parent 2.0.6RELEASE,spring-cloud-dependencies Finchley.SR2。

Ribbon是什么

Ribbon是Netflix发布的开源项目,主要功能是提供客户端的软件负载均衡,支持多种可插拔LB策略。Spring Cloud Ribbon基于Netflix Ribbon实现,是一个基于HTTP和TCP的客户端负载均衡工具。通过Spring Cloud的封装,可以让我们轻松地将面向服务的REST模板请求自动转换成客户端负载均衡的服务调用。

客户端负载均衡

我们通常所说的负载均衡指的是服务端负载均衡,其中分为硬件负载均衡,比如F5;软件负载均衡比如Nginx。不管是硬件负载均衡的设备还是软件负载均衡的软件模块都会维护一个下挂可用的服务端清单,通过心跳检测来剔除故障的服务端节点以保证清单中都是可以正常访问的服务端节点。

而客户端负载均衡和服务端负载均衡最大的不同点在于上面所提到的服务清单所存储的位置。在客户端负载均衡中,所有客户端节点都维护着自己要访问的服务端清单,而这些服务端清单来自服务注册中心。

下面通过对比架构方式来看下两者的区别:

  • 服务端负载均衡:
    服务端负载均衡
  • 客户端负载均衡:
    客户端负载均衡

Ribbon使用

在Spring Cloud微服务架构中Ribbon只需两步:

  • 服务提供者启动多个服务实例并注册到一个注册中心或者多个相关联的服务注册中心;
  • 服务消费者通过调用被@LoadBalanced注解修饰过的RestTemplate来实现面向服务的接口调用。

这里使用一个服务注册中心(Eureka)+2个服务提供者(Order集群)+ 一个服务调用者(User)来演示。如果不了解Eureka,请先去看Spring Cloud组件之Eureka 这篇文章。

因为Eureka中已经集成了Ribbon,所以只需要引入Eureka依赖包,不再需要引用Ribbon的依赖。

服务调用者:User

AppUserApplication.class

@SpringBootApplication
@EnableEurekaClient
public class AppUserApplication {
    public static void main(String[] args) {
        SpringApplication.run(AppUserApplication.class);
    }
}

UserController.class

@RestController
public class UserController {
    @Autowired
    RestTemplate restTemplate;
	
	// 负载均衡需要使用服务名去调用微服务
    private static final String ORDER_URL = "http://ORDER-MICRO";
    
    @RequestMapping("/getOrder.do")
    public R getOrder() {
        return R.success("操作成功", restTemplate.getForObject(ORDER_URL + "/getOrder.do", Object.class));
    }
}   

AppConfig.class

@Configuration
public class AppConfig {
	// @LoadBanlanced 开启负载均衡
    @Bean
    @LoadBalanced
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }

application.yml

server:
  port: 5000

eureka:
  client:
    serviceUrl:
      defaultZone: http://localhost:8081/eureka
    register-with-eureka: false
  instance:
    instance-id: user #此实例注册到eureka服务端的唯一的实例ID
    prefer-ip-address: true #是否显示IP地址

spring:
  application:
    name: user-micro #此实例注册到eureka服务端的name
服务消费者:Order

AppOrderApplication.class

@SpringBootApplication
@EnableEurekaClient
public class AppOrderApplication {
    public static void main(String[] args) {
        SpringApplication.run(AppOrderApplication.class);
    }
}

OrderController:

@RestController
public class OrderController {
    @RequestMapping("/getOrder.do")
    public Map<String, Object> getOrder() {
        Map<String,Object> map = new HashMap();
        map.put("key","order");
        return map;
    }
}

application.yml

server:
  port: 7000

eureka:
  client:
    serviceUrl:
      defaultZone: http://localhost:8081/eureka
  instance:
    instance-id: order #此实例注册到eureka服务端的唯一的实例ID
    prefer-ip-address: true #是否显示IP地址
    
spring:
  application:
    name: order-micro #此实例注册到eureka服务端的name

另外一个服务提供者与Order一样,端口号改为7001,返回值改为map.put("key","order1");这里就不在粘贴代码了。

Eureka服务注册中心

AppEureka.class

@SpringBootApplication
@EnableEurekaServer
public class AppEureka {
    public static void main(String[] args) {
        SpringApplication.run(AppEureka.class);
    }
}

application.yml

server:
  port: 8081

eureka:
  server:
    enable-self-preservation: false #关闭自我保护机制
    eviction-interval-timer-in-ms: 4000 #设置清理间隔(单位:毫秒 默认是60*1000)
  instance:
    hostname: localhost

  client:
    register-with-eureka: false 
    fetch-registry: false 
    serviceUrl:
      defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka

分别启动后,访问Eureka注册中心http://locahost:u8081/,可发现两个order正常启动成功,user因为没有将自己注册到eureka,所以eureka中看不到。
服务启动
从user中访问order服务,http://5000/getOrder.do,连续敲几次可发现每次返回的值不一样,说明客户端负载均衡起作用了。
order1
order2

核心组件IRule

IRule是Ribbon对于负载均衡策略实现的接口,通过实现这个接口,我们可以定义负载均衡策略,下表是Ribbon包含的默认实现。

类名作用
RoundRobinRule按照线性轮询的方式依次选择每个服务实例。
RandomRule从服务实例清单中随机选择一个服务实例。
PredicateBasedRule抽象策略,先通过子类中实现的逻辑来过滤清单,再轮询选择。
AvailabilityFilteringRule继承了PredicateBasedRule,通过线性抽样的方式直接尝试寻找可用且较空闲的实例来使用,优化了父类每次都要遍历所有实例的开销。
ZoneAvoidanceRule继承了PredicateBasedRule,完全遵循父类的过滤主逻辑,“先过滤清单,再轮询选择”。其中过滤条件为组合过滤,以ZoneAvoidandPredicate为主过滤条件,AvailabilityPredicate为次过滤条件。
WeightResponseTimeRule根绝平均响应时间计算所有服务的权重,响应时间越快服务权重越大,共启动时如果统计信息不足,则使用RoundRobinRule策略,等统计信息最够后再切换过来。
RetryRule先按照RoundRobinRule的策略获取服务,如果获取服务失败则在指定时间内反复重试,在获取不到则返回null。
BestAvaliableRule先过滤掉由于多次访问故障而处于断路器跳闸状态的服务,然后选择一个并发量最小的服务。

如何去切换负载均衡的默认实现呢?
在AppConfig中配置即可:

@Configuration
public class AppConfig {

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

    @Bean
    public IRule iRule() {
   		// 返回要实现的负载均衡器
        return new RandomRule();
    }
}

除了使用Ribbon本身提供的,我们还可以自己实现负载均衡算法,只要实现了IRule或者继承自AbstractLoadBalancerRule就可以完成自定义负载均衡。
这里把Ribbon中的RandomRule的具体实现拿出来供读者参考:

public class RandomRule extends AbstractLoadBalancerRule {
    Random rand = new Random();
    public RandomRule() {
    }
    @SuppressWarnings({"RCN_REDUNDANT_NULLCHECK_OF_NULL_VALUE"})
    public Server choose(ILoadBalancer lb, Object key) {
        if (lb == null) {
            return null;
        } else {
            Server server = null;
            while(server == null) {
                if (Thread.interrupted()) {
                    return null;
                }
				
				// 获取可用服务清单列表
                List<Server> upList = lb.getReachableServers();
                // 获取所有服务清单列表
                List<Server> allList = lb.getAllServers();
                int serverCount = allList.size();
                if (serverCount == 0) {
                    return null;
                }
				// 随机选择一个服务Server
                int index = this.rand.nextInt(serverCount);
                server = (Server)upList.get(index);
                if (server == null) {
                    Thread.yield();
                } else {
                    if (server.isAlive()) {
                        return server;
                    }
                    server = null;
                    Thread.yield();
                }
            }
            return server;
        }
    }

    public Server choose(Object key) {
        return this.choose(this.getLoadBalancer(), key);
    }

    public void initWithNiwsConfig(IClientConfig clientConfig) {
    }
}

想了解常见的负载均衡算法原理那可参考这两篇文章
负载均衡算法原理解析(一)
负载均衡算法原理解析(二)


------------本文结束感谢您的阅读------------
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值