产生原因
主要就是为了更加快捷和方便的发送http请求,屏蔽了底层的协议,只需要非常简单的操作就可以发起请求了
OpenFeign与Feign的关系
feign
是spring cloud
组件中的一个轻量级restful
的http
服务客户端,简化接口调用,将http
调用转为rpc
调用,让调用远程接口像调用同进程应用内的接口调用一样简单。
与dubbo
的rpc
远程调用一样,通过动态代理实现接口的调用。feign
通过封装包装请求体、发送http
请求、获取接口响应结果、序列化响应结果等接口调用动作来简化接口的调用。
openfeign
则是spring cloud
在feign
的基础上支持了spring mvc
的注解,如@RequesMapping
、@GetMapping
、@PostMapping
等。openfeign
还实现与Ribbon
的整合。
服务提供者只需要提供API
接口,而不需要像dubbo
那样需要强制使用implements
实现接口,使用fegin
不要求服务提供者在Controller
使用implements
关键字实现接口。
架构图
使用
注解
@EnableFeignClients
由于是导入了FeignClientsRegistrar这个类,从名字就知道是 feign客户端导入的类
FeignClientsRegistrar
这个类实现了四个接口
- ResourceLoaderAware 资源导入
- ImportBeanDefinitionRegistrar 导入bean
- BeanClassLoaderAware bean类加载器感知
- EnvironmentAware 环境感知
@Override
public void registerBeanDefinitions(AnnotationMetadata metadata,
BeanDefinitionRegistry registry) {
//默认配置注入
registerDefaultConfiguration(metadata, registry);
//feignclient注入
registerFeignClients(metadata, registry);
}
registerDefaultConfiguration
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"));
}
}
- 获取元数据
- 对于元数据为空的两种情况处理
- 有默认配置并且有的类被被禁用了
- 有默认并且没有禁用
- 将client配置注册进去
private void registerClientConfiguration(BeanDefinitionRegistry registry, Object name,
Object configuration) {
BeanDefinitionBuilder builder = BeanDefinitionBuilder
.genericBeanDefinition(FeignClientSpecification.class);
builder.addConstructorArgValue(name);
builder.addConstructorArgValue(configuration);
registry.registerBeanDefinition(
name + "." + FeignClientSpecification.class.getSimpleName(),
builder.getBeanDefinition());
}
FeignClientSpecification 就是clien具体一个类,会有对应的属性,配置
registerClientConfiguration
private void registerClientConfiguration(BeanDefinitionRegistry registry, Object name,
Object configuration) {
//genericBeanDefinition 这个方法才是真正生成一个bean的方法
BeanDefinitionBuilder builder = BeanDefinitionBuilder
.genericBeanDefinition(FeignClientSpecification.class);
builder.addConstructorArgValue(name);
builder.addConstructorArgValue(configuration);
//registerBeanDefinition方法进去就是到了spring注册bean的过程了,所以到此为止
registry.registerBeanDefinition(
name + "." + FeignClientSpecification.class.getSimpleName(),
builder.getBeanDefinition());
}
FeignInvocationHandler
feign的动态代理是在ReflectiveFeign里面的一个静态内部类中的
@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);
}
@Override
public boolean equals(Object obj) {
if (obj instanceof FeignInvocationHandler) {
FeignInvocationHandler other = (FeignInvocationHandler) obj;
return target.equals(other.target);
}
return false;
}
@Override
public int hashCode() {
return target.hashCode();
}
@Override
public String toString() {
return target.toString();
}
}
invoke,就是调用的MethodHandler 的invoke的方法
MethodHandler
interface MethodHandler {
Object invoke(Object[] argv) throws Throwable;
}
invoke 两个实现类,DefaultMethodHandler 和SynchronousMethodHandler
DefaultMethodHandler
@Override
public Object invoke(Object[] argv) throws Throwable {
if(handle == null) {
throw new IllegalStateException("Default method handler invoked before proxy has been bound.");
}
return handle.invokeWithArguments(argv);
}
如果处理器不为空,就走这个方法,invokeWithArguments 是java反射里面的一个方法,所以到此为止
SynchronousMethodHandler
@Override
public Object invoke(Object[] argv) throws Throwable {
RequestTemplate template = buildTemplateFromArgs.create(argv);
Retryer retryer = this.retryer.clone();
while (true) {
try {
return executeAndDecode(template);
} catch (RetryableException e) {
retryer.continueOrPropagate(e);
if (logLevel != Logger.Level.NONE) {
logger.logRetry(metadata.configKey(), logLevel);
}
continue;
}
}
}
executeAndDecode
Object executeAndDecode(RequestTemplate template) 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);
// ensure the request is set. TODO: remove in Feign 10
response.toBuilder().request(request).build();
} 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);
// ensure the request is set. TODO: remove in Feign 10
response.toBuilder().request(request).build();
}
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 {
return decode(response);
}
} else if (decode404 && response.status() == 404 && void.class != metadata.returnType()) {
return decode(response);
} 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());
}
}
}
SynchronousMethodHandler的invoke(…)方法,调用了自己的executeAndDecode(…) 请求执行和结果解码方法。该方法的工作步骤:
- 通过RequestTemplate 请求模板实例,生成远程URL请求实例 request
- 用自己的 feign 客户端client成员,excecute(…) 执行请求,并且获取 response 响应
- 对response 响应进行结果解码
Client
这个接口中有一个execute方法,这个方法有两个实现,一个是Default,一个是LoadBalancerFeignClient
这个和我参考中的不一样,应该是版本原因,我不是使用的源码在看到,是直接用依赖看的,我使用的依赖是
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-feign</artifactId>
<version>1.4.7.RELEASE</version>
</dependency>
- Default就是直接发送了http请求
- LoadBalancerFeignClient 内部使用了 Ribben 客户端负载均衡技术完成URL请求处理。在原理上,简单的使用了delegate包装代理模式:Ribben负载均衡组件计算出合适的服务端server之后,由内部包装 delegate 代理客户端完成到服务端server的HTTP请求;所封装的 delegate 客户端代理实例的类型,可以是 Client.Default 默认客户端,也可以是 ApacheHttpClient 客户端类或OkHttpClient 高性能客户端类,还可以其他的定制的feign.Client 客户端实现类型。
评论区