Ribbon
概述
负载均衡分为 集中式
和 进程式
集中式:即在服务的消费方和提供方之间使用独立的LB设施以是硬件,如F5, 也可以是软件,如nginx), 由该设施负责把访问请求通过某种策略转发至服务的提供方;
进程式:将L B逻辑集成到消费方,消费方从服务注册中心获知有哪些地址可用,然后自己再从这些地址中选择出一个合适的服务器。Ribbon就属于进程内LB,它只是一个类库, 集成于消费方进程,消费方通过它来获取到服务提供方的地址。
Ribbon在I作时分成两步
- 第一步先选择EurekaServer ,它优先选择在同一个区域内负载较少的server.
- 第二步再根据用户指定的策略,在从server取到的服务注册列表中选择一个地址。其中Ribbon提供了多种策略:比如轮询、随机和根据响应时间加权。
Ribbon 负载均衡演示
Eureka 默认集成了 Ribbon
Ribbon 可以简单理解为 负载均衡
+ RestTemplate调用
RestTemplate的使用
Ribbon 核心组件IRule
IRule:根据特定算法中从服务列表中选取一个要访问的服务
官方文档明确给出了警告:这个自定义配置类不能放在@ComponentScan所扫描的当前包下以及子包下,否则我们自定,义的这个配置类就会被所有的Ribbon客户端所共享,达不到特殊化定制的目的了
MyRule 包下新建 MySelfRule 规则类
@Configuration
public class MySelfRule {
@Bean
public IRule myRule() {
// 定义为随机
return new RoundRobinRule();
}
}
主启动类上添加 @RibbonClient 注解
@SpringBootApplication
@EnableEurekaClient
@RibbonClient(name = "CLOUD-PAYMENT-SERVICE", configuration = MySelfRule.class)
public class OrderMain80 {
public static void main(String[] args) {
SpringApplication.run(OrderMain80.class, args);
}
}
@RibbonClient(name = “CLOUD-PAYMENT-SERVICE”, configuration = MySelfRule.class) :表示 Ribbon 去访问服务名为 CLOUD-PAYMENT-SERVICE 这个的,配置是用自己定义的 MySelfRule
启动并访问:http://localhost/consumer/payment/get/1
Ribbon 负载均衡算法
原理
默认规则轮询源码
public interface IRule{
/*
* choose one alive server from lb.allServers or
* lb.upServers according to key
*
* @return choosen Server object. NULL is returned if none
* server is available
*/
public Server choose(Object key);
public void setLoadBalancer(ILoadBalancer lb);
public ILoadBalancer getLoadBalancer();
}
public abstract class AbstractLoadBalancerRule implements IRule, IClientConfigAware {
private ILoadBalancer lb;
@Override
public void setLoadBalancer(ILoadBalancer lb){
this.lb = lb;
}
@Override
public ILoadBalancer getLoadBalancer(){
return lb;
}
}
public class RoundRobinRule extends AbstractLoadBalancerRule {
private AtomicInteger nextServerCyclicCounter;
private static final boolean AVAILABLE_ONLY_SERVERS = true;
private static final boolean ALL_SERVERS = false;
private static Logger log = LoggerFactory.getLogger(RoundRobinRule.class);
public RoundRobinRule() {
nextServerCyclicCounter = new AtomicInteger(0);
}
public RoundRobinRule(ILoadBalancer lb) {
this();
setLoadBalancer(lb);
}
public Server choose(ILoadBalancer lb, Object key) {
if (lb == null) {
log.warn("no load balancer");
return null;
}
Server server = null;
int count = 0;
while (server == null && count++ < 10) {
List<Server> reachableServers = lb.getReachableServers();
List<Server> allServers = lb.getAllServers();
int upCount = reachableServers.size();
int serverCount = allServers.size();
if ((upCount == 0) || (serverCount == 0)) {
log.warn("No up servers available from load balancer: " + lb);
return null;
}
int nextServerIndex = incrementAndGetModulo(serverCount);
server = allServers.get(nextServerIndex);
if (server == null) {
/* Transient. */
Thread.yield();
continue;
}
if (server.isAlive() && (server.isReadyToServe())) {
return (server);
}
// Next.
server = null;
}
if (count >= 10) {
log.warn("No available alive servers after 10 tries from load balancer: "
+ lb);
}
return server;
}
/**
* Inspired by the implementation of {@link AtomicInteger#incrementAndGet()}.
*
* @param modulo The modulo to bound the value of the counter.
* @return The next value.
*/
private int incrementAndGetModulo(int modulo) {
for (;;) {
int current = nextServerCyclicCounter.get();
int next = (current + 1) % modulo;
if (nextServerCyclicCounter.compareAndSet(current, next))
return next;
}
}
@Override
public Server choose(Object key) {
return choose(getLoadBalancer(), key);
}
@Override
public void initWithNiwsConfig(IClientConfig clientConfig) {
}
}
手写
修改 8001和8002 controller 添加
@GetMapping(value = "/payment/lb")
public String getPaymentLB() {
return serverPort;
}
注释掉80消费者配置类里面的注解,我们要用我们自己写的
@Configuration
public class ApplicationContextConfig {
/**
* applicationContext.xml <bean id="" class=""> </bean>
* LoadBalanced 负载均衡
* @return
*/
@Bean
// @LoadBalanced
public RestTemplate getRestTemplate() {
return new RestTemplate();
}
}
新建 LoadBalancer 接口
public interface LoadBalancer {
/**
* 获取存活的服务实例列表
*
* @param serviceInstances
* @return
*/
ServiceInstance instances(List<ServiceInstance> serviceInstances);
}
新建 LoadBalancer 接口实现类 MyLB
@Component
public class MyLB implements LoadBalancer {
private AtomicInteger atomicInteger = new AtomicInteger(0);
public final int getAndIncrement() {
int current;
int next;
do {
current = this.atomicInteger.get();
// 超过最大值,为0,重新计数 2147483647 Integer.MAX_VALUE
next = current >= 2147483647 ? 0 : current + 1;
// 自旋锁
} while (!atomicInteger.compareAndSet(current, next));
System.out.println("****第几次访问,次数next:" + next);
return next;
}
/**
* 负载均衡算法:rest接口第几次请求数%服务器集群总数量=实际调用服务器位置下标,每次服务重启动后rest接口计数从1开始.
*
* @param serviceInstances
* @return
*/
@Override
public ServiceInstance instances(List<ServiceInstance> serviceInstances) {
int index = getAndIncrement() % serviceInstances.size();
return serviceInstances.get(index);
}
}
controller 添加
/**
* 自定义负载均衡规则
*/
@Resource
private LoadBalancer loadBalancer;
@GetMapping(value = "/consumer/payment/lb")
public String getPaymentLB() {
List<ServiceInstance> instances = discoveryClient.getInstances("CLOUD-PAYMENT-SERVICE");
if (instances == null || instances.size() <= 0) {
return null;
}
ServiceInstance serviceInstance = loadBalancer.instances(instances);
URI uri = serviceInstance.getUri();
return restTemplate.getForObject(uri + "/payment/lb", String.class);
}