【ribbon】Ribbon的使用与原理

负载均衡介绍

负载均衡(Load Balance),其含义就是指将负载(工作任务)进行平衡、分摊到多个操作单元上进行运行,例如FTP服务器、Web服务器、企业核心应用服务器和其它主要任务服务器等,从而协同完成工作任务。

思考: 如果有多个provider实例,consumer应该如何调用呢?

目前主流的负载均衡方案分为以下两种:

  • 服务端的负载均衡:集中式负载均衡,在消费者和服务提供方中间使用独立的代理方式进行负载,有基于硬件的(比如F5),也有基于软件的(比如Nginx、LVS)。
  • 客户端的负载均衡:客户端根据自己的请求情况做负载均衡,Ribbon就属于客户端自己做负载均衡。

客户端的负载均衡

例如spring cloud中的ribbon,客户端会有一个服务器地址列表,在发送请求前通过负载均衡算法选择一个服务器,然后进行访问,这是客户端负载均衡;即在客户端就进行负载均衡算法分配。

服务端的负载均衡

例如Nginx,通过Nginx进行负载均衡,先发送请求,然后通过负载均衡算法,在多个服务器之间选择一个进行访问;即在服务器端再进行负载均衡算法分配。

常见负载均衡算法

  • 随机:通过随机选择一个服务进行执行,一般这种方式使用较少;
  • 轮询:负载均衡默认实现方式,请求来之后排队处理;
  • 加权轮询:通过对服务器性能的分型,给高配置,低负载的服务器分配更高的权重,均衡各个服务器的压力;
  • 地址hash:通过客户端请求的地址的hash值取模映射进行服务器调度,可尽量保证同一个客户的请求都到同一个服务器
  • 最小连接数:即使请求均衡了,压力不一定会均衡,最小连接数法就是根据服务器的情况,比如请求积压数等参数,将请求分配到当前压力最小的服务器上。

Spring Cloud Alibaba整合Ribbon快速开始

Spring Cloud Ribbon是基于Netflix Ribbon实现的一套客户端的负载均衡工具,Ribbon客户端组件提供一系列的完善的配置,如超时,重试等。通过Load Balancer获取到服务提供的所有机器实例,Ribbon会自动基于某种规则(轮询,随机)去调用这些服务。Ribbon也可以实现我们自己的负载均衡算法。

由于spring-cloud-starter-alibaba-nacos-discovery依赖了ribbon的依赖,所以我们不再需要单独引入ribbon的依赖了,如下面:

<!--添加ribbon的依赖-->
<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
</dependency>

注入RestTemplate时需要添加@LoadBalanced注解,让RestTemplate在请求时拥有客户端负载均衡的能力

package com.morris.user.config;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;@Configuration
public class RestConfig {/*** 默认的RestTemplate,不带负载均衡* @return*/@Beanpublic RestTemplate restTemplate() {return new RestTemplate();}/*** 有负责均衡能力的RestTemplate* @return*/@Bean@LoadBalancedpublic RestTemplate restTemplate2() {return new RestTemplate();}
}

RestTemplate的使用:

package com.morris.user.controller;import com.morris.user.entity.Order;
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;import javax.annotation.Resource;
import java.util.Arrays;
import java.util.List;@RestController
@RequestMapping("user")
public class RestTemplateController {@Resourceprivate RestTemplate restTemplate;@Resourceprivate RestTemplate restTemplate2;@GetMapping("findOrderByUserId")public List<Order> findOrderByUserId(Long userId) {Order[] orders = restTemplate.getForObject("http://127.0.0.1:8020/order/findOrderByUserId?userId=", Order[].class, userId);return Arrays.asList(orders);}@GetMapping("findOrderByUserId2")public List<Order> findOrderByUserId2(Long userId) {Order[] orders = restTemplate2.getForObject("http://order-service/order/findOrderByUserId?userId=", Order[].class, userId);return Arrays.asList(orders);}}

RestTemplate使用时,如果不带负载均衡时URL中的host直接填的是IP或者域名,使用负载均衡时host填的服务提供者在注册中心注册的微服务名。

模拟Ribbon实现负载均衡

为RestTemplate添加ClientHttpRequestInterceptor,拦截器负责从注册中心注册的微服务中轮询选取一个服务,改写URL中的服务名为具体的IP和端口。

