admin管理员组

文章数量:1794759

springCloud面试之Feign

springCloud面试之Feign

什么是Feign

Feign是Netflix公司开发的声明式、模板化的的HTTP客户端工具,可以帮助我们更加优雅的调用HTTP API,同时也使得微服务之间的调用变得简单。 springCloud feign进行了增强,使得Feign更够支持SpringMvc注解

Feign的作用
  • 作为HTTP的客户端替代RestTemplate,支持注解的方式
  • Feign组件中引入了Ribbon和Hystrix组件,这使得Feign也能够为我们提供负载均衡、熔断、降级的功能; 我下面的贴图来自GitHub:openFeign
  • Feign的原理

    先来看一下feign流程

    在微服务启动时,Feign会进行包扫描,对加@FeignClient注解的接口,按照注解的规则,创建远程接口的本地JDK Proxy代理实例。然后,将这些本地Proxy代理实例,注入到Spring IOC容器中。当远程接口的方法被调用,由Proxy代理实例去完成真正的远程访问,并且返回结果。

    下面跟着代码我们来一起看一下:

    1.这里重点讲一下LoadBalancerFeignClient

    public Response execute(Request request, Options options) throws IOException { try { //获取本次请求的URI URI asUri = URI.create(request.url()); //获取服务的名称,FeignClient中定义的服务的名称 String clientName = asUri.getHost(); URI uriWithoutHost = cleanUrl(request.url(), clientName); //存储URI RibbonRequest ribbonRequest = new RibbonRequest(this.delegate, request, uriWithoutHost); //存储connectTimeout、readTimeout的时间等 IClientConfig requestConfig = this.getClientConfig(options, clientName); /*接下来的处理,会分成两步 *1、this.lbClient(clientName) //这里主要是通过CachingSpringLoadBalancerFactory获取FeignLoadBalancer * 下面2步骤会分析如何获取的 *2、executeWithLoadBalancer(ribbonRequest, requestConfig)//这里就是通过LoadBalancer先选出需要调用的server,然后发送请求; * 具体是先通过LoadBalancerCommand获取到要调用的server信 * 然后再通过httpclient发送请求; * 从LazyTracingFeignClient * 下面3步骤会分析如何获取的 * */ return ((RibbonResponse)this.lbClient(clientName).executeWithLoadBalancer(ribbonRequest, requestConfig)).toResponse(); } catch (ClientException var8) { IOException io = this.findIOException(var8); if (io != null) { throw io; } else { throw new RuntimeException(var8); } } } }

    2.CachingSpringLoadBalancerFactory#create(String clientName)

    public FeignLoadBalancer create(String clientName) { //private volatile Map<String, FeignLoadBalancer> cache = new ConcurrentReferenceHashMap(); //cache是一个volatile修饰的Map, FeignLoadBalancer client = (FeignLoadBalancer)this.cache.get(clientName); if (client != null) { return client; } else { //缓存中没有找到,这里先进行config初始化; IClientConfig config = this.factory.getClientConfig(clientName); //从SpringClientFactory中获取ZoneAwareLoadBalancer //ZoneAwareLoadBalancer是ribbon中一个非常重要的类,它用来获取eureka中的server列表,并且进行服务列表的更新; ILoadBalancer lb = this.factory.getLoadBalancer(clientName); ServerIntrospector serverIntrospector = (ServerIntrospector)this.factory.getInstance(clientName, ServerIntrospector.class); //创建FeignLoadBalancer实例 FeignLoadBalancer client = this.loadBalancedRetryFactory != null ? new RetryableFeignLoadBalancer(lb, config, serverIntrospector, this.loadBalancedRetryFactory) : new FeignLoadBalancer(lb, config, serverIntrospector); //将该FeignLoadBalancer放入容器中 this.cache.put(clientName, client); return (FeignLoadBalancer)client; }

    3.选取server、并进行调用

    • AbstractLoadBalancerAwareClient#executeWithLoadBalancer()
      • LoadBalancerCommand.submit()先通过ZoneAwareLoadBalancer获取到server信,再执行调用
    public T executeWithLoadBalancer(final S request, final IClientConfig requestConfig) throws ClientException { LoadBalancerCommand command = this.buildLoadBalancerCommand(request, requestConfig); try { return (IResponse)command.submit(new ServerOperation<T>() { //submit函数中获取到了server信,并在回调函数中进行真正的服务请求 public Observable<T> call(Server server) { URI finalUri = AbstractLoadBalancerAwareClient.this.reconstructURIWithServer(server, request.getUri()); ClientRequest requestForServer = request.replaceUri(finalUri); try { //这里会调用FeignLoadBalancer#FeignLoadBalancer.RibbonResponse execute(...)方法 return Observable.just(AbstractLoadBalancerAwareClient.this.execute(requestForServer, requestConfig)); } catch (Exception var5) { return Observable.error(var5); } } }).toBlocking().single(); } catch (Exception var6) { ...... } }

    4.FeignLoadBalancer#FeignLoadBalancer.RibbonResponse execute(…)

    public FeignLoadBalancer.RibbonResponse execute(FeignLoadBalancer.RibbonRequest request, IClientConfig configOverride) throws IOException { Options options; if (configOverride != null) { //这里会覆盖RibbonProperties中的信 RibbonProperties override = RibbonProperties.from(configOverride); options = new Options(override.connectTimeout(this.connectTimeout), override.readTimeout(this.readTimeout)); } else { options = new Options(this.connectTimeout, this.readTimeout); } /*下面会通过client进行查询,由于我的项目中引入了zipkin,所以这里会调用org.springframework.cloud.sleuth.instrument.web.client.feign.LazyTracingFeignClient * ==>LazyTracingFeignClient.execute(Request request, Options options) * ==>TracingFeignClient.execute(Request request, Options options) * ==>Client.Default.execute(Request request, Options options) */ Response response = request.client().execute(request.toRequest(), options); return new FeignLoadBalancer.RibbonResponse(request.getUri(), response); } 推荐阅读:

    springCloud面试之feign+ribbon+hystirx交互概览

    本文标签: SpringCloudFeign