Spring Cloud Ribbon是基于Netflix Ribbon实现的一套客户端的负载均衡工具,Ribbon客户端组件提供一系列的完善的配置,如超时,重试等。
通过Load Balancer(LB)获取到服务提供的所有机器实例,Ribbon会自动基于某种规则(轮询,随机)去调用这些服务。Ribbon也可以实现我们自己的负载均衡算法。
1.1 什么是客户端的负载均衡进程内的LB,他是一个类库集成到消费端,通过消费端进行获取提供者的地址。
生活中:类似于你去火车站排队进站(有三条通道),只要是正常人,都会排队到人少的队伍中去。
程序中:我们消费端能获取到服务提供者地址列表,然后根据某种策略去获取一个地址进行调用。
生活中:类似于你去火车站排队进站的时候,有一个火车站的引导员告诉你说三号通道人少,你去三号通道排队。
程序中:就是你消费者调用的是nginx的地址,由nginx来选择请求的转发(轮询,权重,ip_hash等)
本文采用nacos进行整合测试,具体源码见gitee链接:Ribbon源码Debug环境
2.1 搭建生产者-订单服务 2.1.1 pom
org.springframework.cloud
spring-cloud-starter-openfeign
com.alibaba.cloud
spring-cloud-starter-alibaba-nacos-discovery
org.springframework.boot
spring-boot-starter-web
2.1.2 yaml
server:
port: 3333 #启动端口
spring:
application:
name: provider
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
2.1.3 启动类
@SpringBootApplication
@EnableDiscoveryClient
public class NacosProviderApplication {
public static void main(String[] args)
{
SpringApplication.run(NacosProviderApplication.class, args);
}
}
2.1.4 业务类
@RestController
@RequestMapping("/order")
public class OrderController {
@Value("${server.port}")
String port;
@GetMapping("/find/instance")
public String queryOrderInstanceId() {
String applicationName = "provider";
String result = applicationName + "-" + port;
System.out.println("**********" + result);
return result;
}
}
2.2 搭建消费者-用户服务
2.2.1 pom
org.springframework.cloud
spring-cloud-starter-openfeign
com.alibaba.cloud
spring-cloud-starter-alibaba-nacos-discovery
org.springframework.boot
spring-boot-starter-web
2.2.2 yaml
server:
port: 5555 #启动端口
spring:
application:
name: consumer
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
consumer:
ribbon:
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule
NFLoadBalancerPingClassName: com.netflix.loadbalancer.PingUrl
2.2.3 配置类
@Configuration
public class AppConfig {
@Bean
@LoadBalanced
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
2.2.4 启动类
@SpringBootApplication
@EnableDiscoveryClient
public class NacosConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(NacosConsumerApplication.class, args);
}
}
2.2.5 业务类
@RestController
@RequestMapping("/user")
public class UserController {
@Autowired
private RestTemplate restTemplate;
@GetMapping("/find/instance")
public String queryOrderInstanceId() {
// provider: 生产者服务名->替换-> 127.0.0.1:3333
String url = "http://provider/order/find/instance";
return restTemplate.getForObject(url, String.class);
}
}
2.3 启动测试
2.3.1 启动Nacos
startup.cmd -m standalone
1.RandomRule:随机选择一个Server。
2.RetryRule:对选定的负载均衡策略机上重试机制,在一个配置时间段内当选择Server不成功,则一直尝试使用subRule的方式选择一个可用的server。
3.RoundRobinRule:轮询选择,轮询index,选择index对应位置的Server。
4.AvailabilityFilteringRule:过滤掉一直连接失败的被标记为circuit tripped的后端Server,
并过滤掉那些高并发的后端Server或者使用一个AvailabilityPredicate来包含过滤server的逻辑,其实就是检查status里记录的各个Server的运行状态。
5.BestAvailableRule:选择一个最小的并发请求的Server,逐个考察Server,如果Server被tripped了,则跳过。
6. WeightedResponseTimeRule:根据响应时间加权,响应时间越长,权重越小,被选中的可能性越低。
7.ZoneAvoidanceRule:复合判断Server所在区域的性能和Server的可用性选择Server
public interface IRule{
// 选择一个server
public Server choose(Object key);
// 设置负载均衡器
public void setLoadBalancer(ILoadBalancer lb);
// 获取负载均衡器
public ILoadBalancer getLoadBalancer();
}
参考自定义负载均衡策略
// 自定义的负载均衡算法
public class MyRandomRule extends AbstractLoadBalancerRule {
// total = 0 // 当total==5以后,我们指针才能往下走,
// index = 0 // 当前对外提供服务的服务器地址,
// total需要重新置为零,但是已经达到过一个5次,我们的index = 1
// 分析:我们5次,但是微服务只有8001 8002 8003 三台,OK?
private int total = 0; // 总共被调用的次数,目前要求每台被调用5次
private int currentIndex = 0; // 当前提供服务的机器号
public Server choose(ILoadBalancer lb, Object key) {
if (lb == null) {
return null;
}
Server server = null;
while (server == null) {
if (Thread.interrupted()) {
return null;
}
List upList = lb.getReachableServers();
List allList = lb.getAllServers();
int serverCount = allList.size();
if (serverCount == 0) {
return null;
}
//private int total = 0; // 总共被调用的次数,目前要求每台被调用5次
//private int currentIndex = 0; // 当前提供服务的机器号
if (total < 5) {
server = upList.get(currentIndex);
total++;
} else {
total = 0;
currentIndex++;
if (currentIndex >= upList.size()) {
currentIndex = 0;
}
}
if (server == null) {
Thread.yield();
continue;
}
if (server.isAlive()) {
return (server);
}
server = null;
Thread.yield();
}
return server;
}
@Override
public Server choose(Object key) {
return choose(getLoadBalancer(), key);
}
@Override
public void initWithNiwsConfig(IClientConfig clientConfig) {
}
}
3.5.2 在SpringBoot主程序扫描的包外定义配置类
@Configuration
public class MySelfRule {
@Bean
public IRule myRule() {
return new MyRandomRule();
}
}
注意:自定义的负载均衡策略不能写在@SpringbootApplication注解的@CompentScan扫描得到的地方,否则自定义的配置类就会被所有的RibbonClients共享。
使用@RibbonClient指定负载均衡策略,在SpringBoot主程序添加@RibbonClient引入配置类
参考: org.springframework.cloud.netflix.ribbon.RibbonClientConfiguration
lClientConfig: Ribbon的客户端配置,默认采用DefaultClientConfiglmpl实现。
lRule: Ribbon的负载均衡策略,默认采用ZoneAvoidanceRule实现,该策略能够在多区域环境下选出最佳区域的实例进行访问。
IPing: Ribbon的实例检查策略,默认采用DummyPing实现,该检查策略是一个特殊的实现,实际上它并不会检查实例是否可用,而是始终返回true,
默认认为所有服务实例都是可用的。
ServerList:服务实例清单的维护机制,默认采用ConfigurationBasedServerList实现
ServerListFilter:服务实例清单过滤机制,默认采ZonePreferenceServerListFilter,该策略能够优先过滤出与请求方处于同区域的服务实例。
lLoadBalancer:负载均衡器,默认采用ZoneAwareLoadBalancer实现,它具备了区域感知的能力。
3.6.1 修改配置
processon、processon流程图
4.1 @RibbonAutoConfiguration配置类
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(RestTemplate.class)
@ConditionalOnBean(LoadBalancerClient.class)
@EnableConfigurationProperties(LoadBalancerRetryProperties.class)
public class LoadBalancerAutoConfiguration {
// 将标记有@LoadBalanced注解的restTemplate都封装到这个集合中
@LoadBalanced
@Autowired(required = false)
private List restTemplates = Collections.emptyList();
@Autowired(required = false)
private List transformers = Collections.emptyList();
@Bean
public SmartInitializingSingleton loadBalancedRestTemplateInitializerDeprecated(
final ObjectProvider restTemplateCustomizers) {
return () -> restTemplateCustomizers.ifAvailable(customizers -> {
for (RestTemplate restTemplate : LoadBalancerAutoConfiguration.this.restTemplates) {
for (RestTemplateCustomizer customizer : customizers) {
// 用RestTemplateCustomizer定制所有的restTemplate
customizer.customize(restTemplate);
}
}
});
}
@Bean
@ConditionalOnMissingBean
public LoadBalancerRequestFactory loadBalancerRequestFactory(
LoadBalancerClient loadBalancerClient) {
return new LoadBalancerRequestFactory(loadBalancerClient, this.transformers);
}
@Configuration(proxyBeanMethods = false)
@ConditionalOnMissingClass("org.springframework.retry.support.RetryTemplate")
static class LoadBalancerInterceptorConfig {
@Bean
public LoadBalancerInterceptor ribbonInterceptor(
LoadBalancerClient loadBalancerClient,
LoadBalancerRequestFactory requestFactory) {
return new LoadBalancerInterceptor(loadBalancerClient, requestFactory);
}
@Bean
@ConditionalOnMissingBean
public RestTemplateCustomizer restTemplateCustomizer(
final LoadBalancerInterceptor loadBalancerInterceptor) {
return restTemplate -> {
List list = new ArrayList(
restTemplate.getInterceptors());
list.add(loadBalancerInterceptor);
// 定制RestTemplate,将LoadBalancerIntercepotr封装到RestTemplate的拦截器集合中
// 被@LoadBalanced注解的restTemplates(装了所有被@LoadBalanced注解的restTemplate)
restTemplate.setInterceptors(list);
};
}
}
}
4.2.1 LoadBalanced注解测试demo
1.定义User类
public class User {
}
2.定义Jak类
public class Jak {
// 模拟LoadBalanced注解,此位置必须要加@LoadBalance注解,否则看不到效果
@LoadBalance
@Autowired(required = false)
private List userList = new ArrayList();
public List getUserList() {
return userList;
}
}
3.自定义LoadBalance注解
@Target({ ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Qualifier
public @interface LoadBalance {
}
4.定义配置类AppConfig
@Configuration
public class AppConfig {
@Bean
@LoadBalance
public User user() {
return new User();
}
@Bean
public User user2() {
return new User();
}
@Bean
public Jak jak() {
return new Jak();
}
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
Jak jak = (Jak) applicationContext.getBean("jak");
System.out.println(jak.getClass());
System.out.println(jak.getUserList().size());
System.out.println(jak.getUserList());
}
}
5.测试不加@LoadBalance
6.测试加@LoadBalance
7. 原理
定时任务每隔10s钟判断是否存活
从nacos获取实例列表

视频教程