全网最清晰 SpringCloud-Feign&Ribbon流程

引入了Feign,也就是引入了Ribbon,可以自己看Feign的starter
一般来说,我们想要feign进行远程调用,只需要如下配置就行,加上@EnableFeignClients
在这里插入图片描述

暴露出来的Feign接口一般是这样的
在这里插入图片描述在这里插入图片描述

其他服务想要调用它,把它引入进来,然后调用就行

一、自动配置
在这里插入图片描述

二、FeignClientRegistrar详解
在这里插入图片描述

  1. registerDefaultConfiguration 看@EnableFeiginClients上有没有配置默认配置类,如果有的话,就注册到容器中
  2. registerFeignClients 这个类更重要,这个类扫描@FeignClients的接口,并且创建代理对象
public void registerFeignClients(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
    ClassPathScanningCandidateComponentProvider scanner = getScanner();
    scanner.setResourceLoader(this.resourceLoader);

    Set<String> basePackages;

    // 获取扫描路径

    // 扫描所有路径,默认情况下扫描启动类下的路径
    for (String basePackage : basePackages) {
        Set<BeanDefinition> candidateComponents = scanner.findCandidateComponents(basePackage);
        // 遍历扫描到的FeignClient的Bean
        for (BeanDefinition candidateComponent : candidateComponents) {
            if (candidateComponent instanceof AnnotatedBeanDefinition) {
                // verify annotated class is an interface
                AnnotatedBeanDefinition beanDefinition = (AnnotatedBeanDefinition) candidateComponent;
                AnnotationMetadata annotationMetadata = beanDefinition.getMetadata();
                Assert.isTrue(annotationMetadata.isInterface(), "@FeignClient can only be specified on an interface");

                Map<String, Object> attributes = annotationMetadata.getAnnotationAttributes(FeignClient.class.getCanonicalName());

                String name = getClientName(attributes);
                // 注册FeignClient的配置
                registerClientConfiguration(registry, name, attributes.get("configuration"));
                // 注册FeignClient
                registerFeignClient(registry, annotationMetadata, attributes);
            }
        }
    }
}
private void registerFeignClient(BeanDefinitionRegistry registry, AnnotationMetadata annotationMetadata, Map<String, Object> attributes) {
    String className = annotationMetadata.getClassName();
    // 生成FeignClientFactoryBean这个BeanDefinition构造器
    BeanDefinitionBuilder definition = BeanDefinitionBuilder.genericBeanDefinition(FeignClientFactoryBean.class);

    // 将@FeignClient的注解属性添加到builder
    definition.addPropertyValue("url", getUrl(attributes));
    definition.addPropertyValue("path", getPath(attributes));
    String name = getName(attributes);
    definition.addPropertyValue("name", name);
    String contextId = getContextId(attributes);
    definition.addPropertyValue("contextId", contextId);
    definition.addPropertyValue("type", className);
    definition.addPropertyValue("decode404", attributes.get("decode404"));
    definition.addPropertyValue("fallback", attributes.get("fallback"));
    definition.addPropertyValue("fallbackFactory", attributes.get("fallbackFactory"));
    definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);

    String alias = contextId + "FeignClient";
    AbstractBeanDefinition beanDefinition = definition.getBeanDefinition();

    // ...

    BeanDefinitionHolder holder = new BeanDefinitionHolder(beanDefinition, className, new String[] { alias });
    // 注册
    BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry);
}

将扫描到的FeignClient接口类最终注册到了Spring容器中,这里我们注意到在设置BeanClass的时候设置的是FeignClientFactoryBean.class, 也就是说往Spring容器中注入的是一个FactoryBean对象,熟悉Spring源码的就知道这是个工厂bean,它是为了创建bean对象,最终会走到它的getObject方法。
在这里插入图片描述

给你们看一下spring实例化的调用栈:
在这里插入图片描述

三、FeignClientFactoryBean详解
其实我们基本上也能猜到,它在做什么? 基本上就是在调用getObject的时候,创建FeignClient代理对象

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

