Spring Cloud——OpenFeign源碼解析

一甩卓、簡介

Feign是一個(gè)http請求調(diào)用的輕量級框架,可以以Java接口注解的方式調(diào)用Http請求蕉斜,而不用像Java中通過封裝HTTP請求報(bào)文的方式直接調(diào)用逾柿。Feign通過處理注解,將請求模板化宅此,當(dāng)實(shí)際調(diào)用的時(shí)候机错,傳入?yún)?shù),根據(jù)參數(shù)再應(yīng)用到請求上父腕,進(jìn)而轉(zhuǎn)化成真正的請求弱匪,這種請求相對而言比較直觀。

Feign被廣泛應(yīng)用在Spring Cloud 的解決方案中璧亮,是學(xué)習(xí)基于Spring Cloud 微服務(wù)架構(gòu)不可或缺的重要組件萧诫。

二斥难、工作原理

  • 主程序入口添加了@EnableFeignClients注解開啟對FeignClient掃描加載處理。根據(jù)Feign Client的開發(fā)規(guī)范帘饶,定義接口并加@FeignClient注解哑诊。

  • 當(dāng)程序啟動(dòng)時(shí),會進(jìn)行包掃描及刻,掃描所有@FeignClients的注解的類搭儒,并且將這些信息注入Spring IOC容器中,當(dāng)定義的的Feign接口中的方法被調(diào)用時(shí)提茁,通過JDK動(dòng)態(tài)代理方式淹禾,來生成具體的RequestTemplate。當(dāng)生成代理時(shí)茴扁,F(xiàn)eign會為每個(gè)接口方法創(chuàng)建一個(gè)RequestTemplate對象铃岔,該對象封裝可HTTP請求需要的全部信息,如請求參數(shù)名峭火,請求方法等信息都是在這個(gè)過程中確定的毁习。

  • 然后RequestTemplate生成Request,然后把Request交給Client去處理卖丸,這里指的Client可以是JDK原生的URLConnection纺且、Apache的HttpClient、也可以是OKhttp稍浆,最后Client被封裝到LoadBalanceClient類载碌,這個(gè)類結(jié)合Ribbon負(fù)載均衡發(fā)起服務(wù)之間的調(diào)用。

三衅枫、源碼分析

使用openfegin我們可以不用在yaml文件添加任何關(guān)于openfeign的配置嫁艇,而只需要在一個(gè)被@Configuration注釋的配置類上或者Application啟動(dòng)類上添加@EnableFeignClients注解。例如:

@EnableFeignClients(basePackages = {"com.yibo.order.center"})
public class OrderCenterApplication {

    public static void main(String[] args) {
        SpringApplication.run(OrderCenterApplication.class,args);
    }
}

basePackages屬性用于指定被@FeignClient注解注釋的接口所在的包的包名弦撩,或者也可以直接指定clients屬性步咪,clients屬性可以直接指定一個(gè)或多個(gè)被@FeignClient注釋的類。

basePackages是一個(gè)數(shù)組益楼,如果被@FeignClient注解注釋的接口比較分散猾漫,可以指定多個(gè)包名,而不使用一個(gè)大的包名感凤,這樣可以減少包掃描耗費(fèi)的時(shí)間悯周,不拖慢應(yīng)用的啟動(dòng)速度。

EnableFeignClients

  • 該注解引入了FeignClientsRegistrar類
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(FeignClientsRegistrar.class)
public @interface EnableFeignClients {

    //basePackages 屬性的別名俊扭,允許使用更簡潔的注釋聲明
    String[] value() default {};

    //掃描包下帶注釋的組件
    String[] basePackages() default {};

    //basePackages() 的類型安全的替代方法队橙,用于指定要掃描帶注釋的組件的軟件包,指定類別的包裝將被掃描萨惑。
    Class<?>[] basePackageClasses() default {};

    //適用于所有自定義@Configuration,可以包含組成客戶端的部分的@Bean 
    Class<?>[] defaultConfiguration() default {};

    //用@FeignClient注釋的類的列表捐康,如果不為空,則禁用類路徑*掃描庸蔼。
    Class<?>[] clients() default {};

}

FeignClientsRegistrar實(shí)現(xiàn)了ImportBeanDefinitionRegistrar解总,它是一個(gè)動(dòng)態(tài)注入bean的接口,Spring Boot啟動(dòng)的時(shí)候姐仅,會去調(diào)用這個(gè)類中的registerBeanDefinitions來實(shí)現(xiàn)動(dòng)態(tài)Bean的裝載花枫。它的作用類似于ImportSelector。

FeignClientsRegistrar

@EnableFeignClients注解使用@Import導(dǎo)入FeignClientsRegistrar類掏膏,這是一個(gè)ImportBeanDefinitionRegistrar劳翰,因此我們重點(diǎn)關(guān)注它的registerBeanDefinitions方法。

class FeignClientsRegistrar
        implements ImportBeanDefinitionRegistrar, ResourceLoaderAware, EnvironmentAware {
        
    @Override
    public void registerBeanDefinitions(AnnotationMetadata metadata,
            BeanDefinitionRegistry registry) {
        //注冊@EnableFeignClients中定義defaultConfiguration屬性下的類馒疹,包裝成FeignClientSpecification佳簸,注冊到Spring容器。
    //在@FeignClient中有一個(gè)屬性:configuration颖变,這個(gè)屬性是表示各個(gè)FeignClient自定義的配置類生均,
        //后面也會通過調(diào)用registerClientConfiguration方法來注冊成FeignClientSpecification到容器。
    //所以腥刹,這里可以完全理解在@EnableFeignClients中配置的是做為兜底的配置马胧,在各個(gè)@FeignClient配置的就是自定義的情況。   
        registerDefaultConfiguration(metadata, registry);
        
        
        //該方法負(fù)責(zé)讀取@EnableFeignClients的屬性衔峰,獲取需要掃描的包名佩脊,
        //然后掃描指定的所有包名下的被@FeignClient注解注釋的接口,
        //將掃描出來的接口調(diào)用registerFeignClient方法注冊到spring容器垫卤。
        registerFeignClients(metadata, registry);
    }
}       

registerDefaultConfiguration

  • 解析EnableFeignClients屬性邻吞,注冊FeignClientSpecification類型的Bean
class FeignClientsRegistrar
        implements ImportBeanDefinitionRegistrar, ResourceLoaderAware, EnvironmentAware {

    private void registerDefaultConfiguration(AnnotationMetadata metadata,
            BeanDefinitionRegistry registry) {
        //解析EnableFeignClients屬性
        Map<String, Object> defaultAttrs = metadata
                .getAnnotationAttributes(EnableFeignClients.class.getName(), true);

        if (defaultAttrs != null && defaultAttrs.containsKey("defaultConfiguration")) {
            String name;
            if (metadata.hasEnclosingClass()) {
                name = "default." + metadata.getEnclosingClassName();
            }
            else {
                name = "default." + metadata.getClassName();
            }
            //注冊客戶端配置
            registerClientConfiguration(registry, name,
                    defaultAttrs.get("defaultConfiguration"));
        }
    }
    
    private void registerClientConfiguration(BeanDefinitionRegistry registry, Object name,
            Object configuration) {
        //加載FeignClientSpecification bean   
        BeanDefinitionBuilder builder = BeanDefinitionBuilder
                .genericBeanDefinition(FeignClientSpecification.class);
        builder.addConstructorArgValue(name);
        builder.addConstructorArgValue(configuration);
        //注冊FeignClientSpecification類型的Bean
        registry.registerBeanDefinition(
                name + "." + FeignClientSpecification.class.getSimpleName(),
                builder.getBeanDefinition());
    }
}

