【ribbon】Ribbon的使用与原理

news2024/11/23 23:43:16

负载均衡介绍

负载均衡(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
     */
    @Bean
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }

    /**
     * 有负责均衡能力的RestTemplate
     * @return
     */
    @Bean
    @LoadBalanced
    public 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 {

    @Resource
    private RestTemplate restTemplate;

    @Resource
    private 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 {

    @Resource
    private DiscoveryClient discoveryClient;

    private final AtomicInteger counter = new AtomicInteger();

    /**
     * 默认的RestTemplate,不带负载均衡
     * @return
     */
    @Bean
    public 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
        @Override
        public 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();

	@Bean
	public 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 {

		@Bean
		public LoadBalancerInterceptor loadBalancerInterceptor(
				LoadBalancerClient loadBalancerClient,
				LoadBalancerRequestFactory requestFactory) {
			return new LoadBalancerInterceptor(loadBalancerClient, requestFactory);
		}

		@Bean
		@ConditionalOnMissingBean
		public 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.coloradmin.cn/o/786721.html

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

相关文章

【全方位解析】如何获取客户端/服务端真实 IP

一、应用场景 1.比如在投票系统开发中&#xff0c;为了防止刷票&#xff0c;我们需要限制每个 IP 地址只能投票一次 2.当网站受到诸如 DDoS&#xff08;Distributed Denial of Service&#xff0c;分布式拒绝服务攻击&#xff09;等攻击时&#xff0c;我们需要快速定位攻击者…

星火汇聚丨高效行动,决胜2023!

7月6日至10日&#xff0c;数字韧性领域标杆企业同创永益营销体系大会在长沙召开&#xff0c;闪耀在全国各地的销售之星集结汇聚。本次大会历时四天&#xff0c;包含营销全员大会、各行业业委会专场会议、销售大比武等业务实践议程&#xff0c;以及飞盘竞技、走进毛泽东故居韶山…

【收藏】用Vue.js来构建你的Web3应用,就像开发 Web2 一样熟悉

作为一名涉足去中心化网络的前端 JavaScript 开发人员&#xff0c;您可能遇到过许多 Web3 开发解决方案。但是&#xff0c;这些解决方案通常侧重于钱包集成和交易执行&#xff0c;这就造成了学习曲线&#xff0c;偏离了熟悉的 Web2 开发体验。 但不用担心&#xff01;有一种解…

ip、域名、DNS、CDN概念

1、概念 ip地址 在网络世界里, 一台服务器或者说一台网络设备对应着一个ip地址, 如果我们需要访问指定的网络设备的资源, 那么我们就需要知道这个ip地址, 然后才能去访问它. 这就好像, 我想去朋友家里, 我必须先知道他家的住址, 才能去拜访它. 在互联网世界中, 所有的通信都是…

Docker数据管理与Dockerfile

目录 Docker 的数据管理 1&#xff0e;数据卷 2&#xff0e;数据卷容器 端口映射 容器互联&#xff08;使用centos镜像&#xff09; Docker 镜像的创建 1&#xff0e;基于现有镜像创建 2&#xff0e;基于本地模板创建 3&#xff0e;基于Dockerfile 创建 联合文件系统…

亚马逊会员日过后站内站外怎么做?

在亚马逊的会员日活动中&#xff0c;众多品牌商家都参与了进来&#xff0c;通过优惠力度和活动策划提高了销售额。但是&#xff0c;会员日过后&#xff0c;如何保持销售增长和用户粘性&#xff0c;需要品牌商家在站内和站外进行策略优化。 一、站内优化 1、提高产品质量的同时…

【Nodejs】Express模板使用

1.Express脚手架的安装 安装Express脚手架有两种方式&#xff1a; 使用express-generator安装 使用命令行进入项目目录&#xff0c;依次执行&#xff1a; cnpm i -g express-generator可通过express -h查看命令行的指令含义 express -hUsage: express [options] [dir] Optio…

28.JavaWeb-Elasticsearch

1.Elasticsearch概述 Elasticsearch 是一个分布式的全文检索引擎。采用Java语言开发&#xff0c;基于Apache协议的开源项目&#xff0c;具有实时搜索&#xff0c;稳定&#xff0c;可靠&#xff0c;快速的特点。 1.1 全文检索引擎 分为通用搜索引擎&#xff08;百度、谷歌&…

苹果发布安全更新,修复了今年第11个零日漏洞!

苹果公司发布了安全更新&#xff0c;修复针对 iPhone、Mac 和 iPad 的零日漏洞。 苹果公司在一份公告中描述了一个 WebKit 漏洞&#xff0c;该漏洞被标记为 CVE-2023-37450&#xff0c;已在本月初的新一轮快速安全响应 (RSR) 更新中得到解决。 本次修补的另一个零日漏洞是一个…

CAD中让时间日期自动填写的方法

图纸的图签中&#xff0c;通常会有一栏是出图日期。有的单位&#xff0c;也会叫做版本号。即哪天出的图。一般情况下&#xff0c;出图日期就是打图当天。 在这样的前期下&#xff0c;图纸由于存在频繁修改&#xff0c;所以出图日期也会存在变化。还有一种情况&#xff0c;就是出…

(四)FLUX语法

以下内容来自 尚硅谷&#xff0c;写这一系列的文章&#xff0c;主要是为了方便后续自己的查看&#xff0c;不用带着个PDF找来找去的&#xff0c;太麻烦&#xff01; 第 4 章 FLUX语法 4.1 认识FLUX语言 1、Flux是一种函数式的数据脚本语言&#xff0c;它旨在将查询、处理、分…

Docker 网络和资源限制

Docker 网络 一、Docker 网络的概念1、Docker 网络实现原理2、查看容器的输出和日志信息3、Docker 的网络模式&#xff1a;4、容器的网络模式 二、网络模式详解1、host模式2、container模式3、none模式4、bridge模式5、自定义网络 三、资源控制1、CPU 资源控制&#xff08;1&am…

MQ - 闲聊MQ一二事儿 (Kafka、RocketMQ 、Pulsar )

文章目录 MQ的发展史阶段一&#xff1a;追求解耦阶段二&#xff1a;追求吞吐量与一致性阶段三&#xff1a;追求平台化 MQ的通用架构主题topic、生产者producer、消费者consumer分区partition MQ 存储KafkaGood Design ---> 磁盘顺序写盘Poor Impact---> topic 数量不能过…

Java Spring和Spring集成Mybatis

0目录 1.Spring 2.Spring集成Mybatis 1.Spring 特性 IOC&#xff1a;控制反转 AOP&#xff1a;面向切面 Spring组成部分 在SMM中起到的作用&#xff08;粘合剂&#xff09; Spring理念 OOP核心思想【万物皆对象】 Spring核心思想【万物皆Bean组件】 Spring优势 低侵入式 …

解决Jmeter响应内容显示乱码

一、问题描述 jmeter在执行接口请求后&#xff0c;返回的响应体里面出现乱码现象&#xff0c;尽管在调了对应请求的响应编码也无用&#xff0c;现找到解决办法。 二、解决办法 进入到jmeter的bin目录下&#xff0c;找到jmeter.properties&#xff0c;通过按ctrlF快速定位查找到…

手机图片转pdf?两种方法介绍

手机图片转pdf&#xff1f;如今&#xff0c;随着生活的数字化&#xff0c;我们的手机中储存了大量的照片。但是&#xff0c;如果需要将这些照片转换成PDF格式&#xff0c;该怎么办呢&#xff1f;下面&#xff0c;小编就给大家介绍三种方法来实现这一目标。 第一种方法&#xff…

SpringBoot 统一功能处理:用户登录权限校验-拦截器、异常处理、数据格式返回

本篇将要学习 Spring Boot 统一功能处理模块&#xff0c;这也是 AOP 的实战环节 用户登录权限的校验实现接口 HandlerInterceptor WebMvcConfigurer异常处理使用注解 RestControllerAdvice ExceptionHandler数据格式返回使用注解 ControllerAdvice 并且实现接口 ResponseBody…

基于STM32CubeMX和keil采用STM32F407的基本定时器中断实现LED闪烁

文章目录 前言1. 电路原理图理解2. 基本定时器2.1 STM32定时器中断的流程&#xff1a;2.2 部分参数详解2.2.1 时钟源2.2.2 预分频系数2.2.3 自动重装载值 3. STM32CubeMX参数配置3.1GPIO配置3.2 时钟配置3.2 配置定时器相关参数3.3 Debug配置3.4 中断配置3.5 代码生成 4. keil代…

【每日一题】—— B. Ternary String (Educational Codeforces Round 87 (Rated for Div. 2))

&#x1f30f;博客主页&#xff1a;PH_modest的博客主页 &#x1f6a9;当前专栏&#xff1a;每日一题 &#x1f48c;其他专栏&#xff1a; &#x1f534; 每日反刍 &#x1f7e1; C跬步积累 &#x1f7e2; C语言跬步积累 &#x1f308;座右铭&#xff1a;广积粮&#xff0c;缓称…

ftp传文件越来越慢的原因,以及解决方案

FTP 是一种常用的文件传输协议&#xff0c;它基于客户端-服务端模型工作&#xff0c;允许用户通过网络传输文件。但是&#xff0c;有时候在使用 FTP 的过程中&#xff0c;文件传输速度会逐渐变慢&#xff0c;这给用户带来了很多困扰。本文将分析 FTP 传文件变慢的原因&#xff…