深入分析Spring Cloud 負(fù)載均衡器架構(gòu)選型
我們這次項(xiàng)目主要從RestTemplate 和 Feign 進(jìn)行選型分析。
一、Spring Cloud Feign分析
Feign是另外一種客戶(hù)端負(fù)載均衡實(shí)現(xiàn)。
我在該模塊寫(xiě)了Feign Client的示例代碼。
【1】spring-cloud-web-demo-api為服務(wù)的sdk模塊
【2】spring-cloud-web-demo-service為提供接口服務(wù)的模塊
【3】spring-cloud-web-demo-client為模擬調(diào)用服務(wù)的模塊
首先在spring-cloud-web-demo-api模塊,定義Feign API。spring-cloud-web-demo為spring-cloud-web-demo-service暴露的服務(wù)名。
@FeignClient(value = "spring-cloud-web-demo")
public interface UserFeign {
@GetMapping(value = "/user/getUserById", produces = "application/json;charset=utf-8")
Object getUserById(@RequestParam(value = "id", required = false) Long id);
//省略
}然后通過(guò)ClientAutoConfiguration自動(dòng)裝配。(client直接引入api包就可以使用,不需要再EnableFeignClients)
@Configuration
@EnableFeignClients("net.teaho.demo.spring.cloud.web.api")
public class ClientAutoConfiguration {
}
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
net.teaho.demo.spring.cloud.web.api.config.ClientAutoConfiguration在service模塊如以往Spring MVC般實(shí)現(xiàn)api模塊接口即可。
@RestController
public class UserController implements UserFeign {
private static final Logger logger = LoggerFactory.getLogger(UserController.class);
@Override
public Object getUserById(Long id) {
return "{\"id\":1, \"name\": \"test\"}";
}
//省略
}在Client模塊,注入bean后直接調(diào)用。
@Component
@Slf4j
public class TestService {
@Autowired
private RestTemplate restTemplate;
public Object getOneUser(){
return userController.getUserById(1L);
}
}二、RestTemplate分析
寫(xiě)了具有客戶(hù)端負(fù)載均衡能力的RestTemplate的請(qǐng)求代碼。
類(lèi)似這樣定義:
@Bean
@LoadBalanced
public RestTemplate restTemplate() {
return new RestTemplate();
}RestTemplate究竟是如何利用注冊(cè)中心實(shí)現(xiàn)客戶(hù)端負(fù)載均衡的呢?
實(shí)現(xiàn)方式: 就是將上面所說(shuō)的LoadBalancerInterceptor負(fù)載均衡攔截器加到標(biāo)注了@LoadBalanced的RestTemplate實(shí)例中。 LoadBalancerInterceptor攔截器會(huì)在執(zhí)行過(guò)程中獲取并設(shè)置適合的目標(biāo)請(qǐng)求實(shí)例,重新構(gòu)造請(qǐng)求URI。
// 將配置中標(biāo)注了@LoadBalanced的RestTemplate注入到這里
@LoadBalanced
@Autowired(required = false)
private List<RestTemplate> restTemplates = Collections.emptyList();
//將注冊(cè)的RestTemplateCustomizer(RestTemplate自定義器)集合處理上面的restTemplates集合
@Bean
public SmartInitializingSingleton loadBalancedRestTemplateInitializerDeprecated(
final ObjectProvider<List<RestTemplateCustomizer>> restTemplateCustomizers) {
return () -> restTemplateCustomizers.ifAvailable(customizers -> {
for (RestTemplate restTemplate : LoadBalancerAutoConfiguration.this.restTemplates) {
for (RestTemplateCustomizer customizer : customizers) {
customizer.customize(restTemplate);
}
}
});
}三、技術(shù)選型
最終選擇使用OpenFeign,下面說(shuō)說(shuō)原因。
和RestTemplate比起來(lái),OpenFeign顯得更適合Spring Boot微服務(wù)。
Open Feign相當(dāng)于(HTTP)RPC,相比起RestTemplate,它直接顯式將API聲明以JAVA接口形式標(biāo)識(shí)出來(lái)。 并且因?yàn)榈讓佑玫膭?dòng)態(tài)代理,它還可以(無(wú)感知地)替換底層實(shí)現(xiàn)。比如,github上就有替換底層邏輯的repo – Open Feign+Dubbo的RPC實(shí)現(xiàn)。
通過(guò)sdk包的形式,方便了調(diào)用,不需要像RestTemplate一樣,客戶(hù)端自行拼接上一串請(qǐng)求參數(shù)。在代碼編寫(xiě)上也清晰。
要使用就必須知道OpenFeign是怎么實(shí)現(xiàn)的呢?
四、OpenFeign 初始化分析
流程圖如下:

看看前面例子里我們引入的OpenFeign的東西
【1】@EnableFeignClients(“net.teaho.demo.spring.cloud.web.api”)
【2】@FeignClient(value = “spring-cloud-web-demo”) 還有自動(dòng)裝配引入的
【3】FeignRibbonClientAutoConfiguration
【4】FeignClientsConfiguration
我們就從這兩個(gè)注解開(kāi)始分析源碼。
【1】首先看@FeignClient注解。
//給接口標(biāo)注成一個(gè)REST調(diào)用方
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface FeignClient {
//服務(wù)名,可以帶協(xié)議前綴,也可以用${property.name}關(guān)聯(lián)一個(gè)配置值。
@AliasFor("name")
String value() default "";
@Deprecated
String serviceId() default "";
//bean name
String contextId() default "";
@AliasFor("value")
String name() default "";
/**
* Sets the <code>@Qualifier</code> value for the feign client.
*/
String qualifier() default "";
//直接指定一個(gè)地址,比如http://localhost:12345,一般用于調(diào)試
String url() default "";
boolean decode404() default false;
/**
* A custom <code>@Configuration</code> for the feign client. Can contain override
* <code>@Bean</code> definition for the pieces that make up the client, for instance
* {@link feign.codec.Decoder}, {@link feign.codec.Encoder}, {@link feign.Contract}.
*
* @see FeignClientsConfiguration for the defaults
*/
//可用于覆蓋FeignClient默認(rèn)設(shè)置
Class<?>[] configuration() default {};
//回滾類(lèi),像我的例子中定義的回滾類(lèi)必須實(shí)現(xiàn)UserFeign接口,看https://cloud.spring.io/spring-cloud-static/Greenwich.SR5/single/spring-cloud.html#spring-cloud-feign-hystrix-fallback
Class<?> fallback() default void.class;
//如果需要對(duì)異常做診斷可用此屬性,https://cloud.spring.io/spring-cloud-static/Greenwich.SR5/single/spring-cloud.html#spring-cloud-feign-hystrix-fallback
Class<?> fallbackFactory() default void.class;
//路徑前綴
String path() default "";
//標(biāo)記bean是否為primary
boolean primary() default true;
}【2】接下來(lái)重點(diǎn)關(guān)注@EnableFeignClients注解是如何掃描FeignClient接口的。
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(FeignClientsRegistrar.class)
public @interface EnableFeignClients {
//省略
}嗯,發(fā)現(xiàn)沒(méi)有,就是FeignClientsRegistrar做處理的。來(lái)分析下重點(diǎn)方法registerFeignClients和registerFeignClient
class FeignClientsRegistrar implements ImportBeanDefinitionRegistrar,
ResourceLoaderAware, EnvironmentAware {
public void registerFeignClients(AnnotationMetadata metadata,
BeanDefinitionRegistry registry) {
//classPath掃描器
ClassPathScanningCandidateComponentProvider scanner = getScanner();
scanner.setResourceLoader(this.resourceLoader);
//ClassPathScanningCandidateComponentProvider掃描的basePackage集合
Set<String> basePackages;
Map<String, Object> attrs = metadata
.getAnnotationAttributes(EnableFeignClients.class.getName());
//掃描器用于掃描標(biāo)注了@FeignClient類(lèi)的攔截器
AnnotationTypeFilter annotationTypeFilter = new AnnotationTypeFilter(
FeignClient.class);
final Class<?>[] clients = attrs == null ? null
: (Class<?>[]) attrs.get("clients");
//clients屬性為空,以@EnableFeignClients的value、basePackage等為根包掃描
if (clients == null || clients.length == 0) {
scanner.addIncludeFilter(annotationTypeFilter);
basePackages = getBasePackages(metadata);
}
//@EnableFeignClients的clients屬性不為空,解析clients的類(lèi)和根包
else {
final Set<String> clientClasses = new HashSet<>();
basePackages = new HashSet<>();
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)));
}
//1.根據(jù)basePackage找到目標(biāo)@FeignClient接口
//2.檢查是否為接口
//3.將找到的接口注冊(cè)為FeignClientFactoryBean
for (String basePackage : basePackages) {
Set<BeanDefinition> candidateComponents = scanner
.findCandidateComponents(basePackage);
for (BeanDefinition candidateComponent : candidateComponents) {
if (candidateComponent instanceof AnnotatedBeanDefinition) {
// verify annotated class is an interface
AnnotatedBeanDefinition beanDefinition = (AnnotatedBeanDefinition) candidateComponent;
AnnotationMetadata annotationMetadata = beanDefinition.getMetadata();
Assert.isTrue(annotationMetadata.isInterface(),
"@FeignClient can only be specified on an interface");
Map<String, Object> attributes = annotationMetadata
.getAnnotationAttributes(
FeignClient.class.getCanonicalName());
String name = getClientName(attributes);
registerClientConfiguration(registry, name,
attributes.get("configuration"));
registerFeignClient(registry, annotationMetadata, attributes);
}
}
}
}
private String getClientName(Map<String, Object> client) {
if (client == null) {
return null;
}
String value = (String) client.get("contextId");
if (!StringUtils.hasText(value)) {
value = (String) client.get("value");
}
if (!StringUtils.hasText(value)) {
value = (String) client.get("name");
}
if (!StringUtils.hasText(value)) {
value = (String) client.get("serviceId");
}
if (StringUtils.hasText(value)) {
return value;
}
throw new IllegalStateException("Either 'name' or 'value' must be provided in @"
+ FeignClient.class.getSimpleName());
}
private void registerFeignClient(BeanDefinitionRegistry registry,
AnnotationMetadata annotationMetadata, Map<String, Object> attributes) {
String className = annotationMetadata.getClassName();
BeanDefinitionBuilder definition = BeanDefinitionBuilder
.genericBeanDefinition(FeignClientFactoryBean.class);
validate(attributes);
definition.addPropertyValue("url", getUrl(attributes));
definition.addPropertyValue("path", getPath(attributes));
String name = getName(attributes);
definition.addPropertyValue("name", name);
String contextId = getContextId(attributes);
definition.addPropertyValue("contextId", contextId);
definition.addPropertyValue("type", className);
definition.addPropertyValue("decode404", attributes.get("decode404"));
definition.addPropertyValue("fallback", attributes.get("fallback"));
definition.addPropertyValue("fallbackFactory", attributes.get("fallbackFactory"));
definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
String alias = contextId + "FeignClient";
AbstractBeanDefinition beanDefinition = definition.getBeanDefinition();
boolean primary = (Boolean)attributes.get("primary"); // has a default, won't be null
beanDefinition.setPrimary(primary);
String qualifier = getQualifier(attributes);
if (StringUtils.hasText(qualifier)) {
alias = qualifier;
}
BeanDefinitionHolder holder = new BeanDefinitionHolder(beanDefinition, className,
new String[] { alias });
BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry);
}
}可以看到最后注冊(cè)beanDefinition時(shí),我們看到注冊(cè)了FeignClientFactoryBean這一FactoryBean。 我們看看工廠bean FeignClientFactoryBean是如何構(gòu)造對(duì)象的。
class FeignClientFactoryBean implements FactoryBean<Object>, InitializingBean,
ApplicationContextAware {
//省略
@Override
public Object getObject() throws Exception {
return getTarget();
}
<T> T getTarget() {
//1.獲取FeignContext,在FeignAutoConfiguration聲明
FeignContext context = applicationContext.getBean(FeignContext.class);
//2.構(gòu)造Feign builder
Feign.Builder builder = feign(context);
//3.如果沒(méi)有設(shè)置url參數(shù)
if (!StringUtils.hasText(this.url)) {
if (!this.name.startsWith("http")) {
url = "http://" + this.name;
}
else {
url = this.name;
}
//4.設(shè)置path
url += cleanPath();
//5.獲取Client(用于執(zhí)行最終HTTP/HTTPS請(qǐng)求,比如LoadBalancerFeignClient),
//構(gòu)造反射實(shí)例
return (T) loadBalance(builder, context, new HardCodedTarget<>(this.type,
this.name, url));
}
//存在url參數(shù),構(gòu)造非loadBalance的請(qǐng)求實(shí)例(target)
if (StringUtils.hasText(this.url) && !this.url.startsWith("http")) {
this.url = "http://" + this.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();
}
builder.client(client);
}
Targeter targeter = get(context, Targeter.class);
return (T) targeter.target(this, builder, context, new HardCodedTarget<>(
this.type, this.name, url));
}
//在FeignContext中獲取一些在FeignClientsConfiguration中聲明,F(xiàn)eign需要用到的組件
protected Feign.Builder feign(FeignContext context) {
FeignLoggerFactory loggerFactory = get(context, FeignLoggerFactory.class);
Logger logger = loggerFactory.create(this.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));
// @formatter:on
configureFeign(context, builder);
return builder;
}
protected <T> T loadBalance(Feign.Builder builder, FeignContext context,
HardCodedTarget<T> target) {
//獲取Client
Client client = getOptional(context, Client.class);
if (client != null) {
builder.client(client);
//從Context獲取Targeter,Targeter用于生成最終target實(shí)例(對(duì)應(yīng)我的例子是被調(diào)用的通過(guò)反射生成的UserFeign實(shí)例)
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?");
}
//省略
}在非調(diào)試情況下(即我們沒(méi)設(shè)置url參數(shù)), 我們來(lái)看看targeter.target(this, builder, context, target)做了什么。
Targeter接口是構(gòu)造被請(qǐng)求的代理bean的類(lèi)。有兩個(gè)實(shí)現(xiàn)類(lèi)HystrixTargeter、DefaultTargeter。
HystrixTargeter會(huì)比默認(rèn)的多設(shè)置一些回滾措施,用到Feign的Contract屬性, 我會(huì)先從DefaultTargeter說(shuō)起。
DefaultTargeter會(huì)通過(guò)Feign.Builder#target(Target target)生成實(shí)例。我們來(lái)看看代碼。
public abstract class Feign {
//省略
public static class Builder {
private final List<RequestInterceptor> requestInterceptors =
new ArrayList<RequestInterceptor>();
private Logger.Level logLevel = Logger.Level.NONE;
private Contract contract = new Contract.Default();
private Client client = new Client.Default(null, null);
private Retryer retryer = new Retryer.Default();
private Logger logger = new NoOpLogger();
private Encoder encoder = new Encoder.Default();
private Decoder decoder = new Decoder.Default();
private QueryMapEncoder queryMapEncoder = new QueryMapEncoder.Default();
private ErrorDecoder errorDecoder = new ErrorDecoder.Default();
private Options options = new Options();
private InvocationHandlerFactory invocationHandlerFactory =
new InvocationHandlerFactory.Default();
private boolean decode404;
private boolean closeAfterDecode = true;
private ExceptionPropagationPolicy propagationPolicy = NONE;
//省略
public <T> T target(Class<T> apiType, String url) {
return target(new HardCodedTarget<T>(apiType, url));
}
public <T> T target(Target<T> target) {
return build().newInstance(target);
}
//默認(rèn)實(shí)現(xiàn)就是創(chuàng)建一個(gè)ReflectiveFeign實(shí)例
public Feign build() {
SynchronousMethodHandler.Factory synchronousMethodHandlerFactory =
new SynchronousMethodHandler.Factory(client, retryer, requestInterceptors, logger,
logLevel, decode404, closeAfterDecode, propagationPolicy);
ParseHandlersByName handlersByName =
new ParseHandlersByName(contract, options, encoder, decoder, queryMapEncoder,
errorDecoder, synchronousMethodHandlerFactory);
return new ReflectiveFeign(handlersByName, invocationHandlerFactory, queryMapEncoder);
}
}
//省略
}在解讀ReflectiveFeign前介紹幾個(gè)概念:
1、InvocationHandlerFactory 是控制反射方法分發(fā)的接口,create方法返回InvocationHandler。
2、InvocationHandlerFactory.MethodHandler 最終將對(duì)代理類(lèi)方法調(diào)用轉(zhuǎn)換成HTTP請(qǐng)求的地方,請(qǐng)看實(shí)現(xiàn)類(lèi)SynchronousMethodHandler
3、InvocationHandlerFactory.Default 默認(rèn)實(shí)現(xiàn),作為構(gòu)造參數(shù)傳入ReflectiveFeign,create方法創(chuàng)建的是new ReflectiveFeign.FeignInvocationHandler(target, dispatch)。
4、ReflectiveFeign.ParseHandlersByName 作為構(gòu)造參數(shù)傳入ReflectiveFeign,核心方法apply(Target key)先將標(biāo)注了@FeignClient的接口的方法解析出待處理的元數(shù)據(jù)List, 然后創(chuàng)建出方法名和方法處理器的map映射Map<String, MethodHandler>String是方法名,方法處理器通過(guò)SynchronousMethodHandler.Factory#create創(chuàng)建。
5、FeignInvocationHandler 為處理一般方法的處理器
6、DefaultMethodHandler 為處理接口默認(rèn)方法的處理器
有了以上介紹,接下來(lái)簡(jiǎn)單分析ReflectiveFeign的newInstance方法。
public class ReflectiveFeign extends Feign {
private final ParseHandlersByName targetToHandlersByName;
private final InvocationHandlerFactory factory;
private final QueryMapEncoder queryMapEncoder;
ReflectiveFeign(ParseHandlersByName targetToHandlersByName, InvocationHandlerFactory factory,
QueryMapEncoder queryMapEncoder) {
this.targetToHandlersByName = targetToHandlersByName;
this.factory = factory;
this.queryMapEncoder = queryMapEncoder;
}
..
/**
* creates an api binding to the {@code target}. As this invokes reflection, care should be taken
* to cache the result.
*/
@SuppressWarnings("unchecked")
@Override
public <T> T newInstance(Target<T> target) {
//創(chuàng)建方法名和方法處理器的map映射
Map<String, MethodHandler> nameToHandler = targetToHandlersByName.apply(target);
Map<Method, MethodHandler> methodToHandler = new LinkedHashMap<Method, MethodHandler>();
List<DefaultMethodHandler> defaultMethodHandlers = new LinkedList<DefaultMethodHandler>();
for (Method method : target.type().getMethods()) {
if (method.getDeclaringClass() == Object.class) {
continue;
//判斷是否為接口的默認(rèn)方法,DefaultMethodHandler的處理邏輯是直接調(diào)用會(huì)原接口的default方法
} else if (Util.isDefault(method)) {
DefaultMethodHandler handler = new DefaultMethodHandler(method);
defaultMethodHandlers.add(handler);
methodToHandler.put(method, handler);
} else {
//方法處理map
methodToHandler.put(method, nameToHandler.get(Feign.configKey(target.type(), method)));
}
}
InvocationHandler handler = factory.create(target, methodToHandler);
//jdk動(dòng)態(tài)代理創(chuàng)建對(duì)象
T proxy = (T) Proxy.newProxyInstance(target.type().getClassLoader(),
new Class<?>[] {target.type()}, handler);
//將默認(rèn)方法處理器也綁定到代理對(duì)象上
for (DefaultMethodHandler defaultMethodHandler : defaultMethodHandlers) {
defaultMethodHandler.bindTo(proxy);
}
return proxy;
}
static class FeignInvocationHandler implements InvocationHandler {
private final Target target;
private final Map<Method, MethodHandler> dispatch;
//省略
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//自定義的equals、hashCode和toString的處理
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ā)調(diào)用到對(duì)應(yīng)方法的InvocationHandlerFactory.MethodHandler
return dispatch.get(method).invoke(args);
}
//省略
}初始化完成。
五、OpenFeign 執(zhí)行分析