registerFeignClients

重點(diǎn)關(guān)注registerFeignClients方法,該方法負(fù)責(zé)讀取@EnableFeignClients的屬性葫男,獲取需要掃描的包名抱冷,然后掃描指定的所有包名下的被@FeignClient注解注釋的接口,將掃描出來的接口調(diào)用registerFeignClient方法注冊到spring容器梢褐。

class FeignClientsRegistrar
        implements ImportBeanDefinitionRegistrar, ResourceLoaderAware, EnvironmentAware {
        
    public void registerFeignClients(AnnotationMetadata metadata,
            BeanDefinitionRegistry registry) {
        ClassPathScanningCandidateComponentProvider scanner = getScanner();
        scanner.setResourceLoader(this.resourceLoader);

        Set<String> basePackages;

        Map<String, Object> attrs = metadata
                .getAnnotationAttributes(EnableFeignClients.class.getName());
        // 掃描帶有FeignClient注解的類      
        AnnotationTypeFilter annotationTypeFilter = new AnnotationTypeFilter(
                FeignClient.class);
        //獲取@EnableFeignClients 中clients的值      
        final Class<?>[] clients = attrs == null ? null
                : (Class<?>[]) attrs.get("clients");
        if (clients == null || clients.length == 0) {
            //如果沒有設(shè)置旺遮,那么設(shè)置類型過濾器
            scanner.addIncludeFilter(annotationTypeFilter);
            // 如果沒有設(shè)置,則掃描的包路徑為 @EnableFeignClients 注解所在的包
            basePackages = getBasePackages(metadata);
        }
        else {
            final Set<String> clientClasses = new HashSet<>();
            basePackages = new HashSet<>();
            //設(shè)置了則使用注解屬性來進(jìn)行掃描注冊
            for (Class<?> clazz : clients) {
                basePackages.add(ClassUtils.getPackageName(clazz));
                clientClasses.add(clazz.getCanonicalName());
            }
            AbstractClassTestingTypeFilter filter = new AbstractClassTestingTypeFilter() {
                @Override
                protected boolean match(ClassMetadata metadata) {
                    String cleaned = metadata.getClassName().replaceAll("\\$", ".");
                    return clientClasses.contains(cleaned);
                }
            };
            scanner.addIncludeFilter(
                    new AllTypeFilter(Arrays.asList(filter, annotationTypeFilter)));
        }
        //循環(huán)掃描注冊
        for (String basePackage : basePackages) {
            Set<BeanDefinition> candidateComponents = scanner
                    .findCandidateComponents(basePackage);
            for (BeanDefinition candidateComponent : candidateComponents) {
                if (candidateComponent instanceof AnnotatedBeanDefinition) {
                    // verify annotated class is an interface
                    // 驗(yàn)證帶注釋的類必須是接口
                    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);
                    //注冊被調(diào)用客戶端配置
                    //注冊(微服務(wù)名).FeignClientSpecification類型的bean
                    //beanname: order-center.FeignClientSpecification
                    registerClientConfiguration(registry, name,
                            attributes.get("configuration"));
                     //注冊 FeignClient
                    registerFeignClient(registry, annotationMetadata, attributes);
                }
            }
        }
    }
}

registerFeignClient

  • 注冊 FeignClient盈咳,組裝BeanDefinition耿眉,實(shí)質(zhì)是一個(gè)FeignClientFactoryBean,然后注冊到Spring IOC容器鱼响。
class FeignClientsRegistrar
        implements ImportBeanDefinitionRegistrar, ResourceLoaderAware, EnvironmentAware {
        
    private void registerFeignClient(BeanDefinitionRegistry registry,
            AnnotationMetadata annotationMetadata, Map<String, Object> attributes) {
        String className = annotationMetadata.getClassName();
        //構(gòu)建FeignClientFactoryBean類型的BeanDefinitionBuilder
        BeanDefinitionBuilder definition = BeanDefinitionBuilder
                .genericBeanDefinition(FeignClientFactoryBean.class);
        validate(attributes);
        //將屬性設(shè)置到 FeignClientFactoryBean 中
        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"));
        //設(shè)置 Autowire注入的類型鸣剪,按類型注入
        definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);

        String alias = contextId + "FeignClient";
        AbstractBeanDefinition beanDefinition = definition.getBeanDefinition();
        beanDefinition.setAttribute(FactoryBean.OBJECT_TYPE_ATTRIBUTE, className);

        // has a default, won't be null
        boolean primary = (Boolean) attributes.get("primary");

        beanDefinition.setPrimary(primary);

        String qualifier = getQualifier(attributes);
        if (StringUtils.hasText(qualifier)) {
            alias = qualifier;
        }
        //將BeanDefinition包裝成BeanDefinitionHolder,用于注冊
        BeanDefinitionHolder holder = new BeanDefinitionHolder(beanDefinition, className,
                new String[] { alias });
        //注冊 BeanDefinition     
        BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry);
    }
}   

至此,注冊完成筐骇,注意此時(shí)僅僅只是注冊到 DefaultListableBeanFactory容器的 beanDefinitionMap中债鸡,并沒有實(shí)例化!

實(shí)例化

  • Spring容器啟動(dòng)铛纬,調(diào)用AbstractApplicationContext#refresh方法厌均,
  • 在refresh方法內(nèi)部調(diào)用finishBeanFactoryInitialization方法對單例bean進(jìn)行初始化,
  • finishBeanFactoryInitialization方法調(diào)用getBean獲取name對應(yīng)的bean實(shí)例,如果不存在,則創(chuàng)建一個(gè)溯泣,即調(diào)用doGetBean方法。
  • doGetBean調(diào)用createBean方法模她,createBean方法調(diào)用doCreateBean方法。
  • doCreateBean()方法主要是根據(jù) beanName懂牧、mbd侈净、args,使用對應(yīng)的策略創(chuàng)建 bean 實(shí)例归苍,并返回包裝類 BeanWrapper用狱。
  • doCreateBean方法中調(diào)用populateBean對 bean 進(jìn)行屬性填充;其中拼弃,可能存在依賴于其他 bean 的屬性夏伊,則會遞歸初始化依賴的 bean 實(shí)例。

getObjectForBeanInstance

在doGetBean方法中會調(diào)用getObjectForBeanInstance方法獲取beanName對應(yīng)的實(shí)例對象(主要用于FactoryBean的特殊處理吻氧,普通Bean會直接返回sharedInstance本身)

