美文网首页
FeignClient 测试

FeignClient 测试

作者: 蓝笔头 | 来源:发表于2021-07-27 09:43 被阅读0次

实验环境

  • JDK: adopt-openjdk-1.8.0_292
  • SpringBoot:2.5.3
  • SpringCloud:2020.0.3

准备条件

(1)Maven 依赖

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>

(2)接收请求的 DemoController

@RestController
@Slf4j
@RequiredArgsConstructor
public class DemoController {
    private final DemoClient demoClient;

    @SneakyThrows
    @GetMapping("/test")
    public String testGet(HttpServletRequest request) {
        log.info("[testGet] remote ip:port = {}:{}", request.getRemoteHost(), request.getRemotePort());
        Thread.sleep(1 * 1000);
        return "testGet";
    }

    @GetMapping("/test-feign")
    public String testFeign(HttpServletRequest request) {
        log.info("[testFeign] remote ip:port = {}:{}", request.getRemoteHost(), request.getRemotePort());
        String result = demoClient.testGet();
        log.info("result:{}", result);
        return "testFeign";
    }
}

(3)调用远程接口的 FeignClient

@FeignClient(value = "demo", url = "http://127.0.0.1:8080")
public interface DemoClient {

    @GetMapping("/test")
    String testGet();
}

实验

(一)使用默认的 Feign 配置

@Slf4j
public class DemoTest {

    public static void main(String[] args) throws Exception {
        String requestUrl = "http://127.0.0.1:8080/test-feign";
        testGet(requestUrl);
    }

    @SneakyThrows
    public static void testGet(String requestUrl) {
        final URL url = new URL(requestUrl);
        final HttpURLConnection connection = (HttpURLConnection) url.openConnection();

        // 如果还没有建立过socket 连接,则建立 socket 连接
        // 如果建立过 socket 连接,则直接从缓存中取出一个可用的
        // 发送 http 请求
        List<String> lines = IOUtils.readLines(connection.getInputStream());
        lines.forEach(System.out::println);
    }

}

执行三次 main() 方法,服务端控制台输出:

2021-07-26 14:15:43.027  INFO 1536 --- [nio-8080-exec-1] com.example.demo.DemoController          : [testFeign] remote ip:port = 127.0.0.1:11992
2021-07-26 14:15:43.037  INFO 1536 --- [nio-8080-exec-2] com.example.demo.DemoController          : [testGet] remote ip:port = 127.0.0.1:11995
2021-07-26 14:15:44.076  INFO 1536 --- [nio-8080-exec-1] com.example.demo.DemoController          : result:testGet
2021-07-26 14:15:46.861  INFO 1536 --- [nio-8080-exec-4] com.example.demo.DemoController          : [testFeign] remote ip:port = 127.0.0.1:12000
2021-07-26 14:15:46.863  INFO 1536 --- [nio-8080-exec-5] com.example.demo.DemoController          : [testGet] remote ip:port = 127.0.0.1:11995
2021-07-26 14:15:47.869  INFO 1536 --- [nio-8080-exec-4] com.example.demo.DemoController          : result:testGet
2021-07-26 14:15:51.001  INFO 1536 --- [nio-8080-exec-7] com.example.demo.DemoController          : [testFeign] remote ip:port = 127.0.0.1:12008
2021-07-26 14:15:51.003  INFO 1536 --- [nio-8080-exec-8] com.example.demo.DemoController          : [testGet] remote ip:port = 127.0.0.1:11995
2021-07-26 14:15:52.019  INFO 1536 --- [nio-8080-exec-7] com.example.demo.DemoController          : result:testGet

源码解析:

package feign;
final class SynchronousMethodHandler implements MethodHandler {
  @Override
  public Object invoke(Object[] argv) throws Throwable {
    RequestTemplate template = buildTemplateFromArgs.create(argv);
    Options options = findOptions(argv);
    return executeAndDecode(template, options);
  }

  Object executeAndDecode(RequestTemplate template, Options options) throws Throwable {
    Request request = targetRequest(template);
    Response response;
    // 调用 client 执行实际的 http 请求
    response = client.execute(request, options);  
  }
}

// 提交 HTTP 请求。 实现应该是线程安全的。
public interface Client {

  // 针对其 request.url() 执行请求并返回响应。
  Response execute(Request request, Options options) throws IOException;
  
  class Default implements Client {
    @Override
    public Response execute(Request request, Options options) throws IOException {
      HttpURLConnection connection = convertAndSend(request, options);
      return convertResponse(connection, request);
    }
    
    HttpURLConnection convertAndSend(Request request, Options options) throws IOException {
      final URL url = new URL(request.url());
      final HttpURLConnection connection = this.getConnection(url);
      
      // 设置连接超时时间
      connection.setConnectTimeout(options.connectTimeoutMillis());
      // 设置读超时时间
      connection.setReadTimeout(options.readTimeoutMillis());

      if (request.body() != null) {
        // 如果当前请求有 body 需要发送,则调用 OutputStream 的 write() 方法
        connection.setDoOutput(true);
        OutputStream out = connection.getOutputStream();
        out.write(request.body());
      }
      return connection;
    }
    
    Response convertResponse(HttpURLConnection connection, Request request) throws IOException {
      // 1. getResponseCode() 方法中会调用 getInputStream() 方法,确认已经连接到服务器,并发送了 http 请求
      int status = connection.getResponseCode();
      String reason = connection.getResponseMessage();

      return Response.builder()
          .status(status)
          .reason(reason)
          .headers(headers)
          .request(request)
          .body(stream, length)
          .build();
    }
    
  }
}

相关文章

网友评论

      本文标题:FeignClient 测试

      本文链接:https://www.haomeiwen.com/subject/rvfgmltx.html