【问题标题】:Servlet multiple request handle at same time on tomcatservlet在tomcat上同时处理多个请求
【发布时间】:2019-02-28 20:10:34
【问题描述】:

我编写了一个 servlet 程序,它调用一个 java 类来生成随机字符串。我还编写了一个 HTML 文件,其中有 1000 个 IFRAME 指向 servlet 地址。如果多个请求将发送到一个 servlet,则每个请求将在单独的线程中处理。所以在这种情况下,正在创建 1000 个线程(1000 个请求)。问题在于,如果请求超过 1000 个,则需要花费大量时间来处理并且无法处理,依此类推。如果我在后端进行复杂的计算,它会变得更慢。为了快速响应,需要在 servlet 级别(多线程)或 tomcat 级别(如果可能)进行哪些更改。有什么建议吗?

Servlet 代码

@WebServlet("/test")
public class MyServlet extends HttpServlet {
            private static final long serialVersionUID = 1L;
private static PrintWriter out=null;
private UserOperation operation=null;
private static int counter=0;
public MyServlet() {
    super();
    operation=new UserOperation();
}
            protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
                            counter++;
                            out=response.getWriter();
                            String output=operation.getResult();
                            System.out.println(counter+":"+output);
                            out.print(output);
                            return;
            }

            protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
                            doGet(request, response);
            }
}

UserOperation.java

import java.util.Random;

public class UserOperation {
            private Random rand=null;
            private int max=9;
            private int min=0;
            private static final String [] RESULT= {"ONE","TWO","THREE","FOUR","FIVE","SIX","SEVEN","EIGHT","NINE","TEN"};
            public UserOperation() {
                            rand=new Random();
            }
            public String getResult() {
                           int randNum=rand.nextInt((max-min)+1)+min;
                            return RESULT[randNum];
            }
}

HTML 文件

1000 次 IFRAME 调用。下面的行在 HTML 文件中被复制粘贴了 1000 次。

<IFRAME src="http://localhost:8080/MultipleRequest/test" width="30px" height="30px"></IFRAME>

【问题讨论】:

  • @NathanHughes Using System.out.println 是为了测试它将返回什么。情况并非如此。问题是servlet如何同时处理更多的请求。如果您查看我的代码,我只是在每个请求和性能方面返回一个随机字符串,它非常慢。
  • 没有创建 1000 个线程...Tomcat 默认有 200 个请求处理线程,其他所有线程都将排队等待线程完成处理。另外,您的测试有多现实……除此之外,您的浏览器甚至可能阻止并发请求,并且当时只有 6 个或什至 1 个到达。此外,您的 servlet 甚至不是线程安全的,而且实际上很危险...删除对 PrintWriter 的静态引用并将其设置为您的方法的本地。

标签: java multithreading jakarta-ee tomcat8


【解决方案1】:

尝试在您的 UserOperation 类中实现 java.lang.Runnable 接口或扩展 java.lang.Thread 类以实现多线程。 还可以尝试通过添加具有适当限制的 -Xmx 参数来增加 Java 的堆大小。

【讨论】:

  • 您的答案中的更多示例会有所帮助并保持讨论清晰。
  • 为什么需要通过实现Runnable或通过Thread类扩展来创建多个线程。 servlet 通过为每个请求创建一个线程来完成相同的部分。所以每个线程都在访问自己的 UserOperation。
  • 好吧,实际上这个配置可能需要一个我没有提到的异步 servlet 配置。 Servlet 本身的多线程在这里不是这样的。由自定义类(通常是业务逻辑)完成的 servlet 中的内部请求应该是 Runnable 线程,并且异步上下文更好地运行此 Runnable 代码。这是对异步实现的更好解释javaee.github.io/tutorial/servlets012.html
  • @ashemez 我尝试了您的解决方案,但它变得更慢了。这里的问题是如何通过 servlet 有效地同时处理更多的请求。我只是在做一个小任务,为每个请求返回一个随机字符串,那么为什么这个小计算很慢。
  • @Srinath 我用异步 servlet 进行了测试,它在 15 秒内产生了所有 servlet 请求。我编辑了我的答案,你可以找到我的 AsyncServlet 代码。
【解决方案2】:

您可以在 servlet 3.0 中使用异步 servlet;这里有一个例子:

https://www.javaworld.com/article/2077995/java-concurrency/java-concurrency-asynchronous-processing-support-in-servlet-3-0.html

仍然存在持续连接需要服务器上的端口的问题。发出的请求将占用一个端口,无论它是否占用一个线程,您都可以使用异步 servlet 使用事件代码来缓解,但无论如何端口部分都将保留。缓解端口短缺的唯一方法是将您的应用程序扩展到多个服务器

【讨论】:

    【解决方案3】:

    首先,您的 servlet 很危险,而且不是线程安全的。先解决这个问题。

    @WebServlet("/test")
    public class MyServlet extends HttpServlet {
        private static final long serialVersionUID = 1L;
        private final AtomicInteger counter = new AtomicInteger(0);
        private final UserOperation operation=null;
    
      public MyServlet() {
        super();
        operation=new UserOperation();
      }
      protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        int count = this.counter.incrementAndGet();
        PrintWriter out=response.getWriter();
        String output=operation.getResult();
        System.out.println(count + ":" + output);
        out.print(output);
        return;
      }
    
      protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request, response);
      }
    }
    

    既然这是线程安全的,那么您认为创建了 1000 个线程的事实是错误的。 Tomcat 有一个默认的请求处理线程池,大小为 200。因此它会同时处理 200 个请求,而不是您想象的 1000 个。

    除此之外,您不应该使用 1000 个 iframe 进行测试,因为您的浏览器通常被限制为对服务器的 6 个并发请求。所以我实际上怀疑您是否收到 1000 个并发请求,但一次只能从浏览器收到 6 个。 Chrome 甚至可能一次将其限制为 1,因为它会看到相同的 URL 并尝试将结果 1 逐 1 缓存。

    总之,我怀疑你的 servlet 很慢,但你的测试方法让它看起来很慢!使用像 Apache JMeter 这样的适当工具向您的服务器发送 1000 个请求,然后测试性能。

    【讨论】:

      【解决方案4】:

      尝试在您的 UserOperation 类中实现 java.lang.Runnable 接口或扩展 java.lang.Thread 类以实现多线程。 还可以尝试通过添加具有适当限制的 -Xmx 参数来增加 Java 的堆大小。

      @WebServlet(urlPatterns={"/AsyncServlet"}, asyncSupported=true)
      public class AsyncServlet extends HttpServlet {
      
          private static PrintWriter out=null;
          private UserOperation operation=null;
          private static int counter=0;
      
          public AsyncServlet() {
              super();
              operation=new UserOperation();
          }
      
         @Override
         public void doGet(HttpServletRequest request,
                           HttpServletResponse response) {
            response.setContentType("text/html;charset=UTF-8");
            final AsyncContext acontext = request.startAsync();
            acontext.start(new Runnable() {
               public void run() {
      
                   counter++;
                   try {
                      HttpServletResponse response = (HttpServletResponse) acontext.getResponse();
      
                      out=response.getWriter();
      
                      String param = acontext.getRequest().getParameter("param");
      
                       String output = operation.getResult();
                       System.out.println(counter + ":"+output);
                       out.print(output);
      
                      acontext.complete();
      
                  } catch (IOException e) {
                      e.printStackTrace();
                  }
               }
            });
         }
      
         public class UserOperation {
             private Random rand=null;
             private int max=9;
             private int min=0;
             private final String [] RESULT= {"ONE","TWO","THREE","FOUR","FIVE","SIX","SEVEN","EIGHT","NINE","TEN"};
             public UserOperation() {
                 rand=new Random();
             }
             public String getResult() {
                 int randNum=rand.nextInt((max-min)+1)+min;
                 return RESULT[randNum];
             }
         }
      }
      

      【讨论】:

        猜你喜欢
        • 2018-01-19
        • 2012-04-02
        • 2013-01-12
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-12-26
        • 1970-01-01
        • 2013-10-18
        相关资源
        最近更新 更多