Ribbon
已经进入维护阶段,不再使用
Spring Cloud Ribbon是基于Netflix Ribbon实现的一套客户端负载均衡的工具。
简单的说,Ribbon是Netflix发布的开源项目,主要功能是提供客户端的软件负载均衡算法和服务调用。Ribbon客户端组件提供一系列完善的配置项如连接超时,重试等。简单的说,就是在配置文件中列出Load Balancer(简称LB)后面所有的机器,Ribbon会自动的帮助你基于某种规则(如简单轮询,随机连接等)去连接这些机器。我们很容易使用Ribbon实现自定义的负载均衡算法。
SpringCloud提供了springcloud loadbalancer来替代Ribbon
SpringCloud loadbalancer概述
是什么
Load Balancer负载均衡简单来说,即将请求平均的分配到多个服务上,从而实现系统的高可用,常见的负载均衡软件有nginx,lvs硬件F5等。
Spring Cloud LoadBalancer是由SpringCloud官方提供的一个开源的、简单易用的客户端负载均衡器,它包含在SpringCloud-commons中用它来替换了以前的Ribbon组件。相比较于Ribbon,SpringCloud LoadBalancer不仅能够支持RestTemplate,还支持WebClient(WeClient是Spring Web Flux中提供的功能,可以实现响应式异步请求)
客户端负载均衡VS服务端负载均衡
- 服务端负载均衡:所有的请求发送到服务端后,由服务端实现转发请求,如nginx。
- 客户端负载均衡: 调用微服务接口时,先在注册中心获取注册信息服务列表并缓存到JVM本地,在本地实现RPC远程服务调用,如loadbalancer
负载均衡的使用
SpringCloud LoadBalancer是一个抽象的接口,提供了许多实现。
在消费服务中使用的是RestTemplate作为LoadBalancer的客户端,使用了@LoadBalanced开启了负载均衡
启动提供服务
先将8001提供者服务复制一份并改为8002端口,那么此时的提供者服务就有两个实例,8001服务和8002服务。
分别启动两个服务后,在consul的控制台界面,可以看到,提供者服务有两个实例。
修改消费服务
Load Balancer是在客户端提供负载均衡,因此需要在消费服务引入Load Balancer
修改80服务
引入依赖
<!--loadbalancer-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>
修改Controller
添加以下内容
@GetMapping(value = "/consumer/pay/get/info")
private String getInfoByConsul() {
return restTemplate.getForObject(PaymentSrv_URL + "/pay/get/info", String.class);
}
通过post访问,发现调用的提供服务的实例是轮询的,实现了负载均衡
负载均衡原理
由官网代码可以看出,可以使用发现客户端获取指定服务的实例列表。
修改80消费服务Controller
加入下面内容
@Resource
private DiscoveryClient discoveryClient;
@GetMapping("/consumer/discover")
public String discovery(){
List<String> services = discoveryClient.getServices(); //获取服务列表
for (String service : services){
System.out.println(service);
}
System.out.println("======================");
List<ServiceInstance> instances=discoveryClient.getInstances("cloud-payment-service"); //获取服务的实例列表
for (ServiceInstance instance : instances){
System.out.println(instance.getInstanceId()+"\t"+instance.getHost()+"\t"+instance.getPort()+"\t"+instance.getUri());
}
return instances.get(0).getServiceId()+":"+instances.get(0).getPort();
}
使用postman发送请求后,结果如下
首先是consul的三个服务
之后是消费服务的两个实例
原理
负载均衡算法(默认是轮询算法):rest接口第几次请求数 % 服务器集群总数量 = 实际调用服务器位置下标 ,每次服务重启动后rest接口计数从1开始。
如: List [0] instances = 127.0.0.1:8002
List [1] instances = 127.0.0.1:8001
当总请求数为1时: 1 % 2 =1 对应下标位置为1 ,则获得服务地址为127.0.0.1:8001
当总请求数位2时: 2 % 2 =0 对应下标位置为0 ,则获得服务地址为127.0.0.1:8002
当总请求数位3时: 3 % 2 =1 对应下标位置为1 ,则获得服务地址为127.0.0.1:8001
当总请求数位4时: 4 % 2 =0 对应下标位置为0 ,则获得服务地址为127.0.0.1:8002
8001和8002组成集群,那么根据负载均衡算法,每次请求都是不一样的机器
负载均衡算法
SpringCloud LoadBalancer默认使用轮询算法RoundRobinLoadBalancer,还提供了一种随机算法RandomLoadBalancer
默认的负载均衡算法
- 轮询:public class RoundRobinLoadBalancer implements ReactorServiceInstanceLoadBalancer
- 随机:public class RandomLoadBalancer implements ReactorServiceInstanceLoadBalancer
两种负载均衡算法均是实现了ReactorServiceInstanceLoadBalancer接口,而 ReactorServiceInstanceLoadBalancer则是继承了ReactorLoadBalancer接口
/*
* Copyright 2012-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.loadbalancer.core;
import reactor.core.publisher.Mono;
import org.springframework.cloud.client.loadbalancer.Request;
import org.springframework.cloud.client.loadbalancer.Response;
import org.springframework.cloud.client.loadbalancer.reactive.ReactiveLoadBalancer;
/**
* A Reactor based implementation of {@link ReactiveLoadBalancer}.
*
* @param <T> - type of the response
* @author Spencer Gibb
*/
public interface ReactorLoadBalancer<T> extends ReactiveLoadBalancer<T> {
/**
* Choose the next server based on the load balancing algorithm.
* @param request - an input request
* @return - mono of response
*/
@SuppressWarnings("rawtypes")
Mono<Response<T>> choose(Request request);
default Mono<Response<T>> choose() {
return choose(REQUEST);
}
}
如果要实现自定义的负载均衡算法,实现ReactorServiceInstanceLoadBalancer接口即可
一般情况,轮询算法足够使用。
算法切换
将轮询算法切换为随机算法
修改RestTemplateConfig即可
@Configuration
@LoadBalancerClient(
//下面的value值大小写一定要和consul里面的名字一样,必须一样,指定的微服务使用随机算法
value = "cloud-payment-service",configuration = RestTemplateConfig.class)
public class RestTemplateConfig
{
@Bean
@LoadBalanced //使用@LoadBalanced注解赋予RestTemplate负载均衡的能力
public RestTemplate restTemplate(){
return new RestTemplate();
}
@Bean
ReactorLoadBalancer<ServiceInstance> randomLoadBalancer(Environment environment,
LoadBalancerClientFactory loadBalancerClientFactory) {
String name = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME);
return new RandomLoadBalancer(loadBalancerClientFactory.getLazyProvider(name, ServiceInstanceListSupplier.class), name);
}
}