详解openfeign集成spring cloud loadbalancer实现负载均衡流程

 更新时间:2023年07月06日 09:45:07   作者:子瞻  
这篇文章主要介绍了openfeign集成spring cloud loadbalancer实现负载均衡流程详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪

Feignclient负载均衡中如何起作用

当我们在项目中调用自己实现的Feignclient负载均衡中如何起作用?请看下图:

图一

在图一中我们可以发现,在ConsumerController中调用自定义的DemoFeignClient方法时,通过spring容器中对DemoFeignclient的代理类的调用最终通过feign.SynchronousMethodHandler.invoke()->openfeign.loadbalancer.execute()->org.springframework.cloud.loadbalancer.blocking.client.FeignBlockingLoadBalancerClient.choose()->org.springframework.cloud.loadbalancer.core.RoundRobinLoadBalancer.choose()这样的调用链,最终执行spring cloud loadbalaner中的轮询负载均横策略!

然而,上图中的这些关键类是如何组合起来发挥作用呢?请我们一起继续分析!spring cloud项目启动后,spring容器解析并加载LoadBalancerClientConfiguration.java配置文件(如下图所示)

图二

然后将"reactorServiceInstanceLoadBalancer"注册到beanDefinitionMap中。
然后会扫描我们声明的controller,因为conroller中注入了DemoFeignClient,因此spring容器会递归创建DemoFeignClient,创建DemoFiegnClient过程中会通过AbstractAutowireCapableBeanFactory.obtainFromSupplier()注入instanceSupplier实例,也就是FeignClientFactoryBean实例!

主要逻辑

下面是org.springframework.cloud.openfeign.FeignClientsRegistrar.registerFeignClient()方法主要逻辑:

private void registerFeignClient(BeanDefinitionRegistry registry, AnnotationMetadata annotationMetadata,
            Map<String, Object> attributes) {
          //忽略非核心代码片段
            BeanDefinitionBuilder definition = BeanDefinitionBuilder.genericBeanDefinition(clazz, () -> {
            //设置url
            factoryBean.setUrl(getUrl(beanFactory, attributes));
            //设置路径
            factoryBean.setPath(getPath(beanFactory, attributes));
            //decode404 布尔值            factoryBean.setDecode404(Boolean.parseBoolean(String.valueOf(attributes.get("decode404"))));
            Object fallback = attributes.get("fallback");
            //设置fallback
            if (fallback != null) {
                factoryBean.setFallback(fallback instanceof Class ? (Class<?>) fallback
                        : ClassUtils.resolveClassName(fallback.toString(), null));
            }
            //设置fallback工厂类
            Object fallbackFactory = attributes.get("fallbackFactory");
            if (fallbackFactory != null) {
                factoryBean.setFallbackFactory(fallbackFactory instanceof Class ? (Class<?>) fallbackFactory
                        : ClassUtils.resolveClassName(fallbackFactory.toString(), null));
            }
            //实例化我们声明的DemoClient
            return factoryBean.getObject();
        });
}

这还没有完,org.springframework.cloud.openfeign.FeignClientFactoryBean.getObject()中最终执行loadBalance();在loadBalance()中实例化FeignInvocationHandler;

调用DemoFeignClient中方法

在项目启动成功之后,我们调用DemoFeignClient中的方法时

图三

通过图三可以发现,描述DemoFeignClient的RootBeanDefinition的类中还有一个叫“instanceSupplier”的类型属性,它的值是“FeignClientsRegistrar$lambda”,那么这个类时什么时候被注入进来的呢?答案就是上面分析项目启动过程中spring容器根据RootBeanDefinition对DemoFeignClient的描述,通过对"FeignClientsRegistrar$lambda"的调用完成对DemoFeignClient实例的创建。

在完成创建DemoFeignClient实例后,我们调用org.springframework.cloud.openfeign.loadbalancer。

FeignBlockingLoadBalancerClient.execute()执行负载均衡策略时,执行到如下代码块

Set<LoadBalancerLifecycle> supportedLifecycleProcessors = LoadBalancerLifecycleValidator
                .getSupportedLifecycleProcessors(
                        loadBalancerClientFactory.getInstances(serviceId, LoadBalancerLifecycle.class),
                        RequestDataContext.class, ResponseData.class, ServiceInstance.class);

"loadBalancerClientFactory.getInstances(serviceId, LoadBalancerLifecycle.class)"逻辑会获取LoadBalancer实例,最终调用LoadBalancerClientConfiguration.java中的reactorServiceInstanceLoadBalancer()实现创建负载均衡实例逻辑

@Bean
    @ConditionalOnMissingBean
    public ReactorLoadBalancer<ServiceInstance> reactorServiceInstanceLoadBalancer(Environment environment,
            LoadBalancerClientFactory loadBalancerClientFactory) {
        String name = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME);
        //创建负载均衡实例,而且我们发现默认的负载均衡实例是轮询负载均衡实例
        return new RoundRobinLoadBalancer(
                loadBalancerClientFactory.getLazyProvider(name, ServiceInstanceListSupplier.class), name);
    }

ok,截止到目前为止,我们的负载均衡实例已经创建完成!

这样一来,我们通过openfeign进行远程调用时,通过下图的调用链

图四

FeignInvocationHandler.invoke()->SynchronousMethodHandler.invoke()->org.springframework.cloud.openfeign.loadbalancer.FeignBlockingLoadBalancerClient.execute()->org.springframework.cloud.loadbalancer.blocking.client.BlockingLoadBalancerClient.choose()->org.springframework.cloud.loadbalancer.core.RoundRobinLoadBalancer.choose(),这样的调用链最终根据RoundRobinLoadBalancer.choose()获取服务实例。