简单的说一下流程:

  1. 构建了一个FeignContext上下文,可以理解为一个FeignClient有一个上下文
  2. 构建Feign.Builder对象,实际上创建的是HystrixFeign.Builder,后面要设置很多内容,才能把Feign创建出来
  3. 调用loadBalance的时候,设置上了client, !!!记住,这个client是LoadBalanceFeignClient,后面有用
  4. targeter.target(this, builder, context, target),开始准备创建代理对象了
  5. 调用到targetWithFallback,接下来又调用到HystrixFeign.builder.target的方法,这个方法拿fallback开始创建Feign,实际上我们得到是ReflectiveFeign
  6. 接下来调用ReflectiveFeign.instance, 这里比较重要
    a. 将方法和SynchronousMethodHandler 放到methodToHander中
    b. 创建InvocationHandler, 创建JDK动态代理,这一步是缺少不了的,一般InvocationHander都会持有很多东西,在代理的时候,要做事情嘛,向这里就是持有了methodToHander
    c. 通过JDK创建动态代理对象

四、InvocationHandler如何接受请求
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

这个client就是刚开始构建HystrixFeignBuilder的时候,设置的LoadBalanceFeignClient
在这里插入图片描述

lbClient(clientName)看看到底是什么负载均衡器,尝试看看
在这里插入图片描述

关注一下它的类继承体系:
在这里插入图片描述

lbClient(clientName).executeWithLoadBalancer 又是如何如何实现负载均衡拿?

public T executeWithLoadBalancer(final S request, final IClientConfig requestConfig) throws ClientException {
    LoadBalancerCommand<T> command = buildLoadBalancerCommand(request, requestConfig);

    try {
        return command.submit(
            new ServerOperation<T>() {
                @Override
                public Observable<T> call(Server server) {
                    // 回调返回选择好的Server对象,并重新构造uri地址
                    URI finalUri = reconstructURIWithServer(server, request.getUri());
                    S requestForServer = (S) request.replaceUri(finalUri);
                    try {
                        // 执行ribbon的request请求
                        return Observable.just(AbstractLoadBalancerAwareClient.this.execute(requestForServer, requestConfig));
                    } 
                    catch (Exception e) {
                        return Observable.error(e);
                    }
                }
            })
            .toBlocking()
            .single();
    } catch (Exception e) {
        // 
    }
    
}

如果不关心负载均衡的情况,这里其实就是直接执行ribbon的request请求了。也就是IClient这个接口类定义的内容。不过FeignLoadBalancer需要进行一次负载选择Server,然后才回调这里的call来发起请求。

跟进submit方法看看,submit方法先是进行了一次选择获得了一个Server对象,然后回调了上面说的ribbon的request执行

public Observable<T> submit(final ServerOperation<T> operation) {
        // ...

        // Use the load balancer
        Observable<T> o = 
                // 选择Server
                (server == null ? selectServer() : Observable.just(server))
                .concatMap(new Func1<Server, Observable<T>>() {
                    @Override
                    // Called for each server being selected
                    public Observable<T> call(Server server) {
                        context.setServer(server);
                        //
                        
                        // Called for each attempt and retry
                        Observable<T> o = Observable
                                .just(server)
                                .concatMap(new Func1<Server, Observable<T>>() {
                                    @Override
                                    public Observable<T> call(final Server server) {
                                        // ...
                                        // 回调ribbon的request请求
                                        return operation.call(server).doOnEach(
                                            // ...
                                        );
                                    }
                                });
                        
                        if (maxRetrysSame > 0) 
                            o = o.retry(retryPolicy(maxRetrysSame, true));
                        return o;
                    }
                });
            
        // ...
    }

继续跟进selectServer,看看是如何选择服务的?

private Observable<Server> selectServer() {
    return Observable.create(new OnSubscribe<Server>() {
        @Override
        public void call(Subscriber<? super Server> next) {
            try {
                // 从上下文中获取
                Server server = loadBalancerContext.getServerFromLoadBalancer(loadBalancerURI, loadBalancerKey);   
                next.onNext(server);
                next.onCompleted();
            } catch (Exception e) {
                next.onError(e);
            }
        }
    });
}

在这里插入图片描述

这里会创建loadBalancer,debug源代码这里显示的是ZoneAwareLoadBalancer,接下来就是调用chonseServer
在这里插入图片描述
在这里插入图片描述

通过不同的负载均衡规则来找一台server,最后拿到server之后 回调之前这部分代码

在这里插入图片描述

  • 15
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值