图解+源码讲解 Feign 请求的流程

541 阅读4分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第21天,点击查看活动详情

图解+源码讲解 Feign 请求的流程

骐骥一跃,不能十步;驽马十驾,功在不舍 -- 荀子

feign 相关文章
图解+源码讲解 Feign 如何将客户端注入到容器中
图解+源码讲解动态代理获取 FeignClient 代理对象
图解+源码讲解代理对象 ReflectiveFeign 分析
图解+源码讲解 Feign 如何选取指定服务
图解+源码讲解 Feign 请求的流程
ribbon 相关文章
图解+源码讲解 Ribbon 如何获取注册中心的实例
图解+源码讲解 Ribbon 原理初探
图解+源码讲解 Ribbon 服务列表更新
图解+源码讲解 Ribbon 服务选择原理
Ribbon 原理初探
eureka 相关文章
eureka-server 项目结构分析
图解+源码讲解 Eureka Server 启动流程分析
图解+源码讲解 Eureka Client 启动流程分析
图解+源码讲解 Eureka Server 注册表缓存逻辑
图解+源码讲解 Eureka Client 拉取注册表流程
图解+源码讲解 Eureka Client 服务注册流程
图解+源码讲解 Eureka Client 心跳机制流程
图解+源码讲解 Eureka Client 下线流程分析
图解+源码讲解 Eureka Server 服务剔除逻辑
图解+源码讲解 Eureka Server 集群注册表同步机制

回顾上文

    执行的是 FeignLoadBalancer 父类 AbstractLoadBalancerAwareClient 的 executeWithLoadBalancer的方法,这个方法里面创建了一个 LoadBalancerCommand 指令,进行任务提交操作,后续的请求应该就是command.submit()方法里面了

public T executeWithLoadBalancer(final S request, final IClientConfig requestConfig) 
    // 创建负载均衡指令
    LoadBalancerCommand<T> command = buildLoadBalancerCommand(request, requestConfig);
    // 提交一个任务操作
    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);
                // 执行请求
                return Observable.just(
                    AbstractLoadBalancerAwareClient.
                    this.execute(requestForServer, requestConfig));
             
            }
        }).toBlocking().single();
}

command.submit()

    我们慢慢来看这个 submit 方法,这个方法好长我们分解来看,就看看里面的核心方法

public Observable<T> submit(final ServerOperation<T> operation) {
    // Use the load balancer
    Observable<T> o = 
        // 这里面的 selectServer() 方法是进行服务选择
            (server == null ? selectServer() : Observable.just(server))
}

服务选择 selectServer()

private Observable<Server> selectServer() {
    return Observable.create(new OnSubscribe<Server>() {
        @Override
        public void call(Subscriber<? super Server> next) {
        // 获取服务 192.168.60.1:9200
        // 通过ribbon 负载均衡器 进行服务获取
        Server server = 
            loadBalancerContext.
            getServerFromLoadBalancer(loadBalancerURI, loadBalancerKey);   
        next.onNext(server);
        next.onCompleted();
        }
    });
}

    选择的服务结果,其实这个结果是通过ribbon的负载均衡器中获取的,通过轮询算法
