Ribbon
Spring Cloud Ribbon
1、基于 Netflix Ribbon 实现的一套客户端负载均衡的工具
2、Ribbon 是 Netflix 发布的开源项目,提供客户端的软件负载均衡算法和服务调用
3、Ribbon 客户端组件提供一系列完善的配置项,如:连接超时,重试等
对比
1、Ribbon
(1)本地客户端负载均衡
(2)在调用微服务接口时,会在注册中心上获取注册信息服务列表,再缓存到 JVM 本地,从而在本地实现 RPC 远程服务调用技术
2、Nginx
(1)服务器负载均衡
(2)客户端所有请求都会交给 Nginx,然后由 Nginx 实现转发请求
负载均衡
1、将用户的请求平摊的分配到多个服务上,从而达到系统的高可用
2、集中式负载均衡
(1)在服务的消费方和提供方之间,使用独立的负载均衡设施,硬件,如:F5,软件,如:Nginx
(2)由该设施负责把访问请求,通过某种策略转发至服务的提供方
3、进程内负载均衡
(1)将负载均衡逻辑集成到消费方,消费方从服务注册中心获取可用地址,根据算法从中选择一个合适服务器
(2)Ribbon 属于进程内负载均衡,它只是一个类库,集成于消费方进程,消费方通过它来获取到服务提供方的地址
Ribbon 工作流程
1、选择注册中心,优先选择在同一个区域内负载较少的 Server
2、根据用户指定策略,从 Server 获取的服务注册列表中,选择一个地址
依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
</dependency>
RestTemplate
1、执行 HTTP 请求的同步客户端,在底层 HTTP 客户端库(如:JDK HttpURLConnection、Apache HttpComponents 等)上暴露了一个简单的、模板方法 API
2、RestTemplate 通过 HTTP 方法为常见的场景提供模板,此外还有支持不太常见的情况的通用交换和执行方法
3、RestTemplate 通常作为一个共享组件使用,然而,它的配置不支持并发修改,因此它的配置通常在启动时准备,如果需要,可以在启动时创建多个不同配置的 RestTemplate 实例,如果这些实例需要共享 HTTP 客户端资源,它们可以使用相同的底层 ClientHttpRequestFactory
4、注意:从 5.0 开始,这个类处于维护模式,今后只接受小的修改和错误的请求。请考虑使用 org.springframework.web.reactive.client.WebClient,它有更现代的 API,并支持同步、异步和流式场景
getForObject
1、返回对象为响应体中数据转化成的对象,可以理解为 JSON
2、通过对指定的 URL 进行 GET 操作,获取响应体,响应(如果有的话)被转换并返回
(1)url:URL
(2)responseType:返回值的类型
@Nullable
public <T> T getForObject(URI url,
Class<T> responseType)
throws RestClientException
(3)URI 模板变量使用给定的 URI 变量进行扩展,如果有的话
@Nullable
public <T> T getForObject(String url,
Class<T> responseType,
Object... uriVariables)
throws RestClientException
(4)URI 模板的变量会使用给定的 Map 进行扩展
@Nullable
public <T> T getForObject(String url,
Class<T> responseType,
Map<String,?> uriVariables)
throws RestClientException
getForEntity
1、返回对象为 ResponseEntity 对象,包含响应中的一些重要信息,比如响应头、响应状态码、响应体等
2、通过对 URI 模板进行 GET 操作,响应被转换并存储在一个 ResponseEntity 中
(1)url:URL
(2)responseType:返回值的类型
public <T> ResponseEntity<T> getForEntity(URI url,
Class<T> responseType)
throws RestClientException
(3)URI 模板变量会使用给定的 URI 变量进行扩展,如果有的话
public <T> ResponseEntity<T> getForEntity(String url,
Class<T> responseType,
Object... uriVariables)
throws RestClientException
(4)URI 模板的变量会使用给定的 Map 进行扩展
public <T> ResponseEntity<T> getForEntity(String url,
Class<T> responseType,
Map<String,?> uriVariables)
throws RestClientException
ResponseEntity
1、HttpEntity 的扩展,添加一个 HttpStatus 状态代码,在 RestTemplate 以及 @Controller 方法中使用
2、返回响应的 HTTP 状态代码,HTTP状态作为一个 HttpStatus 枚举类
public HttpStatus getStatusCode()
3、返回响应的 HTTP 状态代码,将 HTTP 状态作为一个 int 值
public int getStatusCodeValue()
4、返回响应体
@Nullable
public T getBody()
5、返回头信息
public HttpHeaders getHeaders()
postForObject
1、通过将给定的对象 POST 到 URL 来创建一个新的资源,并返回在响应中发现的响应体
2、请求参数可以是一个 HttpEntity,以便在请求中添加额外的 HTTP 头信息
3、请求体,或请求本身,可以是一个 MultiValueMap,以创建一个多部分请求,MultiValueMap 中的值可以是任何代表该部分主体的对象,或者是一个代表具有主体和头信息的部分的 HttpEntity
4、不扩展 URI
@Nullable
public <T> T postForObject(URI url,
@Nullable
Object request,
Class<T> responseType)
throws RestClientException
(1)url:URL
(2)request:将被 POST 的对象(可能为 null)
(3)responseType:返回值的类型
5、URI 模板的变量使用给定的 Map 进行扩展
@Nullable
public <T> T postForObject(String url,
@Nullable
Object request,
Class<T> responseType,
Map<String,?> uriVariables)
throws RestClientException
6、如果有的话,URI 模板变量会使用给定的 URI 变量进行扩展
@Nullable
public <T> T postForObject(String url,
@Nullable
Object request,
Class<T> responseType,
Object... uriVariables)
throws RestClientException
postForEntity
1、通过将给定的对象 POST 到 URL 来创建一个新资源,并将响应作为 ResponseEntity 返回
2、请求参数可以是一个 HttpEntity,以便在请求中添加额外的 HTTP 头信息
3、请求体,或请求本身,可以是一个 MultiValueMap,以创建一个多部分请求,MultiValueMap 中的值可以是任何代表该部分主体的对象,或者是一个代表具有主体和头信息的部分的 HttpEntity
4、不扩展 URI
public <T> ResponseEntity<T> postForEntity(URI url,
@Nullable
Object request,
Class<T> responseType)
throws RestClientException
(1)url:URL
(2)request:将被 POST 的对象(可能为 null)
(3)responseType:返回值的类型
5、URI 模板变量会使用给定的 URI 变量进行扩展,如果有的话
public <T> ResponseEntity<T> postForEntity(String url,
@Nullable
Object request,
Class<T> responseType,
Object... uriVariables)
throws RestClientException
6、URI 模板变量使用给定的 Map 进行扩展
public <T> ResponseEntity<T> postForEntity(String url,
@Nullable
Object request,
Class<T> responseType,
Map<String,?> uriVariables)
throws RestClientException
核心组件:IRule
1、根据特定算法,从服务列表中选择要访问的服务
2、实现类
(1)RoundRobinRule:轮询
(2)RandomRule:随机
(3)RetryRule:先按照 RoundRobinRule 策略获取服务,若获取失败,则在指定时间内进行重试,再获取可用服务
(4)WeightedResponseTimeRule:响应速度越快,实例选择权重越大,越容易被选择
(5)BestAvailableRule:先过滤由于多次访问故障,而处于断路器跳闸状态的服务,然后选择一个并发量最小的服务
(6)AvailabilityFilteringRule:先过滤故障实例,再选择并发量较小的实例
(7)ZoneAvoidanceRule:默认规则,根据注册中心所在区域的性能、可用性,选择服务器
定制 Ribbon 客户端
1、Spring Cloud 允许通过使用 @RibbonClientConfiguration 声明额外的配置(在 RibbonClientConfiguration 之上)来完全控制客户端
2、例
@Configuration
@RibbonClient(name = "custom", configuration = CustomConfiguration.class)
public class TestConfiguration {
}
(1)在这种情况下,客户端由 RibbonClientConfiguration 中的组件,以及 CustomConfiguration 中的任何组件组成(后者通常覆盖前者)
(2)CustomConfiguration class 必须是一个 @Configuration 类,但要注意它不在主应用程序上下文的 @ComponentScan 中。否则,它将被所有的 @RibbonClient 共享,达不到特殊化定制的目的
(3)如果使用 @ComponentScan / @SpringBootApplication,需要采取措施避免它被包含在内,例如:可以把它放在一个单独的、不重叠的包中,或者在 @ComponentScan 中明确指定要扫描的包
轮询算法
1、原理
(1)Rest 接口第 n 次请求 % 服务器集群总数量 = 实际调用服务器位置下标
(2)每次服务重启动后,Rest 接口计数从 1 开始
2、源码
public class RoundRobinRule extends AbstractLoadBalancerRule {
//上一次所调用服务器的下标
private AtomicInteger nextServerCyclicCounter;
//nextServerCyclicCounter默认值为0
public RoundRobinRule() {
nextServerCyclicCounter = new AtomicInteger(0);
}
//选择负载均衡实现
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();
//可达服务器数量为0,或所有服务器数量为0,则返回null
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);
//为null,继续循环
if (server == null) {
/* Transient. */
Thread.yield();
continue;
}
//检查服务器状态,存货且准备服务,则返回
if (server.isAlive() && (server.isReadyToServe())) {
return (server);
}
// Next.
server = null;
}
//尝试超过10次,仍没有可用存活服务器
if (count >= 10) {
log.warn("No available alive servers after 10 tries from load balancer: "
+ lb);
}
return server;
}
//通过递增实现轮询
private int incrementAndGetModulo(int modulo) {
for (;;) {
int current = nextServerCyclicCounter.get();
//next:实际调用服务器位置下标
int next = (current + 1) % modulo;
if (nextServerCyclicCounter.compareAndSet(current, next))
return next;
}
}
}
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战