public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport implements ConfigurableBeanFactory {

    protected Object getObjectForBeanInstance(
            Object beanInstance, String name, String beanName, @Nullable RootBeanDefinition mbd) {

        // Don't let calling code try to dereference the factory if the bean isn't a factory.
        if (BeanFactoryUtils.isFactoryDereference(name)) {
            if (beanInstance instanceof NullBean) {
                return beanInstance;
            }
            // 1.如果name以“&”為前綴溺忧,但是beanInstance不是FactoryBean,則拋異常
            if (!(beanInstance instanceof FactoryBean)) {
                throw new BeanIsNotAFactoryException(beanName, beanInstance.getClass());
            }
            if (mbd != null) {
                mbd.isFactoryBean = true;
            }
            return beanInstance;
        }

        // Now we have the bean instance, which may be a normal bean or a FactoryBean.
        // If it's a FactoryBean, we use it to create a bean instance, unless the
        // caller actually wants a reference to the factory.
        // 2.1 如果beanInstance不是FactoryBean(也就是普通bean)盯孙,則直接返回beanInstance
        if (!(beanInstance instanceof FactoryBean)) {
            return beanInstance;
        }
        //FactoryBean創(chuàng)建出bean實(shí)例返回
        // 3.走到這邊鲁森,代表beanInstance是FactoryBean,但name不帶有“&”前綴振惰,表示想要獲取的是FactoryBean創(chuàng)建的對象實(shí)例
        Object object = null;
        if (mbd != null) {
            mbd.isFactoryBean = true;
        }
        else {
            // 4.如果mbd為空歌溉,則嘗試從factoryBeanObjectCache緩存中獲取該FactoryBean創(chuàng)建的對象實(shí)例
            object = getCachedObjectForFactoryBean(beanName);
        }
        if (object == null) {
            // Return bean instance from factory.
            // 5.只有beanInstance是FactoryBean才能走到這邊,因此直接強(qiáng)轉(zhuǎn)
            FactoryBean<?> factory = (FactoryBean<?>) beanInstance;
            // Caches object obtained from FactoryBean if it is a singleton.
            if (mbd == null && containsBeanDefinition(beanName)) {
                // 6.mbd為空骑晶,但是該bean的BeanDefinition在緩存中存在痛垛,則獲取該bean的MergedBeanDefinition
                mbd = getMergedLocalBeanDefinition(beanName);
            }
            // 7.mbd是否是合成的(這個(gè)字段比較復(fù)雜,mbd正常情況都不是合成的桶蛔,也就是false匙头,有興趣的可以自己查閱資料看看)
            boolean synthetic = (mbd != null && mbd.isSynthetic());
            // 8.從FactoryBean獲取對象實(shí)例
            object = getObjectFromFactoryBean(factory, beanName, !synthetic);
        }
        // 9.返回對象實(shí)例
        return object;
    }
}

getObjectFromFactoryBean

從FactoryBean獲取對象實(shí)例
getObjectForBeanInstance方法中會調(diào)用getObjectFromFactoryBean從FactoryBean獲取對象實(shí)例,即調(diào)用FactoryBean的getObject方法獲取對象實(shí)例仔雷。

public abstract class FactoryBeanRegistrySupport extends DefaultSingletonBeanRegistry {

    protected Object getObjectFromFactoryBean(FactoryBean<?> factory, String beanName, boolean shouldPostProcess) {
        // 1.如果是單例蹂析,并且已經(jīng)存在于單例對象緩存中
        if (factory.isSingleton() && containsSingleton(beanName)) {
            //又見雙重檢查鎖機(jī)制舔示,嘗試再從緩存中獲取,防止多線程下可能有別的線程已完成該單例Bean的創(chuàng)建
            synchronized (getSingletonMutex()) {
                // 2.從FactoryBean創(chuàng)建的單例對象的緩存中獲取該bean實(shí)例
                Object object = this.factoryBeanObjectCache.get(beanName);
                if (object == null) {
                    // 3.調(diào)用FactoryBean的getObject方法獲取對象實(shí)例
                    object = doGetObjectFromFactoryBean(factory, beanName);
                    // Only post-process and store if not put there already during getObject() call above
                    // (e.g. because of circular reference processing triggered by custom getBean calls)
                    Object alreadyThere = this.factoryBeanObjectCache.get(beanName);
                    // 4.如果該beanName已經(jīng)在緩存中存在电抚,則將object替換成緩存中的
                    if (alreadyThere != null) {
                        object = alreadyThere;
                    }
                    else {
                        if (shouldPostProcess) {
                            if (isSingletonCurrentlyInCreation(beanName)) {
                                // Temporarily return non-post-processed object, not storing it yet..
                                return object;
                            }
                            beforeSingletonCreation(beanName);
                            try {
                                // 5.對bean實(shí)例進(jìn)行后置處理惕稻,執(zhí)行所有已注冊的BeanPostProcessor的postProcessAfterInitialization方法
                                //觸發(fā)BeanPostProcessor,第三方框架可以在此用AOP來包裝Bean實(shí)例
                                object = postProcessObjectFromFactoryBean(object, beanName);
                            }
                            catch (Throwable ex) {
                                throw new BeanCreationException(beanName,
                                        "Post-processing of FactoryBean's singleton object failed", ex);
                            }
                            finally {
                                //創(chuàng)建完成后喻频,從緩存鎖定的名字里清除
                                afterSingletonCreation(beanName);
                            }
                        }
                        if (containsSingleton(beanName)) {
                            // 6.將beanName和object放到factoryBeanObjectCache緩存中
                            this.factoryBeanObjectCache.put(beanName, object);
                        }
                    }
                }
                // 7.返回object對象實(shí)例
                return object;
            }
        }
        else {
            // 8.調(diào)用FactoryBean的getObject方法獲取對象實(shí)例
            Object object = doGetObjectFromFactoryBean(factory, beanName);
            if (shouldPostProcess) {
                try {
                    // 9.對bean實(shí)例進(jìn)行后置處理缩宜,執(zhí)行所有已注冊的BeanPostProcessor的postProcessAfterInitialization方法
                    object = postProcessObjectFromFactoryBean(object, beanName);
                }
                catch (Throwable ex) {
                    throw new BeanCreationException(beanName, "Post-processing of FactoryBean's object failed", ex);
                }
            }
            // 10.返回object對象實(shí)例
            return object;
        }
    }
}

主要步驟:

  • 調(diào)用 FactoryBean 的 getObject 方法獲取對象實(shí)例肘迎。
  • 對 bean 實(shí)例進(jìn)行后續(xù)處理甥温,執(zhí)行所有已注冊的 BeanPostProcessor 的 postProcessAfterInitialization 方法。

doGetObjectFromFactoryBean

用FactoryBean的getObject方法獲取對象實(shí)例

public abstract class FactoryBeanRegistrySupport extends DefaultSingletonBeanRegistry {