public Mono<Response<ServiceInstance>> choose(Request request) {
        ServiceInstanceListSupplier supplier = serviceInstanceListSupplierProvider
                .getIfAvailable(NoopServiceInstanceListSupplier::new);
        //返回服务实例
        return supplier.get(request).next()
                .map(serviceInstances -> processInstanceResponse(supplier, serviceInstances));
    }

拿到服务实例后,FeignBlockingLoadBalancerClient.execute()中最终通过LoadBalancerUtils.executeWithLoadBalancerLifecycleProcessing()获取响应结果!

下面是具体代码逻辑:

static Response executeWithLoadBalancerLifecycleProcessing(Client feignClient, Request.Options options,
            Request feignRequest, org.springframework.cloud.client.loadbalancer.Request lbRequest,
            org.springframework.cloud.client.loadbalancer.Response<ServiceInstance> lbResponse,
            Set<LoadBalancerLifecycle> supportedLifecycleProcessors, boolean loadBalanced, boolean useRawStatusCodes)
            throws IOException {
        supportedLifecycleProcessors.forEach(lifecycle -> lifecycle.onStartRequest(lbRequest, lbResponse));
        try {
            //通过openfeign调用,获取响应结果
            Response response = feignClient.execute(feignRequest, options);
            if (loadBalanced) {
                supportedLifecycleProcessors.forEach(
                        lifecycle -> lifecycle.onComplete(new CompletionContext<>(CompletionContext.Status.SUCCESS,
                                lbRequest, lbResponse, buildResponseData(response, useRawStatusCodes))));
            }
            return response;
        }
        catch (Exception exception) {
            if (loadBalanced) {
                supportedLifecycleProcessors.forEach(lifecycle -> lifecycle.onComplete(
                        new CompletionContext<>(CompletionContext.Status.FAILED, exception, lbRequest, lbResponse)));
            }
            throw exception;
        }
    }

以上就是详解openfeign集成spring cloud loadbalancer实现负载均衡流程的详细内容,更多关于spring cloud loadbalancer的资料请关注脚本之家其它相关文章!

相关文章

  • cookie、session和java过滤器结合实现登陆程序

    cookie、session和java过滤器结合实现登陆程序

    这篇文章主要为大家详细介绍了cookie、session和java过滤器结合实现登陆程序的具体代码,感兴趣的朋友可以参考一下
    2016-05-05
  • Java接口返回省市区树形结构的实现

    Java接口返回省市区树形结构的实现

    本文主要介绍了Java接口返回省市区树形结构的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-01-01
  • 解析Java中PriorityQueue优先级队列结构的源码及用法

    解析Java中PriorityQueue优先级队列结构的源码及用法

    优先级队列是一种队列结构,是0个或多个元素的集合,每个元素都有一个优先权,PriorityQueue被内置于JDK中,本文就来解析Java中PriorityQueue优先级队列结构的源码及用法.
    2016-05-05
  • SpringBoot选择自有bean优先加载实现方法

    SpringBoot选择自有bean优先加载实现方法

    在一些需求中,可能存在某些场景,比如先加载自己的bean,然后自己的bean做一些DB操作,初始化配置问题,然后后面的bean基于这个配置文件,继续做其他的业务逻辑。因此有了本文的这个题目
    2023-03-03
  • Mybatis优化检索的方法详解

    Mybatis优化检索的方法详解

    MyBatis是一款优秀的基于Java的持久层框架,它可以将 SQL 语句和数据库中的记录映射成为 Java 对象,并且支持灵活的 SQL 查询语句,在Mybatis中,可以使用动态SQL来灵活构造SQL语句,从而满足各种不同的检索需求,本文介绍Mybatis如何优化检索,需要的朋友可以参考下
    2024-05-05
  • Java+Selenium实现文件上传下载功能详解

    Java+Selenium实现文件上传下载功能详解

    这篇文章主要介绍了java代码如何利用selenium操作浏览器上传和下载文件功能,文中的示例代码讲解详细,具有一定的借鉴价值,需要的可以参考一下
    2023-01-01
  • 基于Luhn算法的银行卡校验规则

    基于Luhn算法的银行卡校验规则

    这篇文章主要为大家介绍了基于Luhn算法的银行卡校验规则,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-05-05
  • Spring中的集合注入代码实例

    Spring中的集合注入代码实例

    这篇文章主要介绍了Spring中的集合注入代码实例,集合注入是指在Spring框架中,通过配置文件或注解的方式将集合类型的数据注入到Bean中,集合类型包括List、Set、Map和Properties等,需要的朋友可以参考下
    2023-11-11
  • SpringMVC实现登录与注册功能的详细步骤

    SpringMVC实现登录与注册功能的详细步骤

    本文介绍了如何通过Maven配置依赖,创建前端登录和注册页面,并实现后端逻辑,详细步骤包括配置文件、创建User类、配置中文过滤器及DispatcherServlet,并使用Spring MVC和JQuery处理前端请求,需要的朋友可以参考下
    2024-11-11
  • Java在Excel中添加水印的实现(单一水印、平铺水印)

    Java在Excel中添加水印的实现(单一水印、平铺水印)

    这篇文章主要介绍了Java在Excel中添加水印的实现(单一水印、平铺水印),文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2021-04-04

最新评论