Spring RestTemplate调用超时

3

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

请提供一种读取下面API的方法。

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

Spring Boot Rest Template 调用:

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]

如果您已经为RestTemplateRequestFactory配置了任何bean以进行API调用,则需要在此处发布。默认情况下,RestTemplate上没有设置超时。 - shinjw
3个回答

2
你可以使用 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的原因吗? - JavaCodeNet

0

像这样的读取超时是在连续数据包之间的最大不活动期之间达到时发生的。当您初始化它时,您将不得不为您的RestTemplate提供一个已配置的ClientHttpRequestFactory读取超时。这里是一个这样做的例子。

如果您必须进行更多此类长时间运行的请求,您可能还希望考虑将您的RestTemplate转换为Bean。这样,您就不必每次都重新配置它。


0
我刚在浏览器中测试了API的GET请求,结果高度嵌套。 您尝试将结果检索到一个Map对象中,但没有指定KeyValue的类型,后者很可能是一个MapList本身。
首先,确定是否可以将原始JSON结果检索为String
ResponseEntity<String> response = restTemplate.getForEntity(url, String.class);

如果这个能够正常工作,那么你就知道通过RestTemplate发送GET请求是有效的。如果不行,你可以根据你在浏览器中观察到的网络响应时间来调整默认的超时设置,以便进行故障排除。

假设你能够将原始的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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接