Springcloud 微服务实战笔记 Ribbon

news2024/12/21 22:32:55

使用

 @Configuration
 public class CustomConfiguration {
     @Bean
     @LoadBalanced // 开启负载均衡能力
     public RestTemplate restTemplate() {
         return new RestTemplate();
     }
 }

可看到使用Ribbon,非常简单,只需将@LoadBalanced注解加在RestTemplate的Bean上,就可以实现负载均衡。

@LoadBalanced

从该注解的源码上的注释,可看到LoadBalancerClient类来配置的

/**
 * Annotation to mark a RestTemplate or WebClient bean to be configured to use a
 * LoadBalancerClient.
 * @author Spencer Gibb
 */
@Target({ ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Qualifier
public @interface LoadBalanced {

}

查看LoadBalancerClient源码,如下:

/**
 * Represents a client-side load balancer.
 *
 * @author Spencer Gibb
 */
public interface LoadBalancerClient extends ServiceInstanceChooser {

    /**
     * Executes request using a ServiceInstance from the LoadBalancer for the specified
     * service.
     * @param serviceId The service ID to look up the LoadBalancer.
     * @param request Allows implementations to execute pre and post actions, such as
     * incrementing metrics.
     * @param <T> type of the response
     * @throws IOException in case of IO issues.
     * @return The result of the LoadBalancerRequest callback on the selected
     * ServiceInstance.
     */
    <T> T execute(String serviceId, LoadBalancerRequest<T> request) throws IOException;

    /**
     * Executes request using a ServiceInstance from the LoadBalancer for the specified
     * service.
     * @param serviceId The service ID to look up the LoadBalancer.
     * @param serviceInstance The service to execute the request to.
     * @param request Allows implementations to execute pre and post actions, such as
     * incrementing metrics.
     * @param <T> type of the response
     * @throws IOException in case of IO issues.
     * @return The result of the LoadBalancerRequest callback on the selected
     * ServiceInstance.
     */
    <T> T execute(String serviceId, ServiceInstance serviceInstance,
            LoadBalancerRequest<T> request) throws IOException;

    /**
     * Creates a proper URI with a real host and port for systems to utilize. Some systems
     * use a URI with the logical service name as the host, such as
     * http://myservice/path/to/service. This will replace the service name with the
     * host:port from the ServiceInstance.
     * @param instance service instance to reconstruct the URI
     * @param original A URI with the host as a logical service name.
     * @return A reconstructed URI.
     */
    URI reconstructURI(ServiceInstance instance, URI original);

}

 * Implemented by classes which use a load balancer to choose a server to send a request
 * to.
 *
 * @author Ryan Baxter
 */
public interface ServiceInstanceChooser {

    /**
     * Chooses a ServiceInstance from the LoadBalancer for the specified service.
     * @param serviceId The service ID to look up the LoadBalancer.
     * @return A ServiceInstance that matches the serviceId.
     */
    ServiceInstance choose(String serviceId);

}

ServiceInstanceChooser看类名便可知用来帮我们从同一个服务的多个服务实例中根据负载均衡策略选择出所需的服务实例。

choose:根据传入的服务名ServiceId,从负载均衡器中选择一个对应服务的实例。

execute:使用从负载均衡器中挑选出的服务实例来执行请求内容。

reconstructURI:为系统构建一个合适的host:port形式的URI。在分布式系统中,我们使用逻辑上的服务名称作为host来构建URI(替代服务实例的host:port形式)进行请求,比如http://myservice/path/to/service。在该操作的定义中,前者ServiceInstance对象是带有host和port的具体服务实例,而后者URI对象则是使用逻辑服务名定义为host 的 URI,而返回的 URI 内容则是通过ServiceInstance的服务实例详情拼接出的具体host:post形式的请求地址。

通过搜索@LoadBalanced使用地方发现,只有org.springframework.cloud.client.loadbalancer.LoadBalancerAutoConfiguration使用到了@LoadBalanced

/**
 * Auto-configuration for Ribbon (client-side load balancing).
 *
 * @author Spencer Gibb
 * @author Dave Syer
 * @author Will Tran
 * @author Gang Li
 */
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(RestTemplate.class)
@ConditionalOnBean(LoadBalancerClient.class)
@EnableConfigurationProperties(LoadBalancerRetryProperties.class)
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);
                }
            }
        });
    }

    @Bean
    @ConditionalOnMissingBean
    public LoadBalancerRequestFactory loadBalancerRequestFactory(
            LoadBalancerClient loadBalancerClient) {
        return new LoadBalancerRequestFactory(loadBalancerClient, this.transformers);
    }

    @Configuration(proxyBeanMethods = false)
    @ConditionalOnMissingClass("org.springframework.retry.support.RetryTemplate")
    static class LoadBalancerInterceptorConfig {

        @Bean
        public LoadBalancerInterceptor ribbonInterceptor(
                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);
            };
        }

    }

    /**
     * Auto configuration for retry mechanism.
     */
    @Configuration(proxyBeanMethods = false)
    @ConditionalOnClass(RetryTemplate.class)
    public static class RetryAutoConfiguration {

        @Bean
        @ConditionalOnMissingBean
        public LoadBalancedRetryFactory loadBalancedRetryFactory() {
            return new LoadBalancedRetryFactory() {
            };
        }

    }

    /**
     * Auto configuration for retry intercepting mechanism.
     */
    @Configuration(proxyBeanMethods = false)
    @ConditionalOnClass(RetryTemplate.class)
    public static class RetryInterceptorAutoConfiguration {

        @Bean
        @ConditionalOnMissingBean
        public RetryLoadBalancerInterceptor ribbonInterceptor(
                LoadBalancerClient loadBalancerClient,
                LoadBalancerRetryProperties properties,
                LoadBalancerRequestFactory requestFactory,
                LoadBalancedRetryFactory loadBalancedRetryFactory) {
            return new RetryLoadBalancerInterceptor(loadBalancerClient, properties,
                    requestFactory, loadBalancedRetryFactory);
        }

        @Bean
        @ConditionalOnMissingBean
        public RestTemplateCustomizer restTemplateCustomizer(
                final RetryLoadBalancerInterceptor loadBalancerInterceptor) {
            return restTemplate -> {
                List<ClientHttpRequestInterceptor> list = new ArrayList<>(
                        restTemplate.getInterceptors());
                list.add(loadBalancerInterceptor);
                restTemplate.setInterceptors(list);
            };
        }

    }

}