image.png

  .concatMap(new Func1<Server, Observable<T>>() {
                @Override
                // Called for each server being selected
                public Observable<T> call(Server server) {
                    // 设置所要访问的服务
                    context.setServer(server);
  // 省略一些代码
}

    到这里已经设置完我们所要访问的服务了,后面是该发起服务请求了

发起请求

    this.execute(requestForServer, requestConfig),这个代码是进行服务网络请求,FeignLoadBalancerexecute 请求,显示设置连接超时和读取超时参数

@Override
public RibbonResponse execute(RibbonRequest request, IClientConfig configOverride)
        throws IOException {
    Request.Options options;
    if (configOverride != null) {
        RibbonProperties override = RibbonProperties.from(configOverride);
        options = new Request.Options(override.connectTimeout(this.connectTimeout),
                override.readTimeout(this.readTimeout));
    }
    else {
        options = new Request.Options(this.connectTimeout, this.readTimeout);
    }
    // 真正的执行请求,并获取响应结果
    Response response = request.client().execute(request.toRequest(), options);
    return new RibbonResponse(request.getUri(), response);
}

    这个 request.client() 是 Default类,它实现了 Client 接口所以是执行的 Default类的 execute 方法

@Override
public Response execute(Request request, Options options) throws IOException {
  // 转换并发送请求
  HttpURLConnection connection = convertAndSend(request, options);
  // 转换响应结果
  return convertResponse(connection, request);
}

转换并发送请求 convertAndSend

    转化并且发送请求的方法,通过 HttpURLConnection 进行方法请求

HttpURLConnection convertAndSend(Request request, Options options) throws IOException {
  // sun.net.www.protocol.http.HttpURLConnection:http://192.168.60.1:9200/good/getGoods
  final HttpURLConnection connection =
      (HttpURLConnection) new URL(request.url()).openConnection();
  // 设置连接超时
  connection.setConnectTimeout(options.connectTimeoutMillis());
  // 设置读取超时
  connection.setReadTimeout(options.readTimeoutMillis());
  connection.setAllowUserInteraction(false);
  connection.setInstanceFollowRedirects(options.isFollowRedirects());
  // 设置请求方法
  connection.setRequestMethod(request.httpMethod().name());
  // 下面也都是进行请求参数的设置
  Collection<String> contentEncodingValues = request.headers().get(CONTENT_ENCODING);
  boolean gzipEncodedRequest =
      contentEncodingValues != null && contentEncodingValues.contains(ENCODING_GZIP);
  boolean deflateEncodedRequest =2000
      contentEncodingValues != null && contentEncodingValues.contains(ENCODING_DEFLATE);

  boolean hasAcceptHeader = false;
  Integer contentLength = null;
  for (String field : request.headers().keySet()) {
    if (field.equalsIgnoreCase("Accept")) {
      hasAcceptHeader = true;
    }
    for (String value : request.headers().get(field)) {
      if (field.equals(CONTENT_LENGTH)) {
        if (!gzipEncodedRequest && !deflateEncodedRequest) {
          contentLength = Integer.valueOf(value);
          connection.addRequestProperty(field, value);
        }
      } else {
        connection.addRequestProperty(field, value);
      }
    }
  }
  // Some servers choke on the default accept string.
  if (!hasAcceptHeader) {
    connection.addRequestProperty("Accept", "*/*");
  }
  ......
  return connection;
}

转换响应结果 convertResponse

    转换结果,获取响应码和响应信息,进行响应结果封装并返回

Response convertResponse(HttpURLConnection connection, Request request)
  int status = connection.getResponseCode();
  // 获取响应信息
  String reason = connection.getResponseMessage();

  if (status < 0) {
    throw new IOException(format("Invalid status(%s) executing %s %s", status,
        connection.getRequestMethod(), connection.getURL()));
  }

  Map<String, Collection<String>> headers = 
      new LinkedHashMap<String, Collection<String>>();
  for (Map.Entry<String, List<String>> field : connection.getHeaderFields().entrySet()) {
    // response message
    if (field.getKey() != null) {
      headers.put(field.getKey(), field.getValue());
    }
  }

  Integer length = connection.getContentLength();
  if (length == -1) {
    length = null;
  }
  InputStream stream;
  if (status >= 400) {
    stream = connection.getErrorStream();
  } else {
    stream = connection.getInputStream();
  }
  // 构建响应结果
  return Response.builder()
      .status(status)
      .reason(reason)
      .headers(headers)
      .request(request)
      .body(stream, length)
      .build();
}

封装响应结果 RibbonResponse

protected RibbonResponse(URI uri, Response response) {
        this.uri = uri;
        this.response = response;
}

输出结果

image.png

小结

  1. 进行指令构建,进行任务提交
  2. 进行服务选择
  3. 通过 HttpURLConnection 进行网络请求
  4. 进行响应结果封装
  5. 结果输出