package com.morris.user.config;import lombok.SneakyThrows;
import org.checkerframework.checker.units.qual.A;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpOutputMessage;
import org.springframework.http.HttpRequest;
import org.springframework.http.client.ClientHttpRequestExecution;
import org.springframework.http.client.ClientHttpRequestInterceptor;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.http.client.support.HttpRequestWrapper;
import org.springframework.web.client.RestTemplate;import javax.annotation.RegEx;
import javax.annotation.Resource;
import java.io.IOException;
import java.net.URI;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;@Configuration
public class MyRibbonConfig {@Resourceprivate DiscoveryClient discoveryClient;private final AtomicInteger counter = new AtomicInteger();/*** 默认的RestTemplate,不带负载均衡* @return*/@Beanpublic RestTemplate restTemplate3() {RestTemplate restTemplate = new RestTemplate();restTemplate.setInterceptors(Collections.singletonList((request, body, execution) -> {String host = request.getURI().getHost();List<ServiceInstance> instances = discoveryClient.getInstances(host);int count = counter.incrementAndGet();int index = count % instances.size();ServiceInstance serviceInstance = instances.get(index);RequestWrapper newRequest = new RequestWrapper(request, serviceInstance.getHost(), serviceInstance.getPort());return execution.execute(newRequest, body);}));return restTemplate;}private static class RequestWrapper extends HttpRequestWrapper {private final String host;private final int port;public RequestWrapper(HttpRequest request, String host, int port) {super(request);this.host = host;this.port = port;}@SneakyThrows@Overridepublic URI getURI() {URI oldUri = super.getURI();return new URI(oldUri.getScheme(), oldUri.getUserInfo(), host, port, oldUri.getPath(), oldUri.getQuery(), oldUri.getFragment());}}}

Ribbon内核源码分析

@LoadBalanced注解原理

主要关注@LoadBalanced注解上有个@Qualifier注解,使用了Spring中限定符。