    private Object doGetObjectFromFactoryBean(final FactoryBean<?> factory, final String beanName)
            throws BeanCreationException {

        Object object;
        try {
            // 1.調(diào)用FactoryBean的getObject方法獲取bean對象實(shí)例
            if (System.getSecurityManager() != null) {
                AccessControlContext acc = getAccessControlContext();
                try {
                    // 1.1 帶有權(quán)限驗(yàn)證的
                    object = AccessController.doPrivileged((PrivilegedExceptionAction<Object>) factory::getObject, acc);
                }
                catch (PrivilegedActionException pae) {
                    throw pae.getException();
                }
            }
            else {
                // 1.2 不帶權(quán)限
                object = factory.getObject();
            }
        }
        catch (FactoryBeanNotInitializedException ex) {
            throw new BeanCurrentlyInCreationException(beanName, ex.toString());
        }
        catch (Throwable ex) {
            throw new BeanCreationException(beanName, "FactoryBean threw exception on object creation", ex);
        }

        // Do not accept a null value for a FactoryBean that's not fully
        // initialized yet: Many FactoryBeans just return null then.
        // 2.getObject返回的是空值妓布,并且該FactoryBean正在初始化中姻蚓,則直接拋異常,
        // 不接受一個(gè)尚未完全初始化的FactoryBean的getObject返回的空值
        if (object == null) {
            if (isSingletonCurrentlyInCreation(beanName)) {
                throw new BeanCurrentlyInCreationException(
                        beanName, "FactoryBean which is currently in creation returned null from getObject");
            }
            object = new NullBean();
        }
        // 3.返回創(chuàng)建好的bean對象實(shí)例
        return object;
    }
}

很簡單的方法匣沼,就是直接調(diào)用 FactoryBean 的 getObject 方法來獲取到對象實(shí)例狰挡。

細(xì)心的同學(xué)可以發(fā)現(xiàn),該方法是以 do 開頭释涛,以 do 開頭的方法是最終進(jìn)行實(shí)際操作的方法加叁,例如本方法就是 FactoryBean 最終實(shí)際進(jìn)行創(chuàng)建 bean 對象實(shí)例的方法。

factory.getObject()

該方法會調(diào)用到FeignClientFactoryBean的getObject方法

class FeignClientFactoryBean
        implements FactoryBean<Object>, InitializingBean, ApplicationContextAware {
        
    @Override
    public Object getObject() throws Exception {
        return getTarget();
    }
}

getObject調(diào)用的是getTarget方法唇撬,它從applicationContext取出FeignContext它匕,F(xiàn)eignContext繼承了NamedContextFactory,它是用來統(tǒng)一維護(hù)feign中各個(gè)feign客戶端相互隔離的上下文窖认。

FeignContext注冊到容器是在FeignAutoConfiguration上完成的豫柬。

@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(Feign.class)
@EnableConfigurationProperties({ FeignClientProperties.class,
        FeignHttpClientProperties.class })
@Import(DefaultGzipDecoderConfiguration.class)
public class FeignAutoConfiguration {

    @Autowired(required = false)
    private List<FeignClientSpecification> configurations = new ArrayList<>();
    
    @Bean
    public FeignContext feignContext() {
        FeignContext context = new FeignContext();
        context.setConfigurations(this.configurations);
        return context;
    }
}   

在初始化FeignContext時(shí),會把configurations在容器中放入FeignContext中扑浸。configurations 的來源就是在前面registerFeignClients方法中將@FeignClient的配置 configuration烧给。

FeignClientFactoryBean#getTarget

  • 構(gòu)建feign.builder,在構(gòu)建時(shí)會向FeignContext獲取配置的Encoder喝噪,Decoder等各種信息础嫡。FeignContext在上文中已經(jīng)提到會為每個(gè)Feign客戶端分配了一個(gè)容器,它們的父容器就是spring容器酝惧。

  • 配置完Feign.Builder之后榴鼎,再判斷是否需要LoadBalance,如果需要系奉,則通過LoadBalance的方法來設(shè)置檬贰。實(shí)際上他們最終調(diào)用的是Target.target()方法。

class FeignClientFactoryBean
        implements FactoryBean<Object>, InitializingBean, ApplicationContextAware {
        
    <T> T getTarget() {
        //實(shí)例化Feign上下文對象FeignContext
        FeignContext context = applicationContext.getBean(FeignContext.class);
        //生成Builder對象缺亮,用來生成Feign
        Feign.Builder builder = feign(context);
        
        //如果url為空翁涤,則走負(fù)載均衡桥言,生成有負(fù)載均衡功能的代理類
        if (!StringUtils.hasText(url)) {
            if (!name.startsWith("http")) {
                url = "http://" + name;
            }
            else {
                url = name;
            }
            url += cleanPath();
            //@FeignClient沒有配置url屬性,返回有負(fù)載均衡功能的代理對象
            return (T) loadBalance(builder, context,
                    new HardCodedTarget<>(type, name, url));
        }
        //如果指定了url葵礼,則生成默認(rèn)的代理類
        if (StringUtils.hasText(url) && !url.startsWith("http")) {
            url = "http://" + url;
        }
        String url = this.url + cleanPath();
        Client client = getOptional(context, Client.class);
        if (client != null) {
            if (client instanceof LoadBalancerFeignClient) {
                // not load balancing because we have a url,
                // but ribbon is on the classpath, so unwrap
                client = ((LoadBalancerFeignClient) client).getDelegate();
            }
            if (client instanceof FeignBlockingLoadBalancerClient) {
                // not load balancing because we have a url,
                // but Spring Cloud LoadBalancer is on the classpath, so unwrap
                client = ((FeignBlockingLoadBalancerClient) client).getDelegate();
            }
            builder.client(client);
        }
        Targeter targeter = get(context, Targeter.class);
        
        //生成默認(rèn)代理類
        return (T) targeter.target(this, builder, context,
                new HardCodedTarget<>(type, name, url));
    }
}

feign(context)

構(gòu)建feign.builder号阿,在構(gòu)建時(shí)會向FeignContext獲取配置的Encoder,Decoder等各種信息鸳粉。FeignContext在上文中已經(jīng)提到會為每個(gè)Feign客戶端分配了一個(gè)容器扔涧,它們的父容器就是spring容器。

class FeignClientFactoryBean
        implements FactoryBean<Object>, InitializingBean, ApplicationContextAware {
        
    protected Feign.Builder feign(FeignContext context) {
        FeignLoggerFactory loggerFactory = get(context, FeignLoggerFactory.class);
        Logger logger = loggerFactory.create(type);

        // @formatter:off
        Feign.Builder builder = get(context, Feign.Builder.class)
                // required values
                .logger(logger) //日志
                .encoder(get(context, Encoder.class))   //編碼器
                .decoder(get(context, Decoder.class))   //解碼器
                .contract(get(context, Contract.class));//驗(yàn)證器
        // @formatter:on
        //處理了鏈接超時(shí)届谈、讀取超時(shí)等配置項(xiàng)
        configureFeign(context, builder);

        return builder;
    }
}

FeignClientFactoryBean#loadBalance

  • 生成具備負(fù)載均衡能力的feign客戶端枯夜,為feign客戶端構(gòu)建起綁定負(fù)載均衡客戶端
class FeignClientFactoryBean
        implements FactoryBean<Object>, InitializingBean, ApplicationContextAware {
        
    protected <T> T loadBalance(Feign.Builder builder, FeignContext context,
            HardCodedTarget<T> target) {
        Client client = getOptional(context, Client.class);
        if (client != null) {
            builder.client(client);
            Targeter targeter = get(context, Targeter.class);
            return targeter.target(this, builder, context, target);
        }

        throw new IllegalStateException(
                "No Feign Client for loadBalancing defined. Did you forget to include spring-cloud-starter-netflix-ribbon?");
    }
}   

FeignClientFactoryBean#getOptional(context, Client.class)

Client client = (Client)this.getOptional(context, Client.class); 從上下文中獲取一個(gè) Client,默認(rèn)是LoadBalancerFeignClient艰山。它是在FeignRibbonClientAutoConfiguration這個(gè)自動(dòng)裝配類中湖雹,通過Import實(shí)現(xiàn)的

@Import({ HttpClientFeignLoadBalancedConfiguration.class,
        OkHttpFeignLoadBalancedConfiguration.class,
        DefaultFeignLoadBalancedConfiguration.class })
public class FeignRibbonClientAutoConfiguration {
    //......
}

DefaultFeignLoadBalancedConfiguration

這里的通過 DefaultFeignLoadBalancedConfiguration 注入客戶端 Client 的實(shí)現(xiàn)

@Configuration(proxyBeanMethods = false)
class DefaultFeignLoadBalancedConfiguration {

    @Bean
    @ConditionalOnMissingBean
    public Client feignClient(CachingSpringLoadBalancerFactory cachingFactory,
            SpringClientFactory clientFactory) {
        return new LoadBalancerFeignClient(new Client.Default(null, null), cachingFactory,
                clientFactory);
    }

}

targeter.target

接下去進(jìn)入targeter.target(this, builder, context, target) ,攜帶著構(gòu)建好的這些對象去創(chuàng)建代理實(shí)例 曙搬,這里有兩個(gè)實(shí)現(xiàn) HystrixTargeter 摔吏、DefaultTargeter 很顯然,我們沒有配置 Hystrix 纵装,這里會走 DefaultTargeter

class DefaultTargeter implements Targeter {

    @Override
    public <T> T target(FeignClientFactoryBean factory, Feign.Builder feign,
            FeignContext context, Target.HardCodedTarget<T> target) {
        return feign.target(target);
    }

}

feign.target

然后會來到 feign.Feign.Builder#target(feign.Target<T>)

public abstract class Feign {

    public <T> T target(Target<T> target) {
        return build().newInstance(target);
    }
    
    public Feign build() {
        Client client = Capability.enrich(this.client, capabilities);
        Retryer retryer = Capability.enrich(this.retryer, capabilities);
        List<RequestInterceptor> requestInterceptors = this.requestInterceptors.stream()
                .map(ri -> Capability.enrich(ri, capabilities))
                .collect(Collectors.toList());
        Logger logger = Capability.enrich(this.logger, capabilities);
        Contract contract = Capability.enrich(this.contract, capabilities);
        Options options = Capability.enrich(this.options, capabilities);
        Encoder encoder = Capability.enrich(this.encoder, capabilities);
        Decoder decoder = Capability.enrich(this.decoder, capabilities);
        InvocationHandlerFactory invocationHandlerFactory =
                Capability.enrich(this.invocationHandlerFactory, capabilities);
        QueryMapEncoder queryMapEncoder = Capability.enrich(this.queryMapEncoder, capabilities);

        SynchronousMethodHandler.Factory synchronousMethodHandlerFactory =
                new SynchronousMethodHandler.Factory(client, retryer, requestInterceptors, logger,
                logLevel, decode404, closeAfterDecode, propagationPolicy, forceDecoding);
        ParseHandlersByName handlersByName =
                new ParseHandlersByName(contract, options, encoder, decoder, queryMapEncoder,
                errorDecoder, synchronousMethodHandlerFactory);
        return new ReflectiveFeign(handlersByName, invocationHandlerFactory, queryMapEncoder);
    }
}

ReflectiveFeign.newInstance

最終會調(diào)用 ReflectiveFeign.newInstance

這個(gè)方法是用來創(chuàng)建一個(gè)動(dòng)態(tài)代理的方法征讲,在生成動(dòng)態(tài)代理之前,會根據(jù)Contract協(xié)議(協(xié)議解析規(guī)則橡娄,解析接口類的注解信息诗箍,解析成內(nèi)部的MethodHandler的處理方式。

從實(shí)現(xiàn)的代碼中可以看到熟悉的Proxy.newProxyInstance方法產(chǎn)生代理類瀑踢。而這里需要對每個(gè)定義的接口方法進(jìn)行特定的處理實(shí)現(xiàn)扳还,所以這里會出現(xiàn)一個(gè)MethodHandler的概念,就是對應(yīng)方法級別的InvocationHandler橱夭。

public class ReflectiveFeign extends Feign {

    @Override
    public <T> T newInstance(Target<T> target) {
        // 解析接口注解信息
        //根據(jù)接口類和Contract協(xié)議解析方式氨距,解析接口類上的方法和注解,轉(zhuǎn)換成內(nèi)部的MethodHandler處理方式
        Map<String, MethodHandler> nameToHandler = targetToHandlersByName.apply(target);
        Map<Method, MethodHandler> methodToHandler = new LinkedHashMap<Method, MethodHandler>();
        List<DefaultMethodHandler> defaultMethodHandlers = new LinkedList<DefaultMethodHandler>();
        
        // 根據(jù)方法類型
        for (Method method : target.type().getMethods()) {
            if (method.getDeclaringClass() == Object.class) {
                continue;
            } else if (Util.isDefault(method)) {
                DefaultMethodHandler handler = new DefaultMethodHandler(method);
                defaultMethodHandlers.add(handler);
                methodToHandler.put(method, handler);
            } else {
                methodToHandler.put(method, nameToHandler.get(Feign.configKey(target.type(), method)));
            }
        }
        InvocationHandler handler = factory.create(target, methodToHandler);
        
        // 基于Proxy.newProxyInstance 為接口類創(chuàng)建動(dòng)態(tài)實(shí)現(xiàn)棘劣,將所有的請求轉(zhuǎn)換給InvocationHandler 處理俏让。
        T proxy = (T) Proxy.newProxyInstance(target.type().getClassLoader(),
                new Class<?>[] {target.type()}, handler);

        for (DefaultMethodHandler defaultMethodHandler : defaultMethodHandlers) {
            defaultMethodHandler.bindTo(proxy);
        }
        return proxy;
    }
}

targetToHandlersByName.apply

targetToHandlersByName.apply(target) :根據(jù)Contract協(xié)議規(guī)則,解析接口類的注解信息茬暇,解析成內(nèi)部表現(xiàn):targetToHandlersByName.apply(target);會解析接口方法上的注解首昔,從而解析出方法粒度的特定的配置信息,然后生產(chǎn)一個(gè)SynchronousMethodHandler 然后需要維護(hù)一個(gè)<method糙俗,MethodHandler>的map勒奇,放入InvocationHandler的實(shí)現(xiàn)FeignInvocationHandler中。

public class ReflectiveFeign extends Feign {

    static final class ParseHandlersByName {
        public Map<String, MethodHandler> apply(Target target) {
            List<MethodMetadata> metadata = contract.parseAndValidateMetadata(target.type());
            Map<String, MethodHandler> result = new LinkedHashMap<String, MethodHandler>();
            for (MethodMetadata md : metadata) {
                BuildTemplateByResolvingArgs buildTemplate;
                if (!md.formParams().isEmpty() && md.template().bodyTemplate() == null) {
                buildTemplate =
                        new BuildFormEncodedTemplateFromArgs(md, encoder, queryMapEncoder, target);
                } else if (md.bodyIndex() != null) {
                    buildTemplate = new BuildEncodedTemplateFromArgs(md, encoder, queryMapEncoder, target);
                } else {
                    buildTemplate = new BuildTemplateByResolvingArgs(md, queryMapEncoder, target);
                }
                if (md.isIgnored()) {
                    result.put(md.configKey(), args -> {
                        throw new IllegalStateException(md.configKey() + " is not a method handled by feign");
                    });
                } else {
                    result.put(md.configKey(),
                        factory.create(target, md, buildTemplate, options, decoder, errorDecoder));
                }
            }
            return result;
        }
    }
}  

SpringMvcContract:當(dāng)前Spring Cloud 微服務(wù)解決方案中巧骚,為了降低學(xué)習(xí)成本赊颠,采用了Spring MVC的部分注解來完成 請求協(xié)議解析格二,也就是說 ,寫客戶端請求接口和像寫服務(wù)端代碼一樣:客戶端和服務(wù)端可以通過SDK的方式進(jìn)行約定竣蹦,客戶端只需要引入服務(wù)端發(fā)布的SDK API顶猜,就可以使用面向接口的編碼方式對接服務(wù)。

OpenFeign調(diào)用過程 :

在前面的分析中痘括,我們知道OpenFeign最終返回的是一個(gè) ReflectiveFeign.FeignInvocationHandler 的對象长窄。那么當(dāng)客戶端發(fā)起請求時(shí),會進(jìn)入到 FeignInvocationHandler.invoke 方法中纲菌,這個(gè)大家都知道挠日,它是一個(gè)動(dòng)態(tài)代理的實(shí)現(xiàn)。

public class ReflectiveFeign extends Feign {

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        if ("equals".equals(method.getName())) {
            try {
                Object otherHandler =
                        args.length > 0 && args[0] != null ? Proxy.getInvocationHandler(args[0]) : null;
                return equals(otherHandler);
            } catch (IllegalArgumentException e) {
                return false;
            }
        } else if ("hashCode".equals(method.getName())) {
            return hashCode();
        } else if ("toString".equals(method.getName())) {
            return toString();
        }
        // 利用分發(fā)器篩選方法驰后,找到對應(yīng)的handler 進(jìn)行處理
        return dispatch.get(method).invoke(args);
    }
}  

