Java中重复调用HTTP服务发JSON请求获响应遇阻
解决HttpURLConnection重复调用时卡在getInputStream()的问题
我看你遇到的问题很典型——第一次调用solveInstance()访问本地scheduler服务完全正常,但第二次就卡在getInputStream()这一行,程序直接僵住,既不报错也没输出。这种情况十有八九是HTTP连接资源没有正确释放导致的,咱们一步步来分析和解决。
首先先还原你的核心代码场景:
public static void solveInstanceSet(String instanceSetName) { // run each instance in an Instance Set try { // get the instanceSet File File instanceSet = new File(instanceSetName); Scanner scanner = new Scanner(instanceSet); // solve each instance in the file while(scanner.hasNext()) { String instanceName = scanner.nextLine(); solveInstance(instanceName); } } catch(IOException e) { System.out.println("Exception: Cannot read the File contains the Instances to run!"); } } public static void solveInstance(String instanceName){ // solve a problem instance try{ String query_url = "http://localhost:8807/scheduler"; // read Request to a JSON Object JSONParser parser = new JSONParser(); JSONObject request = (JSONObject) parser.parse(new FileReader(instanceName)); // open connection URL url = new URL(query_url); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); conn.setConnectTimeout(5000); conn.setRequestProperty("Content-Type", "application/json"); // 核心逻辑:发送请求、获取响应的代码未正确释放资源 } catch(Exception e) { // 异常处理 } }
问题根源
HttpURLConnection默认启用了HTTP Keep-Alive(连接复用),第一次请求完成后,连接会被放到连接池里供后续请求复用。但如果你的代码没有正确关闭请求的输出流、响应的输入流(甚至错误流),这个连接就不会被释放回连接池。当连接池里的连接都被占用且没有释放时,第二次请求就会阻塞在获取连接/输入流的步骤,也就是你看到的卡在getInputStream()的情况。
解决方案:强制确保所有资源被正确关闭
解决的核心就是在每次请求结束后,不管成功还是失败,都要关闭所有相关的流资源,最好用Java的try-with-resources语法,它会自动帮你关闭实现了AutoCloseable接口的资源,避免手动遗漏。
修改后的solveInstance()方法示例:
public static void solveInstance(String instanceName){ // solve a problem instance try{ String query_url = "http://localhost:8807/scheduler"; // 读取请求JSON JSONParser parser = new JSONParser(); JSONObject request = (JSONObject) parser.parse(new FileReader(instanceName)); URL url = new URL(query_url); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); try { conn.setConnectTimeout(5000); conn.setRequestProperty("Content-Type", "application/json; charset=UTF-8"); // 发送JSON请求体必须设置为POST方法并允许输出 conn.setRequestMethod("POST"); conn.setDoOutput(true); // 发送JSON请求体,用try-with-resources自动关闭OutputStream try (OutputStream os = conn.getOutputStream()) { byte[] requestBytes = request.toJSONString().getBytes(StandardCharsets.UTF_8); os.write(requestBytes, 0, requestBytes.length); } // 获取响应码,判断是成功还是错误 int responseCode = conn.getResponseCode(); // 不管成功还是错误,都要获取对应的流并关闭 try (InputStream is = responseCode >= 200 && responseCode < 300 ? conn.getInputStream() : conn.getErrorStream()) { if (is != null) { // 读取响应内容(可根据你的需求调整处理逻辑) BufferedReader reader = new BufferedReader(new InputStreamReader(is)); StringBuilder responseBuilder = new StringBuilder(); String line; while ((line = reader.readLine()) != null) { responseBuilder.append(line); } System.out.println("处理实例 " + instanceName + " 完成,响应:" + responseBuilder.toString()); } } } finally { // 手动断开连接,确保资源被彻底释放(即使流已关闭,这一步更保险) conn.disconnect(); } } catch (Exception e) { e.printStackTrace(); System.out.println("处理实例 " + instanceName + " 时发生异常"); } }
额外注意点
- 处理错误流:如果服务端返回4xx/5xx的错误码,直接调用
getInputStream()会抛出IOException,这时候必须用getErrorStream()获取错误响应,并且也要关闭这个流,否则连接还是不会释放。 - try-with-resources的使用:所有实现了
AutoCloseable的资源(比如OutputStream、InputStream)都应该放在try-with-resources里,不用手动调用close(),避免因为异常导致close被跳过。 - 显式设置请求方法:因为要发送JSON请求体,必须设置
conn.setRequestMethod("POST")和conn.setDoOutput(true),否则无法发送请求体。
这样修改后,每次请求完成后所有资源都会被正确释放,连接池里的连接可以正常复用,第二次及以后的调用就不会卡住了。
内容的提问来源于stack exchange,提问作者leo
相关产品推荐
相关产品推荐

