SpringCloud系列之客户端负载均衡Netflix Ribbon
1. 什么是负载均衡?
负载均衡是一种基础的网络服务,它的核心原理是按照指定的负载均衡算法,将请求分配到后端服务集群上,从而为系统提供并行处理和高可用的能力。提到负载均衡,你可能想到nginx。对于负载均衡,一般分为服务端负载均衡和客户端负载均衡
服务端负载均衡:在消费者和服务提供方中间使用独立的代理方式进行负载,有硬件的负载均衡器,比如 F5,也有软件,比如 Nginx。
客户端负载均衡:所谓客户端负载均衡,就是客户端根据自己的请求情况做负载,本文介绍的Netflix Ribbon就是客户端负载均衡的组件
2. 什么是Netflix Ribbon?
在上一章的学习中,我们知道了微服务的基本概念,知道怎么基于Ribbon+restTemplate的方式实现服务调用,接着上篇博客,我们再比较详细学习客户端负载均衡Netflix Ribbon,学习本博客之前请先学习上篇博客,然后再学习本篇博客
Ribbon 是由 Netflix 发布的负载均衡器,它有助于控制 HTTP 和 TCP 的客户端的行为。Ribbon 属于客户端负载均衡。
3. Netflix Ribbon实验环境准备
环境准备:
- JDK 1.8
- SpringBoot2.2.1
- SpringCloud(Hoxton.SR6)
- Maven 3.2+
- 开发工具
- IntelliJ IDEA
- smartGit
创建一个SpringBoot Initialize项目,详情可以参考我之前博客:SpringBoot系列之快速创建项目教程
可以引入Eureka Discovery Client,也可以单独添加Ribbon
Spring Cloud Hoxton.SR6版本不需要引入spring-cloud-starter-netflix-ribbon,已经默认集成
也可以单独添加Ribbon依赖:
本博客的是基于spring-cloud-starter-netflix-eureka-client
进行试验,试验前要运行eureka服务端,eureka服务提供者,代码请参考上一章博客
补充:IDEA中多实例运行方法
step1:如图,不要加上勾选
step2:指定不同的server端口和实例id,如图:
启动成功后,是可以看到多个实例的
4. Netflix Ribbon API使用
使用LoadBalancerClient
:
@Autowired
LoadBalancerClient loadBalancerClient;
@Test
void contextLoads() {
ServiceInstance serviceInstance = loadBalancerClient.choose("EUREKA-SERVICE-PROVIDER");
URI uri = URI.create(String.format("http://%s:%s", serviceInstance.getHost() , serviceInstance.getPort()));
System.out.println(uri.toString());
}
构建BaseLoadBalancer
实例例子:
@Test
void testLoadBalancer(){
// 服务列表
List<Server> serverList = Arrays.asList(new Server("localhost", 8083), new Server("localhost", 8084));
// 构建负载实例
BaseLoadBalancer loadBalancer = LoadBalancerBuilder.newBuilder().buildFixedServerListLoadBalancer(serverList);
loadBalancer.setRule(new RandomRule());
for (int i = 0; i < 5; i++) {
String result = LoadBalancerCommand.<String>builder().withLoadBalancer(loadBalancer).build()
.submit(new ServerOperation<String>() {
public Observable<String> call(Server server) {
try {
String address = "http://" + server.getHost() + ":" + server.getPort()+"/EUREKA-SERVICE-PROVIDER/api/users/mojombo";
System.out.println("调用地址:" + address);
return Observable.just("");
} catch (Exception e) {
return Observable.error(e);
}
}
}).toBlocking().first();
System.out.println("result:" + result);
}
}
5. 负载均衡@LoadBalanced
Ribbon负载均衡实现,RestTemplate 要加上@LoadBalanced
package com.example.springcloud.ribbon.configuration;
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;
/**
* <pre>
* RestConfiguration
* </pre>
*
* <pre>
* @author mazq
* 修改记录
* 修改后版本: 修改人: 修改日期: 2020/07/31 09:43 修改内容:
* </pre>
*/
@Configuration
public class RestConfiguration {
@Bean
@LoadBalanced
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
yaml配置:
server:
port: 8082
spring:
application:
name: eureka-service-consumer
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka/
fetch-registry: true
register-with-eureka: false
healthcheck:
enabled: false
instance:
status-page-url-path: http://localhost:8761/actuator/info
health-check-url-path: http://localhost:8761/actuator//health
prefer-ip-address: true
instance-id: eureka-service-consumer8082
关键点,使用SpringCloud的@LoadBalanced
,才能调http://EUREKA-SERVICE-PROVIDER/api/users/? 接口的数据,浏览器是不能直接调的
import com.example.springcloud.ribbon.bean.User;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.client.RestTemplate;
import java.net.URI;
@SpringBootApplication
@EnableEurekaClient
@RestController
@Slf4j
public class SpringcloudRibbonApplication {
@Autowired
RestTemplate restTemplate;
public static void main(String[] args) {
SpringApplication.run(SpringcloudRibbonApplication.class, args);
}
@GetMapping("/findUser/{username}")
public User index(@PathVariable("username")String username){
return restTemplate.getForObject("http://EUREKA-SERVICE-PROVIDER/api/users/"+username,User.class);
}
}
6. 定制Netflix Ribbon client
具体怎么定制?可以参考官网,@RibbonClient
指定定制的配置类既可
package com.example.springcloud.ribbon.configuration;
import com.example.springcloud.ribbon.component.MyRule;
import com.netflix.loadbalancer.IPing;
import com.netflix.loadbalancer.IRule;
import com.netflix.loadbalancer.PingUrl;
import org.springframework.cloud.netflix.ribbon.ZonePreferenceServerListFilter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* <pre>
* Ribbon Clients configuration
* </pre>
*
* <pre>
* @author mazq
* 修改记录
* 修改后版本: 修改人: 修改日期: 2020/07/29 14:22 修改内容:
* </pre>
*/
//@Configuration(proxyBeanMethods = false)
//@IgnoreComponentScan
public class RibbonClientConfiguration {
// @Autowired
// IClientConfig config;
@Bean
public IRule roundRobinRule() {
return new MyRule();
}
@Bean
public ZonePreferenceServerListFilter serverListFilter() {
ZonePreferenceServerListFilter filter = new ZonePreferenceServerListFilter();
filter.setZone("myTestZone");
return filter;
}
@Bean
public IPing ribbonPing() {
return new PingUrl();
}
}
在Application类加上@RibbonClient
,name是为服务名称,跟bootstrap.yml配置的一样既可
@RibbonClient(name = "eureka-service-provider",configuration = RibbonClientConfiguration.class)
特别注意:官网这里特意提醒,这里的意思是说@RibbonClient指定的配置类必须加@Configuration
(不过在Hoxton.SR6版本经过我的验证,其实是可以不加的,加了反而可能报错),@ComponentScan扫描要排除自定义的配置类,否则,它由所有@RibbonClients共享。如果你使用@ComponentScan(或@SpringBootApplication)
其实就是想让我们排除这个配置的全局扫描,所以我们可以进行编码,写个注解类@IgnoreComponentScan
,作用于类,指定@Target(ElementType.TYPE)
package com.example.springcloud.ribbon.configuration;
import java.lang.annotation.*;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface IgnoreComponentScan {
}
加上自定义的注解类
任何在Application加上代码,避免全局扫描:
@ComponentScan(excludeFilters={@ComponentScan.Filter(type= FilterType.ANNOTATION,value= IgnoreComponentScan.class)})
7. Netflix Ribbon常用组件
ps:介绍Netflix Ribbon的负载策略之前,先介绍Netflix Ribbon常用组件及其作用:
组件 | 作用 |
---|---|
ILoadBalancer | 定义一系列的操作接口,比如选择服务实例。 |
IRule | 负载算法策略,内置算法策略来为服务实例的选择提供服务。 |
ServerList | 负责服务实例信息的获取(可以获取配置文件中的,也可以从注册中心获取。) |
ServerListFilter | 过滤掉某些不想要的服务实例信息。 |
ServerListUpdater | 更新本地缓存的服务实例信息。 |
IPing | 对已有的服务实例进行可用性检查,保证选择的服务都是可用的。 |
8. 定制Netflix Ribbon策略
因为服务提供者是多实例的,所以再写个接口测试,调用了哪个实例,来看看Netflix Ribbon的负载策略
@Autowired
LoadBalancerClient loadBalancerClient;
@GetMapping(value = {"/test"})
public String test(){
ServiceInstance serviceInstance = loadBalancerClient.choose("EUREKA-SERVICE-PROVIDER");
URI uri = URI.create(String.format("http://%s:%s", serviceInstance.getHost() , serviceInstance.getPort()));
System.out.println(uri.toString());
return uri.toString();
}
部署成功,多次调用,可以看到每次调用的服务实例都不一样?其实Netflix Ribbon默认是按照轮询的方式调用的
要定制Netflix Ribbon的负载均衡策略,需要实现AbstractLoadBalancerRule抽象类,下面给出类图:
Netflix Ribbon内置了如下的负载均衡策略,引用https://juejin.im/post/6854573215587500045的归纳:
ok,接着我们可以在配置类,修改规则
@Bean
public IRule roundRobinRule() {
return new BestAvailableRule();
}
测试,基本都是调8083这个实例,因为这个实例性能比较好
显然,也可以自己写个策略类,代码参考com.netflix.loadbalancer.RandomRule
,网上也有很多例子,思路是修改RandomRule原来的策略,之前随机调服务实例一次,现在改成每调5次后,再调其它的服务实例
package com.example.springcloud.ribbon.component;
import com.netflix.client.config.IClientConfig;
import com.netflix.loadbalancer.AbstractLoadBalancerRule;
import com.netflix.loadbalancer.ILoadBalancer;
import com.netflix.loadbalancer.Server;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ThreadLocalRandom;
public class MyRule extends AbstractLoadBalancerRule
{
// 总共被调用的次数,目前要求每台被调用5次
private int total = 0;
// 当前提供服务的机器号
private int index = 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<Server> upList = lb.getReachableServers();
// 获取所有服务列表
List<Server> allList = lb.getAllServers();
int serverCount = allList.size();
if (serverCount == 0) {
// 没有获取到服务
return null;
}
//int index = chooseRandomInt(serverCount);
//server = upList.get(index);
if(total < 5)
{
server = upList.get(index);
total++;
}else {
total = 0;
index++;
if(index >= upList.size())
{
index = 0;
}
}
if (server == null) {
// 释放线程
Thread.yield();
continue;
}
if (server.isAlive()) {
return (server);
}
server = null;
Thread.yield();
}
return server;
}
protected int chooseRandomInt(int serverCount) {
return ThreadLocalRandom.current().nextInt(serverCount);
}
@Override
public Server choose(Object key) {
return choose(getLoadBalancer(), key);
}
@Override
public void initWithNiwsConfig(IClientConfig clientConfig) {
}
}
修改IRule ,返回MyRule
@Bean
public IRule roundRobinRule() {
return new MyRule();
}
附录:
ok,本博客参考官方教程进行实践,仅仅作为入门的学习参考资料,详情可以参考Spring Cloud官方文档https://docs.spring.io/spring-cloud-netflix/docs/2.2.x-SNAPSHOT/reference/html/#customizing-the-ribbon-client
代码例子下载:code download
优质学习资料参考:
Ribbon负载均衡 -> 源码剖析:Ribbon负载均衡 -> 源码剖析
方志鹏大佬系列Spring Cloud博客:https://www.fangzhipeng.com/spring-cloud.html
使用Spring Cloud与Docker实战微服务:https://eacdy.gitbooks.io/spring-cloud-book/content/
程序员DD大佬系列Spring Cloud博客:http://blog.didispace.com/spring-cloud-learning/
SpringCloud系列之客户端负载均衡Netflix Ribbon的更多相关文章
- Spring Cloud官方文档中文版-客户端负载均衡:Ribbon
官方文档地址为:http://cloud.spring.io/spring-cloud-static/Dalston.SR2/#_spring_cloud_netflix 文中例子我做了一些测试在:h ...
- 【Spring Cloud】客户端负载均衡组件——Ribbon(三)
一.负载均衡 负载均衡技术是提高系统可用性.缓解网络压力和处理能力扩容的重要手段之一. 负载均衡可以分为服务器负载均衡和客户端负载均衡,服务器负载均衡由服务器实现,客户端只需正常访问:客户端负载均衡技 ...
- 【SpringCloud微服务实战学习系列】客户端负载均衡Spring Cloud Ribbon
Spring Cloud Ribbon是一个基于HTTP和TCP的客户端负载均衡工具,它基于Netflix Ribbon实现.通过Spring Cloud的封装,可以让我们轻松地将面向服务的RES模板 ...
- SpringCloud 客户端负载均衡:Ribbon
目录 Ribbon 介绍 开启客户端负载均衡,简化 RestTemplate 调用 负载均衡策略 Ribbon 介绍 Ribbon 是 Netflix 提供的一个基于 Http 和 TCP 的客户端负 ...
- 客户端负载均衡:Ribbon
Ribbon是一个客户端的负载均衡器,可以提供很多HTTP和TCP的控制行为.Feign已经使用了Ribbon,所以如果你使用了@FeignClient,Riboon也同样被应用了. Ribbon核心 ...
- SpringCloud实战2-Ribbon客户端负载均衡
https://www.cnblogs.com/huangjuncong/p/9022055.html
- springcloud(十二):Ribbon客户端负载均衡介绍
springcloud(十二):Ribbon客户端负载均衡介绍 Ribbon简介 使用分布式微服务脚骨的应用系统,在部署的时候通常会为部分或者全部微服务搭建集群环境,通过提供多个实例来提高系统的稳定型 ...
- ③SpringCloud 实战:使用 Ribbon 客户端负载均衡
这是SpringCloud实战系列中第三篇文章,了解前面第两篇文章更有助于更好理解本文内容: ①SpringCloud 实战:引入Eureka组件,完善服务治理 ②SpringCloud 实战:引入F ...
- springcloud 之Ribbon客户端负载均衡配置使用
pom.xml添加配置说明:这里服务注册与发现用的是Eureka,所以消费者端需要引入eureka,使用EurekaClient来调用服务 <dependency> <groupId ...
随机推荐
- java 和 c++ 的三目运算符的区别
转载请注明出处:http://www.cnblogs.com/liangyongrui/p/6348001.html 以前很少用java,就知道java和c++差不多. 今天就踩了一个坑. 不吐糟,直 ...
- Linux系统中虚拟设备文件的各种实用用法
大家好,我是良许. 大家知道,在 Linux 下,一切皆文件,对于设备文件也是如此.我们在工作的过程中,经常会看到 /dev/null 这个玩意,那它到底是什么呢? 专业地讲,/dev/null 是一 ...
- 重学 Java 设计模式:实战模版模式「模拟爬虫各类电商商品,生成营销推广海报场景」
作者:小傅哥 博客:https://bugstack.cn - 原创系列专题文章 沉淀.分享.成长,让自己和他人都能有所收获! 一.前言 黎明前的坚守,的住吗? 有人举过这样一个例子,先给你张北大的录 ...
- Centos8 - 图形界面和命令行切换
查看目前默认的启动方式 systemctl get-default 命令行模式:multi-user.target 图形界面模式:graphical.target 设置为图形界面模式 systemct ...
- 重学 Java 设计模式:实战访问者模式「模拟家长与校长,对学生和老师的不同视角信息的访问场景」
作者:小傅哥 博客:https://bugstack.cn - 原创系列专题文章 沉淀.分享.成长,让自己和他人都能有所收获! 一.前言 能力,是你前行的最大保障 年龄会不断的增长,但是什么才能让你不 ...
- Tomcat双击startup.bat闪退的原因及解决方式
很久不碰Tomcat了,最近因为种种原因需要重新投入到Java Web的怀抱,所以又重新接触了Tomcat 我下载了tomcat的压缩包将其解压缩到某个位置,我这里是D盘下的tomcat文件夹中,但是 ...
- Java 字符串比较、拼接问题
@ 目录 1.字符串的比较 1. 1 字符串常量池 1.2 String类型的比较方式 1.3 String的创建方式 1.3.1 直接使用"="进行赋值 1.3.2 使用&quo ...
- Python之进程、线程、协程篇
本节内容 操作系统发展史介绍 进程.与线程区别 python GIL全局解释器锁 线程 语法 join 线程锁之Lock\Rlock\信号量 将线程变为守护进程 Event事件 queue队列 生产者 ...
- 用PyInstaller打包用PyQt5编写的python程序
0.背景 本弱初学PyQt5,写了一个GUI小程序,但在用PyInstaller打包时出现了不少问题,现将几个比较大的问题记录如下,希望以后能记住. 1. 资源打包 首先是资源打包的问题,我写的程序引 ...
- Go的100天之旅-03变量
变量 变量介绍 变量这个词来源于数学,类似方程中的x.y,代表的是存储在计算机中的值.这里主要介绍Go和其它编程语言不一样的地方,在前面我们提到过,Go是一门静态语言.静态语言区别动态语言一个重要的特 ...