目录
上图就是 Open Feign 的层次和执行流程。
开发的时候我们一般会写一个接口,Spring 在启动的时候会扫描到这个拥有 FeignClient 注解的接口,然后生成jdk动态代理,等启动完成后一个请求发过来,然后路由到某个方法,生成 http 请求,执行请求,获取http响应结果,解码,返回。
@FeignClient(name = "demo-provider")
public interface DemoProviderFeignClient {
@GetMapping("/echo")
String echo(@RequestParam("name") String name);
}
启动时
启动类添加 @EnableFeignClients 注解,然后通过 @Import 注入 FeignClientsRegistrar.class
class FeignClientsRegistrar
implements ImportBeanDefinitionRegistrar, ResourceLoaderAware, EnvironmentAware {
...
@Override
public void registerBeanDefinitions(AnnotationMetadata metadata,
BeanDefinitionRegistry registry) {
registerDefaultConfiguration(metadata, registry);
registerFeignClients(metadata, registry);
}
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);
}
}
在这个方法中,它会扫描项目中所有的 FeignClient 并生成代理类,并注入 FeignClientFactoryBean ,显然 FactoryBean 的 getObject() 方法会具体执行生成 FeignClient 的逻辑。
public abstract class Feign {
public static class Builder {
public <T> T target(Target<T> target) {
return build().newInstance(target);
}
}
}
public class ReflectiveFeign extends Feign {
@Override
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>();
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);
T proxy = (T) Proxy.newProxyInstance(target.type().getClassLoader(),
new Class<?>[] {target.type()}, handler);
for (DefaultMethodHandler defaultMethodHandler : defaultMethodHandlers) {
defaultMethodHandler.bindTo(proxy);
}
return proxy;
}
}
factory.create(target, methodToHandler) 的具体代码是:
static final class Default implements InvocationHandlerFactory {
@Override
public InvocationHandler create(Target target, Map<Method, MethodHandler> dispatch) {
return new ReflectiveFeign.FeignInvocationHandler(target, dispatch);
}
}
它解析完 Contract (契约) 后就把 结果Map 包裹起来放到 jdk 动态代理的 proxy 里面。
在 apply() 方法中,
List<MethodMetadata> metadata = contract.parseAndValidatateMetadata(key.type());
这段代码是使用 SpringMvcContract 解析 FeignClient 接口里的方法
SpringMvcContract 是 Open Feign 用来解析 @RequestMapping 的,最后生成一个 MethodMetadata ,因为我们几乎只使用 SpringMvcContract ,所以就只分析它,不分析
QueryMap, HeaderMap 这些东西,但它们的作用都是差不多的,是同一个流程里的不同分支细节而已。
public class SpringMvcContract extends Contract.BaseContract
implements ResourceLoaderAware {
@Override
public MethodMetadata parseAndValidateMetadata(Class<?> targetType, Method method) {
this.processedMethods.put(Feign.configKey(targetType, method), method);
MethodMetadata md = super.parseAndValidateMetadata(targetType, method);
RequestMapping classAnnotation = findMergedAnnotation(targetType,
RequestMapping.class);
if (classAnnotation != null) {
// produces - use from class annotation only if method has not specified this
if (!md.template().headers().containsKey(ACCEPT)) {
parseProduces(md, method, classAnnotation);
}
// consumes -- use from class annotation only if method has not specified this
if (!md.template().headers().containsKey(CONTENT_TYPE)) {
parseConsumes(md, method, classAnnotation);
}
// headers -- class annotation is inherited to methods, always write these if
// present
parseHeaders(md, method, classAnnotation);
}
return md;
}
}
Contract 解析完成后保存到一个 Map 中,value 是SynchronousMethodHandler 类型,它是MethodHandler 类型。
result.put(md.configKey(),
factory.create(key, md, buildTemplate, options, decoder, errorDecoder));
public MethodHandler create(Target<?> target,
MethodMetadata md,
RequestTemplate.Factory buildTemplateFromArgs,
Options options,
Decoder decoder,
ErrorDecoder errorDecoder) {
return new SynchronousMethodHandler(target, client, retryer, requestInterceptors, logger,
logLevel, md, buildTemplateFromArgs, options, decoder,
errorDecoder, decode404, closeAfterDecode, propagationPolicy);
}
启动流程到此为止。
请求到来
当一个请求过来时,它就会被 proxy 拦截,进入到 FeignInvocationHandler 的 invoke 方法中。
很快就进入 SynchronousMethodHandler 的 invoke 方法,看到了 RequestTemplate
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) {
...
}
continue;
}
}
}
我们忽然看到请求有“Binary data”,但它不一定说明请求已经被encode了。 RequestTemplate template = buildTemplateFromArgs.create(argv); 会调用 RequestTemplate resolve(Map<String, ?> variables) 方法,在这个方法中有这样一行代码: resolved.body(this.body.expand(variables));
public final class Request {
public static class Body {
public Request.Body expand(Map<String, ?> variables) {
if (bodyTemplate == null)
return this;
return encoded(bodyTemplate.expand(variables).getBytes(encoding), encoding);
}
public static Request.Body encoded(byte[] bodyData, Charset encoding) {
return new Request.Body(bodyData, encoding, null);
}
}
}
虽然有encoded() 方法,但实际上却并没有被执行。 下面这个才是Open Feign 的编码实现,但也并有被执行。
public interface Encoder {
Type MAP_STRING_WILDCARD = Util.MAP_STRING_WILDCARD;
void encode(Object object, Type bodyType, RequestTemplate template) throws EncodeException;
/**
* Default implementation of {@code Encoder}.
*/
class Default implements Encoder {
@Override
public void encode(Object object, Type bodyType, RequestTemplate template) {
if (bodyType == String.class) {
template.body(object.toString());
} else if (bodyType == byte[].class) {
template.body((byte[]) object, null);
} else if (object != null) {
throw new EncodeException(
format("%s is not a type supported by this encoder.", object.getClass()));
}
}
}
}
注意:在http传输的时候可以指定 Content-Length,也可以使用 Transfer-Encoding: chunked 来做分块传输编码,动态输出。
if (contentLength != null) {
connection.setFixedLengthStreamingMode(contentLength);
} else {
connection.setChunkedStreamingMode(8196);
}
然后进入到 executeAndDecode() 方法,构建请求,使用 httpClient 执行http 请求,接收http响应。
Object executeAndDecode(RequestTemplate template, Options options) throws Throwable {
Request request = targetRequest(template);
if (logLevel != Logger.Level.NONE) {
logger.logRequest(metadata.configKey(), logLevel, request);
}
Response response;
long start = System.nanoTime();
try {
response = client.execute(request, options);
} 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);
boolean shouldClose = true;
try {
if (logLevel != Logger.Level.NONE) {
response =
logger.logAndRebufferResponse(metadata.configKey(), logLevel, response, elapsedTime);
}
if (Response.class == metadata.returnType()) {
if (response.body() == null) {
return response;
}
if (response.body().length() == null ||
response.body().length() > MAX_RESPONSE_BUFFER_SIZE) {
shouldClose = false;
return response;
}
// Ensure the response body is disconnected
byte[] bodyData = Util.toByteArray(response.body().asInputStream());
return response.toBuilder().body(bodyData).build();
}
if (response.status() >= 200 && response.status() < 300) {
if (void.class == metadata.returnType()) {
return null;
} else {
Object result = decode(response);
shouldClose = closeAfterDecode;
return result;
}
} else if (decode404 && response.status() == 404 && void.class != metadata.returnType()) {
Object result = decode(response);
shouldClose = closeAfterDecode;
return result;
} else {
throw errorDecoder.decode(metadata.configKey(), response);
}
} catch (IOException e) {
if (logLevel != Logger.Level.NONE) {
logger.logIOException(metadata.configKey(), logLevel, e, elapsedTime);
}
throw errorReading(request, response, e);
} finally {
if (shouldClose) {
ensureClosed(response.body());
}
}
}
targetRequest(template); 会执行一个请求拦截器链,添加一些请求头参数。
Request targetRequest(RequestTemplate template) {
for (RequestInterceptor interceptor : requestInterceptors) {
interceptor.apply(template);
}
return target.apply(template);
}
public interface RequestInterceptor {
/**
* Called for every request. Add data using methods on the supplied {@link RequestTemplate}.
*/
void apply(RequestTemplate template);
}
LoadBalancerFeignClient会使用Ribbon去注册中心获取到一个demo-provider的实例,然后重建http请求。
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) {
...
}
}
}
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();
}
...
}
最后进入到 Client 执行 execute 方法,如果不走 loadbalance 也会进入到这里,算是殊途同归。
class Default implements Client {
@Override
public Response execute(Request request, Options options) throws IOException {
HttpURLConnection connection = convertAndSend(request, options);
return convertResponse(connection, request);
}
}
响应返回后,从输入流中解码,获取结果,open feign 执行完毕。
Open Feign 是使用 SpringDecoder 完成解码的。
public class SpringDecoder implements Decoder {
private ObjectFactory<HttpMessageConverters> messageConverters;
@Override
public Object decode(final Response response, Type type)
throws IOException, FeignException {
if (type instanceof Class || type instanceof ParameterizedType
|| type instanceof WildcardType) {
@SuppressWarnings({ "unchecked", "rawtypes" })
HttpMessageConverterExtractor<?> extractor = new HttpMessageConverterExtractor(
type, this.messageConverters.getObject().getConverters());
return extractor.extractData(new FeignResponseAdapter(response));
}
throw new DecodeException(response.status(),
"type is not an instance of Class or ParameterizedType: " + type,
response.request());
}
}
上图中的变量 extractor 是许多 HttpMessageConverter 的列表,它会选取其中一种合适的转换器完成解析。在这里选取了 StringHttpMessageConverter 。对于 HttpMessageConverter ,Spring Mvc 在处理结果返回数据的时候也是这样使用的。
public class StringHttpMessageConverter extends AbstractHttpMessageConverter<String> {
@Override
protected String readInternal(Class<? extends String> clazz, HttpInputMessage inputMessage) throws IOException {
Charset charset = getContentTypeCharset(inputMessage.getHeaders().getContentType());
return StreamUtils.copyToString(inputMessage.getBody(), charset);
}
}
这样得到解析结果了。
未完待续。。。