【ribbon】Ribbon的使用与原理

这篇具有很好参考价值的文章主要介绍了【ribbon】Ribbon的使用与原理。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

负载均衡介绍

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

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

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

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

客户端的负载均衡

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

【ribbon】Ribbon的使用与原理,springcloud,ribbon,spring cloud,后端,spring boot,java

服务端的负载均衡

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

【ribbon】Ribbon的使用与原理,springcloud,ribbon,spring cloud,后端,spring boot,java

常见负载均衡算法

  • 随机:通过随机选择一个服务进行执行,一般这种方式使用较少;
  • 轮询:负载均衡默认实现方式,请求来之后排队处理;
  • 加权轮询:通过对服务器性能的分型,给高配置,低负载的服务器分配更高的权重,均衡各个服务器的压力;
  • 地址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文章来源地址https://www.toymoban.com/news/detail-606942.html

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));
}

到了这里,关于【ribbon】Ribbon的使用与原理的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【Spring Cloud】Nacos及Ribbon组件的使用

    🎉🎉欢迎来到我的CSDN主页!🎉🎉 🏅我是Java方文山,一个在CSDN分享笔记的博主。📚📚 🌟推荐给大家我的专栏《Spring Cloud》。🎯🎯 👉点击这里,就可以查看我的主页啦!👇👇 Java方文山的个人主页 🎁如果感觉还不错的话请给我点赞吧!🎁🎁 💖期待你的加入,一起

    2024年01月25日
    浏览(52)
  • Spring Cloud学习笔记(Ribbon):Ribbon的应用样例

    这是本人学习的总结,主要学习资料如下 - 马士兵教育 我们都知道 Ribbon 是用于负载均衡的。提供同一种服务的 Client 可能有多个,比如有多个 User Client 提供查询用户信息的服务,使用 Ribbon 就能简单地达到负载均衡的效果。 想要使用 Ribbon ,无论是服务提供者还是调用服务

    2024年04月25日
    浏览(27)
  • spring cloud 之 ribbon

    Spring Cloud Ribbon 是基于 Netflix Ribbon 实现的一套客户端负载均衡的工具, 主要功能是提供客户端负载均衡算法和服务调用 。简单的说,就是在配置文件中列出 Load Balance( LB: 将用户的请求平摊的分配到多个服务上 )后面的所有机器,Ribbon 会自动的帮助我们基于某种规则(如

    2024年02月12日
    浏览(30)
  • Spring Cloud Ribbon:负载均衡

    1. 介绍 Spring Cloud Ribbon 1.1 什么是 Spring Cloud Ribbon Spring Cloud Ribbon是Netflix开源的负载均衡器,它为分布式服务提供了客户端负载均衡的功能。Ribbon可以根据一系列的负载均衡算法和配置策略,将客户端的请求动态分发到多个服务实例上,以实现高可用性和性能优化。 1.2 负载均

    2024年02月19日
    浏览(30)
  • 【Spring Cloud 四】Ribbon负载均衡

    【Spring Cloud一】微服务基本知识 【Spring Cloud 三】Eureka服务注册与服务发现 目前公司项目使用的注册中心主要是Spring Cloud Alibaba的Nacos做的注册中心和配置中心。并且Nacos使用了Ribbon作为默认的负载均衡器。但是相当于将Ribbon的负载均衡给透明化了,日常开发明面上是看不到

    2024年02月14日
    浏览(35)
  • 【Spring Cloud系列】- Ribbon详解与实战

    在前面的文章 Eureka详解与实战、Eureka Client应用、RestTemplate详解及其负载均衡几篇文章中,已经介绍了Spring Cloud基本应用,本文将从讲解在进程层面的负载均衡,在Spring Cloud中如何使用Ribbon做系统应用层面的负载均衡使用。 Ribbon 是netflix 公司开源的基于客户端的负载均衡组件

    2024年02月15日
    浏览(34)
  • Ribbon:Spring Cloud负载均衡与服务调用组件

    负载均衡? Ribbon实现服务调用? Ribbon实现负载均衡? 切换负载均衡策略? 定制负载均衡策略? 负载均衡 负载均衡(Load Balance),将用户的请求平分到多个服务器上运行,以扩展服务器带宽、增强数据处理能力、增加吞吐量、提高网络的可用性和灵活性的目的。 服务端负载

    2024年02月03日
    浏览(36)
  • Spring Cloud之负载均衡与服务调用(Ribbon)

    目录 Ribbon 简介 负载均衡 简介 负载均衡方式 服务端负载均衡 工作原理 特点 客户端负载均衡 工作原理 特点 对比 实现 负载均衡策略 切换负载均衡策略 定制负载均衡策略 超时与重试 单个服务配置 全局配置 服务调用 示例         Ribbon 是 Netflix 公司发布的开源组件,其

    2024年02月08日
    浏览(43)
  • 【Spring Cloud】Ribbon 中的几种负载均衡策略

    负载均衡通常有两种实现手段,一种是服务端负载均衡器,另一种是客户端负载均衡器,而我们今天的主角 Ribbon 就属于后者——客户端负载均衡器。 服务端负载均衡器的问题是,它提供了更强的流量控制权,但无法满足不同的消费者希望使用不同负载均衡策略的需求,而使

    2024年02月15日
    浏览(50)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包