我正在使用大约为视频提供服务的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();
        }
    }
}

My test procedure:

  • 我在Eclipse上运行了几分钟的代码 . 代码开始下载url输入指向URLDownloader的500个并发文件

  • 然后我按"Terminate"立即关闭所有线程,模拟用户观看视频几分钟并关闭浏览器的场景,因为他对此感到厌倦 .

在Eclipse上运行的tomcat实例上观察到:

  • 我几乎所有500个连接都挂在tomcat上,我可以从Eclise Debug视图中看到"Daemon Thread [http-nio-8080-exec-949] (Running)" .

  • 经过一段时间后,我看到所有这些都立即从Eclipse Debug视图开始终止和disapeader .

stack of a dangling thread:

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);
}

My tomcat connector setting on server.xml

<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将会耗尽并且新用户无法连接到服务器 .

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

URLDownload class:

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的名称