您似乎想关闭可能发生在移动设备上的废弃 HTTP 连接。
@RestController
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
@Bean
public EmbeddedServletContainerFactory getEmbeddedServletContainerFactory() {
TomcatEmbeddedServletContainerFactory containerFactory = new TomcatEmbeddedServletContainerFactory();
containerFactory
.addConnectorCustomizers(new TomcatConnectorCustomizer() {
@Override
public void customize(Connector connector) {
((AbstractProtocol) connector.getProtocolHandler()).setConnectionTimeout(100);
}
});
return containerFactory;
}
@RequestMapping
public String echo(@RequestBody String body) {
return body;
}
}
为了快速运行我的测试,连接超时设置为 100 毫秒。数据在chunks 中发送。在每个块之间,正在运行的线程暂停 x 毫秒。
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = DemoApplication.class)
@WebIntegrationTest("server.port:19000")
public class DemoApplicationTests {
private static final int CHUNK_SIZE = 1;
private static final String HOST = "http://localhost:19000/echo";
@Rule
public ExpectedException expectedException = ExpectedException.none();
@Test
public void slowConnection() throws Exception {
final HttpURLConnection connection = openChunkedConnection();
OutputStreamWriter out = new OutputStreamWriter(connection.getOutputStream());
writeAndWait(500, out, "chunk1");
writeAndWait(1, out, "chunk2");
out.close();
expectedException.expect(IOException.class);
expectedException.expectMessage("Server returned HTTP response code: 400 for URL: " + HOST);
assertResponse("chunk1chunk2=", connection);
}
@Test
public void fastConnection() throws Exception {
final HttpURLConnection connection = openChunkedConnection();
OutputStreamWriter out = new OutputStreamWriter(connection.getOutputStream());
writeAndWait(1, out, "chunk1");
writeAndWait(1, out, "chunk2");
out.close();
assertResponse("chunk1chunk2=", connection);
}
private void assertResponse(String expected, HttpURLConnection connection) throws IOException {
Scanner scanner = new Scanner(connection.getInputStream()).useDelimiter("\\A");
Assert.assertEquals(expected, scanner.next());
}
private void writeAndWait(int millis, OutputStreamWriter out, String body) throws IOException, InterruptedException {
out.write(body);
Thread.sleep(millis);
}
private HttpURLConnection openChunkedConnection() throws IOException {
final URL url = new URL(HOST);
final HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setDoOutput(true);
connection.setChunkedStreamingMode(CHUNK_SIZE);
return connection;
}
}
将包org.apache.catalina.core的日志级别设置为DEBUG
logging.level.org.apache.catalina.core=DEBUG
您可以看到SocketTimeoutException 用于slowConnection 测试。
我不知道您为什么要将 HTTP 状态代码 502 作为错误响应状态。 HTTP 502 说:
502(坏网关)状态码表示服务器,而
作为网关或代理,从
它在尝试完成请求时访问的入站服务器。
客户端Postman 调用您的服务器应用程序。我在两者之间看不到任何网关或代理。
如果您只是将您的问题压缩到最低限度并且实际上您想自己构建一个代理,您可以考虑使用Netflix Zuul。
23.03.2016 更新:
这就是 OP 在 Stackoverflow 上提出问题的根本原因:
我对 longpolling 所做的是,从服务 api 中,我将线程休眠一段时间并唤醒它,然后一次又一次地执行此操作,直到某些 db 状态完成。
该实现实际上阻止了 Tomcat 工作线程处理新的 HTTP 请求。因此,您的请求吞吐量会随着每次额外的长时间运行操作而降低。
我建议将长时间运行的操作卸载到单独的线程中。客户端(浏览器)发起一个新的请求来获取结果。
根据处理状态,服务器返回结果或通知/错误/警告/。
这是一个非常简单的例子:
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import static org.springframework.http.HttpStatus.CREATED;
import static org.springframework.http.HttpStatus.NOT_FOUND;
import static org.springframework.http.HttpStatus.OK;
@RestController
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
private ExecutorService executorService = Executors.newFixedThreadPool(10);
private Map<String, String> results = new ConcurrentHashMap<>();
@RequestMapping(path = "put/{key}", method = RequestMethod.POST)
public ResponseEntity<Void> put(@PathVariable String key) {
executorService.submit(() -> {
try {
//simulate a long running process
Thread.sleep(10000);
results.put(key, "success");
} catch (InterruptedException e) {
results.put(key, "error " + e.getMessage());
Thread.currentThread().interrupt();
}
});
return new ResponseEntity<>(CREATED);
}
@RequestMapping(path = "get/{key}", method = RequestMethod.GET)
public ResponseEntity<String> get(@PathVariable String key) {
final String result = results.get(key);
return new ResponseEntity<>(result, result == null ? NOT_FOUND : OK);
}
}