前言

上一篇文章中我们通过自己开发了一个负载均衡组件,实现了随机算法的负载均衡功能,如果要实现其他算法,还需要修改代码增加相应的功能。这一篇文章,我们将介绍一个更简单的负载均衡实现,使用**@LoadBalanced**注解实现负载均衡的功能。

项目实战

创建项目

同样的,我们的项目现在依然有一个registry注册中心,一个provider服务提供者,接下来,我们再次修改一下consumer服务消费者的代码:

@EnableEurekaClient
@SpringBootApplication
@RestController
public class ConsumerApplication {
    public static void main(String[] args) {
        SpringApplication.run(ConsumerApplication.class, args);
    }
    @Bean
    @LoadBalanced
    RestTemplate restTemplate() {
        return new RestTemplate();
    }
    @Autowired
    DiscoveryClient discoveryClient;
    @Autowired
    RestTemplate restTemplate;
    @GetMapping("/hello2")
    public String hello2(String name) {
        String returnInfo = restTemplate.getForObject(  "http://provider/hello?name={1}", String.class, name);
        return returnInfo;
    }
}

在这个版本,同样的,还是创建RestTemplate Bean对象,不同的是上面仅仅增加了@LoadBalanced注解。

启动项目验证

依然正确返回了结果!

太神奇了吧,比我们自己开发的负载均衡组件简单太多了吧,仅仅在restTemplate() 方法上面增加了一个@LoadBalanced注解,怎么就实现的呢?废话不说,为了一探究竟,扒一扒源码吧!

源码分析

首先,点击@LoadBalanced注解进去,没有什么特别之处,那么我们在想想,Spring在创建Bean实例的时候,注解在什么地方起了作用?什么?不知道?翻一下这篇文章吧:

肝了两周,一张图解锁Spring核心源码

通过回顾Spring启动以及Bean的生命周期创建过程,我们就会发现加上@LoadBalancer注解后,项目启动时就会加载LoadBalancerAutoConfiguration这个配置类(通过spring-cloud-commons包下面的的spring.factories)。通过查看该配置类源码,发现其有个静态内部类LoadBalancerInterceptorConfig,其内部又创建了一个负载均衡拦截器:LoadBalancerInterceptor,该拦截器包含有一个loadBalancerClient参数:

@ConditionalOnMissingClass({"org.springframework.retry.support.RetryTemplate"})
    static class LoadBalancerInterceptorConfig {
        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);
            };
        }
    }

我们继续点击LoadBalancerInterceptor类进入,发现intercept方法,该方法中调用了LoadBalancerClient的execute方法,

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

LoadBalancerClient是一个接口,点击进去我们发现其实现类是RibbonLoadBalancerClient,查看其继承关系:

通过接口中的方法名称,我们可以猜想,choose方法就是选择其中服务列表中其中一个服务,reconstructURI方法就是重新构造请求的URI。

选择服务

choose方法是在RibbonLoadBalancerClient实现类中实现的

    public ServiceInstance choose(String serviceId, Object hint) {
        Server server = this.getServer(this.getLoadBalancer(serviceId), hint);
        return server == null ? null : new RibbonLoadBalancerClient.RibbonServer(serviceId, server, this.isSecure(server, serviceId), this.serverIntrospector(serviceId).getMetadata(server));
    }

在这个方法中,先调用 getServer 方法获取服务,这个方法最终会调用 ILoadBalancer 接口的 chooseServer 方法,而 ILoadBalancer 接口的实现类默认是ZoneAwareLoadBalancer。

ZoneAwareLoadBalancer 继承自 DynamicServerListLoadBalancer ,而在 DynamicServerListLoadBalancer 的构造方法中,调用了 this.restOfInit(clientConfig);在restOfInit这个方法中,通过 this.updateListOfServers()来获取服务列表;

而在chooseServer ()方法中,就会根据负载均衡算法,选择其中一个服务并返回:

 BaseLoadBalancer zoneLoadBalancer = this.getLoadBalancer(zone);
 server = zoneLoadBalancer.chooseServer(key);

地址替换

选择其中一个服务信息后,怎么将接口从 http://provider/hello 变为 http://localhost:8003/hello 呢?还记得上面我们说的reconstructURI方法吗?通过配置类LoadBalancerAutoConfiguration加载后,会注入LoadBalancerInterceptor拦截器,该拦截器会拦截我们的请求,并对请求地址进行处理,重构方法的具体实现在 LoadBalancerContext 类的 reconstructURIWithServer 方法中

public URI reconstructURIWithServer(Server server, URI original) {
        String host = server.getHost();
        int port = server.getPort();
        String scheme = server.getScheme();
        if (host.equals(original.getHost()) && port == original.getPort() && scheme == original.getScheme()) {
            return original;
        } else {
            if (scheme == null) {
                scheme = original.getScheme();
            }
            if (scheme == null) {
                scheme = (String)this.deriveSchemeAndPortFromPartialUri(original).first();
            }
            try {
                StringBuilder sb = new StringBuilder();
                sb.append(scheme).append("://");
                if (!Strings.isNullOrEmpty(original.getRawUserInfo())) {
                    sb.append(original.getRawUserInfo()).append("@");
                }
                sb.append(host);
                if (port >= 0) {
                    sb.append(":").append(port);
                }
                sb.append(original.getRawPath());
                if (!Strings.isNullOrEmpty(original.getRawQuery())) {
                    sb.append("?").append(original.getRawQuery());
                }
                if (!Strings.isNullOrEmpty(original.getRawFragment())) {
                    sb.append("#").append(original.getRawFragment());
                }
                URI newURI = new URI(sb.toString());
                return newURI;
            } catch (URISyntaxException var8) {
                throw new RuntimeException(var8);
            }
        }
    }

