首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >为什么野生蝇服务器不能在建立httpSession之后处理并发REST请求

为什么野生蝇服务器不能在建立httpSession之后处理并发REST请求
EN

Stack Overflow用户
提问于 2020-01-07 12:41:09
回答 1查看 406关注 0票数 1

我遇到了一个我想要理解的难题。我实现了一个非常简单的示例REST服务,它运行在Wildfly服务器中。我有一个独立的多线程Java测试程序,它使用Apache http客户端库来访问服务。客户端程序有两个独立的线程,同时访问REST服务。在没有在服务器上建立httpSession的情况下运行测试程序时,服务器将并发处理请求(如服务器日志中所示)。如果线程是在建立httpSession之后运行的,则请求由服务器以顺序的方式处理(如第二个服务器日志所示)。请注意,两个线程共享一个会话。

这是故意的吗?我在这里错过了什么?提前谢谢。

以下是我所处环境的基本要素:

  • Wildfly Server Info: Wildfly-17.0.0.使用独立的、高可用性的配置在Windows 10上最后运行-ha.xml
  • Client Info: ApacheHTTPClient4.5.5(托管版本)。

这里是表示这两种情况的服务器日志:

代码语言:javascript
复制
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  
...
代码语言:javascript
复制
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服务实现

代码语言:javascript
复制
@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);
    }
  }

}

这里是客户端源代码

代码语言:javascript
复制
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);
  }
}
EN

回答 1

Stack Overflow用户

发布于 2020-01-16 13:43:23

在JBossDeveloper论坛上发布这个问题后,提供了以下答案。

请参阅文档:可分发的Web应用程序,会话并发中关于会话并发的部分。

通过在关联的缓存配置上放松从REPEATABLE_READ到READ_COMMITTED的隔离,可以解决此问题。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/59628633

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档