SynchronousMethodHandler#invoke

而接著肆资,在invoke方法中矗愧,會調(diào)用 this.dispatch.get(method)).invoke(args) 灶芝。this.dispatch.get(method) 會返回一個(gè)SynchronousMethodHandler,進(jìn)行攔截處理唉韭。這個(gè)方法會根據(jù)參數(shù)生成完成的RequestTemplate對象夜涕,這個(gè)對象是Http請求的模版,代碼如下属愤。

final class SynchronousMethodHandler implements MethodHandler {

    @Override
    public Object invoke(Object[] argv) throws Throwable {
        RequestTemplate template = buildTemplateFromArgs.create(argv);
        Options options = findOptions(argv);
        Retryer retryer = this.retryer.clone();
        while (true) {
            try {
                return executeAndDecode(template, options);
            } catch (RetryableException e) {
                try {
                    retryer.continueOrPropagate(e);
                } catch (RetryableException th) {
                    Throwable cause = th.getCause();
                    if (propagationPolicy == UNWRAP && cause != null) {
                        throw cause;
                    } else {
                        throw th;
                    }
                }
                if (logLevel != Logger.Level.NONE) {
                    logger.logRetry(metadata.configKey(), logLevel);
                }
                continue;
            }
        }
    }
}

SynchronousMethodHandler#executeAndDecode

經(jīng)過上述的代碼女器,我們已經(jīng)將restTemplate拼裝完成,上面的代碼中有一個(gè) executeAndDecode() 方法住诸,該方法通過RequestTemplate生成Request請求對象驾胆,然后利用Http Client獲取response,來獲取響應(yīng)信息贱呐。

final class SynchronousMethodHandler implements MethodHandler {

    Object executeAndDecode(RequestTemplate template, Options options) throws Throwable {
        //轉(zhuǎn)化為Http請求報(bào)文
        Request request = targetRequest(template);

        if (logLevel != Logger.Level.NONE) {
            logger.logRequest(metadata.configKey(), logLevel, request);
        }

        Response response;
        long start = System.nanoTime();
        try {
            //發(fā)起遠(yuǎn)程通信
            response = client.execute(request, options);
            
            //獲取返回結(jié)果
            response = response.toBuilder()
                    .request(request)
                    .requestTemplate(template)
                    .build();
        } catch (IOException e) {
            if (logLevel != Logger.Level.NONE) {
                logger.logIOException(metadata.configKey(), logLevel, e, elapsedTime(start));
            }
            throw errorExecuting(request, e);
        }
        long elapsedTime = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start);


        if (decoder != null)
            return decoder.decode(response, metadata.returnType());

        CompletableFuture<Object> resultFuture = new CompletableFuture<>();
        asyncResponseHandler.handleResponse(resultFuture, metadata.configKey(), response,
                    metadata.returnType(),elapsedTime);

        try {
            if (!resultFuture.isDone())
                throw new IllegalStateException("Response handling not done");

            return resultFuture.join();
        } catch (CompletionException e) {
            Throwable cause = e.getCause();
            if (cause != null)
                throw cause;
            throw e;
        }
    }
}

經(jīng)過上面的分析丧诺,這里的 client.execute 的 client 的類型是LoadBalancerFeignClient


LoadBalancerFeignClient#execute

這里就很自然的進(jìn)入 LoadBalancerFeignClient#execute

public class LoadBalancerFeignClient implements Client {

    @Override
    public Response execute(Request request, Request.Options options) throws IOException {
        try {
            URI asUri = URI.create(request.url());
            String clientName = asUri.getHost();
            URI uriWithoutHost = cleanUrl(request.url(), clientName);
            FeignLoadBalancer.RibbonRequest ribbonRequest = new FeignLoadBalancer.RibbonRequest(
                    this.delegate, request, uriWithoutHost);

            IClientConfig requestConfig = getClientConfig(options, clientName);
            return lbClient(clientName)
                    .executeWithLoadBalancer(ribbonRequest, requestConfig).toResponse();
        }
        catch (ClientException e) {
            IOException io = findIOException(e);
            if (io != null) {
                throw io;
            }
            throw new RuntimeException(e);
        }
    }
}

其實(shí)這個(gè)execute里面得流程就是 Ribbon 的那一套。我們可以簡單的看一下奄薇。首先是構(gòu)造URI驳阎,構(gòu)造RibbonRequest,選擇 LoadBalance馁蒂,發(fā)起調(diào)用呵晚。

來看一下lbClient 選擇負(fù)載均衡器的時(shí)候做了什么

public class LoadBalancerFeignClient implements Client {

    private FeignLoadBalancer lbClient(String clientName) {
        return this.lbClientFactory.create(clientName);
    }
    
