摘要: 背景 最近一直在作项目,其中的一个功能点,主要是访问外部网站并获取页面的字符串,具体的网站url彻底是由用户输入,因此存在必定的安全隐患。 从测试来看,若是给定的一部电影的url地址,连接会一直不能被关闭,直到数据流被读完,若是来个几十次这样的请求,应用估计也差很少崩溃了 ...html
最近一直在作项目,其中的一个功能点,主要是访问外部网站并获取页面的字符串,具体的网站url彻底是由用户输入,因此存在必定的安全隐患。java
从测试来看,若是给定的一部电影的url地址,连接会一直不能被关闭,直到数据流被读完,若是来个几十次这样的请求,应用估计也差很少崩溃了apache
说明: 项目中使用的HttpClient版本是3.0.1api
通常的HttpClient使用例子:缓存
1.MultiThreadedHttpConnectionManager manager = new MultiThreadedHttpConnectionManager(); 2. HttpClient client = new HttpClient(manager); 3. client.setConnectionTimeout(30000); 4. client.setTimeout(30000); 5. 6. GetMethod get = new GetMethod("http://download.jboss.org/jbossas/7.0/jboss-7.0.0.Alpha1/jboss-7.0.0.Alpha1.zip"); 7. try { 8. client.executeMethod(get); //发起请求 9. String result = get.getResponseBodyAsString(); //获取数据 10. } catch (Exception e) { 11. } finally { 12. get.releaseConnection(); //释放连接 13. }
1."main" prio=10 tid=0x0899e800 nid=0x4010 runnable [0xb7618000..0xb761a1c8] 2. java.lang.Thread.State: RUNNABLE 3. at java.net.SocketInputStream.socketRead0(Native Method) 4. at java.net.SocketInputStream.read(SocketInputStream.java:129) 5. at java.io.BufferedInputStream.fill(BufferedInputStream.java:218) 6. at java.io.BufferedInputStream.read1(BufferedInputStream.java:258) 7. at java.io.BufferedInputStream.read(BufferedInputStream.java:317) 8. - locked <0xb23a4c30> (a java.io.BufferedInputStream) 9. at org.apache.commons.httpclient.ContentLengthInputStream.read(ContentLengthInputStream.java:156) 10. at org.apache.commons.httpclient.ContentLengthInputStream.read(ContentLengthInputStream.java:170) 11. at org.apache.commons.httpclient.ChunkedInputStream.exhaustInputStream(ChunkedInputStream.java:338) 12. at org.apache.commons.httpclient.ContentLengthInputStream.close(ContentLengthInputStream.java:104) 13. at java.io.FilterInputStream.close(FilterInputStream.java:155) 14. at org.apache.commons.httpclient.AutoCloseInputStream.notifyWatcher(AutoCloseInputStream.java:179) 15. at org.apache.commons.httpclient.AutoCloseInputStream.close(AutoCloseInputStream.java:143) 16. at org.apache.commons.httpclient.HttpMethodBase.releaseConnection(HttpMethodBase.java:1341)
目前httpClient3.1只支持3种timeout的设置:安全
分析一下问题,咱们须要的是一个HttpClient整个连接读取的一个超时时间,包括请求发起,Http Head解析,response流读取的一系列时间的总和。 异步
目标很明确,对应的修正后的测试代码:socket
1.final MultiThreadedHttpConnectionManager manager = new MultiThreadedHttpConnectionManager(); 2. final HttpClient client = new HttpClient(manager); 3. client.setConnectionTimeout(30000); 4. client.setTimeout(30000); 5. final GetMethod get = new GetMethod( 6. "http://download.jboss.org/jbossas/7.0/jboss-7.0.0.Alpha1/jboss-7.0.0.Alpha1.zip"); 7. 8. Thread t = new Thread(new Runnable() { 9. 10. @Override 11. public void run() { 12. try { 13. client.executeMethod(get); 14. String result = get.getResponseBodyAsString(); 15. } catch (Exception e) { 16. // ignore 17. } 18. } 19. }, "Timeout guard"); 20. t.setDaemon(true); 21. t.start(); 22. try { 23. t.join(5000l); //等待5s后结束 24. } catch (InterruptedException e) { 25. System.out.println("out finally start"); 26. ((MultiThreadedHttpConnectionManager) client.getHttpConnectionManager()).shutdown(); 27. System.out.println("out finally end"); 28. } 29. if (t.isAlive()) { 30. System.out.println("out finally start"); 31. ((MultiThreadedHttpConnectionManager) client.getHttpConnectionManager()).shutdown(); 32. System.out.println("out finally end"); 33. t.interrupt(); 34. // throw new TimeoutException(); 35. } 36. System.out.println("done");
这里经过Thread.join方法,设置了超时时间为5000 ms,这是比较早的用法。 若是熟悉cocurrent包的,能够直接使用Future和ThreadPoolExecutor进行异步处理,缓存对应的Thread。ide
1.ExecutorService service = Executors.newCachedThreadPool(); 2. Future future = service.submit(new Callable<String>() { 3. 4. @Override 5. public String call() throws Exception { 6. 7. try { 8. client.executeMethod(get); 9. return get.getResponseBodyAsString(); 10. } catch (Exception e) { 11. e.printStackTrace(); 12. } finally { 13. System.out.println("future finally start"); 14. ((MultiThreadedHttpConnectionManager) client.getHttpConnectionManager()).shutdown(); 15. System.out.println("future finally end"); 16. } 17. 18. return ""; 19. } 20. 21. }); 22. 23. try { 24. future.get(5000, TimeUnit.MILLISECONDS); 25. } catch (Exception e) { 26. System.out.println("out finally"); 27. e.printStackTrace(); 28. ((MultiThreadedHttpConnectionManager) client.getHttpConnectionManager()).shutdown(); 29. System.out.println("out finally end"); 30. } 31. 32. service.shutdown();
看下release的实现: 测试
1.public void releaseConnection() { 2. 3. if (responseStream != null) { 4. try { 5. // FYI - this may indirectly invoke responseBodyConsumed. 6. responseStream.close(); // 会先关闭流 7. } catch (IOException e) { 8. // the connection may not have been released, let's make sure 9. ensureConnectionRelease(); 10. } 11. } else { 12. // Make sure the connection has been released. If the response 13. // stream has not been set, this is the only way to release the 14. // connection. 15. ensureConnectionRelease(); 16. } 17. }
1.public void close() throws IOException { 2. if (!closed) { 3. try { 4. ChunkedInputStream.exhaustInputStream(this); 5. } finally { 6. // close after above so that we don't throw an exception trying 7. // to read after closed! 8. closed = true; 9. } 10. } 11. }
1.static void exhaustInputStream(InputStream inStream) throws IOException { 2. // read and discard the remainder of the message 3. byte buffer[] = new byte[1024]; 4. while (inStream.read(buffer) >= 0) { 5. ; 6. } 7. }
说明: