英文:
Read timed out on Spring RestTemplate call
问题
我有一个返回 JSON 数据的 API,使用的是 GET 方法。由于是 GET 请求,在浏览器中打开该 URL 可以正常工作并显示 JSON 数据,然而,当我使用 RestTemplate 来获取 JSON 数据时却失败了。
请提供一种读取下方 API 的方法。
API 地址:https://www.nseindia.com/api/option-chain-indices?symbol=NIFTY
Spring Boot RestTemplate 调用:
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]
英文:
I have an API which returns a json it is of type GET method. Since it is GET, when I open the URL in browser it works fine and renders the json, however, upon using RestTemplate to retrieve the json it fails.
Could you please provide a way to read below API.
API URL: https://www.nseindia.com/api/option-chain-indices?symbol=NIFTY
Spring Boot Rest Template Call:
final String uri = "https://www.nseindia.com/api/option-chain-indices?symbol=NIFTY";
RestTemplate restTemplate = new RestTemplate();
Map result = restTemplate.getForObject(uri, Map.class);
Error:
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]
答案1
得分: 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 {
// ... (同样的结构,请参考原文)
}
@Data
public static class CE {
// ... (同样的结构,请参考原文)
}
@Data
public static class Datum {
// ... (同样的结构,请参考原文)
}
@Data
public static class Records {
// ... (同样的结构,请参考原文)
}
@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());
};
}
}
由于请求结果很大,你需要调整缓冲区大小。另外,你可能希望考虑使用非阻塞/流式解决方案来处理这个问题。
英文:
You could use the WebClient from webflux instead:
- Add the dependency
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
- Create the 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
}
}
- Let WebClient make the call
@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());
};
}
}
You have to tune the buffer size as the request result is huge.
Also, you might want to consider using non-blocking/stream solution to handle this.
答案2
得分: 0
我刚刚在浏览器中测试了API的GET请求,结果嵌套非常深。您正在尝试将结果检索到一个Map
对象中,但是您没有为Key
或Value
指定类型,而后者很可能是一个Map
或List
本身。
首先,确定您是否可以将原始JSON结果检索为String
。
ResponseEntity<String> response = restTemplate.getForEntity(url, String.class);
如果这样可以工作,那么您将会知道GET请求通过RestTemplate
正在工作。如果不行,您可以根据您在浏览器中观察到的网络响应时间,调查默认的超时设置并进行调整。
假设您可以将原始JSON结果检索为String
,那么您的下一步将是分析返回的JSON数据的“形状”(即结构)。如果您希望将JSON结果转换为POJO(普通Java对象),您将需要创建一个与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
英文:
I tested the API GET request in a browser moments ago, and the results are highly nested. You are attempting to retrieve the results into a Map
object, but you are not specifying the types for Key
or Value
, the latter of which is likely a Map
or List
itself.
First, determine if you can retrieve the raw JSON results into a String
.
ResponseEntity<String> response = restTemplate.getForEntity(url, String.class);
If this works, then you will know that the GET request is working via RestTemplate
. If not, you can troubleshoot the default timeout settings and adjust based on how long you observe the network response to take in the browser, for instance.
Assuming you can retrieve the raw JSON results into a String
, then your next step will be to analyze the "shape" of the JSON data returned. If you want the JSON results converted into a POJO, you will have to create a class (or set of classes) that match the "shape" of the JSON data.
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
得分: 0
Read timeouts like this occur when you reach the max period of inactivity between consecutive data packets. You'll have to provide a read timeout configured ClientHttpRequestFactory to your RestTemplate when you initialize it. Here is one example of doing this.
You may also want to consider making your RestTemplate into a Bean if you have to make more of these long running requests. This way you don't have to configure this again each time.
英文:
Read timeouts like this occur when you reach the max period of inactivity between consecutive data packets. You'll have to provide a read timeout configured ClientHttpRequestFactory to your RestTemplate when you initialize it. Here is one example of doing this.
You may also want to consider making your RestTemplate into a Bean if you have to make more of these long running requests. This way you don't have to configure this again each time.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论