Feign之远程JDK代理对象请求发送链路追踪(三)

1.前言

从第一篇和第二篇中,可以看到Feign最终会为每个带有@FeignClient注解的interface生成一个JDK代理对象。那么在在通过feign进行远程调用时,一定会走到这个类的invoke方法中去。所以接下来我们探究一下invoke方法究竟在干什么~~
Feign之远程JDK代理对象请求发送链路追踪(三)_第1张图片



2.FeignInvocationHandler的invoke方法

@Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
      if ("equals".equals(method.getName())) {
        try {
          Object otherHandler =
              args.length > 0 && args[0] != null ? Proxy.getInvocationHandler(args[0]) : null;
          return equals(otherHandler);
        } catch (IllegalArgumentException e) {
          return false;
        }
      } else if ("hashCode".equals(method.getName())) {
        return hashCode();
      } else if ("toString".equals(method.getName())) {
        return toString();
      }
			// 这个代码块中,上面基本都是走不到的,核心就是这个了。
      return dispatch.get(method).invoke(args);
    }

2.1 那么这个dispatch是什么呢?

Feign之远程JDK代理对象请求发送链路追踪(三)_第2张图片

这是我自己debug出来的dispatch中的信息。其实就是关于接口中每个方法的信息还有ReflectiveFeign所包含的信息。
Feign之远程JDK代理对象请求发送链路追踪(三)_第3张图片

2.2 dispatch怎么来的呢?

其实就是在ReflectiveFeignnewInstance方法中构建并传入代理对象的InvocationHanlder的一组Map
Feign之远程JDK代理对象请求发送链路追踪(三)_第4张图片

下面是nameToHandler获取与结构图
Feign之远程JDK代理对象请求发送链路追踪(三)_第5张图片

这里其实我们就可以看出,每个Feign的JDK代理对象的InvocationHanlder中都包含一个SynchronousMethodHandler的集合。我们想要获取接口方法的任何信息都可以通过SynchronousMethodHandler来完成。

那么我们就能知道dispatch.get(method)获取的就是一个SynchronousMethodHandler

接下来就是SynchronousMethodHandlerinvoke调用了。



3.SynchronousMethodHandler的invoke方法

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) {
        try {
					// 重试间隔计算与睡眠(sleep),如果超过次数,直接抛出异常
          retryer.continueOrPropagate(e);
        } catch (RetryableException th) {
          Throwable cause = th.getCause();
          if (propagationPolicy == UNWRAP && cause != null) {
            throw cause;
          } else {
            throw th;
          }
        }
        if (logLevel != Logger.Level.NONE) {
          logger.logRetry(metadata.configKey(), logLevel);
        }
        continue;
      }
    }
  }

3.1 RequestTemplate http请求构建模板

Feign之远程JDK代理对象请求发送链路追踪(三)_第6张图片

RequestTemplate中包含头信息,queryString参数,请求方法,请求体,uri信息等等。这基本都是发送http请求需要的基础信息。


3.2 executeAndDecode 执行请求并解码返回结果

Object executeAndDecode(RequestTemplate template) throws Throwable {
		// 将RequestTemplate储存的请求信息转化为Request
		// 对了SynchronousMethodHandler中的拦截器也得加到request中
		// 其实这里的拦截器并不是http请求的拦截器,这个拦截器的作用其实是对RequestTemplate的
		// 毕竟这个Request是通过RequestTemplate创建的
    Request request = targetRequest(template);

    if (logLevel != Logger.Level.NONE) {
      logger.logRequest(metadata.configKey(), logLevel, request);
    }

    Response response;
    long start = System.nanoTime();
    try {
			// 通过LoadBalancerFeignClient来继续往下执行
      response = client.execute(request, options);
    } catch (IOException e) {
	      // 打日志抛异常
    }
    long elapsedTime = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start);

    boolean shouldClose = true;
    try {
      if (logLevel != Logger.Level.NONE) {
        response =
            logger.logAndRebufferResponse(metadata.configKey(), logLevel, response, elapsedTime);
      }
      if (Response.class == metadata.returnType()) {
				// 返回结果直接为Reponse的
        if (response.body() == null) {
          return response;
        }
				// 对于响应结果为空和超过最大响应长度的,不进行转化处理
        if (response.body().length() == null ||
            response.body().length() > MAX_RESPONSE_BUFFER_SIZE) {
          shouldClose = false;
          return response;
        }
        // Ensure the response body is disconnected
        byte[] bodyData = Util.toByteArray(response.body().asInputStream());
        return response.toBuilder().body(bodyData).build();
      }
      if (response.status() >= 200 && response.status() < 300) {
				// 一般正常返回都会到这里
        if (void.class == metadata.returnType()) {
          //针对空返回
					return null;
        } else {
					// 解码器解码
          Object result = decode(response);
          shouldClose = closeAfterDecode;
          return result;
        }
      } else if (decode404 && response.status() == 404 && void.class != metadata.returnType()) {
        Object result = decode(response);
        shouldClose = closeAfterDecode;
        return result;
      } else {
        throw errorDecoder.decode(metadata.configKey(), response);
      }
    } catch (IOException e) {
      if (logLevel != Logger.Level.NONE) {
        logger.logIOException(metadata.configKey(), logLevel, e, elapsedTime);
      }
      throw errorReading(request, response, e);
    } finally {
      if (shouldClose) {
        ensureClosed(response.body());
      }
    }
  }

