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 {
/**
* /echo/welcome/{name}
*/
@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(); // optional
logger.info("time: " + stopwatch); // formatted string like "12.3 ms"
return response;
}
}
}
test-case
@Test
public void test01() {
Feign feign = Feign.builder()
//SSLSocketFactory:SSLSocket工厂
//HostnameVerifier:实现主机名验证功能;在握手期间,如果URL的主机名和服务器的标识主机名不匹配,则验证机制可以回调此接口实现程序来确定是否应该允许此连接
.client(new Client.Default(null, null))
.encoder(new Encoder.Default())
.decoder(new Decoder.Default())
.contract(new Contract.Default()) // 契约: 定义@FeignClient接口中支持的 注解和值的类型
.options(new Request.Options(100,200)) //定义如超时时间等
.retryer(Retryer.NEVER_RETRY) //重试策略
.logLevel(Logger.Level.FULL)
.errorDecoder(new ErrorDecoder.Default())
.queryMapEncoder(new BeanQueryMapEncoder())
.addCapability(new ClientWatchCapability())
// .decode404()
//添加拦截器
.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 {
/**
* Called for every request.
* Add data using methods on the supplied {@link RequestTemplate}.
*/
void apply(RequestTemplate template);
}
Capability
public interface Capability {
/**
* 将capabilities拓展能力依次 增强componentToEnrich
*/
static <E> E enrich(E componentToEnrich, List<Capability> capabilities) {
return capabilities.stream()
// invoke each individual capability and feed the result to the next one.
// This is equivalent to:
// Capability cap1 = ...;
// Capability cap2 = ...;
// Capability cap2 = ...;
// Contract contract = ...;
// Contract contract1 = cap1.enrich(contract);
// Contract contract2 = cap2.enrich(contract1);
// Contract contract3 = cap3.enrich(contract2);
// or in a more compact version
// Contract enrichedContract = cap3.enrich(cap2.enrich(cap1.enrich(contract)));
.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() {
//一系列Capability.enrich()增强....
//1.
SynchronousMethodHandler.Factory synchronousMethodHandlerFactory =
new SynchronousMethodHandler.Factory(client, retryer, requestInterceptors, logger,
logLevel, decode404, closeAfterDecode, propagationPolicy, forceDecoding);
//2.
ParseHandlersByName handlersByName =
new ParseHandlersByName(contract, options, encoder, decoder, queryMapEncoder,
errorDecoder, synchronousMethodHandlerFactory);
//3. 构建ReflectiveFeign返回
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) {
//a. factory创建 SynchronousMethodHandler
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 {
//1.1 使用传递来的方法参数argv来创建请求模板RequestTemplate。 --- 见BuildTemplateByResolvingArgs
RequestTemplate template = buildTemplateFromArgs.create(argv);
//1.2 查找参数中是否存在Options: 用来定义如连接超时等配置
Options options = findOptions(argv);
Retryer retryer = this.retryer.clone();
while (true) {
//重试---略
//1.3 执行
return executeAndDecode(template, options);
}
}
Object executeAndDecode(RequestTemplate template, Options options) throws Throwable {
//1.4
Request request = targetRequest(template);
//略....
//1.5 调用client.execute() --- 内部调用 HttpURLConnection执行http请求
Response response = client.execute(request, options);
return decoder.decode(response, metadata.returnType());
}
//1.4
Request targetRequest(RequestTemplate template) {
//for-each 执行RequestInterceptor.apply(RequestTemplate)
for (RequestInterceptor interceptor : requestInterceptors) {
interceptor.apply(template);
}
//将RequestTemplate转换为实际的Request对象
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);
//Method中是否存在参数累心为URI,如果有,将该参数url设置为Target的url
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);
}
//md.configkey == target.type + method.name +args 组成 方法的指纹id
//为各个方法创建 MethodHandler
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; // invocationHandlerFactory.Default
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) {
//1 为target.targetType所有的方法 创建对应的 MethodHandler
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)) { //isDefault略...
DefaultMethodHandler handler = new DefaultMethodHandler(method);
defaultMethodHandlers.add(handler);
methodToHandler.put(method, handler);
} else {
//2 记录target.targetType中method 和它对应的targetType
methodToHandler.put(method, nameToHandler.get(Feign.configKey(target.type(), method)));
}
//3 InvocationHandlerFactory.Default创建代理对象
InvocationHandler handler = factory.create(target, methodToHandler);
//3 创建实例
T proxy = (T) Proxy.newProxyInstance(target.type().getClassLoader(),
new Class<?>[] {target.type()}, handler);
//5 default略...
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 {
//找到method对应的 MethodHandler,执行invoke方法;
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
请求。
- 根据Contract和Target.type()解析List<MethodMetadata> metadata,创建
生成的代理类
详细生成方法: https://blog.csdn.net/it_freshman/article/details/78873842