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 ); 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 { 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 () { 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()) .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<>(); 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