美文网首页
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