gpt4 book ai didi

java - 在等待 "long"时间时重用 tomcat 线程

转载 作者:搜寻专家 更新时间:2023-10-30 21:10:30 25 4
gpt4 key购买 nike

配置
网络服务器:Nginx
应用服务器:默认配置为 200 个请求服务线程的 Tomcat
我的服务器的预期响应时间:~30 秒(有很多第三方依赖项)

场景
应用程序需要每 10 秒生成一次 token 供其使用。 token 生成的预期时间约为 5 秒,但由于它是通过网络联系的第三方系统,这显然不一致,可能长达 10 秒。
在 token 生成过程中,每秒有近 80% 的传入请求需要等待。

我认为应该发生的事情
由于等待 token 生成的请求将不得不等待“长时间”,因此在等待 token 生成过程完成时,没有理由将这些请求服务重新用于服务其他传入请求。
基本上,如果我的 20% 继续得到服务,那将是有意义的。如果等待的线程没有被用于其他请求,将达到 tomcat 请求服务限制并且服务器基本上会阻塞,这并不是任何开发人员会喜欢的。

我尝试了什么
最初我希望切换到 tomcat NIO 连接器可以完成这项工作。但是看了this之后比较起来,我真的不抱希望了。尽管如此,我尝试强制请求等待 10 秒,但没有成功。
现在我正在考虑我需要的线路,有点,在等待时搁置请求,并且需要向 tomcat 发出该线程可以自由重用的信号。同样,当请求准备好向前移动时,我将需要 tomcat 从它的线程池中给我一个线程。但我不知道如何去做,或者即使这是可能的。

任何指导或帮助?

最佳答案

您需要一个异步 servlet,但您还需要对外部 token 生成器进行异步 HTTP 调用。如果您仍然在某个地方为每个 token 请求创建一个线程,那么通过将请求从 servlet 传递到带有线程池的 ExecutorService,您将一无所获。您必须将线程与 HTTP 请求分离,以便一个线程可以处理多个 HTTP 请求。这可以通过像 Apache 这样的异步 HTTP 客户端来实现 Asynch HttpClientAsync Http Client .

首先你必须创建一个像这样的异步 servlet

public class ProxyService extends HttpServlet {

private CloseableHttpAsyncClient httpClient;

@Override
public void init() throws ServletException {
httpClient = HttpAsyncClients.custom().
setMaxConnTotal(Integer.parseInt(getInitParameter("maxtotalconnections"))).
setMaxConnPerRoute(Integer.parseInt(getInitParameter("maxconnectionsperroute"))).
build();
httpClient.start();
}

@Override
public void destroy() {
try {
httpClient.close();
} catch (IOException e) { }
}

@Override
public void doGet(HttpServletRequest request, HttpServletResponse response) {
AsyncContext asyncCtx = request.startAsync(request, response);
asyncCtx.setTimeout(ExternalServiceMock.TIMEOUT_SECONDS * ExternalServiceMock.K);
ResponseListener listener = new ResponseListener();
asyncCtx.addListener(listener);
Future<String> result = httpClient.execute(HttpAsyncMethods.createGet(getInitParameter("serviceurl")), new ResponseConsumer(asyncCtx), null);
}

}

此 servlet 使用 Apache Asynch HttpClient 执行异步 HTTP 调用。请注意,您可能需要配置每个路由的最大连接数,因为根据 RFC 2616 规范,HttpAsyncClient 默认情况下只允许最多两个并发连接到同一主机。您还可以配置许多其他选项,如 HttpAsyncClient configuration 所示。 . HttpAsyncClient 的创建成本很高,因此您不希望在每次 GET 操作时都创建它的实例。

一个监听器挂接到 AsyncContext,此监听器仅在上面的示例中用于处理超时。

public class ResponseListener implements AsyncListener {

@Override
public void onStartAsync(AsyncEvent event) throws IOException {
}

@Override
public void onComplete(AsyncEvent event) throws IOException {
}

@Override
public void onError(AsyncEvent event) throws IOException {
event.getAsyncContext().getResponse().getWriter().print("error:");
}

@Override
public void onTimeout(AsyncEvent event) throws IOException {
event.getAsyncContext().getResponse().getWriter().print("timeout:");
}

}

然后您需要一个 HTTP 客户端的使用者。此消费者通过调用 complete() 通知 AsyncContext什么时候buildResult()由 HttpClient 在内部执行,作为返回 Future<String> 的步骤给来电者 ProxyService服务小程序。

public class ResponseConsumer extends AsyncCharConsumer<String> {

private int responseCode;
private StringBuilder responseBuffer;
private AsyncContext asyncCtx;

public ResponseConsumer(AsyncContext asyncCtx) {
this.responseBuffer = new StringBuilder();
this.asyncCtx = asyncCtx;
}

@Override
protected void releaseResources() { }

@Override
protected String buildResult(final HttpContext context) {
try {
PrintWriter responseWriter = asyncCtx.getResponse().getWriter();
switch (responseCode) {
case javax.servlet.http.HttpServletResponse.SC_OK:
responseWriter.print("success:" + responseBuffer.toString());
break;
default:
responseWriter.print("error:" + responseBuffer.toString());
}
} catch (IOException e) { }
asyncCtx.complete();
return responseBuffer.toString();
}

@Override
protected void onCharReceived(CharBuffer buffer, IOControl ioc) throws IOException {
while (buffer.hasRemaining())
responseBuffer.append(buffer.get());
}

@Override
protected void onResponseReceived(HttpResponse response) throws HttpException, IOException {
responseCode = response.getStatusLine().getStatusCode();
}

}

ProxyService servlet 的 web.xml 配置可能是这样的

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
id="WebApp_ID" version="3.0" metadata-complete="true">
<display-name>asyncservlet-demo</display-name>

<servlet>
<servlet-name>External Service Mock</servlet-name>
<servlet-class>ExternalServiceMock</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>

<servlet>
<servlet-name>Proxy Service</servlet-name>
<servlet-class>ProxyService</servlet-class>
<load-on-startup>1</load-on-startup>
<async-supported>true</async-supported>
<init-param>
<param-name>maxtotalconnections</param-name>
<param-value>200</param-value>
</init-param>
<init-param>
<param-name>maxconnectionsperroute</param-name>
<param-value>4</param-value>
</init-param>
<init-param>
<param-name>serviceurl</param-name>
<param-value>http://127.0.0.1:8080/asyncservlet/externalservicemock</param-value>
</init-param>
</servlet>

<servlet-mapping>
<servlet-name>External Service Mock</servlet-name>
<url-pattern>/externalservicemock</url-pattern>
</servlet-mapping>

<servlet-mapping>
<servlet-name>Proxy Service</servlet-name>
<url-pattern>/proxyservice</url-pattern>
</servlet-mapping>

</web-app>

延迟数秒的 token 生成器模拟 servlet 可能是:

public class ExternalServiceMock extends HttpServlet{

public static final int TIMEOUT_SECONDS = 13;
public static final long K = 1000l;

public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
Random rnd = new Random();
try {
Thread.sleep(rnd.nextInt(TIMEOUT_SECONDS) * K);
} catch (InterruptedException e) { }
final byte[] token = String.format("%10d", Math.abs(rnd.nextLong())).getBytes(ISO_8859_1);
response.setContentType("text/plain");
response.setCharacterEncoding(ISO_8859_1.name());
response.setContentLength(token.length);
response.getOutputStream().write(token);
}

}

你可以得到一个fully working example at GitHub .

关于java - 在等待 "long"时间时重用 tomcat 线程,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43600831/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com