一甩卓、簡介
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ù)就可以看到效果氓皱。