这段自动装配的代码的含义不难理解,就是利用了RestTempllate的拦截器,使用RestTemplateCustomizer对所有标注了@LoadBalanced的RestTemplate Bean添加了一个LoadBalancerInterceptor拦截器,而这个拦截器的作用就是对请求的URI进行转换获取到具体应该请求哪个服务实例ServiceInstance。

@LoadBalanced @Autowired(required = false)

这两个注解在一起的意思:只注入@LoadBalanced注解的Bean ,带有@LoadBalanced注解的RestTemplate会在原有的拦截器基础上加上LoadBalancerInterceptor拦截器

@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);
                }
            }
        });
    }
@Bean
        @ConditionalOnMissingBean
        public RestTemplateCustomizer restTemplateCustomizer(
                final LoadBalancerInterceptor loadBalancerInterceptor) {
            return restTemplate -> {
                List<ClientHttpRequestInterceptor> list = new ArrayList<>(
                        restTemplate.getInterceptors());
                list.add(loadBalancerInterceptor);
                restTemplate.setInterceptors(list);
            };
        }

可看到RestTemplate是可以设置拦截器。

org.springframework.cloud.client.loadbalancer.LoadBalancerInterceptor实现如下:

/**
 * @author Spencer Gibb
 * @author Dave Syer
 * @author Ryan Baxter
 * @author William Tran
 */
