我遇到了一个我想理解的难题。我已经实现了在Wildfly服务器中运行的一个非常简单的REST服务示例。我有一个独立的多线程Java测试程序,该程序使用Apache http客户端库来访问服务。客户端程序具有两个独立的线程,可同时访问REST服务。如果在服务器上未建立httpSession的情况下运行测试程序,则服务器将同时处理请求(如服务器日志中所示)。如果线程在建立httpSession之后运行,则服务器将按顺序处理请求(如第二个服务器日志中所示)。请注意,两个线程共享一个会话。
这是设计使然吗?我在这里想念什么?提前致谢。
这是我所处环境的基础知识:
以下是代表两种情况的服务器日志:
Server log when no http session is established. Requests run concurrently when there is no session. 13:39:13,384 INFO (default task-5) Getting widget 3 13:39:13,384 INFO (default task-6) Getting widget 4 13:39:13,384 INFO (default task-6) DELAYING widget 4 13:39:13,384 INFO (default task-5) Returning widget 3 13:39:13,404 INFO (default task-6) DELAY COMPLETE widget 4 13:39:13,404 INFO (default task-6) Returning widget 4 13:39:13,408 INFO (default task-6) Getting widget 3 13:39:13,408 INFO (default task-6) Returning widget 3 13:39:13,409 INFO (default task-5) Getting widget 4 13:39:13,409 INFO (default task-5) DELAYING widget 4 13:39:13,413 INFO (default task-6) Getting widget 3 13:39:13,413 INFO (default task-6) Returning widget 3 13:39:13,419 INFO (default task-6) Getting widget 3 13:39:13,419 INFO (default task-6) Returning widget 3 13:39:13,425 INFO (default task-6) Getting widget 3 13:39:13,425 INFO (default task-6) Returning widget 3 13:39:13,430 INFO (default task-6) Getting widget 3 13:39:13,430 INFO (default task-6) Returning widget 3 13:39:13,436 INFO (default task-5) DELAY COMPLETE widget 4 13:39:13,436 INFO (default task-5) Returning widget 4 13:39:13,436 INFO (default task-6) Getting widget 3 13:39:13,436 INFO (default task-6) Returning widget 3 13:39:13,441 INFO (default task-6) Getting widget 4 13:39:13,441 INFO (default task-5) Getting widget 3 13:39:13,442 INFO (default task-6) DELAYING widget 4 13:39:13,442 INFO (default task-5) Returning widget 3 13:39:13,447 INFO (default task-5) Getting widget 3 13:39:13,447 INFO (default task-5) Returning widget 3 ...
Server log after having established a http session. Requests run sequentially after having established a session. 13:41:08,761 INFO (default task-5) Session established PA7xR3HCYMR3A2S6iX-4Ziw1dGVNejPktseYMMdm 13:41:08,808 INFO (default task-5) Getting widget 4 13:41:08,808 INFO (default task-5) DELAYING widget 4 13:41:08,829 INFO (default task-5) DELAY COMPLETE widget 4 13:41:08,829 INFO (default task-5) Returning widget 4 13:41:08,833 INFO (default task-6) Getting widget 3 13:41:08,833 INFO (default task-6) Returning widget 3 13:41:08,837 INFO (default task-5) Getting widget 4 13:41:08,837 INFO (default task-5) DELAYING widget 4 13:41:08,857 INFO (default task-5) DELAY COMPLETE widget 4 13:41:08,857 INFO (default task-5) Returning widget 4 13:41:08,860 INFO (default task-6) Getting widget 3 13:41:08,860 INFO (default task-6) Returning widget 3 13:41:08,863 INFO (default task-5) Getting widget 4 13:41:08,864 INFO (default task-5) DELAYING widget 4 13:41:08,884 INFO (default task-5) DELAY COMPLETE widget 4 13:41:08,884 INFO (default task-5) Returning widget 4 13:41:08,887 INFO (default task-6) Getting widget 3 13:41:08,887 INFO (default task-6) Returning widget 3 13:41:08,890 INFO (default task-5) Getting widget 4 13:41:08,890 INFO (default task-5) DELAYING widget 4 13:41:08,911 INFO (default task-5) DELAY COMPLETE widget 4 13:41:08,911 INFO (default task-5) Returning widget 4 13:41:08,914 INFO (default task-6) Getting widget 3 13:41:08,915 INFO (default task-6) Returning widget 3 ...
这是REST服务实现
@Path("concurrencyTest") @Stateless public class ConcurrencyTestWs extends BaseWs { private static final Logger log = LoggerFactory .getLogger(ConcurrencyTestWs.class); @SuppressWarnings("static-method") @GET @Path("/widget") public Response getWidget(@Context HttpServletRequest request, @QueryParam(value = "widgetId") Long widgetId) { try { log.info(String.format("Getting widget %d", widgetId)); if ((widgetId % 2) == 0) { log.info(String.format("DELAYING widget %d", widgetId)); try { Thread.sleep(20); } catch (Exception e) { System.err.println(e.getMessage()); } log.info(String.format("DELAY COMPLETE widget %d", widgetId)); } log.info(String.format("Returning widget %d", widgetId)); return Response.ok(String.format("Widget %d retrieved", widgetId)) .build(); } catch (Exception e) { return RestUtilities.getInternalError(request, "Widget", widgetId, e); } } @SuppressWarnings("static-method") @POST @Path("/establishSession") public Response establishSession(@Context HttpServletRequest request) { try { HttpSession session = request.getSession(true); log.info(String.format("Session established %s", session.getId())); Response response = Response.ok().build(); return response; } catch (Exception e) { return RestUtilities.getInternalError(request, e); } } @SuppressWarnings("static-method") @POST @Path("/terminateSession") public Response terminateSession(@Context HttpServletRequest request) { try { HttpSession session = request.getSession(false); log.info(String.format("Terminating session %s", (session == null) ? "null" : session.getId())); Response response = Response.ok().build(); return response; } catch (Exception e) { return RestUtilities.getInternalError(request, e); } } }
这里是客户端源代码
public class WidgetFetchTest {
private final static String restServiceUrl = "http://dtp002.novodynamics.com:8081/TEST/REST/v1.0/concurrencyTest";
private final CloseableHttpClient httpClient;
private final CookieStore cookieStore;
public WidgetFetchTest() {
this.cookieStore = new BasicCookieStore();
RequestConfig requestConfig = RequestConfig.custom()
.setConnectTimeout(60000).setConnectionRequestTimeout(60000)
.setSocketTimeout(60000).setCookieSpec(CookieSpecs.STANDARD).build();
@SuppressWarnings("resource")
PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager();
connectionManager.setMaxTotal(100);
connectionManager.setDefaultMaxPerRoute(40);
this.httpClient = HttpClients.custom()
.setDefaultRequestConfig(requestConfig)
.setDefaultCookieStore(this.cookieStore)
.setConnectionManager(connectionManager).build();
}
public void run() {
establishSession(this.httpClient);
DocFetchRunnable r1 = new DocFetchRunnable("4", this.httpClient);
DocFetchRunnable r2 = new DocFetchRunnable("3", this.httpClient);
ExecutorService exec = Executors.newFixedThreadPool(2);
exec.execute(r1);
exec.execute(r2);
try {
Thread.sleep(60000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
protected static void establishSession(CloseableHttpClient client) {
try {
URIBuilder ub = new URIBuilder(
String.format("%s%s", restServiceUrl, "/establishSession"));
String url = ub.toString();
HttpContext ctx = HttpClientContext.create();
try (CloseableHttpResponse response = client.execute(new HttpPost(url),
ctx)) {
// intentionally empty.
} catch (Exception e) {
e.printStackTrace();
}
} catch (URISyntaxException e) {
e.printStackTrace();
}
}
protected static void getWidget(CloseableHttpClient client, String widgetId) {
try {
URIBuilder ub = new URIBuilder(
String.format("%s%s", restServiceUrl, "/widget"));
ub.addParameter("widgetId", widgetId);
String url = ub.toString();
HttpContext ctx = HttpClientContext.create();
try (CloseableHttpResponse response = client.execute(new HttpGet(url),
ctx)) {
// intentionally empty.
} catch (Exception e) {
e.printStackTrace();
}
} catch (URISyntaxException e) {
e.printStackTrace();
}
}
private class DocFetchRunnable implements Runnable {
private String widgetId;
CloseableHttpClient client;
public DocFetchRunnable(String widgetId, CloseableHttpClient client) {
this.widgetId = widgetId;
this.client = client;
}
@Override
public void run() {
for (int i = 0; i < 50; i++) {
System.out.println(
String.format("Fetching widget %s - %d", this.widgetId, i));
System.out.flush();
getWidget(this.client, this.widgetId);
System.out.println(
String.format("Widget fetched %s - %d", this.widgetId, i));
System.out.flush();
}
}
}
public static void main(String[] args) {
WidgetFetchTest dft = new WidgetFetchTest();
dft.run();
System.exit(0);
}
}
我遇到了一个我想理解的难题。我已经实现了在Wildfly服务器中运行的一个非常简单的REST服务示例。我有一个独立的多线程Java测试程序,... ...>
在JBossDeveloper论坛上发布此问题后,提供了以下答案。
请参阅文档中有关会话并发性的部分:Distributable Web Applications, Session Concurrency