【问题标题】:Read timed out on Spring RestTemplate callSpring RestTemplate 调用读取超时
【发布时间】:2020-12-20 00:04:22
【问题描述】:

我有一个返回 json 的 API,它是 GET 方法类型。因为它是 GET,所以当我在浏览器中打开 URL 时,它可以正常工作并呈现 json,但是,在使用 RestTemplate 检索 json 时它会失败。

能否请您提供一种阅读以下 API 的方法。

API 网址:https://www.nseindia.com/api/option-chain-indices?symbol=NIFTY

Spring Boot Rest 模板调用:

final String uri = "https://www.nseindia.com/api/option-chain-indices?symbol=NIFTY";
RestTemplate restTemplate = new RestTemplate();
Map result = restTemplate.getForObject(uri, Map.class);

错误:

java.net.SocketTimeoutException: Read timed out
    at java.base/java.net.SocketInputStream.socketRead0(Native Method) ~[na:na]
    at java.base/java.net.SocketInputStream.socketRead(SocketInputStream.java:115) ~[na:na]
    at java.base/java.net.SocketInputStream.read(SocketInputStream.java:168) ~[na:na]
    at java.base/java.net.SocketInputStream.read(SocketInputStream.java:140) ~[na:na]
    at java.base/sun.security.ssl.SSLSocketInputRecord.read(SSLSocketInputRecord.java:448) ~[na:na]
    at java.base/sun.security.ssl.SSLSocketInputRecord.bytesInCompletePacket(SSLSocketInputRecord.java:68) ~[na:na]
    at java.base/sun.security.ssl.SSLSocketImpl.readApplicationRecord(SSLSocketImpl.java:1104) ~[na:na]
    at java.base/sun.security.ssl.SSLSocketImpl$AppInputStream.read(SSLSocketImpl.java:823) ~[na:na]
    at java.base/java.io.BufferedInputStream.fill(BufferedInputStream.java:252) ~[na:na]
    at java.base/java.io.BufferedInputStream.read1(BufferedInputStream.java:292) ~[na:na]
    at java.base/java.io.BufferedInputStream.read(BufferedInputStream.java:351) ~[na:na]
    at java.base/sun.net.www.http.HttpClient.parseHTTPHeader(HttpClient.java:746) ~[na:na]
    at java.base/sun.net.www.http.HttpClient.parseHTTP(HttpClient.java:689) ~[na:na]

【问题讨论】:

  • 如果您为RestTemplate,RequestFactory 配置了任何 bean 来进行 API 调用,您将需要在此处提供它。默认情况下,RestTemplate没有设置超时时间

标签: java spring spring-boot rest


【解决方案1】:

您可以改用 webflux 中的 WebClient:

- 添加依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-webflux</artifactId>
</dependency>

- 创建 POJO

@Data
public class Root {
    private Records records;
    private Filtered filtered;

    @Data
    public static class PE {
        private int strikePrice;
        private String expiryDate;
        private String underlying;
        private String identifier;
        private int openInterest;
        private int changeinOpenInterest;
        private double pchangeinOpenInterest;
        private int totalTradedVolume;
        private double impliedVolatility;
        private double lastPrice;
        private double change;
        private double pChange;
        private int totalBuyQuantity;
        private int totalSellQuantity;
        private int bidQty;
        private double bidprice;
        private int askQty;
        private double askPrice;
        private double underlyingValue;
    }

    @Data
    public static class CE {
        private int strikePrice;
        private String expiryDate;
        private String underlying;
        private String identifier;
        private int openInterest;
        private int changeinOpenInterest;
        private int pchangeinOpenInterest;
        private int totalTradedVolume;
        private int impliedVolatility;
        private int lastPrice;
        private double change;
        private double pChange;
        private int totalBuyQuantity;
        private int totalSellQuantity;
        private int bidQty;
        private double bidprice;
        private int askQty;
        private double askPrice;
        private double underlyingValue;
    }

    @Data
    public static class Datum {
        private int strikePrice;
        private String expiryDate;
        private PE PE;
        private CE CE;
    }

    @Data
    public static class Records {
        private List<String> expiryDates;
        private List<Datum> data;
        private String timestamp;
        private double underlyingValue;
        private List<Integer> strikePrices;
    }

    @Data
    public static class Filtered {
        //TODO
    }
}

- 让 WebClient 拨打电话

@SpringBootApplication
public class MultipleConfigurationPropertiesApplication {

    public static void main(String[] args) {
        SpringApplication.run(MultipleConfigurationPropertiesApplication.class, args);
    }

    @Bean
    CommandLineRunner commandLineRunner() {
        return args -> {
            WebClient client = WebClient.builder()
                    .baseUrl("https://www.nseindia.com")
                    .exchangeStrategies(ExchangeStrategies.builder()
                            .codecs(configurer -> configurer
                                    .defaultCodecs()
                                    .maxInMemorySize(16 * 1024 * 1024))
                            .build())
                    .build();

            Mono<Root> result = client.get()
                    .uri("/api/option-chain-indices?symbol=NIFTY").accept(MediaType.APPLICATION_JSON)
                    .retrieve()
                    .bodyToMono(Root.class);

            System.out.println(result.block());
        };
    }
}

由于请求结果很大,您必须调整缓冲区大小。

此外,您可能需要考虑使用非阻塞/流式解决方案来处理此问题。

【讨论】:

  • 谢谢@Christian,使用 WebClient 成功了。它不能与 RestClient 一起使用的任何具体原因?由于 https 网址?
【解决方案2】:

我刚才在浏览器中测试了 API GET 请求,结果是高度嵌套的。您正在尝试将结果检索到 Map 对象中,但您没有指定 KeyValue 的类型,后者可能是 MapList 本身。

首先,确定您是否可以将原始 JSON 结果检索到 String

ResponseEntity<String> response = restTemplate.getForEntity(url, String.class);

如果这有效,那么您将知道 GET 请求正在通过RestTemplate 工作。如果没有,您可以对默认的timeout settings 进行故障排除,并根据您观察到的网络响应在浏览器中占用的时间进行调整。

假设您可以将原始 JSON 结果检索到 String,那么您的下一步将是分析返回的 JSON 数据的“形状”。如果要将 JSON 结果转换为 POJO,则必须创建一个与 JSON 数据的“形状”相匹配的类(或一组类)。

records:
    
  expiryDates   […]
  data  […]
  timestamp "31-Aug-2020 15:30:00"
  underlyingValue   11387.5
  strikePrices  […]

filtered:   
  data  […]
  CE    
    totOI   367314
    totVol  4988131
  PE    
    totOI   261696
    totVol  5501580

【讨论】:

    【解决方案3】:

    当您达到连续数据包之间的最大不活动时间时,会发生这样的读取超时。初始化时,您必须向 RestTemplate 提供读取超时配置的 ClientHttpRequestFactory。 Here 就是这样做的一个例子。

    如果您必须发出更多这些长时间运行的请求,您可能还需要考虑将您的 RestTemplate 变成一个 Bean。这样您就不必每次都重新配置。

    【讨论】:

      猜你喜欢
      • 2012-11-29
      • 2018-10-21
      • 1970-01-01
      • 2014-12-04
      • 2017-10-10
      • 2019-06-05
      • 2019-08-14
      • 1970-01-01
      相关资源
      最近更新 更多