好久没写博客了, 突然想起自己的springcloud系列文章就讲了配置中心,注册中心,负载均衡和熔断。那么今天就来分析一下openfegin 的原理,后续会分析gateway的原理。
我个人的习惯,在使用springcloud 的组件时,会先去用一下原生依赖的使用方法,因为在springboot的自动装配下让使用变得简单,但使用也更加机械化,导致我们会无法看透到底在用一个什么东西。 所以我们先单独分析一下openfeign的使用以及其本身的原理,然后再来分析springcloud如何进行的整合(下篇)。
openfegin是由neflix公司开源出来,再迭代了数个版本之后彻底改名为openfeign,在被springcloud整合之后也是十分的受大家的欢迎。
public interface HelloOpenfeign {
@RequestLine("GET /feign?feign={feign}")
@Headers("Content-Type: application/x-www-form-urlencoded")
String feign(@Param("feign") String feign);
@RequestLine("POST /feignPost")
@Headers("Content-Type: application/x-www-form-urlencoded")
String feignPost(@Param("feign") String feign);
@RequestLine("POST /feignJson")
@Headers("Content-Type: application/json")
String feignForm(@Param("feign") String feign);
public static void main(String[] args) {
HelloOpenfeign client = Feign.builder().target(HelloOpenfeign.class, "http://localhost:12001");
String result = client.feign("YourBatman");
System.out.println(result);
}
}
哎,是不是没有想到,使用方法竟然如此简单, 其实就是解析接口所有的方法,根据你配置的注解完成http参数的拼装,然后给目的地的ip:port就可以了。最后获取动态代理对象,那么就像调用本地方法一样调用http请求了。
前面其实已经讲了内部原理的大致流程, 也就是解析接口中的方法, 然后生成代理对象; 在真正调用时,解析传递的参数,然后封装request,最后进行真正远程调用, 最后对返回的结果解析封装成接口方法的返回类。
下面开始结合源码进行关键点的分析。
入口方法就是 feign.Feign.Builder#target(java.lang.Class, java.lang.String)
public static class Builder {
... 省略其他方法
public <T> T target(Target<T> target) {
return build().newInstance(target);
}
public Feign build() {
// 用于创建真正执行的handler 的工厂
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);
// build模式创建的Feign接口的实现类
return new ReflectiveFeign(handlersByName, invocationHandlerFactory, queryMapEncoder);
}
}
然后来看一下ReflectiveFeign
public class ReflectiveFeign extends Feign {
@Override
public <T> T newInstance(Target<T> target) {
// 用 接口方法解析器解析所有的方法, 产生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)) {
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;
}
}
然后简单分析一下参数解析的过程, 主要是 ParseHandlersByName
这个类
public Map<String, MethodHandler> apply(Target key) {
List<MethodMetadata> metadata = contract.parseAndValidatateMetadata(key.type());
Map<String, MethodHandler> result = new LinkedHashMap<String, MethodHandler>();
for (MethodMetadata md : metadata) {
BuildTemplateByResolvingArgs buildTemplate;
if (!md.formParams().isEmpty() && md.template().bodyTemplate() == null) {
// 一般的post,有表单数据的生成器
buildTemplate = new BuildFormEncodedTemplateFromArgs(md, encoder, queryMapEncoder);
} else if (md.bodyIndex() != null) {
buildTemplate = new BuildEncodedTemplateFromArgs(md, encoder, queryMapEncoder);
} else {
// 一般的get, 没有表单数据的生成器
buildTemplate = new BuildTemplateByResolvingArgs(md, queryMapEncoder);
}
// 这里是关键的创建 SynchronousMethodHandler
result.put(md.configKey(),
factory.create(key, md, buildTemplate, options, decoder, errorDecoder));
}
return result;
}
看到这其实openfeign
的初始化流程已经很明确, 然后是动态代理的执行过程的分析 具体见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 {
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();
}
// 关键的方法
return dispatch.get(method).invoke(args);
}
... 省略多余代码
}
final class SynchronousMethodHandler implements MethodHandler {
@Override
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) {
Throwable cause = th.getCause();
if (propagationPolicy == UNWRAP && cause != null) {
throw cause;
} else {
throw th;
}
}
if (logLevel != Logger.Level.NONE) {
logger.logRetry(metadata.configKey(), logLevel);
}
continue;
}
}
}
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 {
// 这里又是经典的策略模式, 也是springcloud集成负载均衡的关键点
// 这里是进行远程调用
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.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);
}
}
... 省略多余代码
}
// 默认的client实现
class Default implements Client {
private final SSLSocketFactory sslContextFactory;
private final HostnameVerifier hostnameVerifier;
@Override
public Response execute(Request request, Options options) throws IOException { // 是最简单的 jdk提供的http连接器
HttpURLConnection connection = convertAndSend(request, options);
return convertResponse(connection, request);
}
}
基本上feigin的启动和执行过程都分析到了, 只有一些参数解析的和响应对象解析的没有分析(这些细节不影响我们宏观把控feign的流程).
整体来说 feign 的使用非常的便捷, 但是不爽的地方在于就我们需要自己调用Feign.builder().target
来生成代理对象.不过好在有spring来帮我们做了一些琐碎的事情来避免我们针对每个接口都调用target方法, 就像Aop的整合一样, 省去了我们自己生产动态代理对象.
下一篇我就会讲述springcloud下openfeign的使用,整合的原理以及自动配置的原理.