服务之间如果需要相互访问,可以使用RestTemplate,也可以使用Feign客户端访问,它会默认使用Ribbon来实现负载均衡。
Feign是一个声明式的伪Http客户端,它使得编写Http客户端变得更加简单。使用Feign,只需要创建一个接口并注解。它具有可插拔的注解特性,支持可插拔的编码器和解码器,整合了Ribbo和Hystrix以及与Eureka结合实现负载均衡和断路器等效果。
为什么还要Feign
Ribbon已经实现了对RestTemplate请求的拦截,增加了负载均衡的实现,而RestTemplate已经简化了HTTP请求,而只需要提供一些模板代码。
Feign在此基础上做了进一步封装,这样只需要创建一个接口并用注解方式来配置它,即可完成对服务提供方的接口绑定,简化了Ribbon+RestTemplate调用客户端的开发量。
Feign的主要功能
- 可插拔的HTTP客户端实现
- 可插拔的编码/解码器
- 对HTTP请求进行gzip压缩,且可设定压缩的阈值
- 自带负载均衡实现Ribbon
- 自带熔断器Hystrix
- 请求日志详细信息
- 自带重试处理器
Feign工作原理
Feign是一个伪Java HTTP客户端,它不做任何的请求处理,而只是通过处理注解生成Request模板,从而简化了HTTP API的开发。开发人员只需通过注解方式定制Request API模板。
@FeignClient注解过的接口方法被调用的时候,会通过JDK的动态代理生成具体的RequestTemplate模板对象,然后根据这个RequestTemplate生成HTTP的Request对象,最后该请求被LoadBalanceClient通过Ribbon做到负载均衡。
Feign使用
@FeignClient(value = "user-service", configuration = FeignConfiguration.class)
public interface UserFeignClient {
@GetMapping(value = "/simple/{id}")
public User findById(@PathVariable("id") Long id);
@PostMapping(value = "/user")
public User postUser(@RequestBody User user);
// 该请求不会成功,只要参数是复杂对象,即使指定了是GET方法,feign依然会以POST方法进行发送请求。
@GetMapping(value = "/get-user")
public User getUser(User user);
@GetMapping("list-all")
public List<User> listAll() ;
}
@Configuration
public class FeignConfiguration {
@Bean
Logger.Level feignLoggerLevel() {
return Logger.Level.FULL;
}
}
//利用spring-security获取credentials,并添加到请求头上
@Component
public class UserFeignClientInterceptor implements RequestInterceptor {
private static final String AUTHORIZATION_HEADER = "Authorization";
private static final String BEARER_TOKEN_TYPE = "Bearer";
@Autowired
private AuthenticationUtil authenticationUtil;
@Override
public void apply(RequestTemplate template) {
SecurityContext securityContext = SecurityContextHolder.getContext();
Authentication authentication = securityContext.getAuthentication();
if (authentication == null) {
//for job
authentication = authenticationUtil.getAuthentication();
}
if (authentication != null) {
template.header(AUTHORIZATION_HEADER, String.format("%s %s", BEARER_TOKEN_TYPE, authentication.getCredentials()));
}
}
}
源码解析
与Ribbon不一样,Feign需要使用@EnableFeignClients来开启feign的功能(Ribbon提供了auto-configuration类),@EnableFeignClients注解主要救赎import了FeignClientsRegistrar。
因为FeignClientsRegistrar实现了ImportBeanDefinitionRegistrar,所以主要关注它的registerBeanDefinitions()方法,这个方法在处理Configuration类的时候会被ConfigurationClassPostProcessor通过ConfigurationClassBeanDefinitionReader调用。
class FeignClientsRegistrar
implements ImportBeanDefinitionRegistrar, ResourceLoaderAware, EnvironmentAware {
。。。省略代码
@Override
public void registerBeanDefinitions(AnnotationMetadata metadata,
BeanDefinitionRegistry registry) {
//注册默认的Configuration Bean
registerDefaultConfiguration(metadata, registry);
//注册FeignClient相关bean
registerFeignClients(metadata, registry);
}
//注册默认Configuration
private void registerDefaultConfiguration(AnnotationMetadata metadata,
BeanDefinitionRegistry registry) {
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"));
}
}
//主要代码
public void registerFeignClients(AnnotationMetadata metadata,
BeanDefinitionRegistry registry) {
ClassPathScanningCandidateComponentProvider scanner = getScanner();
scanner.setResourceLoader(this.resourceLoader);
Set<String> basePackages;
//获取EnableFeignClients注解的元数据
Map<String, Object> attrs = metadata
.getAnnotationAttributes(EnableFeignClients.class.getName());
AnnotationTypeFilter annotationTypeFilter = new AnnotationTypeFilter(
FeignClient.class);
final Class<?>[] clients = attrs == null ? null
: (Class<?>[]) attrs.get("clients");
if (clients == null || clients.length == 0) {
//scanner添加exclude filter,指定注解是@FeignClient
scanner.addIncludeFilter(annotationTypeFilter);
//获取配置的basepackages
basePackages = getBasePackages(metadata);
}
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)));
}
for (String basePackage : basePackages) {
//通过scanner获取basepackag下面满足条件的组件
Set<BeanDefinition> candidateComponents = scanner
.findCandidateComponents(basePackage);
for (BeanDefinition candidateComponent : candidateComponents) {
//如果是AnnotatedBeanDefinition,进行处理
if (candidateComponent instanceof AnnotatedBeanDefinition) {
// verify annotated class is an interface
AnnotatedBeanDefinition beanDefinition = (AnnotatedBeanDefinition) candidateComponent;
//获取BeanDefinition的元数据
AnnotationMetadata annotationMetadata = beanDefinition.getMetadata();
Assert.isTrue(annotationMetadata.isInterface(),
"@FeignClient can only be specified on an interface");
//获取@FeignClient注解的元数据
Map<String, Object> attributes = annotationMetadata
.getAnnotationAttributes(
FeignClient.class.getCanonicalName());
//获取客户端名称
String name = getClientName(attributes);
//注册客户端所持有的Configuration配置
registerClientConfiguration(registry, name,
attributes.get("configuration"));
//开始注册Feign客户端
registerFeignClient(registry, annotationMetadata, attributes);
}
}
}
}
//组装Feign客户端BeanDefinition
private void registerFeignClient(BeanDefinitionRegistry registry,
AnnotationMetadata annotationMetadata, Map<String, Object> attributes) {
//根据@FeignClient注解的元数据组装FeignClient需要的一些property
String className = annotationMetadata.getClassName();
//注意这里注册的是FeignClientFactoryBean,是一个FactoryBean
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);
}
FeignClientFactoryBean
@Override
//factoryBean听过的方法,获取目标对象bean
public Object getObject() throws Exception {
return getTarget();
}
//getObject()实际调用方法
<T> T getTarget() {
//从容器中获取FeignContext类型的bean
FeignContext context = this.applicationContext.getBean(FeignContext.class);
//根据FeignContext创建Feign.builder对象
Feign.Builder builder = feign(context);
//开始拼接URL
if (!StringUtils.hasText(this.url)) {
if (!this.name.startsWith("http")) {
this.url = "http://" + this.name;
}
else {
this.url = this.name;
}
this.url += cleanPath();
//如果是http打头的,直接调用loadBalance()方法
return (T) loadBalance(builder, context,
new HardCodedTarget<>(this.type, this.name, this.url));
}
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创建Feign.builder对象
protected Feign.Builder feign(FeignContext context) {
FeignLoggerFactory loggerFactory = get(context, FeignLoggerFactory.class);
Logger logger = loggerFactory.create(this.type);
// @formatter:off
//从congtext中获取所需要的instance,最后生成Feign.builder对象
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
//配置feign
configureFeign(context, builder);
return builder;
}
//配置feign
protected void configureFeign(FeignContext context, Feign.Builder builder) {
//从容器中获取FeignClientProperties对象
FeignClientProperties properties = this.applicationContext
.getBean(FeignClientProperties.class);
if (properties != null) {
if (properties.isDefaultToProperties()) {
configureUsingConfiguration(context, builder);
configureUsingProperties(
properties.getConfig().get(properties.getDefaultConfig()),
builder);
configureUsingProperties(properties.getConfig().get(this.contextId),
builder);
}
else {
configureUsingProperties(
properties.getConfig().get(properties.getDefaultConfig()),
builder);
configureUsingProperties(properties.getConfig().get(this.contextId),
builder);
configureUsingConfiguration(context, builder);
}
}
else {
configureUsingConfiguration(context, builder);
}
}
//通过Configuration类配置Feign
protected void configureUsingConfiguration(FeignContext context,
Feign.Builder builder) {
//设置Retryer重试机制
Retryer retryer = getOptional(context, Retryer.class);
if (retryer != null) {
builder.retryer(retryer);
}
//配置Error解码器
ErrorDecoder errorDecoder = getOptional(context, ErrorDecoder.class);
if (errorDecoder != null) {
builder.errorDecoder(errorDecoder);
}
//设置Request.Options
Request.Options options = getOptional(context, Request.Options.class);
if (options != null) {
builder.options(options);
}
//配置RequestInterceptor,我们在这里这是Header
Map<String, RequestInterceptor> requestInterceptors = context
.getInstances(this.contextId, RequestInterceptor.class);
if (requestInterceptors != null) {
builder.requestInterceptors(requestInterceptors.values());
}
//设置decode404
if (this.decode404) {
builder.decode404();
}
}
//与上面从Configuration类配置feign类似
protected void configureUsingProperties(
FeignClientProperties.FeignClientConfiguration config,
Feign.Builder builder) {
if (config == null) {
return;
}
//
if (config.getConnectTimeout() != null && config.getReadTimeout() != null) {
builder.options(new Request.Options(config.getConnectTimeout(),
config.getReadTimeout()));
}
if (config.getRetryer() != null) {
Retryer retryer = getOrInstantiate(config.getRetryer());
builder.retryer(retryer);
}
if (config.getErrorDecoder() != null) {
ErrorDecoder errorDecoder = getOrInstantiate(config.getErrorDecoder());
builder.errorDecoder(errorDecoder);
}
if (config.getRequestInterceptors() != null
&& !config.getRequestInterceptors().isEmpty()) {
// this will add request interceptor to builder, not replace existing
for (Class<RequestInterceptor> bean : config.getRequestInterceptors()) {
RequestInterceptor interceptor = getOrInstantiate(bean);
builder.requestInterceptor(interceptor);
}
}
if (config.getDecode404() != null) {
if (config.getDecode404()) {
builder.decode404();
}
}
if (Objects.nonNull(config.getEncoder())) {
builder.encoder(getOrInstantiate(config.getEncoder()));
}
if (Objects.nonNull(config.getDecoder())) {
builder.decoder(getOrInstantiate(config.getDecoder()));
}
if (Objects.nonNull(config.getContract())) {
builder.contract(getOrInstantiate(config.getContract()));
}
}
//通过Ribbon使请求负载均衡
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?");
}
loadBalance与Targeter调用
上面代码中显示FactoryBean获得target的实现,在创建Feign.builder之后,走LoadBalance方法。
protected <T> T loadBalance(Feign.Builder builder, FeignContext context,
HardCodedTarget<T> target) {
//这里获得LoadBalancerFeignClient
Client client = getOptional(context, Client.class);
if (client != null) {
//将Client放入Feign.builder中
builder.client(client);
//获取target,这里是HystrixTarget
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?");
}
//hystrixTarger内部会获得是否提供fallback
public <T> T target(FeignClientFactoryBean factory, Feign.Builder feign,
FeignContext context, Target.HardCodedTarget<T> target) {
if (!(feign instanceof feign.hystrix.HystrixFeign.Builder)) {
return feign.target(target);
}
feign.hystrix.HystrixFeign.Builder builder = (feign.hystrix.HystrixFeign.Builder) feign;
SetterFactory setterFactory = getOptional(factory.getName(), context,
SetterFactory.class);
if (setterFactory != null) {
builder.setterFactory(setterFactory);
}
Class<?> fallback = factory.getFallback();
if (fallback != void.class) {
return targetWithFallback(factory.getName(), context, target, builder,
fallback);
}
Class<?> fallbackFactory = factory.getFallbackFactory();
if (fallbackFactory != void.class) {
return targetWithFallbackFactory(factory.getName(), context, target, builder,
fallbackFactory);
}
return feign.target(target);
}
//feign.target()实现
public <T> T target(Target<T> target) {
return build().newInstance(target);
}
//这段代码就是核心,会创建代理类
public <T> T newInstance(Target<T> target) {
Map<String, MethodHandler> nameToHandler = targetToHandlersByName.apply(target);
Map<Method, MethodHandler> methodToHandler = new LinkedHashMap<Method, MethodHandler>();
List<DefaultMethodHandler> defaultMethodHandlers = new LinkedList<DefaultMethodHandler>();
//根据需要处理的name转化成methodName
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
InvocationHandler handler = factory.create(target, methodToHandler);
//生成代理对象
T proxy = (T) Proxy.newProxyInstance(target.type().getClassLoader(),
new Class<?>[] {target.type()}, handler);
for (DefaultMethodHandler defaultMethodHandler : defaultMethodHandlers) {
defaultMethodHandler.bindTo(proxy);
}
return proxy;
}
//创建InvocationHandler,类型时HystrixInvocationHandler
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();
}
//InvocationHandler的invoke代码,核心实现
@Override
public Object invoke(final Object proxy, final Method method, final Object[] args)
。。。省略平常代码
//封装
HystrixCommand<Object> hystrixCommand =
new HystrixCommand<Object>(setterMethodMap.get(method)) {
@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 {
Object fallback = fallbackFactory.create(getExecutionException());
Object result = fallbackMethodMap.get(method).invoke(fallback, args);
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());
}
}
};
//各种方法调用分派
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();
}
网友评论