Java 中的并发 HTTP URL 连接
我正在开发一个工具,可以从给定文件夹中获取所有文本文件。这些文本文件包含一堆 http URL。要求是我对这些 URL 执行 GET 请求,记录响应代码并可选择打印输入流。 我必须以多线程方式执行此操作,并且线程数应该是可配置的。因此,我使用 TestNG 并使用并行=true 的数据提供程序来进行设置。这是我到目前为止所拥有的。 我这样做对吗?
编辑#2 我发现的另一个问题是,当我针对 90K URL 和 100 个线程运行此工具时......请求在一开始发送得很快,但一段时间后它们会显着减慢。这里可能有什么问题?
编辑#1 我可以针对少量记录(例如数千条)运行此工具,但无法针对大量 URL(250K)和线程(2500)运行该工具。我正在使用 ReportNG 生成报告。以下是当我针对 250K URL 和 2500 个线程运行工具时间歇性遇到的异常。
java.net.SocketException: Unexpected end of file from server
at sun.net.www.http.HttpClient.parseHTTPHeader(Unknown Source)
at sun.net.www.http.HttpClient.parseHTTP(Unknown Source)
at sun.net.www.http.HttpClient.parseHTTPHeader(Unknown Source)
at sun.net.www.http.HttpClient.parseHTTP(Unknown Source)
at sun.net.www.protocol.http.HttpURLConnection.getInputStream(Unknown Source)
at java.net.HttpURLConnection.getResponseCode(Unknown Source)
at com.HTTPConn.doHttpUrlConnectionAction(HTTPConn.java:83)
at sun.reflect.GeneratedMethodAccessor4.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at
org.testng.internal.MethodInvocationHelper.invokeMethod(MethodInvocationHelper.java:80)
at org.testng.internal.Invoker.invokeMethod(Invoker.java:673)
at org.testng.internal.Invoker.invokeTestMethod(Invoker.java:842)
at org.testng.internal.TestMethodWithDataProviderMethodWorker.call(TestMethodWithDataProviderMethodWorker.java:73)
at org.testng.internal.TestMethodWithDataProviderMethodWorker.call(TestMethodWithDataProviderMethodWorker.java:14)
[TestNG] Reporter org.uncommons.reportng.HTMLReporter@eee36c failed
org.uncommons.reportng.ReportNGException: Failed generating HTML report.
at org.uncommons.reportng.HTMLReporter.generateReport(HTMLReporter.java:118)
at org.testng.TestNG.generateReports(TestNG.java:1053)
at org.testng.TestNG.run(TestNG.java:1016)
at com.TestNGRunner.main(TestNGRunner.java:83)
Caused by: java.lang.NullPointerException
at java.lang.String.compareTo(Unknown Source)
at org.uncommons.reportng.TestResultComparator.compare(TestResultComparator.java:29)
at org.uncommons.reportng.TestResultComparator.compare(TestResultComparator.java:25)
at java.util.Collections.indexedBinarySearch(Unknown Source)
at java.util.Collections.binarySearch(Unknown Source)
at org.uncommons.reportng.HTMLReporter.sortByTestClass(HTMLReporter.java:229)
at org.uncommons.reportng.HTMLReporter.createResults(HTMLReporter.java:180)
at org.uncommons.reportng.HTMLReporter.generateReport(HTMLReporter.java:110)
... 3 more
public class HTTPConn {
Logger log;
File folder;
boolean isStreamRequired;
@BeforeClass(alwaysRun=true)
public void setup(){
log = Logger.getLogger(HTTPConn.class);
folder = new File("c:\path\to\folder\from\where\I\want\to\read\textfiles");
isStreamRequired = false;
}
@DataProvider(name="getRecords", parallel=true)
public Object[][] getURLsFromTheFile() throws IOException {
try {
File [] listFiles = folder.listFiles();
int i=0;
Object[][] result = null;
ArrayList<String> totalList = new ArrayList<String>();
for(int ind=0;ind<listFiles.length;ind++){
if(listFiles[ind].isFile()){
List<String> arrList =FileUtils.readLines(listFiles[ind]);
totalList.addAll(arrList);//add files one by one
}
}
result=new Object[totalList.size()][];
for(String s:totalList){
result[i]=new Object[]{s};
i++;
}
return result;
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
return null;
}
}
@Test(dataProvider = "getRecords")
public void doHttpUrlConnectionAction(String desiredUrl){
try {
URL url = new URL(desiredUrl);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("GET");
connection.connect();
int responseCode = connection.getResponseCode();
log.info("\nResponse Code:"+ desiredUrl+"\n"+responseCode+"\n");
if(responseCode!=200)
Assert.fail("Response Code:"+responseCode);
else
Reporter.log("Response Code:"+responseCode);
// read the output from the server
if(isStreamRequired) {
//read the input stream and print it to log
reader.close();
}//isStreamOutputRequired
connection.disconnect();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
I am developing a tool that grabs all the text files from a given folder. These text files contain bunch of http URL. The requirement is that I do a GET request to these URL's , log the response code and optionally print the input stream.
I have to perform this in multi-threaded fashion and the thread count should be configurable. So I am using TestNG and using data provider with parallel=true to set this up. Here is what I have so far.
Am I doing this right?
Edit#2
The other issue I found is that when I run this tool for 90K URLs and with 100 threads..The requests are sent fast in the beginning but they slow down remarkably after a while. What could be the issue here?
Edit#1
I am able to run this tool for small number of records like a few thousands but I am not able it for a significantly high number of URLs(250K) and threads (2500). I am using ReportNG to generate reports. Here are the exceptions I get intermittently when I run my tool for 250K URLs and 2500 threads.
java.net.SocketException: Unexpected end of file from server
at sun.net.www.http.HttpClient.parseHTTPHeader(Unknown Source)
at sun.net.www.http.HttpClient.parseHTTP(Unknown Source)
at sun.net.www.http.HttpClient.parseHTTPHeader(Unknown Source)
at sun.net.www.http.HttpClient.parseHTTP(Unknown Source)
at sun.net.www.protocol.http.HttpURLConnection.getInputStream(Unknown Source)
at java.net.HttpURLConnection.getResponseCode(Unknown Source)
at com.HTTPConn.doHttpUrlConnectionAction(HTTPConn.java:83)
at sun.reflect.GeneratedMethodAccessor4.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at
org.testng.internal.MethodInvocationHelper.invokeMethod(MethodInvocationHelper.java:80)
at org.testng.internal.Invoker.invokeMethod(Invoker.java:673)
at org.testng.internal.Invoker.invokeTestMethod(Invoker.java:842)
at org.testng.internal.TestMethodWithDataProviderMethodWorker.call(TestMethodWithDataProviderMethodWorker.java:73)
at org.testng.internal.TestMethodWithDataProviderMethodWorker.call(TestMethodWithDataProviderMethodWorker.java:14)
[TestNG] Reporter org.uncommons.reportng.HTMLReporter@eee36c failed
org.uncommons.reportng.ReportNGException: Failed generating HTML report.
at org.uncommons.reportng.HTMLReporter.generateReport(HTMLReporter.java:118)
at org.testng.TestNG.generateReports(TestNG.java:1053)
at org.testng.TestNG.run(TestNG.java:1016)
at com.TestNGRunner.main(TestNGRunner.java:83)
Caused by: java.lang.NullPointerException
at java.lang.String.compareTo(Unknown Source)
at org.uncommons.reportng.TestResultComparator.compare(TestResultComparator.java:29)
at org.uncommons.reportng.TestResultComparator.compare(TestResultComparator.java:25)
at java.util.Collections.indexedBinarySearch(Unknown Source)
at java.util.Collections.binarySearch(Unknown Source)
at org.uncommons.reportng.HTMLReporter.sortByTestClass(HTMLReporter.java:229)
at org.uncommons.reportng.HTMLReporter.createResults(HTMLReporter.java:180)
at org.uncommons.reportng.HTMLReporter.generateReport(HTMLReporter.java:110)
... 3 more
public class HTTPConn {
Logger log;
File folder;
boolean isStreamRequired;
@BeforeClass(alwaysRun=true)
public void setup(){
log = Logger.getLogger(HTTPConn.class);
folder = new File("c:\path\to\folder\from\where\I\want\to\read\textfiles");
isStreamRequired = false;
}
@DataProvider(name="getRecords", parallel=true)
public Object[][] getURLsFromTheFile() throws IOException {
try {
File [] listFiles = folder.listFiles();
int i=0;
Object[][] result = null;
ArrayList<String> totalList = new ArrayList<String>();
for(int ind=0;ind<listFiles.length;ind++){
if(listFiles[ind].isFile()){
List<String> arrList =FileUtils.readLines(listFiles[ind]);
totalList.addAll(arrList);//add files one by one
}
}
result=new Object[totalList.size()][];
for(String s:totalList){
result[i]=new Object[]{s};
i++;
}
return result;
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
return null;
}
}
@Test(dataProvider = "getRecords")
public void doHttpUrlConnectionAction(String desiredUrl){
try {
URL url = new URL(desiredUrl);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("GET");
connection.connect();
int responseCode = connection.getResponseCode();
log.info("\nResponse Code:"+ desiredUrl+"\n"+responseCode+"\n");
if(responseCode!=200)
Assert.fail("Response Code:"+responseCode);
else
Reporter.log("Response Code:"+responseCode);
// read the output from the server
if(isStreamRequired) {
//read the input stream and print it to log
reader.close();
}//isStreamOutputRequired
connection.disconnect();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
你尝试过吗?只需打印线程 ID 即可再次检查这些方法是否确实并行运行。如果不是,请通过电子邮件给我发送一个小型的独立测试类来显示问题。
Have you tried it? Just print the thread ids to double check that these methods are indeed running in parallel. If they're not, email me a small self contained test class that shows the problem.
这是更新和答案。
我的堆空间已经用完了,而且我的代码中没有捕获 Throwable。一旦我添加,发现我的内存不足。所以我使用了 -Xmx512 标志,如下所示
java -Xmx512m -jar Tool.jar
这本身并没有解决问题。 ReportNG 存在问题,我不断收到异常情况。所以我再次禁用了 ReportNG,
这并没有解决问题,对于超过 100K 的 URL,现在 TestNG 报告开始消耗大量内存,因此我还必须通过将标志 setUseDefaultListeners 更改为 false 来禁用 TestNG 报告
。开始为任意数量的记录工作。只是目前我拥有的唯一报告是 log4j,我可以接受这一点。
感谢大家看到这里。
Here is the update and answer.
I was running out of heap space and I was not catching Throwable in my code. Once I added that found out that I am running out of memory. So I used -Xmx512 flag like below
java -Xmx512m -jar Tool.jar
This in itself did not solve the problem. There was an issue with ReportNG and I was continuously getting exceptions. So I disabled using ReportNG
Again this did not solve the issue, For URLs more that 100K, now TestNG report started consuming lot of memory, so I had to disable the TestNG report as well by changing the flag setUseDefaultListeners to false
After all these changes everything started working for any number of records. It is just that the only reporting I have at this point is log4j and I can live with that.
Thanks to all for taking a look at this.