public class LoadBalancerInterceptor implements ClientHttpRequestInterceptor {

    private LoadBalancerClient loadBalancer;

    private LoadBalancerRequestFactory requestFactory;

    public LoadBalancerInterceptor(LoadBalancerClient loadBalancer,
            LoadBalancerRequestFactory requestFactory) {
        this.loadBalancer = loadBalancer;
        this.requestFactory = requestFactory;
    }

    public LoadBalancerInterceptor(LoadBalancerClient loadBalancer) {
        // for backwards compatibility
        this(loadBalancer, new LoadBalancerRequestFactory(loadBalancer));
    }

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

}

可看出:将LoadBalancerClient以及LoadBalancerRequestFactory创建的请求封装到LoadBalancerInterceptor中,并在intercept()方法中,使用LoadBalancerClient执行请求。

RestTemplate调用过程

RestTemplate.getForObject/getForEntity... --> RestTemplate.excute --> RestTemplate.doExecute --> org.springframework.http.client.ClientHttpRequestFactory#createRequest

--> org.springframework.http.client.AbstractClientHttpRequest#execute

--> org.springframework.http.client.AbstractClientHttpRequest#executeInternal

--> org.springframework.http.client.InterceptingClientHttpRequest#executeInternal

--> org.springframework.http.client.InterceptingClientHttpRequest.InterceptingRequestExecution#execute

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

--> org.springframework.cloud.client.loadbalancer.LoadBalancerClient#execute(java.lang.String, org.springframework.cloud.client.loadbalancer.LoadBalancerRequest)

--> org.springframework.cloud.netflix.ribbon.RibbonLoadBalancerClient#execute(java.lang.String, org.springframework.cloud.client.loadbalancer.LoadBalancerRequest)

整个流程最终可看到执行拦截器的execute方法,最终调用负载均衡的execute方法

最终调用org.springframework.cloud.client.loadbalancer.LoadBalancerInterceptor#intercept

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

org.springframework.cloud.netflix.ribbon.RibbonLoadBalancerClient#execute(java.lang.String, org.springframework.cloud.client.loadbalancer.LoadBalancerRequest, java.lang.Object)

public <T> T execute(String serviceId, LoadBalancerRequest<T> request, Object hint)
            throws IOException {
        ILoadBalancer loadBalancer = getLoadBalancer(serviceId);
        Server server = getServer(loadBalancer, hint);
        if (server == null) {
            throw new IllegalStateException("No instances available for " + serviceId);
        }
        RibbonServer ribbonServer = new RibbonServer(serviceId, server,
                isSecure(server, serviceId),
                serverIntrospector(serviceId).getMetadata(server));

        return execute(serviceId, ribbonServer, request);
    }

此处是RibbonLoadBalancerClient的execute方法,首先获取负载均衡器(getLoadBalancer),然后通过负载均衡器获取服务,看下getServer()方法实现:

protected Server getServer(ILoadBalancer loadBalancer, Object hint) {
        if (loadBalancer == null) {
            return null;
        }
        // Use 'default' on a null hint, or just pass it on?
        return loadBalancer.chooseServer(hint != null ? hint : "default");
    }
  public Server chooseServer(Object key) {
        if (counter == null) {
            counter = createCounter();
        }
        counter.increment();
        if (rule == null) {
            return null;
        } else {
            try {
                return rule.choose(key);
            } catch (Exception e) {
                logger.warn("LoadBalancer [{}]:  Error choosing server for key {}", name, key, e);
                return null;
            }
        }
    }

可看到最终是调用策略器的choose方法,选择服务器。

参考资料:

Spring Cloud微服务实战

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

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

相关文章

Django 分页(表单)

目录 一、手动分页二、分页器分页 一、手动分页 1、概念 页码&#xff1a;很容易理解&#xff0c;就是一本书的页码每页数量&#xff1a;就是一本书中某一页中的内容&#xff08;数据量&#xff0c;比如第二页有15行内容&#xff09;&#xff0c;这 15 就是该页的数据量 每一…

