微服务中,不同的服务之间相互调用主要有两种方式,一种是RPC方式,另外一种是事件驱动方式,也就是发消息方式。
RPC的方式就是远程函数调用,像RESTFul,gRPC, DUBBO 都是这种方式。它一般是同步的,可以马上得到结果。在实际中,大多数应用都要求立刻得到结果,这时同步方式更有优势,代码也更简单。而feign自然也是基于这种方式来做的。
什么是Feign feign的github上的介绍:
Feign is a Java to HTTP client binder inspired by Retrofit, JAXRS-2.0, and WebSocket. Feign’s first goal was reducing the complexity of binding Denominator uniformly to HTTP APIs regardless of ReSTfulness.
简单的来说,Feign是由Retrofit,JAXRS-2.0和WebSocket启发的一个java的 RESTFUL api请求工具。 Feign的主要目标是将Java Http Clients变得简单。
简单使用Feign 首先在启动类上添加注解:
1 2 3 4 5 6 7 @SpringCloudApplication @EnableFeignClients public class Application { public static void main (String[] args) { SpringApplication.run(Application.class, args); } }
然后编写一个接口:
1 2 3 4 5 6 7 8 @FeignClient(name = "item-service") public interface UserClient { @GetMapping("/{id}") User getUserById (@PathVariable("id") Long id) ; @GetMapping("/list") List<User> getUserByIds (@RequestParam("ids") List<Long> ids) ; }
最后在controller中调用:
1 2 3 4 5 6 7 8 9 10 11 12 13 @Controller @RequestMapping("/user") public class UserController { @Autowired private UserClient userClient; @GetMapping @ResponseBody public User queryUserById (@RequestParam("id") Long id) { User user = this .userClient.queryUserById(id); return user; } }
上面就是feign的整个流程了,只需要创建一个接口来描述http请求,然后我们就可以像调用java方法一样完成api的请求,非常的优雅。
Feign原理 从上面的Feign的使用来看,Feign就是充当了一个适配器的角色:将一个java接口翻译成一个http请求,然后用HttpURLConnection去发送这个请求。
首先通过扫包注入FeignClient的bean,这个源码在FeignClientsRegistrar类中:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 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" )); } }
这段代码先在启动配置上检查是否有@EnableFeignClients注解,如果有,则开启包扫描,扫描被@FeignClient注解的接口,扫描到FeignClient之后,将其注入到ioc容器中。
然后就是核心实现,通过jdk动态代理,在请求FeignClient的方法的时候,会拦截这个方法。具体代码在ReflectiveFeign类,如下:
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 @SuppressWarnings("unchecked") @Override public <T> T newInstance (Target<T> target) { 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; }
最终是在SynchronousMethodHandler类进行拦截处理,通过参数生成一个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 31 32 33 34 @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 ; } } } Object executeAndDecode (RequestTemplate template) throws Throwable { Request request = targetRequest(template); Response response; long start = System.nanoTime(); try { response = client.execute(request, options); 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); } }
然后将这个request对象交个client处理,处理完成之后获取到response。client是一个接口,默认的实现为它的一个静态内部类Default,在这个类中,使用HttpURLConnection完成的网络请求:
1 2 3 4 5 @Override public Response execute (Request request, Options options) throws IOException { HttpURLConnection connection = convertAndSend(request, options); return convertResponse(connection).toBuilder().request(request).build(); }
Feign负载均衡实现 最终向容器中注入的是LoadBalancerFeignClient,即负载均衡客户端:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 @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); } }
可以看到是调用execute方法,最终是通过executeWithLoadBalancer方法获取结果的:
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 T executeWithLoadBalancer (final S request, final IClientConfig requestConfig) throws ClientException { LoadBalancerCommand<T> command = buildLoadBalancerCommand(request, requestConfig); try { return command.submit( new ServerOperation <T>() { @Override public Observable<T> call (Server server) { URI finalUri = reconstructURIWithServer(server, request.getUri()); S requestForServer = (S) request.replaceUri(finalUri); try { return Observable.just(AbstractLoadBalancerAwareClient.this .execute(requestForServer, requestConfig)); } catch (Exception e) { return Observable.error(e); } } }) .toBlocking() .single(); } catch (Exception e) { Throwable t = e.getCause(); if (t instanceof ClientException) { throw (ClientException) t; } else { throw new ClientException (e); } } }
最终在submit方法上:
1 2 3 4 5 6 7 8 9 Observable<T> o = (server == null ? selectServer() : Observable.just(server)) .concatMap(new Func1 <Server, Observable<T>>() { @Override public Observable<T> call (Server server) { });
通过selectServer方法,实现负载均衡:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 private Observable<Server> selectServer () { return Observable.create(new OnSubscribe <Server>() { @Override public void call (Subscriber<? super Server> next) { try { Server server = loadBalancerContext.getServerFromLoadBalancer(loadBalancerURI, loadBalancerKey); next.onNext(server); next.onCompleted(); } catch (Exception e) { next.onError(e); } } }); }
最终交给loadBalancerContext也就是Ribbon来做的。
回顾:
通过@EnableFeignClients开启FeignClient
扫包,将@FeignClient注解的类加入到ioc容器
接口方法被调用,通过jdk动态代理拦截,生成Request
Requset交给Client处理,Client又被包装为LoadBalanceClient类,这个类结合Ribbon做负载均衡,最终生成Response