FeignClient

从源码分析FeignClient

1
public abstract class Feign

Feign这个类是为了简化对http apis的请求。

通过 newInstance 方法,产生Target,来代表对应的Http Apis。

1
public abstract <T> T newInstance(Target<T> target);

产生一个 http api的实例。

1
2
3
4
5
6
7
8
9
10
11
12
13
public interface Target<T> {
/* The type of the interface this target applies to. */
Class<T> type();

/* configuration key associated with this target. */
String name();

/* base HTTP URL of the target. */
String url();

//从 template中产生Request
public Request apply(RequestTemplate input);
}

apply被用来产生request,基于传入的template input,加入header和参数,产生一个不可变的Request。

例如:

1
2
3
4
5
public Request apply(RequestTemplate input) {
input.insert(0, url());
input.replaceHeader(&quot;X-Auth&quot;, currentToken);
return input.asRequest();
}

target的主要作用是产生Request, 作为Http请求的参数。

Feign的一个默认实现:ReflectiveFeign,newInstance最终返回了一个动态代理:

1
2
3
Proxy.newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)

最重要的是要看InvocationHandler的实现。

在初始化 Feign的时候,在Feign.Builder中,选择的FeignInvocationHandler作为InvocationHandler的实现。

FeignInvocationHandler中,包含多个MethodHandler, 默认实现为:SynchronousMethodHandler。

在SynchronousMethodHandler中,包含着Client和Retryer,这个类中,实现了最终的Http的请求。

MethodHandler

SynchronousMethodHandler , 在这个类里面最终实现了对 http client 的调用。

1
2
3
4
class SynchronousMethodHandler implements MethodHandler {
private final Client client;
private final Retryer retryer;
}

InvocationHandler

