我正在使用大约为视频提供服务的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();
}
}
}
我的测试程序:
在Eclipse上运行的tomcat实例上观察:
悬挂线程的堆栈:
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的名称