将 Python 和 Rust 融合在一起,为 pyQuil® 4.0 带来和谐

文章目录 前言设定方向从 Rust 库构建 Python 软件包改装 pyQuil异步困境回报&#xff1a;功能和性能结论 前言 pyQuil 一直是在 Rigetti 量子处理单元&#xff08;QPUs&#xff09;上构建和运行量子程序的基石&#xff0c;通过我们的 Quantum Cloud Services&#xff08;QCS™…

EBU7140 Security and Authentication(三)密钥管理;IP 层安全

B3 密钥管理 密钥分类&#xff1a; 按时长&#xff1a; short term&#xff1a;短期密钥&#xff0c;用于一次加密。long term&#xff1a;长期密钥&#xff0c;用于加密或者授权。 按服务类型&#xff1a; Authentication keys&#xff1a;公钥长期&#xff0c;私钥短期…

【HarmonyOS开发】共享包HAR和HSP的创建和使用以及三方库的发布

OpenHarmony提供了两种共享包&#xff0c;HAR&#xff08;Harmony Archive&#xff09;静态共享包&#xff0c;和HSP&#xff08;Harmony Shared Package&#xff09;动态共享包。 HAR与HSP都是为了实现代码和资源的共享&#xff0c;都可以包含代码、C库、资源和配置文件&…

C语言——操作符

一、算数操作符 1、(加操作符) 用于将两个数相加&#xff0c;例&#xff1a;3 3结果为6 2、-(减操作符) 用于将两个数相减&#xff0c;例&#xff1a;3 - 3结果为0 3、*(乘操作符) 用于将两个数相乘&#xff0c;例&#xff1a;3 * 3结果为9 4、/(除操作符) 用于将两个…

fastadmin学习02-修改后台管理员账号密码

问题 如果是别人部署好的fastadmin网站不知道后台登录地址和账号密码怎么办 后台登录地址 public目录下有一个很奇怪的php就是后台登录地址啦 忘记账号密码 找到fa_admin&#xff0c;fa_是前缀&#xff0c;肯能每个项目不太一样 UPDATE fa_admin set password1d020dee8ec…

计算机毕业论文内容参考|基于智能搜索引擎的图书管理系统的设计与实现

文章目录 摘要前言绪论课题背景国内外现状与趋势课题内容相关技术与方法介绍系统分析系统设计系统实现系统测试总结与展望摘要 本文介绍了基于智能搜索引擎的图书管理系统的设计与实现。该系统旨在提供一个高效、智能化的图书管理平台,帮助用户更快、更准确地找到所需的图书资…

使用Apache Commons SCXML实现状态机管理

第1章&#xff1a;引言 大家好&#xff0c;我是小黑&#xff0c;咱们程序员在开发过程中&#xff0c;经常会遇到需要管理不同状态和状态之间转换的场景。比如&#xff0c;一个在线购物的订单&#xff0c;它可能有“新建订单”、“已支付”、“配送中”、“已完成”等状态。在这…

Linux/Unix/国产化操作系统常用命令(二)

目录 后CentOS时代国产化操作系统国产化操作系统有哪些常用Linux命令关于Linux的LOGO 后CentOS时代 在CentOS 8发布后&#xff0c;就有了一些变化和趋势&#xff0c;可以说是进入了"后CentOS时代"。这个时代主要表现在以下几个方面&#xff1a; CentOS Stream的引入…

Spring技术内幕笔记之SpringMvc

WebApplicationContext接口的类继承关系 org.springframework.web.context.ContextLoader#initWebApplicationContext 对IOC容器的初始化 SpringMvc如何设计 DispatcherServlet类继承关系 MVC处理流程图如下&#xff1a; DispatcherServlet的工作大致可以分为两个部分&#xf…

Spring Boot 如何使用 Maven 实现多环境配置管理

