1.Ribbon 简介
Ribbon是Netflix发布的负载均衡器,它有助于控制HTTP和TCP客户端的行为。为Ribbon配置服务提供者地址列表后,Ribbon就可基于某种负载均衡算法,自动地帮助服务消费者去请求。Ribbon默认为我们提供了很多的负载均衡算法,例如轮询、随机等。当然,我们也可以为Ribbon实现自定义的负载均衡算法。
a. RoundRobinRule
默认的,轮询规则,也是很多高级规则中退避的一种策略
BaseLoadBalancer类中的setRule方法
b. AvailabilityFilteringRule
会过滤掉打开熔断的服务或者是高并发连接数量的服务
c. WeightedResponseTimeRule
通过服务的平均响应时间,给每一个服务一个权重,响应时间越长,权重越小,开始统计信息不足,应用轮询策略
// 开启权重配置
spring-cloud-order-service.ribbon.NFLoadBalancerRuleClassName=com.gupaoedu.springcloud.example.springclouduserservice.GpDefineIpHashRule
d. RetryRule
先按照轮询策略,如果请求服务失败,会在指定时间内(30s)进行重试
e. BestAvailableRule
先过滤掉断路器的服务,然后选择一个并发量最小的
f. RandomRule
随机获取一个服务
2. RoundRobinRule 轮询
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;
}
3.RandomRule 随机
/**
* Randomly choose from all living servers
*/
@edu.umd.cs.findbugs.annotations.SuppressWarnings(value = "RCN_REDUNDANT_NULLCHECK_OF_NULL_VALUE")
public Server choose(ILoadBalancer lb, Object key) {
if (lb == null) {
return null;
}
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) {
/*
* No servers. End regardless of pass, because subsequent passes
* only get more restrictive.
*/
return null;
}
int index = chooseRandomInt(serverCount);
server = upList.get(index);
if (server == null) {
/*
* The only time this should happen is if the server list were
* somehow trimmed. This is a transient condition. Retry after
* yielding.
*/
Thread.yield();
continue;
}
if (server.isAlive()) {
return (server);
}
// Shouldn't actually happen.. but must be transient or a bug.
server = null;
Thread.yield();
}
return server;
}
4.RetryRule 重试
默认先使用轮训算法(IRule subRule = new RoundRobinRule()),再使用重试算法;
默认0.5s重试一次(long maxRetryMillis = 500)
package com.netflix.loadbalancer;
import com.netflix.client.config.IClientConfig;
public class RetryRule extends AbstractLoadBalancerRule {
IRule subRule = new RoundRobinRule();
long maxRetryMillis = 500;
public RetryRule() {
}
public RetryRule(IRule subRule) {
this.subRule = (subRule != null) ? subRule : new RoundRobinRule();
}
public RetryRule(IRule subRule, long maxRetryMillis) {
this.subRule = (subRule != null) ? subRule : new RoundRobinRule();
this.maxRetryMillis = (maxRetryMillis > 0) ? maxRetryMillis : 500;
}
public void setRule(IRule subRule) {
this.subRule = (subRule != null) ? subRule : new RoundRobinRule();
}
public IRule getRule() {
return subRule;
}
public void setMaxRetryMillis(long maxRetryMillis) {
if (maxRetryMillis > 0) {
this.maxRetryMillis = maxRetryMillis;
} else {
this.maxRetryMillis = 500;
}
}
public long getMaxRetryMillis() {
return maxRetryMillis;
}
@Override
public void setLoadBalancer(ILoadBalancer lb) {
super.setLoadBalancer(lb);
subRule.setLoadBalancer(lb);
}
/*
* Loop if necessary. Note that the time CAN be exceeded depending on the
* subRule, because we're not spawning additional threads and returning
* early.
*/
//具体重试代码
public Server choose(ILoadBalancer lb, Object key) {
long requestTime = System.currentTimeMillis();
long deadline = requestTime + maxRetryMillis;
Server answer = null;
answer = subRule.choose(key);
if (((answer == null) || (!answer.isAlive()))
&& (System.currentTimeMillis() < deadline)) {
InterruptTask task = new InterruptTask(deadline
- System.currentTimeMillis());
while (!Thread.interrupted()) {
answer = subRule.choose(key);
if (((answer == null) || (!answer.isAlive()))
&& (System.currentTimeMillis() < deadline)) {
/* pause and retry hoping it's transient */
Thread.yield();
} else {
break;
}
}
task.cancel();
}
if ((answer == null) || (!answer.isAlive())) {
return null;
} else {
return answer;
}
}
@Override
public Server choose(Object key) {
return choose(getLoadBalancer(), key);
}
@Override
public void initWithNiwsConfig(IClientConfig clientConfig) {
}
}
5.自定义的负载均衡算法
根据ip的hash获取访问地址
package com.example.demo;
import com.netflix.client.config.IClientConfig;
import com.netflix.loadbalancer.AbstractLoadBalancerRule;
import com.netflix.loadbalancer.ILoadBalancer;
import com.netflix.loadbalancer.Server;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import java.util.List;
/**
* 自定义根据IP的hash值取模访问服务提供者集群
*/
public class CustomizeIpHashRule extends AbstractLoadBalancerRule {
public Server choose(ILoadBalancer lb,Object key){
if (lb==null){
return null;
}else {
Server server = null;
while (server==null){
//获取可用的服务实例列表
List<Server> upList = lb.getReachableServers();
//获取所有的服务实例列表
List<Server> allList = lb.getAllServers();
int serverCount = allList.size();
if(serverCount == 0){
return null;
}
int i=ipAddressHash(serverCount);
server = upList.get(i);
}
return server;
}
}
private int ipAddressHash(int serverCount) {
ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
String remoteAddr = requestAttributes.getRequest().getRemoteAddr();
int code = Math.abs(remoteAddr.hashCode());
return code%serverCount;
}
@Override
public void initWithNiwsConfig(IClientConfig iClientConfig) {
}
@Override
public Server choose(Object key) {
return choose(getLoadBalancer(),key);
}
}
6. 配置负载均衡算法引用
spring.application.name=eureka-user-service
server.port=8081
eureka.client.service-url.defaultZone=http://localhost:8761/eureka,http://localhost:8762/eureka
#命名规则: 服务提供者[applicationName].ribbon.NFLoadBalancerRuleClassName= 引用 负载均衡算法 路径
#eureka-product-service.ribbon.NFLoadBalancerRuleClassName = com.netflix.loadbalancer.RandomRule
#自定义负载均衡算法引用
eureka-product-service.ribbon.NFLoadBalancerRuleClassName=com.example.demo.CustomizeIpHashRule
#clientName.ribbon.NFLoadBalancerClass; #配置ILoadBalancer的实现类
#lientName.ribbon.NFLoadBalancerRuleClassName; #配置IRule的实现类
#clientName.ribbon.NFLoadBalancerPingClassName; #配置IPing的实现类
#clientName.ribbon.NFWSServerListClassName; 配置ServerList的实现类
#clientName.ribbon.NIWSServerListFilterClassName; 配置ServerListFilter的实现类
ping服务器(默认10s),比重试retry30秒要快
import com.netflix.loadbalancer.IPing;
import com.netflix.loadbalancer.Server;
public class MyPing implements IPing{
@Override
public boolean isAlive(Server server) {
System.out.println("isAlive"+server.getHost()+":"+server.getPort());
return true;
}
}
spring.application.name=spring-cloud-user-service
management.endpoints.jmx.exposure.include=*
management.endpoints.web.exposure.include=*
management.endpoint.health.show-details=always
# spring cloud access&secret config
# 可以访问如下地址查看: https://usercenter.console.aliyun.com/#/manage/ak
alibaba.cloud.access-key=****
alibaba.cloud.secret-key=****
# 应用服务 WEB 访问端口
server.port=8088
# 配置指定服务的提供者的地址列表
spring-cloud-order-service.ribbon.listOfServers=\
localhost:8080,localhost:8082
//ping 服务器配置
spring-cloud-order-service.ribbon.NFLoadBalancerPingClassName=com.gupaoedu.springcloud.example.springclouduserservice.MyPing
spring-cloud-order-service.ribbon.NFLoadBalancerRuleClassName=com.gupaoedu.springcloud.example.springclouduserservice.GpDefineIpHashRule