    public FeignLoadBalancer create(String clientName) {
        FeignLoadBalancer client = this.cache.get(clientName);
        if (client != null) {
            return client;
        }
        IClientConfig config = this.factory.getClientConfig(clientName);
        ILoadBalancer lb = this.factory.getLoadBalancer(clientName);
        ServerIntrospector serverIntrospector = this.factory.getInstance(clientName,
                ServerIntrospector.class);
        client = this.loadBalancedRetryFactory != null
                ? new RetryableFeignLoadBalancer(lb, config, serverIntrospector,
                        this.loadBalancedRetryFactory)
                : new FeignLoadBalancer(lb, config, serverIntrospector);
        this.cache.put(clientName, client);
        return client;
    }   
}

可以得出的結(jié)論就是 this.factory.getLoadBalancer(clientName) 跟Ribbon 源碼里的獲取方式一樣,無疑這里獲取的就是默認(rèn)的 ZoneAwareLoadBalancer沫屡。然后包裝成一個(gè) FeignLoadBalancer 進(jìn)行返回饵隙。

既然負(fù)載均衡器選擇完了,那么一定還有個(gè)地方通過該負(fù)載去選擇一個(gè)服務(wù)沮脖,接著往下看:

public abstract class AbstractLoadBalancerAwareClient<S extends ClientRequest, T extends IResponse> extends LoadBalancerContext implements IClient<S, T>, IClientConfigAware {

    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) {
                        URI finalUri = reconstructURIWithServer(server, request.getUri());
                        S requestForServer = (S) request.replaceUri(finalUri);
                        try {
                            return Observable.just(AbstractLoadBalancerAwareClient.this.execute(requestForServer, requestConfig));
                        } 
                        catch (Exception e) {
                            return Observable.error(e);
                        }
                    }
                })
                .toBlocking()
                .single();
        } catch (Exception e) {
            Throwable t = e.getCause();
            if (t instanceof ClientException) {
                throw (ClientException) t;
            } else {
                throw new ClientException(e);
            }
        }
    }
}

上面這段代碼就是通過獲取到的負(fù)載進(jìn)行執(zhí)行請求金矛,但是這個(gè)時(shí)候 服務(wù)還沒有選擇劫瞳,我們跟進(jìn)去 submit 請求看一看究竟:

public class LoadBalancerCommand<T> {

    public Observable<T> submit(final ServerOperation<T> operation) {
        final ExecutionInfoContext context = new ExecutionInfoContext();
        
        if (listenerInvoker != null) {
            try {
                listenerInvoker.onExecutionStart();
            } catch (AbortExecutionException e) {
                return Observable.error(e);
            }
        }

        final int maxRetrysSame = retryHandler.getMaxRetriesOnSameServer();
        final int maxRetrysNext = retryHandler.getMaxRetriesOnNextServer();

        // Use the load balancer
        Observable<T> o = 
                (server == null ? selectServer() : Observable.just(server))
                .concatMap(new Func1<Server, Observable<T>>() {
                    //省略......
                });
            
        //省略......
    }
}

可以看到這里有個(gè) selectServer的方法 ,跟進(jìn)去:

public class LoadBalancerCommand<T> {

    private final LoadBalancerContext loadBalancerContext;

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


public class LoadBalancerContext implements IClientConfigAware {

    public Server getServerFromLoadBalancer(@Nullable URI original, @Nullable Object loadBalancerKey) throws ClientException {
        String host = null;
        int port = -1;
        if (original != null) {
            host = original.getHost();
        }
        if (original != null) {
            Pair<String, Integer> schemeAndPort = deriveSchemeAndPortFromPartialUri(original);        
            port = schemeAndPort.second();
        }

        // Various Supported Cases
        // The loadbalancer to use and the instances it has is based on how it was registered
        // In each of these cases, the client might come in using Full Url or Partial URL
        ILoadBalancer lb = getLoadBalancer();
        if (host == null) {
            // Partial URI or no URI Case
            // well we have to just get the right instances from lb - or we fall back
            if (lb != null){
                Server svc = lb.chooseServer(loadBalancerKey);
                if (svc == null){
                    throw new ClientException(ClientException.ErrorType.GENERAL,
                            "Load balancer does not have available server for client: "
                                    + clientName);
                }
                host = svc.getHost();
                if (host == null){
                    throw new ClientException(ClientException.ErrorType.GENERAL,
                            "Invalid Server for :" + svc);
                }
                logger.debug("{} using LB returned Server: {} for request {}", new Object[]{clientName, svc, original});
                return svc;
            } else {
                // No Full URL - and we dont have a LoadBalancer registered to
                // obtain a server
                // if we have a vipAddress that came with the registration, we
                // can use that else we
                // bail out
                if (vipAddresses != null && vipAddresses.contains(",")) {
                    throw new ClientException(
                            ClientException.ErrorType.GENERAL,
                            "Method is invoked for client " + clientName + " with partial URI of ("
                            + original
                            + ") with no load balancer configured."
                            + " Also, there are multiple vipAddresses and hence no vip address can be chosen"
                            + " to complete this partial uri");
                } else if (vipAddresses != null) {
                    try {
                        Pair<String,Integer> hostAndPort = deriveHostAndPortFromVipAddress(vipAddresses);
                        host = hostAndPort.first();
                        port = hostAndPort.second();
                    } catch (URISyntaxException e) {
                        throw new ClientException(
                                ClientException.ErrorType.GENERAL,
                                "Method is invoked for client " + clientName + " with partial URI of ("
                                + original
                                + ") with no load balancer configured. "
                                + " Also, the configured/registered vipAddress is unparseable (to determine host and port)");
                    }
                } else {
                    throw new ClientException(
                            ClientException.ErrorType.GENERAL,
                            this.clientName
                            + " has no LoadBalancer registered and passed in a partial URL request (with no host:port)."
                            + " Also has no vipAddress registered");
                }
            }
        } else {
            // Full URL Case
            // This could either be a vipAddress or a hostAndPort or a real DNS
            // if vipAddress or hostAndPort, we just have to consult the loadbalancer
            // but if it does not return a server, we should just proceed anyways
            // and assume its a DNS
            // For restClients registered using a vipAddress AND executing a request
            // by passing in the full URL (including host and port), we should only
            // consult lb IFF the URL passed is registered as vipAddress in Discovery
            boolean shouldInterpretAsVip = false;

            if (lb != null) {
                shouldInterpretAsVip = isVipRecognized(original.getAuthority());
            }
            if (shouldInterpretAsVip) {
                Server svc = lb.chooseServer(loadBalancerKey);
                if (svc != null){
                    host = svc.getHost();
                    if (host == null){
                        throw new ClientException(ClientException.ErrorType.GENERAL,
                                "Invalid Server for :" + svc);
                    }
                    logger.debug("using LB returned Server: {} for request: {}", svc, original);
                    return svc;
                } else {
                    // just fall back as real DNS
                    logger.debug("{}:{} assumed to be a valid VIP address or exists in the DNS", host, port);
                }
            } else {
                // consult LB to obtain vipAddress backed instance given full URL
                //Full URL execute request - where url!=vipAddress
                logger.debug("Using full URL passed in by caller (not using load balancer): {}", original);
            }
        }
        // end of creating final URL
        if (host == null){
            throw new ClientException(ClientException.ErrorType.GENERAL,"Request contains no HOST to talk to");
        }
        // just verify that at this point we have a full URL

        return new Server(host, port);
    }
}

可以看到的是這里獲取到了之前構(gòu)造好的 ZoneAwareLoadBalancer 然后調(diào)用 chooseServer 方法獲取server 绷柒,這個(gè)是跟Ribbon 中是一樣的流程志于。

獲取到了server 后,會回調(diào)先前 executeWithLoadBalancer 方法里構(gòu)造的 ServerOperation 的 call 方法:

return command.submit(
    new ServerOperation<T>() {
        @Override
        public Observable<T> call(Server server) {
            URI finalUri = reconstructURIWithServer(server, request.getUri());
            S requestForServer = (S) request.replaceUri(finalUri);
            try {
                return Observable.just(AbstractLoadBalancerAwareClient.this.execute(requestForServer, requestConfig));
            } 
            catch (Exception e) {
                return Observable.error(e);
            }
        }
    })
    .toBlocking()
    .single();

然后會執(zhí)行 AbstractLoadBalancerAwareClient.this.execute(requestForServer, requestConfig) 進(jìn)行最后的調(diào)用废睦,實(shí)際上這里走的是 FeignLoadBalancer#execute

public class FeignLoadBalancer extends
        AbstractLoadBalancerAwareClient<FeignLoadBalancer.RibbonRequest, FeignLoadBalancer.RibbonResponse> {

    @Override
    public RibbonResponse execute(RibbonRequest request, IClientConfig configOverride)
            throws IOException {
        Request.Options options;
        if (configOverride != null) {
            RibbonProperties override = RibbonProperties.from(configOverride);
            options = new Request.Options(override.connectTimeout(this.connectTimeout),
                    override.readTimeout(this.readTimeout));
        }
        else {
            options = new Request.Options(this.connectTimeout, this.readTimeout);
        }
        Response response = request.client().execute(request.toRequest(), options);
        return new RibbonResponse(request.getUri(), response);
    }
}

而這里調(diào)用的request.client().execute(request.toRequest(), options) 則是 DefaultFeignLoadBalancedConfiguration 注入的 LoadBalancerFeignClient 伺绽,在構(gòu)造 LoadBalancerFeignClient 的時(shí)候 ,傳遞了個(gè) feign.Client.Default 嗜湃,然后利用 feign.Client.Default 構(gòu)造了一個(gè) RibbonRequest奈应。

所以這里走 feign.Client.Default#execute :

public interface Client {

