概述
ribbon是客户端的负载均衡
客户端把所有的服务列表拉取到本地,然后使用策略算法,最终连接那个服务
eureka客户端从eureka服务器获得服务列表,ribbon使用策略获得一个服务,RestTemplate向这个服务发送http请求
ribbon默认是区域轮寻策略
策略
默认实现:
ZoneAvoidanceRule(区域权衡策略):复合判断Server所在区域的性能和Server的可用性,轮询选择服务器。
其他规则:
BestAvailableRule(最低并发策略):会先过滤掉由于多次访问故障而处于断路器跳闸状态的服务,然后选择一个并发量最小的服务。逐个找服务,如果断路器打开,则忽略。
RoundRobinRule(轮询策略):以简单轮询选择一个服务器。按顺序循环选择一个server。
RandomRule(随机策略):随机选择一个服务器。
AvailabilityFilteringRule(可用过滤策略):会先过滤掉多次访问故障而处于断路器跳闸状态的服务和过滤并发的连接数量超过阀值得服务,然后对剩余的服务列表安装轮询策略进行访问。
WeightedResponseTimeRule(响应时间加权策略):据平均响应时间计算所有的服务的权重,响应时间越快服务权重越大,容易被选中的概率就越高。刚启动时,如果统计信息不中,则使用RoundRobinRule(轮询)策略,等统计的信息足够了会自动的切换到WeightedResponseTimeRule。响应时间长,权重低,被选择的概率低。反之,同样道理。此策略综合了各种因素(网络,磁盘,IO等),这些因素直接影响响应时间。
RetryRule(重试策略):先按照RoundRobinRule(轮询)的策略获取服务,如果获取的服务失败则在指定的时间会进行重试,进行获取可用的服务。如多次获取某个服务失败,就不会再次获取该服务。主要是在一个时间段内,如果选择一个服务不成功,就继续找可用的服务,直到超时。
测试代码:
前提是,启动一个eureka服务端服务,启动两个eureka客户端服务,端口分别为8080、8081,同时,客户端中的两个服务中,都提供getPort方法,返回当前服务的端口号
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class MainController {
@Autowired
HealthStatusService healthStatusSrv;
@Value("${server.port}")
private String port;
@GetMapping("getHi")
public String getHi() {
return "Hi";
}
@GetMapping("getPort")
public String getPort() {
return "my port: " + port;
}
@GetMapping("/health")
public String health(@RequestParam("status") Boolean status) {
healthStatusSrv.setStatus(status);
return healthStatusSrv.getStatus();
}
}
ribbon测试代码:
EurekaClientApplication.java
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
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;
@SpringBootApplication
@Configuration
public class EurekaClientApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaClientApplication.class, args);
}
@Bean
// @LoadBalanced // 添加ribbon
public RestTemplate getRestTemplate() {
return new RestTemplate();
}
/**
* 代码中注入ribbon的负载均衡策略
*/
// @Bean
// public IRule myRule() {
// return new RoundRobinRule();
// return new RandomRule();
// }
}
RibbonController.java 测试代码
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
@RestController
@RequestMapping("/ribbon")
public class RibbonController {
@Autowired
private DiscoveryClient client;
@Autowired
private LoadBalancerClient lb;
@Autowired
private RestTemplate restTemplate;
/**
* ribbon负载均衡
* 默认策略、自定义配制策略测试
* @return
*/
@GetMapping("client1")
public String client1() {
// ribbon 完成客户端的负载均衡,过滤掉down了的节点
ServiceInstance instances = lb.choose("client");
String url = "http://" + instances.getHost() + ":" + instances.getPort() + "/getPort";
return restTemplate.getForObject(url, String.class);
}
private AtomicInteger atomicInteger = new AtomicInteger();
/**
* ribbon负载均衡
* 自定义策略测试
* @return
*/
@GetMapping("client2")
public String client2() {
List<ServiceInstance> instances = client.getInstances("client");
// 自定义的算法,随机
// int nextInt = new Random().nextInt(instances.size());
// 自定义的算法,轮巡
int andIncrement = atomicInteger.getAndIncrement();
int nextInt = andIncrement % instances.size();
ServiceInstance serviceInstance = instances.get(nextInt);
String url = "http://" + serviceInstance.getHost() + ":" + serviceInstance.getPort() + "/getPort";
return restTemplate.getForObject(url, String.class);
}
}
配制文件
application.properties
ribbon配制策略
Ribbon脱离Eureka
#续约发送间隔默认30秒,心跳间隔
#eureka.instance.lease-renewal-interval-in-seconds=5
#表示eureka client间隔多久去拉取服务注册信息,默认为30秒,对于api-gateway,如果要迅速获取服务注册状态,可以缩小该值,比如5秒
#eureka.client.registry-fetch-interval-seconds=5
# 续约到期时间(默认90秒)
#eureka.instance.lease-expiration-duration-in-seconds=60
#设置服务注册中心的URL,用于client和server端交流
eureka.client.service-url.defaultZone=http://admin:123@euk1.com:7001/eureka/
server.port=8090
spring.application.name=client2
#配制某个服务的ribbon策略,名称为{服务名称}.ribbon.NFLoadBalancerRuleClassName
client.ribbon.NFLoadBalancerRuleClassName=com.netflix.loadbalancer.RandomRule
#配制所有服务的ribbon策略
#ribbon.NFLoadBalancerRuleClassName=com.netflix.loadbalancer.RandomRule
#Ribbon脱离Eureka
#ribbon.eureka.enabled=false
#Ribbon脱离Eureka后的服务列表集合
#ribbon.listOfServers=localhost:8080,localhost:8081
关于ribbon策略的代码跟踪
注:此代码跟踪使用Spring Cloud Hoxton版本
<spring-cloud.version>Hoxton.SR11</spring-cloud.version>
lb.choose("client")
点进choose方法,找实现类RibbonLoadBalancerClient
@Override
public ServiceInstance choose(String serviceId) {
return choose(serviceId, null);
}
点击choose方法
/**
* New: Select a server using a 'key'.
* @param serviceId of the service to choose an instance for
* @param hint to specify the service instance
* @return the selected {@link ServiceInstance}
*/
public ServiceInstance choose(String serviceId, Object hint) {
Server server = getServer(getLoadBalancer(serviceId), hint);
if (server == null) {
return null;
}
return new RibbonServer(serviceId, server, isSecure(server, serviceId),
serverIntrospector(serviceId).getMetadata(server));
}
此处查找getServer方法
protected Server getServer(ILoadBalancer loadBalancer, Object hint) {
if (loadBalancer == null) {
return null;
}
// Use 'default' on a null hint, or just pass it on?
return loadBalancer.chooseServer(hint != null ? hint : "default");
}
查找 loadBalancer.chooseServer实现方法,选择ZoneAwareLoadBalancer类
@Override
public Server chooseServer(Object key) {
if (!ENABLED.get() || getLoadBalancerStats().getAvailableZones().size() <= 1) {
logger.debug("Zone aware logic disabled or there is only one zone");
return super.chooseServer(key);
}
Server server = null;
try {
LoadBalancerStats lbStats = getLoadBalancerStats();
Map<String, ZoneSnapshot> zoneSnapshot = ZoneAvoidanceRule.createSnapshot(lbStats);
logger.debug("Zone snapshots: {}", zoneSnapshot);
if (triggeringLoad == null) {
triggeringLoad = DynamicPropertyFactory.getInstance().getDoubleProperty(
"ZoneAwareNIWSDiscoveryLoadBalancer." + this.getName() + ".triggeringLoadPerServerThreshold", 0.2d);
}
if (triggeringBlackoutPercentage == null) {
triggeringBlackoutPercentage = DynamicPropertyFactory.getInstance().getDoubleProperty(
"ZoneAwareNIWSDiscoveryLoadBalancer." + this.getName() + ".avoidZoneWithBlackoutPercetage", 0.99999d);
}
Set<String> availableZones = ZoneAvoidanceRule.getAvailableZones(zoneSnapshot, triggeringLoad.get(), triggeringBlackoutPercentage.get());
logger.debug("Available zones: {}", availableZones);
if (availableZones != null && availableZones.size() < zoneSnapshot.keySet().size()) {
String zone = ZoneAvoidanceRule.randomChooseZone(zoneSnapshot, availableZones);
logger.debug("Zone chosen: {}", zone);
if (zone != null) {
BaseLoadBalancer zoneLoadBalancer = getLoadBalancer(zone);
server = zoneLoadBalancer.chooseServer(key);
}
}
} catch (Exception e) {
logger.error("Error choosing server using zone aware logic for load balancer={}", name, e);
}
if (server != null) {
return server;
} else {
logger.debug("Zone avoidance logic is not invoked.");
return super.chooseServer(key);
}
}
查找super.chooseServer方法
最后进入 BaseLoadBalancer类中
从这三个类中,一路找来
查看rule初始化
最后
以上就是刻苦面包为你收集整理的Ribbon笔记关于ribbon策略的代码跟踪的全部内容,希望文章能够帮你解决Ribbon笔记关于ribbon策略的代码跟踪所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复