1
2
3
4
class FeignInvocationHandler implements InvocationHandler {

private final Target target;
private final Map<Method, MethodHandler> dispatch;

同时,在Feign.Builder中,

Client使用的是java默认的java.net.HttpUrlConnection

Retryer,的默认值为重试5次。

Feign-hystrix

对Feign的扩展:

1 容许Feign的接口返回HystrixCommand 或者 rx.Observable (通过HystrixDelegatingContract 来实现)

2 对接口调用进行包装,加入了断路器。(通过HystrixInvocationHandler实现)

在HystrixInvocationHandler中,invoke方法,对httpclient的调用,使用HystrixCommand的方式:

1
2
3
4
5
6
7
8
9
10
11
12
HystrixCommand<Object> hystrixCommand = new HystrixCommand<Object>(setterMethodMap.get(method)) {
@Override
protected Object run() throws Exception {
try {
return HystrixInvocationHandler.this.dispatch.get(method).invoke(args);
} catch (Exception e) {
throw e;
} catch (Throwable t) {
throw (Error) t;
}
}
}

与spring的集成

在spring-cloud-netflix-core中,有spring和feign集成的代码。

在FeignClientFactoryBean中,重新定义了 Feign.Builder:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class FeignClientFactoryBean implements FactoryBean<Object>, InitializingBean,
ApplicationContextAware {

@Override
public Object getObject() throws Exception {
FeignContext context = applicationContext.getBean(FeignContext.class);
//从FeignContext中生成Feign.Builder
Feign.Builder builder = feign(context);
if (!StringUtils.hasText(this.url)) {
String url;//处理url
//loadbalance ribbon?
return loadBalance(builder, context,
new HardCodedTarget<>(this.type,
this.name, url));
}

}

在spring中,Feign的初始化依赖于FeignContext。

1
2
3
4
5
6
7
public class FeignContext extends NamedContextFactory<FeignClientSpecification> {

public FeignContext() {
super(FeignClientsConfiguration.class, "feign", "feign.client.name");
}

}

NamedContextFactory可以创建一组子上下文, 每个子上下文中可以使用一组的Specification来定义bean

Ribbon

一个客户端负载均衡器,运行在客户端上。

###LoadBalancerClient

最重要的一个类: LoadBalancerClient 负载均衡器的客户端。

继承关系: ServiceInstanceChooser <- LoadBalancerClient <- RibbonLoadBalancerClient

1
2
3
4
5
6
7
8
9
10
11
/**
* Represents a client side load balancer
*/
public interface LoadBalancerClient extends ServiceInstanceChooser {

/**
* execute request using a ServiceInstance from the LoadBalancer for the specified
* service
*/
<T> T execute(String serviceId, LoadBalancerRequest<T> request) throws IOException;
//......
1
2
3
4
5
6
7
8
9
10
11
/**
* Implemented by classes which use a load balancer to choose a server to
* send a request to.
*/
public interface ServiceInstanceChooser {

/**
* Choose a ServiceInstance from the LoadBalancer for the specified service
*/
ServiceInstance choose(String serviceId);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
 class RibbonLoadBalancerClient implements LoadBalancerClient {
@Override
public <T> T execute(String serviceId, LoadBalancerRequest<T> request) throws IOException {
//最终选择service的任务还是交与ILoadBalancer来做。
ILoadBalancer loadBalancer = getLoadBalancer(serviceId);
Server server = getServer(loadBalancer);
if (server == null) {
throw new IllegalStateException("No instances available for " + serviceId);
}
RibbonServer ribbonServer = new RibbonServer(serviceId, server, isSecure(server,
serviceId), serverIntrospector(serviceId).getMetadata(server));

return execute(serviceId, ribbonServer, request);
}

###ILoadBalancer

最终选择service的任务还是交与ILoadBalancer来做。

ILoadBalancer <- BaseLoadBalancer <- DynamicServerListLoadBalancer

在DynamicServerListLoadBalancer中,是如何获取和刷新服务列表的?

首先在构造函数中->initWithNiwsConfig() -> restOfInit() -> updateListOfServers()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class DynamicServerListLoadBalance{
@VisibleForTesting
public void updateListOfServers() {
List<T> servers = new ArrayList<T>();
if (serverListImpl != null) {
servers = serverListImpl.getUpdatedListOfServers();
LOGGER.debug("List of Servers for {} obtained from Discovery client: {}",
getIdentifier(), servers);

if (filter != null) {
servers = filter.getFilteredListOfServers(servers);
LOGGER.debug("Filtered List of Servers for {} obtained from Discovery client: {}",
getIdentifier(), servers);
}
}
updateAllServerList(servers);
}

ServerList 的具体实现类: serverListImpl 来负责最终的刷新。ServerList 有各种实现,比方说用Consul的话,实现就是ConsulServerList。

参考:https://blog.csdn.net/forezp/article/details/74820899

FeignLoadBalancer

CachingSpringLoadBalancerFactory 会返回一个 FeignLoadBalancer

Robbin会retry:

在CachingSpringLoadBalancerFactory中,创建FeignLoadBalancer的时候

1
2
3
4
5
6
7
8
9
10
11
12
13
public FeignLoadBalancer create(String clientName) {
if (this.cache.containsKey(clientName)) {
return this.cache.get(clientName);
}
IClientConfig config = this.factory.getClientConfig(clientName);
ILoadBalancer lb = this.factory.getLoadBalancer(clientName);
ServerIntrospector serverIntrospector = this.factory.getInstance(clientName, ServerIntrospector.class);
//通过enableRetry来控制是否重试
FeignLoadBalancer client = enableRetry ? new RetryableFeignLoadBalancer(lb, config, serverIntrospector,
loadBalancedRetryPolicyFactory) : new FeignLoadBalancer(lb, config, serverIntrospector);
this.cache.put(clientName, client);
return client;
}

Feign and Ribbon

当结合使用Feign和Ribbon的时候, SynchronousMethodHandler 中的client 类型为:LoadBalancerFeignClient。 在execute的时候,会创建一个FeignLoadBalancer 来执行executeWithLoadBalancer(),这个函数的作用是把请求交给选中的服务来处理,而不是指定一个服务。FeignLoadBalancer包含一个ILoadBalancer

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
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) {
IOException io = findIOException(e);
if (io != null) {
throw io;
}
throw new RuntimeException(e);
}
}

}
1
2
3
4
5
6
7
8
9
10
11
public T executeWithLoadBalancer(final S request, final IClientConfig requestConfig) throws ClientException {
RequestSpecificRetryHandler handler = getRequestSpecificRetryHandler(request, requestConfig);
LoadBalancerCommand<T> command = LoadBalancerCommand.<T>builder()
.withLoadBalancerContext(this)
.withRetryHandler(handler)
.withLoadBalancerURI(request.getUri())
.build();

try {
//在这个里面选择一个服务进行调用。
return command.submit(