feign10.12源码解析

[toc]

时序图

主要组件

Contract

Contract用于解析method源数据、参数类型和注解等。

默认使用feign.Contract.Default。注册处理了@Headers,@RequestLine,@Body,@Param等注解。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
public Default() {
super.registerClassAnnotation(Headers.class, (header, data) -> {
final String[] headersOnType = header.value();
checkState(headersOnType.length > 0, "Headers annotation was empty on type %s.",
data.configKey());
final Map<String, Collection<String>> headers = toMap(headersOnType);
headers.putAll(data.template().headers());
data.template().headers(null); // to clear
data.template().headers(headers);
});
super.registerMethodAnnotation(RequestLine.class, (ann, data) -> {
final String requestLine = ann.value();
checkState(emptyToNull(requestLine) != null,
"RequestLine annotation was empty on method %s.", data.configKey());

final Matcher requestLineMatcher = REQUEST_LINE_PATTERN.matcher(requestLine);
if (!requestLineMatcher.find()) {
throw new IllegalStateException(String.format(
"RequestLine annotation didn't start with an HTTP verb on method %s",
data.configKey()));
} else {
data.template().method(HttpMethod.valueOf(requestLineMatcher.group(1)));
data.template().uri(requestLineMatcher.group(2));
}
data.template().decodeSlash(ann.decodeSlash());
data.template()
.collectionFormat(ann.collectionFormat());
});
super.regodAnnotation(Body.class, (ann, data) -> {
final String body = ann.value();
checkState(emptyToNull(body) != null, "Body annotation was empty on method %s.",
data.configKey());
if (body.indexOf('{') == -1) {
data.template().body(body);
} else {
data.template().bodyTemplate(body);
}
});
...
...

对于我们平常使用的Spring来说,使用的是SpringMVC的注解。在spring-cloud-openfeign-core中,Spring提供了SpringMvcContract,注册了SpringMVC的注解。

Encoder和Decoder

Encoder和Decoder主要对Request和Responde的编码和解码

Client

Client用于执行最终的http请求。

1
2
3
4
5
6
7
8
9
10
11
12
public interface Client {

/**
* Executes a request against its {@link Request#url() url} and returns a response.
*
* @param request safe to replay.
* @param options options to apply to this request.
* @return connected response, {@link Response.Body} is absent or unread.
* @throws IOException on a network error connecting to {@link Request#url()}.
*/
Response execute(Request request, Options options) throws IOException;
}

默认使用的是Client.Default,通过源码可以看到默认是使用HttpURLConnection来请求http的。

1
2
3
4
5
6
7
class Default implements Client {
@Override
public Response execute(Request request, Options options) throws IOException {
HttpURLConnection connection = convertAndSend(request, options);
return convertResponse(connection, request);
}
}

Feign对HttpClient和OkHttp的支持。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public final class ApacheHttpClient implements Client {
private final HttpClient client;

public ApacheHttpClient() {
this(HttpClientBuilder.create().build());
}
@Override
public Response execute(Request request, Request.Options options) throws IOException {
HttpUriRequest httpUriRequest;
try {
httpUriRequest = toHttpUriRequest(request, options);
} catch (URISyntaxException e) {
throw new IOException("URL '" + request.url() + "' couldn't be parsed into a URI", e);
}
HttpResponse httpResponse = client.execute(httpUriRequest);
return toFeignResponse(httpResponse, request);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
public final class OkHttpClient implements Client {
private final okhttp3.OkHttpClient delegate;

public OkHttpClient() {
this(new okhttp3.OkHttpClient());
}

public OkHttpClient(okhttp3.OkHttpClient delegate) {
this.delegate = delegate;
}
@Override
public feign.Response execute(feign.Request input, feign.Request.Options options)
throws IOException {
okhttp3.OkHttpClient requestScoped;
if (delegate.connectTimeoutMillis() != options.connectTimeoutMillis()
|| delegate.readTimeoutMillis() != options.readTimeoutMillis()
|| delegate.followRedirects() != options.isFollowRedirects()) {
requestScoped = delegate.newBuilder()
.connectTimeout(options.connectTimeoutMillis(), TimeUnit.MILLISECONDS)
.readTimeout(options.readTimeoutMillis(), TimeUnit.MILLISECONDS)
.followRedirects(options.isFollowRedirects())
.build();
} else {
requestScoped = delegate;
}
Request request = toOkHttpRequest(input);
Response response = requestScoped.newCall(request).execute();
return toFeignResponse(response, input).toBuilder().request(input).build();
}
}

Target

Target比较没什么存在感,但是却关系到最后的Request生成。

1
2
3
public interface Target<T> {
public Request apply(RequestTemplate input);
}

默认使用feign.Target.HardCodedTarget,用于将baseuri和请求的path进行拼接。

1
2
3
4
5
6
7
8
9
public static class HardCodedTarget<T> implements Target<T> {
@Override
public Request apply(RequestTemplate input) {
if (input.url().indexOf("http") != 0) {
input.target(url());
}
return input.request();
}
}

在feign源码中,还有另外一个实现类LoadBalancingTarget

1
2
3
4
5
6
7
8
9
10
11
12
13
public class LoadBalancingTarget<T> implements Target<T> {
@Override
public Request apply(RequestTemplate input) {
Server currentServer = lb.chooseServer(null);
String url = format("%s://%s%s", scheme, currentServer.getHostPort(), path);
input.target(url);
try {
return input.request();
} finally {
lb.getLoadBalancerStats().incrementNumRequests(currentServer);
}
}
}

Capability

在feign客户端构建过程中,对以上组件都可以进行能力加强

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
  public Feign build() {
// Capability 能力加强
Client client = Capability.enrich(this.client, capabilities);
Retryer retryer = Capability.enrich(this.retryer, capabilities);
List<RequestInterceptor> requestInterceptors = this.requestInterceptors.stream()
.map(ri -> Capability.enrich(ri, capabilities))
.collect(Collectors.toList());
Logger logger = Capability.enrich(this.logger, capabilities);
Contract contract = Capability.enrich(this.contract, capabilities);
Options options = Capability.enrich(this.options, capabilities);
Encoder encoder = Capability.enrich(this.encoder, capabilities);
Decoder decoder = Capability.enrich(this.decoder, capabilities);
InvocationHandlerFactory invocationHandlerFactory =
Capability.enrich(this.invocationHandlerFactory, capabilities);
QueryMapEncoder queryMapEncoder = Capability.enrich(this.queryMapEncoder, capabilities);

...

}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
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())
//找到方法名为"enrich",并且返回类型相等的方法.
.filter(method -> method.getName().equals("enrich"))
.filter(method -> method.getReturnType().isInstance(target))
.findFirst()
.map(method -> {
try {
return (E) method.invoke(capability, target);
} catch (IllegalAccessException | IllegalArgumentException
| InvocationTargetException e) {
throw new RuntimeException("Unable to enrich " + target, e);
}
})
.orElse(target);
}

在feign源码中有这些实现类HystrixCapability,Metrics4Capability,Metrics5Capability,MicrometerCapability.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
public final class HystrixCapability implements Capability {

private SetterFactory setterFactory = new SetterFactory.Default();
private final Map<Class, Object> fallbacks = new HashMap<>();

/**
* Allows you to override hystrix properties such as thread pools and command keys.
*/
public HystrixCapability setterFactory(SetterFactory setterFactory) {
this.setterFactory = setterFactory;
return this;
}

@Override
public Contract enrich(Contract contract) {
return new HystrixDelegatingContract(contract);
}

@Override
public InvocationHandlerFactory enrich(InvocationHandlerFactory invocationHandlerFactory) {
return (target, dispatch) -> new HystrixInvocationHandler(target, dispatch, setterFactory,
fallbacks.containsKey(target.type())
? new FallbackFactory.Default<>(fallbacks.get(target.type()))
: null);
}

public <E> Capability fallback(Class<E> api, E fallback) {
fallbacks.put(api, fallback);

return this;
}

}

Spring整合

在spring-cloud-openfeign-core下,提供了Spring和feign的整合.

FeignClientFactoryBean:创建feign代理类

SpringMvcContract:解析methodMetaData

FeignClientsConfiguration:配置feign的一些组件

FeignAutoConfiguration:配置httpclient