Spring Boot 如何使用 Maven 实现多环境配置管理 实现多环境配置有以下几个重要原因&#xff1a; 适应不同的部署环境&#xff1a;在实际部署应用程序时&#xff0c;通常会有多个不同的部署环境&#xff0c;如开发环境、测试环境、生产环境等。每个环境可能需要不同的配置&…

Linux 命令tail

命令作用 tail 命令用于显示文件的末尾内容&#xff0c;默认显示文件的最后 10 行。通常情况下&#xff0c;tail 命令用于实时查看动态日志文件&#xff0c;可以使用 -f 参数跟踪文件内容的变化。 语法 tail [选项] [文件名] 参数 以 log.txt 为例演示参数效果 -n -linesK…

解决报错:找不到显卡

今天做实验碰到一个问题&#xff1a;torch找不到显卡&#xff1a; 打开任务管理器&#xff0c;独显直接没了&#xff0c;一度以为是要去修电脑了&#xff0c;突然想到上次做实验爆显存&#xff0c;屏蔽了gpu用cpu训练&#xff1a; import os os.environ["CUDA_DEVICE_OR…

【华为数据之道学习笔记】9-3构建以元数据为基础的安全隐私保护框架

以元数据为基础的安全隐私治理 有决策权的公司高层已经意识到安全隐私的重要性&#xff0c;在变革指导委员会以及各个高层会议纪要中都明确指明安全隐私是变革优先级非常高的主题&#xff0c;安全是一切业务的保障。 基于这个大前提&#xff0c;我们构建了以元数据为基础的安全…

【Spark精讲】记一个SparkSQL引擎层面的优化:SortMergeJoinExec

SparkSQL的Join执行流程 如下图所示&#xff0c;在分析不同类型的Join具体执行之前&#xff0c;先介绍Join执行的基本框架&#xff0c;框架中的一些概念和定义是在不同的SQL场景中使用的。 在Spark SQL中Join的实现都基于一个基本的流程&#xff0c;根据角色的不同&#xff0…

zookeeper未授权访问漏洞增加用户认证修复

linux机器中使用root命令行cd到zookeeper的bin文件夹下 启动zookeeper ./zkCli.sh # 启动zookeeper如果此时有未授权漏洞&#xff0c;可通过以下命令验证。 getAcl / #可以看到默认是world:anyone 就相当于无权限访问验证结果显示没有用户认证也可执行一些命令 增添用户认…

[GKCTF 2020]ez三剑客-eztypecho

[GKCTF 2020]ez三剑客-eztypecho 考点&#xff1a;Typecho反序列化漏洞 打开题目&#xff0c;发现是typecho的CMS 尝试跟着创建数据库发现不行&#xff0c;那么就搜搜此版本的相关信息发现存在反序列化漏洞 参考文章 跟着该文章分析来&#xff0c;首先找到install.php&#xf…

LeetCode第32题 : 最长有效括号

题目介绍 给你一个只包含 ( 和 ) 的字符串&#xff0c;找出最长有效&#xff08;格式正确且连续&#xff09;括号子串的长度。 示例 1&#xff1a; 输入&#xff1a;s "(()" 输出&#xff1a;2 解释&#xff1a;最长有效括号子串是 "()" 示例 2&#xf…

DALLE·3的图片一致性火了!

DALLE3的图片一致性火了&#xff01; DALLE3 利用种子值&GenID的一致性作图 防止大家不知道&#xff0c;这里拓展一下种子值&#xff0c;DALL每次生成的图像都会有一个新的、随机的种子值。我们可以利用种子值进行角色的变换 每次生成的图像都会有一个新的、随机的种子值…

主流大语言模型集体曝出训练数据泄露漏洞

内容概要&#xff1a; 安全研究人员发现&#xff0c;黑客可利用新的数据提取攻击方法从当今主流的大语言模型&#xff08;包括开源和封闭&#xff0c;对齐和未对齐模型&#xff09;中大规模提取训练数据。当前绝大多数大语言模型的记忆&#xff08;训练数据&#xff09;可被恢…