上圖是OpenFeign構(gòu)造的代理對(duì)象被調(diào)用時(shí)的時(shí)序圖。
1、代理對(duì)象被執(zhí)行
2、找到對(duì)應(yīng)SynchronousMethodHandler進(jìn)行方法調(diào)用。
3、構(gòu)造RequestTemplate
4、LoadBalancerFeignClient執(zhí)行負(fù)載請(qǐng)求
5、FeignLoadBalancer通過(guò)ILoadBalancer選擇合適Server,通過(guò)Server重組URI,通過(guò)RibbonRequest持有的Client執(zhí)行實(shí)際HTTP請(qǐng)求包裝成Response。
6、SynchronousMethodHandler通過(guò)Decoder將請(qǐng)求響應(yīng)用Decoder解碼成最終結(jié)果。
下面介紹執(zhí)行過(guò)程中涉及到源碼中的部分組件。
1、RequestTemplate 是一個(gè)HTTP請(qǐng)求內(nèi)容的抽象。
2、RequestTemplate.Factory 將方法參數(shù)解析成RequestTemplate。
3、Retryer 我在上面的時(shí)序圖沒(méi)有標(biāo)注出來(lái),實(shí)際上它在SynchronousMethodHandler的執(zhí)行中控制重試邏輯。
4、RequestInterceptor 在SynchronousMethodHandler發(fā)起執(zhí)行中,會(huì)使用該攔截器對(duì)RequestTemplate進(jìn)行處理。這是一個(gè)拓展點(diǎn)。
5、Logger 執(zhí)行請(qǐng)求時(shí)打日志(在debug時(shí)打)。默認(rèn)為L(zhǎng)ogger.Level.NONE即不打日志,可以增加bean覆蓋。
- Logger.Level.NONE 不打印信息
- Logger.Level.BASIC 打印請(qǐng)求url和響應(yīng)碼。
- Logger.Level.HEADERS 打印BASIC信息外加header信息
- Logger.Level.FULL 打印所有
6、LoadBalancerFeignClient Client接口的實(shí)現(xiàn)類(lèi),是具有負(fù)載均衡能力的Client。Client接口為執(zhí)行HTTP的接口,Client.Default是最終發(fā)出HTTP請(qǐng)求的類(lèi)。