    class Default implements Client {
        @Override
        public Response execute(Request request, Options options) throws IOException {
          HttpURLConnection connection = convertAndSend(request, options);
          return convertResponse(connection, request);
        }
    }
}

利用 JDK 提供的 HttpURLConnection 發(fā)起遠(yuǎn)程的 HTTP通訊。至此發(fā)起請求的流程就完成了购披。下面附上一張這個(gè)過程的流程圖:

對于Ribbon的調(diào)用過程請參考:http://www.reibang.com/p/f3db11f045cc

OpenFeign Configuration :

針對 feign 的 Configuration杖挣,官方給我們提供了很多的個(gè)性化配置,具體可以參考 org.springframework.cloud.openfeign.FeignClientProperties.FeignClientConfiguration

public static class FeignClientConfiguration {

    // 日志
    private Logger.Level loggerLevel;
    // 連接超時(shí)
    private Integer connectTimeout;

    private Integer readTimeout;
    //重試
    private Class<Retryer> retryer;
    //解碼
    private Class<ErrorDecoder> errorDecoder;

    private List<Class<RequestInterceptor>> requestInterceptors;
    // 編碼
    private Boolean decode404;

    private Class<Decoder> decoder;

    private Class<Encoder> encoder;
    // 解析
    private Class<Contract> contract;

    private ExceptionPropagationPolicy exceptionPropagationPolicy;
}

這里舉個(gè)簡單的例子刚陡,以Logger 為例惩妇。我們想為每個(gè)不同的 FeignClient 設(shè)置日志級別。

1筐乳、添加配置類:

@Configuration
public class FooConfiguration {
    @Bean
    Logger.Level feignLoggerLevel() {
        return Logger.Level.FULL;
    }
}

2歌殃、配置日志級別 ,logging.level + FeignClient 包的全路徑。

logging.level.com.wuzz.FeignClientService: DEBUG

就這樣就配置完成了蝙云。重啟服務(wù)就可以看到效果氓皱。

參考:
https://www.cnblogs.com/lucky-yqy/p/14589434.html

https://www.cnblogs.com/wuzhenzhao/p/13680807.html

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市勃刨,隨后出現(xiàn)的幾起案子波材,更是在濱河造成了極大的恐慌,老刑警劉巖身隐,帶你破解...
    沈念sama閱讀 212,816評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件廷区,死亡現(xiàn)場離奇詭異,居然都是意外死亡抡医,警方通過查閱死者的電腦和手機(jī)躲因,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,729評論 3 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來忌傻,“玉大人大脉,你說我怎么就攤上這事∷ⅲ” “怎么了镰矿?”我有些...
    開封第一講書人閱讀 158,300評論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長俘种。 經(jīng)常有香客問我秤标,道長绝淡,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,780評論 1 285
  • 正文 為了忘掉前任苍姜,我火速辦了婚禮牢酵,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘衙猪。我一直安慰自己馍乙,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,890評論 6 385
  • 文/花漫 我一把揭開白布垫释。 她就那樣靜靜地躺著丝格,像睡著了一般。 火紅的嫁衣襯著肌膚如雪棵譬。 梳的紋絲不亂的頭發(fā)上显蝌,一...
    開封第一講書人閱讀 50,084評論 1 291
  • 那天,我揣著相機(jī)與錄音订咸,去河邊找鬼曼尊。 笑死,一個(gè)胖子當(dāng)著我的面吹牛算谈,可吹牛的內(nèi)容都是我干的涩禀。 我是一名探鬼主播,決...
    沈念sama閱讀 39,151評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼然眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了葵腹?” 一聲冷哼從身側(cè)響起高每,我...
    開封第一講書人閱讀 37,912評論 0 268
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎践宴,沒想到半個(gè)月后鲸匿,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,355評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡阻肩,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,666評論 2 327
  • 正文 我和宋清朗相戀三年带欢,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片烤惊。...
    茶點(diǎn)故事閱讀 38,809評論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡乔煞,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出柒室,到底是詐尸還是另有隱情渡贾,我是刑警寧澤,帶...
    沈念sama閱讀 34,504評論 4 334
  • 正文 年R本政府宣布雄右,位于F島的核電站空骚,受9級特大地震影響纺讲,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜囤屹,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,150評論 3 317
  • 文/蒙蒙 一熬甚、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧肋坚,春花似錦则涯、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,882評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至峦剔,卻和暖如春档礁,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背吝沫。 一陣腳步聲響...
    開封第一講書人閱讀 32,121評論 1 267
  • 我被黑心中介騙來泰國打工呻澜, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人惨险。 一個(gè)月前我還...
    沈念sama閱讀 46,628評論 2 362
  • 正文 我出身青樓羹幸,卻偏偏與公主長得像,于是被迫代替她去往敵國和親辫愉。 傳聞我的和親對象是個(gè)殘疾皇子栅受,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,724評論 2 351

推薦閱讀更多精彩內(nèi)容