@Target({ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Qualifier
public @interface LoadBalanced {
}

LoadBalancerAutoConfiguration

LoadBalancerAutoConfiguration注入了2个重要的Bean:

  • SmartInitializingSingleton
  • LoadBalancerInterceptor

@LoadBalanced使用了@Qualifier,Spring中@Qualifier用于筛选限定注入Bean。@LoadBalanced利用@Qualifier作为restTemplates注入的筛选条件,筛选出具有负载均衡标识的RestTemplate。

被@LoadBalanced注解的restTemplate会被定制,添加LoadBalancerInterceptor拦截器。

SmartInitializingSingleton是在所有的bean都实例化完成之后才会调用的,所以在bean的实例化期间使用@LoadBalanced修饰的restTemplate是不具备负载均衡作用的,比如在项目的启动过程中要使用调用某个微服务,此时RestTemplate是不具备负载均衡作用的。

public class LoadBalancerAutoConfiguration {@LoadBalanced@Autowired(required = false)private List<RestTemplate> restTemplates = Collections.emptyList();@Autowired(required = false)private List<LoadBalancerRequestTransformer> transformers = Collections.emptyList();@Beanpublic SmartInitializingSingleton loadBalancedRestTemplateInitializerDeprecated(final ObjectProvider<List<RestTemplateCustomizer>> restTemplateCustomizers) {return () -> restTemplateCustomizers.ifAvailable(customizers -> {for (RestTemplate restTemplate : LoadBalancerAutoConfiguration.this.restTemplates) {for (RestTemplateCustomizer customizer : customizers) {customizer.customize(restTemplate);}}});}@Configuration(proxyBeanMethods = false)@ConditionalOnMissingClass("org.springframework.retry.support.RetryTemplate")static class LoadBalancerInterceptorConfig {@Beanpublic LoadBalancerInterceptor loadBalancerInterceptor(LoadBalancerClient loadBalancerClient,LoadBalancerRequestFactory requestFactory) {return new LoadBalancerInterceptor(loadBalancerClient, requestFactory);}@Bean@ConditionalOnMissingBeanpublic RestTemplateCustomizer restTemplateCustomizer(final LoadBalancerInterceptor loadBalancerInterceptor) {return restTemplate -> {List<ClientHttpRequestInterceptor> list = new ArrayList<>(restTemplate.getInterceptors());list.add(loadBalancerInterceptor);restTemplate.setInterceptors(list);};}}
}

SmartInitializingSingleton会获取容器中所有加了@LoadBalanced注解的RestTemplate,然后为这些RestTemplate逐个添加LoadBalancerInterceptor。

如果不使用@LoadBalanced注解,也可以通过添加LoadBalancerInterceptor拦截器让restTemplate起到负载均衡器的作用,这种方式就可以在启动的过程中使用RestTemplate的负载均衡功能。

@Bean
public RestTemplate restTemplate(LoadBalancerInterceptor loadBalancerInterceptor) {RestTemplate restTemplate = new RestTemplate();//注入loadBalancerInterceptor拦截器restTemplate.setInterceptors(Arrays.asList(loadBalancerInterceptor));return restTemplate;
}

LoadBalancerInterceptor拦截器

LoadBalancerInterceptor主要使用RibbonLoadBalancerClient来根据不同的负载均衡算法选择实例,然后使用LoadBalancerRequestFactory来对请求进行改写,与我们手写的Ribbon功能类似。

org.springframework.cloud.client.loadbalancer.LoadBalancerInterceptor#intercept

public ClientHttpResponse intercept(final HttpRequest request, final byte[] body,final ClientHttpRequestExecution execution) throws IOException {final URI originalUri = request.getURI();String serviceName = originalUri.getHost();Assert.state(serviceName != null,"Request URI does not contain a valid hostname: " + originalUri);return this.loadBalancer.execute(serviceName,this.requestFactory.createRequest(request, body, execution));
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/7704.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【《Go编程进阶实战:开发命令行应用、HTTP应用和gRPC应用》——指导你使用Go语言构建健壮的、生产级别的应用程序】

谷歌在2009年发布了Go编程语言&#xff0c;并于2012年发布了1.0版。Go语言具有强大的兼容性&#xff0c;一直用于编写可扩展的重量级程序(命令行应用程序、关键基础设施工具乃至大规模分布式系统)。凭借简单性、丰富的标准库和蓬勃发展的第三方软件包生态系统&#xff0c;Go语言…

工程安全监测无线振弦采集仪在建筑物中的应用

工程安全监测无线振弦采集仪在建筑物中的应用 工程安全监测无线振弦采集仪是一种用于建筑物结构安全监测的设备&#xff0c;它采用了无线传输技术&#xff0c;具有实时性强、数据精度高等优点&#xff0c;被广泛应用于建筑物结构的实时监测和预警。下面将从设备的特点、应用场…

FPGA中RAM的结构理解

FPGA中RAM的结构理解 看代码的过程中对RAM的结构不是很理解&#xff0c;搞脑子一片浆糊&#xff0c;反复推算&#xff0c;好不容易理清了思路&#xff0c;记录下来&#xff0c;防止忘记。开辟的RAM总容量为128bytes&#xff0c;数据的位宽为32位&#xff08;即一个单元有32bit…

WebGL系列教程:WebGL入门

一、WebGL简介 1.1 概述 WebGL(全写 Web Graphics Library)是一种 3D 绘图标准,这种绘图技术标准允许把 JavaScript 和 OpenGL ES 2.0 结合在一起,通过增加 OpenGL ES 2.0 的一个 JavaScript 绑 定,WebGL 可以为 HTML5 Canvas 提供硬件 3D 加速渲染,这样 Web 开发人员就可…

WPF 如何设置全局的订阅发布事件

文章目录 前言代码逻辑修改 总结 前言 我们需要一个全局事件订阅发布功能&#xff0c;实现页面通讯。使两个毫无关系的页面通过一个中间量进行通讯。 代码 IEventAggregator&#xff1a;消息订阅集合 这个是Prism提供的消息订阅功能。使用如下 设置订阅类型&#xff0c;即…

Linux の shell 基本语法

变量 shell中变量比较特殊&#xff0c;变量名和等号之间不能有空格。其它的跟常见的变成语言类似 命名规则&#xff1a; 命名只能使用英文字母&#xff0c;数字和下划线&#xff0c;首个字符不能以数字开头。 中间不能有空格&#xff0c;可以使用下划线 _。 不能使用标点符号。…

Flask 创建文件目录,删除文件目录

项目结构 app.py from flask import Flask, render_template, request, redirect, url_for import osapp Flask(__name__) BASE_DIR os.path.abspath(os.path.dirname(__file__)) FILE_DIR os.path.join(BASE_DIR, testfile)app.route(/, methods[GET, POST]) def index():…

HTTP和HTTPS的区别

一、两者概念 1.1 HTTP概念 HTTP 的全称是超文本传输协议&#xff08;HyperText Transfer Protocol&#xff09; 是一种用于分布式、协作式和超媒体信息系统的应用协议&#xff0c;简单来说就是一种分布和接收HTML页面的方法&#xff0c;被用于在Web浏览器和网站服务器之间传递…

认识tomcat

Tomcat作为流行的JavaWeb应用服务器,其工作原理可以简要总结如下几点: Tomcat实现了Servlet和JSP规范,可以运行相关的Web应用。 Tomcat由Catalina、Coyote和Jasper等模块组成。Catalina负责Servlet容器,Coyote实现连接器。 服务器由一个或多个服务构成,服务包含多个连接器和容…

jenkins

Gitlab添加钩子 测试钩子 添加完成后&#xff0c;下面会出现钩子选择。点击test中的&#xff0c;push event。 出现successful&#xff0c;既添加成功。 如果添加失败&#xff0c;报错&#xff0c;更改Network

JMerter安装配置以及使用(笔记记录)

JMerter安装配置以及使用&#xff08;笔记记录&#xff09; 安装JDK安装JMeterJMeter使用元件执行的顺序参数详解参数配置之CSV数据文件设置断言响应断言JSON断言 数据提取XPath提取器JSON提取器 JMeter属性JMeter录制脚本JMeter直连数据库逻辑控制器如果&#xff08;IF&#x…

数据库概述和DDL语句(学会并使用数据库day1)

数据库概述和DDL语句&#xff08;day1&#xff09; 一、数据库概述概念数据库的集中式控制有什么优点数据库分类mysql数据库mysql简介基本术语数据表的组成 数据库管理系统数据库管理系统、数据库和表的关系 二、SQL的概念三、SQL语句分类1、SQL语句被分为四大类2、MySQL的语法…

Databend 开源周报第 103 期

Databend 是一款现代云数仓。专为弹性和高效设计&#xff0c;为您的大规模分析需求保驾护航。自由且开源。即刻体验云服务&#xff1a;https://app.databend.cn 。 Whats On In Databend 探索 Databend 本周新进展&#xff0c;遇到更贴近你心意的 Databend 。 创建网络策略 …

Blender自动化脚本,无人值守批量渲图/渲视频

渲染视频是个非常耗时的大工程&#xff0c;如果要渲染多个视频或者每个视频还需要切换不同的贴图、颜色等&#xff0c;工作量就更离谱了&#xff0c;所以不得不用脚本实现自动化。 Blender的脚本是用Python编写&#xff0c;比PS的js要方便很多。再下载一套Blender对应版本的AP…

24.实现线性拟合和相关系数(matlab程序)

1.简述 1. 基本语法 1.1 corr函数基本语法 语法 说明 rho corr(X) 返回输入矩阵X中每对列之间的两两线性相关系数矩阵。 rho corr(X, Y) 返回输入矩阵X和Y中每对列之间的两两相关系数矩阵。 [rho, pval] corr(X, Y) 返回pval&#xff0c;一个p值矩阵&#xff0c…

redis安装

安装redis 安装依赖环境 yum install -y gcc cd /opt tar -xf redis-5.0.7.tar cd redis-5.0.7/ make && make install PREFIX/usr/local/redis installvim /opt/redis-5.0.7/utils/install_server.shcd /opt/redis-5.0.7/utils ./install_server.sh Please select the…

极简并优雅的在IDEA使用Git远程拉取项目和本地推送项目

连接Git 搜索Git然后将你下载好的Git的文件目录位置给他弄进去就行 本地分支管理 分支管理通常是在IDEA的右下角找到 连接远程仓库 方法1本地项目推送到远程仓库 如果当前项目还没交给Git管理的则按照以下图所示先将项目交给Git管理 然后此时文件都会是红色的&#xff0c;这表…

c++ 职责链+策略模式,实现链式过程处理

想象一下一件商品的加工&#xff0c;需要经过多道工艺流程处理。这一道道工艺流程就像是一个职责链&#xff0c;从头到尾传递。就好像网络传输时&#xff0c;消息发送到对端主机&#xff0c;经过协议栈&#xff0c;在这个过程中&#xff0c;它在不同的协议栈层级中被一层层抽丝…

《实战AI模型》——赶上GPT3.5的大模型LLaMA 2可免费商用,内含中文模型推理和微调解决方案

目录 准备环境及命令后参数导入: 导入模型: 准备LoRA: 导入datasets: 配置Config:

C# 存在重复元素

217 存在重复元素 给你一个整数数组 nums 。如果任一值在数组中出现 至少两次 &#xff0c;返回 true &#xff1b;如果数组中每个元素互不相同&#xff0c;返回 false 。 示例 1&#xff1a; 输入&#xff1a;nums [1,2,3,1] 输出&#xff1a;true 示例 2&#xff1a; 输…