7、FeignLoadBalancer FeignLoadBalancer通過(guò)ILoadBalancer選擇合適Server,通過(guò)Server重組URI,通過(guò)RibbonRequest持有的Client執(zhí)行實(shí)際HTTP請(qǐng)求包裝成Response。
8、LoadBalancerCommand ribbon的rxJava實(shí)現(xiàn),執(zhí)行負(fù)載流程邏輯的組件。
9、ILoadBalancer ribbon的負(fù)載均衡器抽象。
熔斷: 在FeignClientsConfiguration中, 當(dāng)配置了feign.hystrix.enabled,Feign Builder使用HystrixFeign.builder()。
所以build的時(shí)候新建HystrixInvocationHandler和HystrixDelegatingContract實(shí)例。
Feign build(final FallbackFactory<?> nullableFallbackFactory) {
super.invocationHandlerFactory(new InvocationHandlerFactory() {
@Override
public InvocationHandler create(Target target,
Map<Method, MethodHandler> dispatch) {
return new HystrixInvocationHandler(target, dispatch, setterFactory,
nullableFallbackFactory);
}
});
super.contract(new HystrixDelegatingContract(contract));
return super.build();
}來(lái)看看HystrixInvocationHandler的hystrix調(diào)用代碼
final class HystrixInvocationHandler implements InvocationHandler {
//省略
@Override
public Object invoke(final Object proxy, final Method method, final Object[] args)
throws Throwable {
//省略
HystrixCommand<Object> hystrixCommand =
new HystrixCommand<Object>(setterMethodMap.get(method)) {
//實(shí)際執(zhí)行
@Override
protected Object run() throws Exception {
try {
return HystrixInvocationHandler.this.dispatch.get(method).invoke(args);
} catch (Exception e) {
throw e;
} catch (Throwable t) {
throw (Error) t;
}
}
@Override
protected Object getFallback() {
if (fallbackFactory == null) {
return super.getFallback();
}
try {
//用配置的fallbackFactory創(chuàng)建fallback實(shí)例
Object fallback = fallbackFactory.create(getExecutionException());
Object result = fallbackMethodMap.get(method).invoke(fallback, args);
//根據(jù)fallback對(duì)象的returntype解析包裝內(nèi)的結(jié)果返回
if (isReturnsHystrixCommand(method)) {
return ((HystrixCommand) result).execute();
} else if (isReturnsObservable(method)) {
// Create a cold Observable
return ((Observable) result).toBlocking().first();
} else if (isReturnsSingle(method)) {
// Create a cold Observable as a Single
return ((Single) result).toObservable().toBlocking().first();
} else if (isReturnsCompletable(method)) {
((Completable) result).await();
return null;
} else {
return result;
}
} catch (IllegalAccessException e) {
// shouldn't happen as method is public due to being an interface
throw new AssertionError(e);
} catch (InvocationTargetException e) {
// Exceptions on fallback are tossed by Hystrix
throw new AssertionError(e.getCause());
}
}
};
//根據(jù)方法的return去返回結(jié)果
if (Util.isDefault(method)) {
return hystrixCommand.execute();
} else if (isReturnsHystrixCommand(method)) {
return hystrixCommand;
} else if (isReturnsObservable(method)) {
// Create a cold Observable
return hystrixCommand.toObservable();
} else if (isReturnsSingle(method)) {
// Create a cold Observable as a Single
return hystrixCommand.toObservable().toSingle();
} else if (isReturnsCompletable(method)) {
return hystrixCommand.toObservable().toCompletable();
}
return hystrixCommand.execute();
}
//省略
}到此這篇關(guān)于Spring Cloud 負(fù)載均衡器架構(gòu)選型的文章就介紹到這了,更多相關(guān)Spring Cloud 負(fù)載均衡內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- Alibaba?SpringCloud集成Nacos、openFeign實(shí)現(xiàn)負(fù)載均衡的解決方案
- SpringCloud使用Ribbon實(shí)現(xiàn)負(fù)載均衡的流程步驟
- Spring?cloud負(fù)載均衡@LoadBalanced?&?LoadBalancerClient
- SpringCloud集成Eureka并實(shí)現(xiàn)負(fù)載均衡的過(guò)程詳解
- SpringCloud中的Ribbon負(fù)載均衡詳細(xì)解讀
- 關(guān)于SpringCloud中Ribbon的7種負(fù)載均衡策略解析
- 詳解SpringCloud LoadBalancer 新一代負(fù)載均衡器
- Spring?Cloud?Alibaba負(fù)載均衡實(shí)現(xiàn)方式
相關(guān)文章
Java中的HashMap弱引用之WeakHashMap詳解
這篇文章主要介紹了Java中的HashMap弱引用之WeakHashMap詳解,當(dāng)內(nèi)存空間不足,Java虛擬機(jī)寧愿拋出OutOfMemoryError錯(cuò)誤,使程序異常終止,也不會(huì)靠隨意回收具有強(qiáng)引用的對(duì)象來(lái)解決內(nèi)存不足的問(wèn)題,需要的朋友可以參考下2023-09-09
SpringBoot整合Redis實(shí)現(xiàn)附近位置查找(LBS)功能
Redis 提供了 GEO 數(shù)據(jù)結(jié)構(gòu),可以高效地存儲(chǔ)和查詢(xún)地理位置數(shù)據(jù),本文將介紹如何使用 Spring Boot + Redis 來(lái)實(shí)現(xiàn)附近位置查找,需要的可以了解下2025-03-03
Java項(xiàng)目實(shí)現(xiàn)尋找迷宮出路
這篇文章主要為大家詳細(xì)介紹了Java項(xiàng)目實(shí)現(xiàn)尋找迷宮出路,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-05-05
Mybatis實(shí)現(xiàn)動(dòng)態(tài)建表代碼實(shí)例
這篇文章主要介紹了Mybatis實(shí)現(xiàn)動(dòng)態(tài)建表代碼實(shí)例,解釋一下,就是指根據(jù)傳入的表名,動(dòng)態(tài)地創(chuàng)建數(shù)據(jù)庫(kù)表,以供后面的業(yè)務(wù)場(chǎng)景使用,2023-10-10
而使用 Mybatis 的動(dòng)態(tài) SQL,就能很好地為我們解決這個(gè)問(wèn)題,需要的朋友可以參考下
在Java生產(chǎn)環(huán)境下進(jìn)行性能監(jiān)控與調(diào)優(yōu)的詳細(xì)過(guò)程
在Java生產(chǎn)環(huán)境下進(jìn)行性能監(jiān)控與調(diào)優(yōu)是一個(gè)復(fù)雜但重要的過(guò)程,它涉及到多個(gè)方面,包括代碼分析、JVM監(jiān)控、線程管理、垃圾收集優(yōu)化、內(nèi)存管理、數(shù)據(jù)庫(kù)交互等,下面我將提供一個(gè)詳細(xì)的概述和示例代碼,需要的朋友可以參考下2025-02-02
Java實(shí)戰(zhàn)個(gè)人博客系統(tǒng)的實(shí)現(xiàn)流程
讀萬(wàn)卷書(shū)不如行萬(wàn)里路,只學(xué)書(shū)上的理論是遠(yuǎn)遠(yuǎn)不夠的,只有在實(shí)戰(zhàn)中才能獲得能力的提升,本篇文章手把手帶你用java+springboot+mybatis+redis+vue+elementui+Mysql實(shí)現(xiàn)一個(gè)個(gè)人博客系統(tǒng),大家可以在過(guò)程中查缺補(bǔ)漏,提升水平2022-01-01
Java如何實(shí)現(xiàn)微信支付v3的支付回調(diào)
這篇文章主要給大家介紹了關(guān)于Java如何實(shí)現(xiàn)微信支付v3的支付回調(diào),微信實(shí)現(xiàn)支付功能與支付寶實(shí)現(xiàn)支付功能是相似的,文中給了詳細(xì)的示例代碼,需要的朋友可以參考下2023-07-07