可以看到该方法中,将原始的请求地址original,替换成了选取的服务的IP和端口。并最终调用该服务的接口方法。

看到这里,再想想我们上一章的内容,是不是有异曲同工之妙?

总结

通过添加@LoadBalanced注解,就及其简单的实现了负载均衡的功能,与其说是Ribbon的强大,不如说是Spring的强大,Spring在整个上下文创建过程中,在不同的时机开放了一个又一个的接口,这就为各种组件的继承提供了遍历,同时也进一步促进了Spring生态的快速发展。

到此这篇关于SpringCloud超详细讲解负载均衡组件的文章就介绍到这了,更多相关SpringCloud负载均衡组件内容请搜索Devmax以前的文章或继续浏览下面的相关文章希望大家以后多多支持Devmax!

SpringCloud超详细讲解负载均衡组件Ribbon源码的更多相关文章

  1. Yii2中组件的注册与创建方法

    这篇文章主要介绍了Yii2之组件的注册与创建的实现方法,非常不错,具有参考借鉴价值,需要的朋友可以参考下

  2. SpringCloud超详细讲解微服务网关Zuul基础

    这篇文章主要介绍了SpringCloud Zuul微服务网关,负载均衡,熔断和限流,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下

  3. Android ListView UI组件使用说明

    这篇文章主要介绍了Android ListView UI组件使用说明,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下

  4. Flutter 首页必用组件NestedScrollView的示例详解

    今天介绍的组件是NestedScrollView,大部分的App首页都会用到这个组件。对Flutter 首页必用组件NestedScrollView的相关知识感兴趣的一起看看吧

  5. SpringCloud gateway+zookeeper实现网关路由的详细搭建

    这篇文章主要介绍了SpringCloud gateway+zookeeper实现网关路由,本文通过图文实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下

  6. React进阶学习之组件的解耦之道

    这篇文章主要给大家介绍了关于React进阶之组件的解耦之道,文中通过详细的示例代码给大家介绍了组件分割与解耦的方法,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面跟着小编来一起学习学习吧。

  7. 使用Vant框架list组件遇到的坑及解决

    这篇文章主要介绍了使用Vant框架list组件遇到的坑及解决方案,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教

  8. vue实现自定义组件挂载原型上

    这篇文章主要介绍了vue实现自定义组件挂载原型上方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教

  9. Vue组件如何设置Props实例详解

    props主要用于组件的传值,他的工作就是为了接收外面传过来的数据,与data、el、ref是一个级别的配置项,下面这篇文章主要给大家介绍了关于Vue组件如何设置Props的相关资料,需要的朋友可以参考下

  10. React.js组件实现拖拽排序组件功能过程解析

    这篇文章主要介绍了React.js组件实现拖拽排序组件功能过程解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下

随机推荐

  1. 基于EJB技术的商务预订系统的开发

    用EJB结构开发的应用程序是可伸缩的、事务型的、多用户安全的。总的来说,EJB是一个组件事务监控的标准服务器端的组件模型。基于EJB技术的系统结构模型EJB结构是一个服务端组件结构,是一个层次性结构,其结构模型如图1所示。图2:商务预订系统的构架EntityBean是为了现实世界的对象建造的模型,这些对象通常是数据库的一些持久记录。

  2. Java利用POI实现导入导出Excel表格

    这篇文章主要为大家详细介绍了Java利用POI实现导入导出Excel表格,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

  3. Mybatis分页插件PageHelper手写实现示例

    这篇文章主要为大家介绍了Mybatis分页插件PageHelper手写实现示例,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪

  4. (jsp/html)网页上嵌入播放器(常用播放器代码整理)

    网页上嵌入播放器,只要在HTML上添加以上代码就OK了,下面整理了一些常用的播放器代码,总有一款适合你,感兴趣的朋友可以参考下哈,希望对你有所帮助

  5. Java 阻塞队列BlockingQueue详解

    本文详细介绍了BlockingQueue家庭中的所有成员,包括他们各自的功能以及常见使用场景,通过实例代码介绍了Java 阻塞队列BlockingQueue的相关知识,需要的朋友可以参考下

  6. Java异常Exception详细讲解

    异常就是不正常,比如当我们身体出现了异常我们会根据身体情况选择喝开水、吃药、看病、等 异常处理方法。 java异常处理机制是我们java语言使用异常处理机制为程序提供了错误处理的能力,程序出现的错误,程序可以安全的退出,以保证程序正常的运行等

  7. Java Bean 作用域及它的几种类型介绍

    这篇文章主要介绍了Java Bean作用域及它的几种类型介绍,Spring框架作为一个管理Bean的IoC容器,那么Bean自然是Spring中的重要资源了,那Bean的作用域又是什么,接下来我们一起进入文章详细学习吧

  8. 面试突击之跨域问题的解决方案详解

    跨域问题本质是浏览器的一种保护机制,它的初衷是为了保证用户的安全,防止恶意网站窃取数据。那怎么解决这个问题呢?接下来我们一起来看

  9. Mybatis-Plus接口BaseMapper与Services使用详解

    这篇文章主要为大家介绍了Mybatis-Plus接口BaseMapper与Services使用详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪

  10. mybatis-plus雪花算法增强idworker的实现

    今天聊聊在mybatis-plus中引入分布式ID生成框架idworker,进一步增强实现生成分布式唯一ID,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

返回
顶部