上面我们需要关注的其实就两个点:

  1. response = client.execute(request, options),将请求交给LoadBalancerFeignClient进行处理

  2. Object result = decode(response),对返回结果进行解码


3.3 LoadBalancerFeignClient的execute做了啥?

  1. 拿出服务名,清理url地址
  2. 构建FeignLoadBalancer.RibbonRequest
  3. 获取FeignLoadBalancer去执行executeWithLoadBalancer

那现在主要研究一下executeWithLoadBalancer这个方法,这个方法很关键,而且也比较比较复杂。

FeignLoadBalancerexecuteWithLoadBalancer方法在其父类AbstractLoadBalancerAwareClient中。


3.4 AbstractLoadBalancerAwareClient的executeWithLoadBalancer来自rxjava的降维打击

其实在真正看到这里之前,我一直都以为feign走的是ribbon+restTemplate的那套逻辑。直到看到了

AbstractLoadBalancerAwareClientexecuteWithLoadBalancer方法。

public T executeWithLoadBalancer(final S request, final IClientConfig requestConfig) throws ClientException {
		    // 构建一个LoadBalancerCommand,将LoadBalancerContext,request,requestConfig都丢到
				// 这个对象中,而LoadBalancerContext中就包含了ILoadBalancer,也就是
				// DyanmicServerListLoadBalancer
				LoadBalancerCommand<T> command = buildLoadBalancerCommand(request, requestConfig);

        try {
            return command.submit(
								// 传递进去一个匿名对象
                new ServerOperation<T>() {
                    @Override
										// 这个是在LoadBalancerCommand中会被调用
										// 在LoadBalancerCommand中,会通过ILoadBalancer去选择一个服务
										// 然后传递到这个方法中去
										// 所以feign与ribbon的结合,以及ribbon的服务选择在
										// LoadBalancerCommand中
                    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);
            }
        }
        
    }

在这个方法和LoadBalancerCommandsubmit方法中,存在一些Rxjava的类的使用,这个我还没有研究过,所以暂时只能先跳过,挑重点看了。

简单看一下LoadBalancerCommand方法的submit方法,看看如何选择服务的。
Feign之远程JDK代理对象请求发送链路追踪(三)_第7张图片
Feign之远程JDK代理对象请求发送链路追踪(三)_第8张图片

深入看一下selectServer方法
Feign之远程JDK代理对象请求发送链路追踪(三)_第9张图片

继续跟踪getServerFromLoadBalancer
Feign之远程JDK代理对象请求发送链路追踪(三)_第10张图片
ok,到此结束。

ribbon按照自己的策略选择了服务。


3.5 AbstractLoadBalancerAwareClient.this.execute(requestForServer, requestConfig) 将通过ribbon获取的服务进行http请求发送

具体的调用链路为:

  1. AbstractLoadBalancerAwareClient.this.execute
  2. org.springframework.cloud.openfeign.ribbon.FeignLoadBalancer#execute
  3. feign.Client.Default#execute
  4. feign.Client.Default#convertAndSend
    Feign之远程JDK代理对象请求发送链路追踪(三)_第11张图片
    最后直接用HttpURLConnection 直接撸了消息发送。这也是我始料未及的。

3.6 补个漏 解码器解码

Feign之远程JDK代理对象请求发送链路追踪(三)_第12张图片



4.小结

今天看了在进行远程Feign调用时,会通过注入的JDK代理对象调用FeignInvocationHandlerinvoke方法,然后一路最终到进行http请求发送。

现在简单描述一下整个流程和核心组件:

  1. feign请求首先进入FeignInvocationHandler的invoke方法,invoke进行方法处理器的选择
  2. FeignInvocationHandler通过invoke方法选择出对应方法的SynchronousMethodHandler进行处理
  3. SynchronousMethodHandler 构建请求模板RequestTemplate和请求Request
  4. SynchronousMethodHandler 将构建的请求交给LoadBalancerFeignClient
  5. LoadBalancerFeignClient构建RibbonRequest,将请求交给FeignLoadBalancer进行处理
  6. FeignLoadBalancer构建LoadBalancerCommand
  7. LoadBalancerCommand通过submit方法去调用ILoadBalancer.chooseServer(key)去获取server
  8. LoadBalancerCommand将获取的server交给FeignLoadBalancer
  9. FeignLoadBalancer将server和请求交给Client
  10. Client 通过HttpURLConnection 去创建连接并发送请求

至此,feign的请求的发送链路就算看完了。

其实里面还有关于Encoder如果组装头信息,queryString,消息体;

Contract如果将springmvc的注解进行解析并生成对应的url,这些暂时都还没去深入的看。

后续看看有没有探讨和深入的价值。

你可能感兴趣的:(springcloud,java,feign,spring,cloud,新星计划)