https://spring-cloud-wiki.readthedocs.io/zh_CN/latest/pages/feign.html
简介
Feign是一个声明式的Web Service客户端,它的目的就是让Web Service调用更加简单。Feign提供了HTTP请求的模板,通过编写简单的接口和注解,就可以定义好HTTP请求的参数、格式、地址等信息。Feign会完全代理HTTP请求,开发时只需要像调用方法一样调用它就可以完成服务请求及相关处理。 开源地址:https://github.com/OpenFeign/feign。Feign整合了Ribbon负载和Hystrix熔断,可以不再需要显式地使用这两个组件 。总体来说,Feign具有如下特性:
- 可插拔的注解支持,包括Feign注解和JAX-RS注解;
- 支持可插拔的HTTP编码器和解码器;
- 支持Hystrix和它的Fallback;
- 支持Ribbon的负载均衡;
- 支持HTTP请求和响应的压缩。
Spring Cloud Feign 致力于处理客户端与服务器之间的调用需求,简单来说,使用Spring Cloud Feign组件,他本身整合了Ribbon和Hystrix。可设计一套稳定可靠的弹性客户端调用方案,避免整个系统出现雪崩效应。
本文不会涉及Spring Cloud , 仅做Feign源码分析用。
前提
已有站点
已有站点http://localhost:8080/echo/welcome/jim
maven依赖
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-core</artifactId>
<version>11.8</version>
</dependency>
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-slf4j</artifactId>
<version>11.8</version>
</dependency>
实践
客户端代码EchoClient
public interface EchoClient {
@Headers({"Accept:*/*", "Accept-Language: zh-cn"})
@RequestLine("GET /welcome/{name}")
String welcome(URI baseUri, @Param("name") String name);
}
RequestInterceptor
public class Global1RequestInterceptor implements RequestInterceptor {
private org.slf4j.Logger logger = LoggerFactory.getLogger(this.getClass());
@Override
public void apply(RequestTemplate template) {
logger.info("应用于:{}",template.request().url());
}
}
Capability
public class ClientWatchCapability implements Capability {
@Override
public Client enrich(Client client) {
return new WatchableClient(null, null);
}
public static class WatchableClient extends Client.Default {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
public WatchableClient(SSLSocketFactory sslContextFactory, HostnameVerifier hostnameVerifier) {
super(sslContextFactory, hostnameVerifier);
}
@Override
public Response execute(Request request, Request.Options options) throws IOException {
Stopwatch stopwatch = Stopwatch.createStarted();
Response response = super.execute(request, options);
stopwatch.stop();
logger.info("time: " + stopwatch);
return response;
}
}
}
test-case
@Test
public void test01() {
Feign feign = Feign.builder()
.client(new Client.Default(null, null))
.encoder(new Encoder.Default())
.decoder(new Decoder.Default())
.contract(new Contract.Default())
.options(new Request.Options(100,200))
.retryer(Retryer.NEVER_RETRY)
.logLevel(Logger.Level.FULL)
.errorDecoder(new ErrorDecoder.Default())
.queryMapEncoder(new BeanQueryMapEncoder())
.addCapability(new ClientWatchCapability())
.requestInterceptors(Lists.newArrayList(new Global1RequestInterceptor(), new Global2RequestInterceptor()))
.build();
EchoClient client = feign.newInstance(Target.EmptyTarget.create(EchoClient.class));
URI baseUri = URI.create("http://localhost:8080/echo");
String result = client.welcome(baseUri,"jim");
System.out.println(result);
}
执行结果
Feign属性
public abstract class Feign {
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 FieldQueryMapEncoder();
private ErrorDecoder errorDecoder = new ErrorDecoder.Default();
private Options options = new Options();
}
Contract
契约: 定义@FeignClient接口中支持的 注解和值的类型.
public interface Contract {
List<MethodMetadata> parseAndValidateMetadata(Class<?> targetType);
}
- Contract.Default: 支持
@RequestLine("GET /welcome/{name}") - SpringMvcContract:支持
@RequestMapping,@GetMapping 等注解。
RequestInterceptor
作用类似于SpringMvc重的RequestInterceptor.intercept(),
public interface RequestInterceptor {
void apply(RequestTemplate template);
}
Capability
public interface Capability {
static <E> E enrich(E componentToEnrich, List<Capability> capabilities) {
return capabilities.stream()
.reduce(
componentToEnrich,
(component, capability) -> invoke(component, capability),
(component, enrichedComponent) -> enrichedComponent);
}
static <E> E invoke(E target, Capability capability) {
return Arrays.stream(capability.getClass().getMethods())
.filter(method -> method.getName().equals("enrich"))
.filter(method -> method.getReturnType().isInstance(target))
.findFirst()
.map(method -> {
return (E) method.invoke(capability, target);
})
.orElse(target);
}
default Client enrich(Client client) {
return client;
}
}
源码分析
Feign.builder()..newInstance(Target.EmptyTarget.create(EchoClient.class));
build
public abstract class Feign {
private InvocationHandlerFactory invocationHandlerFactory = new InvocationHandlerFactory.Default();
public Feign build() {
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);
}
}
}
1.SynchronousMethodHandler
final class SynchronousMethodHandler implements MethodHandler {
static class Factory {
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, forceDecoding);
}
}
@Override
public Object invoke(Object[] argv) throws Throwable {
RequestTemplate template = buildTemplateFromArgs.create(argv);
Options options = findOptions(argv);
Retryer retryer = this.retryer.clone();
while (true) {
return executeAndDecode(template, options);
}
}
Object executeAndDecode(RequestTemplate template, Options options) throws Throwable {
Request request = targetRequest(template);
Response response = client.execute(request, options);
return decoder.decode(response, metadata.returnType());
}
Request targetRequest(RequestTemplate template) {
for (RequestInterceptor interceptor : requestInterceptors) {
interceptor.apply(template);
}
return target.apply(template);
}
}
BuildTemplateByResolvingArgs
private static class BuildTemplateByResolvingArgs implements RequestTemplate.Factory {
protected final MethodMetadata metadata;
protected final Target<?> target;
RequestTemplate mutable = RequestTemplate.from(metadata.template());
mutable.feignTarget(target);
if (metadata.urlIndex() != null) {
int urlIndex = metadata.urlIndex();
mutable.target(String.valueOf(argv[urlIndex]));
}
RequestTemplate template = resolve(argv, mutable, varBuilder);
return template;
}
}
Target
中间讲解一段关于Target
public interface Target<T> {
Class<T> type();
String name();
String url();
public Request apply(RequestTemplate input);
public static class HardCodedTarget<T> implements Target<T> {
}
public static class EmptyTarget<T> implements Target<T> {
}
}
例
new Target.HardCodedTarget(EchoClient.class,"http://localhost:8080/echo");
String result = client.welcome(URI.create(""),"jim");
EchoClient client = feign.newInstance(Target.EmptyTarget.create(EchoClient.class));
String result = client.welcome(URI.create("http://localhost:8080/echo"),"jim");
client.execute
@Override
public Response execute(Request request, Options options) throws IOException {
HttpURLConnection connection = convertAndSend(request, options);
return convertResponse(connection, request);
}
2.ParseHandlersByName
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 || md.alwaysEncodeBody()) {
buildTemplate = new BuildEncodedTemplateFromArgs(md, encoder, queryMapEncoder, target);
} else {
buildTemplate = new BuildTemplateByResolvingArgs(md, queryMapEncoder, target);
}
result.put(md.configKey(),factory.create(target, md, buildTemplate, options, decoder, errorDecoder));
}
return result;
}
}
}
Feign.newInstance(Target)
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;
}
@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;
}
}
InvocationHandlerFactory.Default.create()
public interface InvocationHandlerFactory {
InvocationHandler create(Target target, Map<Method, MethodHandler> dispatch);
interface MethodHandler {
Object invoke(Object[] argv) throws Throwable;
}
static final class Default implements InvocationHandlerFactory {
@Override
public InvocationHandler create(Target target, Map<Method, MethodHandler> dispatch) {
return new ReflectiveFeign.FeignInvocationHandler(target, dispatch);
}
}
}
代理对象: ReflectiveFeign.FeignInvocationHandler
static class FeignInvocationHandler implements InvocationHandler {
private final Target target;
private final Map<Method, MethodHandler> dispatch;
FeignInvocationHandler(Target target, Map<Method, MethodHandler> dispatch) {
this.target = checkNotNull(target, "target");
this.dispatch = checkNotNull(dispatch, "dispatch for %s", target);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
return dispatch.get(method).invoke(args);
}
}
时序图
执行原理:
- 创建EchoClient的代理类Proxy,它的
invocationHandler 类型是ReflectiveFeign.FeignInvocationHandler - 根据
Map<Method, MethodHandler> dispatch 找到method 对应的MethodHandler,最终逻辑交由它来执行,此时的methoHandler类型为SynchronousMethodHandler 3.SynchronousMethodHandler 的操作分为如下步骤
- 根据Contract和Target.type()解析List<MethodMetadata> metadata,创建
空的 RequestTemplate - 解析调用方法的
args[] ,填充 RequestTemplate - 将
RequestTemplate 转换为Request - 使用Client.execute(request,option),它内部使用HttpURLConnection来执行
http 请求。
生成的代理类
详细生成方法: https://blog.csdn.net/it_freshman/article/details/78873842
|