当客户端断开连接时,Tomcat会发生悬空连接

时间:2015-12-14 09:20:26

标签: java tomcat8 connector

我正在使用大约为视频提供服务的Tomcat实例。 400 MB大小。我写了一个简单的测试用例来创建500个线程来从tomcat服务器下载视频文件

public void startTestCase(){
    //URLDownload class opens a connection to given URL and read the input stream 
    ArrayList<Thread> list = new ArrayList<>();
    for(int i = 500; i > 0; i--){
        list.add(new Thread(new URLDownloader(url)));
    }
    int t = 0;
    for (Thread thread : list) {
        System.out.println(t++);
        thread.start();
    }

    for (Thread thread : list) {
        try {
            System.out.println(t--);
            thread.join();
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}

我的测试程序:

  1. 我在Eclipse上运行代码几分钟。代码开始下载url输入指向URLDownloader
  2. 的500个并发文件
  3. 然后我按下&#34;终止&#34;一次性关闭所有线程,模拟用户观看视频几分钟并关闭浏览器的场景,因为他对此感到厌倦。
  4. 在Eclipse上运行的tomcat实例上观察:

    1. 我几乎将所有500个连接挂在tomcat上,我可以在Eclise Debug视图中看到它&#34;守护程序线程[http-nio-8080-exec-949](正在运行)&#34;。
    2. 经过一段时间后,我立即看到所有这些都从Eclipse Debug视图开始终止并解除了它。
    3. 悬挂线程的堆栈:

      Daemon Thread [http-nio-8080-exec-1049] (Suspended) 
      Unsafe.park(boolean, long) line: not available [native method]  
      LockSupport.parkNanos(Object, long) line: 215   
      AbstractQueuedSynchronizer$ConditionObject.awaitNanos(long) line: 2078  
      TaskQueue(LinkedBlockingQueue<E>).poll(long, TimeUnit) line: 467    
      TaskQueue.poll(long, TimeUnit) line: 85 
      TaskQueue.poll(long, TimeUnit) line: 31 
      ThreadPoolExecutor(ThreadPoolExecutor).getTask() line: 1066 
      ThreadPoolExecutor(ThreadPoolExecutor).runWorker(ThreadPoolExecutor$Worker) line: 1127  
      ThreadPoolExecutor$Worker.run() line: 617   
      TaskThread$WrappingRunnable.run() line: 61  
      TaskThread(Thread).run() line: 745  
      

      注意:

      有一次,我似乎无法再次重现,所有的线程都被暂停在我的文件服务servlet上的以下代码行

       while ((read = input.read(buffer)) > 0) {
          output.write(buffer, 0, read); // <-- at this point 
          numberOfBytesRead += read;
          rd.numberOfKBSent = numberOfBytesRead;
          sleep(15);
      }
      

      我在server.xml上的tomcat连接器设置

      <Connector connectionTimeout="20000" port="8080" protocol="org.apache.coyote.http11.Http11NioProtocol" redirectPort="8443" acceptCount="5000" maxConnections="5000" maxThreads="5000" acceptorThreadCount="8"/>
      

      我使用http://tomcat.apache.org/tomcat-7.0-doc/config/http.html

      准备了它

      我认为我错过了上面一些重要的参数。一旦用户断开下载,有没有办法杀死线程?我需要具有高可用性以尽可能多地为并发用户提供服务,如果所有断开连接的线程都在闲逛,那么我的maxThreads将会耗尽并且新用户无法连接到服务器。

      服务器设置:
      1 Gbps本地连接
      4核心,8 HT处理器
      Tomcat 8实例在Eclipse Mars调试模式下运行 上面列出的tomcat连接器设置下的HTTP连接器详细信息

      URLDownload类:

      class URLDownloader implements Runnable {
          String url;
          public URLDownloader(String url){
              this.url = url;
          }
          @Override
          public void run() {
              try {
                  URLConnection conn = new URL(url).openConnection();
                  InputStream in = conn.getInputStream();
                  byte[] b = new byte[1024];
                  int i = 0;
                  while((i= in.read(b)) != -1){
      
                  }
              } catch (MalformedURLException e) {
                  // TODO Auto-generated catch block
                  e.printStackTrace();
              } catch (IOException e) {
                  // TODO Auto-generated catch block
                  e.printStackTrace();
              }
          }
      
      }
      

      =======编辑添加一个卡在output.write(缓冲区,0,读取)的线程的堆栈跟踪; =======

      Daemon Thread [http-nio-8080-exec-2040] (Suspended) 
      owns: InternalNioOutputBuffer  (id=32497)   
      owns: NioChannel  (id=32498)    
      Unsafe.park(boolean, long) line: not available [native method]  
      LockSupport.parkNanos(Object, long) line: 215   
      CountDownLatch$Sync(AbstractQueuedSynchronizer).doAcquireSharedNanos(int, long) line: 1037  
      CountDownLatch$Sync(AbstractQueuedSynchronizer).tryAcquireSharedNanos(int, long) line: 1328 
      CountDownLatch.await(long, TimeUnit) line: 277  
      NioEndpoint$KeyAttachment.awaitLatch(CountDownLatch, long, TimeUnit) line: 1375 
      NioEndpoint$KeyAttachment.awaitWriteLatch(long, TimeUnit) line: 1378    
      NioBlockingSelector.write(ByteBuffer, NioChannel, long) line: 114   
      NioSelectorPool.write(ByteBuffer, NioChannel, Selector, long, boolean) line: 173    
      InternalNioOutputBuffer.writeToSocket(ByteBuffer, boolean, boolean) line: 139   
      InternalNioOutputBuffer.addToBB(byte[], int, int) line: 197 
      InternalNioOutputBuffer.access$000(InternalNioOutputBuffer, byte[], int, int) line: 41  
      InternalNioOutputBuffer$SocketOutputBuffer.doWrite(ByteChunk, Response) line: 320   
      IdentityOutputFilter.doWrite(ByteChunk, Response) line: 84  
      InternalNioOutputBuffer(AbstractOutputBuffer<S>).doWrite(ByteChunk, Response) line: 256 
      Response.doWrite(ByteChunk) line: 503   
      OutputBuffer.realWriteBytes(byte[], int, int) line: 388 
      ByteChunk.append(byte[], int, int) line: 315    
      OutputBuffer.writeBytes(byte[], int, int) line: 418 
      OutputBuffer.write(byte[], int, int) line: 406  
      CoyoteOutputStream.write(byte[], int, int) line: 97 
      VideoStream.copy(RandomAccessFile, OutputStream, long, long, VideoStream$RequestData) line: 502 <-- this is where it dangle
      VideoStream.processRequest(HttpServletRequest, HttpServletResponse, boolean, VideoStream$RequestData) line: 402 
      VideoStream.doGet(HttpServletRequest, HttpServletResponse) line: 171    
      VideoStream(HttpServlet).service(HttpServletRequest, HttpServletResponse) line: 622 
      VideoStream(HttpServlet).service(ServletRequest, ServletResponse) line: 729 
      ApplicationFilterChain.internalDoFilter(ServletRequest, ServletResponse) line: 291  
      ApplicationFilterChain.doFilter(ServletRequest, ServletResponse) line: 206  
      WsFilter.doFilter(ServletRequest, ServletResponse, FilterChain) line: 52    
      ApplicationFilterChain.internalDoFilter(ServletRequest, ServletResponse) line: 239  
      ApplicationFilterChain.doFilter(ServletRequest, ServletResponse) line: 206  
      StandardWrapperValve.invoke(Request, Response) line: 219    
      StandardContextValve.invoke(Request, Response) line: 106    
      NonLoginAuthenticator(AuthenticatorBase).invoke(Request, Response) line: 502    
      StandardHostValve.invoke(Request, Response) line: 142   
      ErrorReportValve.invoke(Request, Response) line: 79 
      AccessLogValve(AbstractAccessLogValve).invoke(Request, Response) line: 610  
      StandardEngineValve.invoke(Request, Response) line: 88  
      CoyoteAdapter.service(Request, Response) line: 518  
      Http11NioProcessor(AbstractHttp11Processor<S>).process(SocketWrapper<S>) line: 1091 
      Http11NioProtocol$Http11ConnectionHandler(AbstractProtocol$AbstractConnectionHandler<S,P>).process(SocketWrapper<S>, SocketStatus) line: 668    
      Http11NioProtocol$Http11ConnectionHandler.process(SocketWrapper<NioChannel>, SocketStatus) line: 223    
      NioEndpoint$SocketProcessor.doRun(SelectionKey, NioEndpoint$KeyAttachment) line: 1517   
      NioEndpoint$SocketProcessor.run() line: 1474    
      ThreadPoolExecutor(ThreadPoolExecutor).runWorker(ThreadPoolExecutor$Worker) line: 1142  
      ThreadPoolExecutor$Worker.run() line: 617   
      TaskThread$WrappingRunnable.run() line: 61  
      TaskThread(Thread).run() line: 745  
      

      VideoStream servlet是我提供视频文件的servlet的名称

0 个答案:

没有答案