文章目录
ps:这是我的个人笔记地址:TinkerBell学习笔记
1.0 Ribbon 概述
Spring Cloud Ribbon: 是一个基于 HTTP 和 TCP 的客户端负载均衡工具
公司:Netfix
作用:可以让我们轻松地将面向服务的 REST 模版请求 自动转换成客户端负载均衡的服务调用。Ribbon 客户端组件提供了一套完善的配置项,比如连接超时,重试等。
负载均衡策略:轮询(Ribbon默认) hash 权重 ….
使用方法:在 Spring Cloud 构建的微服务系统中, Ribbon 作为服务消费者的负载均衡器,有两种使用方式:
1.Ribbon+RestTemplate
2.Ribbon+OpenFeign(OpenFeign默认集成了 Ribbon)
2.0 Ribbon 快速入门
2.1 使用自定义负载均衡
思路:用Random随机数实现负载均衡 图中一个消费者 两个提供者
消费者调用提供者 实现负载均衡 , 所以三个依赖都是eureka-client
2.2 创建Eureka-server注册中心
(这里不会的可以翻看第一章节)
2.3 创建 provider-1 和 provider-2
新建SpringBoot项目 勾选Spring Web+ eureka client
将provider-1和-2 拟用相同的应用名称provider(Id),分别修改端口8081和 8082
分别在启动类上开启@EnableEurekaClient
server:
port: 8081 #provider-1 用8081 provider-2用8082
spring:
application:
name: provider #相同不用改
eureka:
client:
service-url: #eureka 服务端和客户端的交互地址,集群用,隔开
defaltZone: http://localhost:8761/eureka
fetch-registry: true #是否拉取服务列表
register-with-eureka: true #是否注册自己(单机 eureka 一般关闭注册自己,集群注意打开)
registry-fetch-interval-seconds: 5 # 表示 eureka-client 间隔多久去拉取服务注册信息
instance:
hostname: localhost
instance-id: ${eureka.instance.hostname}:${spring.application.name}:${server.port}
prefer-ip-address: true # 服务列表以 ip 的形式展示
lease-renewal-interval-in-seconds: 10 # #表示 eureka server 发送心跳给 server 端的频率
lease-expiration-duration-in-seconds: 20 #至上一次收到 client 的心跳之后,等待
#下一次心跳的超时时间,在这个时间内若没收到下一次心跳,则将移除该实例
2.4 provider提供接口"/info"
2.5 创建 consumer
新建SpringBoot项目 勾选Spring Web -eureka client 确认
在consumer项目中添加 Ribbon依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
<version>2.2.9.RELEASE</version>
</dependency>
2.6 编写 consumer 的启动类
@SpringBootApplication
@EnableEurekaClient
public class ConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(ConsumerApplication.class, args);
}
//RestTemplate:在java中发起远程调用的一个工具;将它标记为Bean 注入IOC容器,就不用每次都new一个
@Bean
public RestTemplate restTemplate(){
return new RestTemplate();
}
}
2.7 编写 consumer 的 TestController
package com.tinkerbell.Controller;
import com.netflix.loadbalancer.ILoadBalancer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.util.ObjectUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import java.util.List;
import java.util.Random;
/**
* @Author: 小叮当
* @Description: 消费者访问提供者 使用自定义Random做负载均衡
* @Date Created in 2022/9/3 下午 9:05
* @Web www.tinkerbell9433.top/ ----TinkerBell学习笔记
*/
@RestController
public class TestController {
//通过服务发现客户端去做服务发现(通过应用名称访问另一个服务)
@Autowired
private DiscoveryClient discoveryClient;
//java的一个远程调用工具(即一个服务到另一个服务,暂时这么理解吧)
@Autowired
RestTemplate restTemplate;
//采用随机策略做负载均衡
static Random random = new Random();
@GetMapping("testBalance")
public String testBalance(String serverId){
//通过传入的应用名称做服务发现 得到该服务的实例信息(ip 端口 ...)
List<ServiceInstance> instances = discoveryClient.getInstances(serverId);
//情况1 传参错误 或者服务列表为空
if (ObjectUtils.isEmpty(instances)){
return "服务列表为空";
}
//情况2 服务列表不为空 先自己做一个负载均衡
ServiceInstance serviceInstance = loadBalance(instances);
String host = serviceInstance.getHost();
int port = serviceInstance.getPort();
String url ="http://"+host+":"+port+"/info";
System.out.println("本次我调用的是:"+url);
String forObject = restTemplate.getForObject(url, String.class);
System.out.println(forObject);
return forObject;
}
private ServiceInstance loadBalance(List<ServiceInstance> instances){
//拼接 url 去调用 ip:port 先自己实现不用 ribbon
ServiceInstance serviceInstance = instances.get(random.nextInt(instances.size()));
return serviceInstance;
}
}
2.8 搭建完的项目结构
简单介绍:消费者通过discoveryClient做服务发现,参数为serverId(也就是应用名称provider),此时两个“提供者”的形式是:一个名称,两台实例,就相当于传入provider做服务发现可以找到8081,8082两台,并通过自定义负载均衡做来回调用,从而达到效果。
2.9 测试访问
先启动注册中心 eureka-server 再陆续启动provider-1和2,consumer(有条件的等一会儿在进行测试,Eureka拉去服务列表需要时间)
consumer:localhost:8080 ,Provider-1和2分别是8081和8082
访问:http://localhost:8080/testBalance?serverId=provider
3.1 使用Ribbon做负载均衡
只需要对 consumer 改造即可,改造启动类 改造 controller,
谁想实现负载均衡效果,就谁来
3.2 修改RestTemplate
@SpringBootApplication
@EnableEurekaClient
public class ConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(ConsumerApplication.class, args);
}
//RestTemplate:在java中发起远程调用的一个工具;将它标记为Bean 注入IOC容器,就不用每次都new一个
@Bean
@LoadBalanced //ribbon 的负载均衡注解
public RestTemplate restTemplate(){
return new RestTemplate();
}
}
3.2修改RibbonController
package com.tinkerbell.Controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
/**
* @Author: 小叮当
* @Description: Ribbon负载均衡
* @Date Created in 2022/9/3 下午 9:05
* @Web www.tinkerbell9433.top/ ----TinkerBell学习笔记
*/
@RestController
public class RibbonController {
//此时的RestTemplate已经集成了Ribbon的负载均衡策略 直接调用就可以出现效果
@Autowired
RestTemplate restTemplate;
@GetMapping("testBalance")
public String testBalance(String serverId){
String url ="http://"+serverId+"/info";
System.out.println("本次我调用的是:"+url);
String forObject = restTemplate.getForObject(url, String.class);
System.out.println(forObject);
return forObject;
}
}
3.3 测试成功-Ribbon负载均衡
访问http://localhost:8080/testBalance?serverId=provider
4.1 Ribbon 原理分析
4.2 Ribbon 要做什么事情?
**先通过 “http://” + serviceId + “/info” 我们思考 ribbon 在真正调用之前需要做什么? **
restTemplate.getForObject(“http://provider/info”, String.class);
想要把上面这个请求执行成功,我们需要以下几步
- 拦截该请求;
- 获取该请求的 URL 地址:http://provider/info
- 截取 URL 地址中的 provider
- 从服务列表中找到 key 为 provider 的服务实例的集合(服务发现)
- 根据负载均衡算法选出一个符合的实例
- 拿到该实例的 host 和 port,重构原来 URL 中的 provider
- 真正的发送 restTemplate.getForObject(“http://ip:port/info”,String.class)
简而言之:Ribbon拦截到provider后做服务发现拿到该应用名称的集合,并拿到这些实例的ip和端口,重构url。
4.3 负载均衡之前的服务列表是从何而来呢?
Ribbon 里面有没有服务列表?
Ribbon 只做负载均衡和远程调用 服务列表从哪来?
从 eureka 来 ,Ribbon 没有服务发现的功能,但是 eureka 有,所以 ribbon 和 eureka 完美结合
Ribbon 有一个核心接口 ILoadBalance(承上(eureka)启下(Rule))
我们发现在负载均衡之前,服务列表已经有数据了
4.4 Ribbon 把 服务列表缓存起来,脏读怎么处理?
Ribbon 中使用了一个 PING 机制 (心跳监测)
从 eureka 中拿到服务列表,缓存到本地,ribbon 搞了个定时任务,隔一段时间就去循环 ping 一下每个服务节点是否存活
4.5 Ribbon 负载均衡的实现和几种算法
1.RoundRobinRule–轮询 请求次数 % 机器数量
2.RandomRule–随机
3.权重
- iphash 3.AvailabilityFilteringRule --会先过滤掉由于多次访问故障处于断路器跳闸状态的服 务,还有并发的连接数量超过阈值的服务,然后对于剩余的服务列表按照轮询的策略进行访问
5.WeightedResponseTimeRule–根据平均响应时间计算所有服务的权重,响应时间越快服 务权重越大被选中的概率越大。刚启动时如果同统计信息不足,则使用轮询的策略,等统计信 息足够会切换到自身规则
6.RetryRule-- 先按照轮询的策略获取服务,如果获取服务失败则在指定的时间内会进行重 试,获取可用的服务
7.BestAvailableRule --会先过滤掉由于多次访问故障而处于断路器跳闸状态的服务,然后 选择一个并发量小的服务
8.ZoneAvoidanceRule – 默认规则,复合判断 Server 所在区域的性能和 Server 的可用 行选择服务器。 Ribbon 默认使用哪一个负载均衡算法: ZoneAvoidanceRule :区间内亲和轮询的算法!通过一个 key 来区分
5.1 如何修改默认的负载均衡算法
5.2 通过配置文件修改(指定某一个服务使用什么算法)
provider: #提供者的服务名称,那么访问该服务的时候就会按照自定义的负载均衡算法
ribbon:
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule #几种算法的全限定类名
5.3 配置消费者调用任何服务都用某种算法(全局)
@Bean
public IRule myRule() { //指定调用所有的服务都用此算法
return new RandomRule();
}
6.1 Ribbon的常用配置
ribbon: #全局的设置
eager-load:
enabled: false # ribbon 一启动不会主动去拉取服务列表,当实际使用时才 去拉取 是否立即加载
http:
client:
enabled: false # 在 ribbon 最后要发起 Http 的调用调用,我们认为是 RestTemplate 完成的,其实最后是 HttpURLConnection 来完成的,这里面设置为 true , 可以把 HttpUrlConnection->HttpClient
okhttp:
enabled: false #HttpUrlConnection 来完成的,这里面设置为 true ,可以 把 HttpUrlConnection->OkHttpClient(也是发 http 请求的,它在移动端的开发用的多)
provider: #提供者的服务名称,那么访问该服务的时候就会按照自定义的负载均衡算法
ribbon:
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule #修改默认负载均衡算法,几种算法的全限定类名
#修改默认负载均衡算法,几种算法的全限定类名
# NFLoadBalancerClassName: #loadBalance 策略
# NFLoadBalancerPingClassName: #ping 机制策略
# NIWSServerListClassName: #服务列表策略
# NIWSServerListFilterClassName: #服务列表过滤策略
#ZonePreferenceServerListFilter 默认是优先过滤非一个区的服务列表