我用Java编写了一个软件,该软件通过使用代理发送HTTP请求来检查代理是否正常工作。
它从数据库中获取大约30,000个代理,然后尝试检查它们是否可运行。从数据库接收到的代理曾经以ArrayList<String>
的形式返回,但由于以下原因已更改为Deque<String>
。
程序的工作方式是有一个ProxyRequest
对象,该对象分别将IP和端口存储为String和int。 ProxyRequest
对象具有方法isWorkingProxy()
,该方法尝试使用代理发送请求并返回boolean
是否成功。
此ProxyRequest
对象由RunnableProxyRequest
对象包裹,该对象在重写的super.isWorkingProxy()
方法中调用run()
。根据{{1}}的响应,super.isWorkingProxy()
对象将更新MySQL数据库。
请注意,MySQL数据库的更新为RunnableProxyRequest
。
它使用FixedThreadPool(在VPS上)在750个线程上运行,但朝 最后,它变得非常慢(卡在〜50个线程上),这显然 表示垃圾收集器正在工作。这是问题。
我已经尝试了以下方法来改善延迟,但似乎不起作用:
1)使用synchronized()
代理并使用Deque<String>
获得代理所在的Deque.pop()
。 (我相信)可以使String
不断变小,从而可以改善由GC引起的延迟。
2)设置Deque<String>
,其中con.setConnectTimeout(this.timeout);
这样,连接应在5秒钟内返回结果。如果没有,则该线程已完成,并且不应再在线程池中处于活动状态。
除此之外,我不知道有什么其他方法可以提高性能。
有人可以为我推荐一种方法来提高性能,以防止/停止GC滞后于线程末尾吗?我知道有一个关于{Java threads slow down towards the end of processing)的Stackoverflow问题,但是我已经尝试了答案中的所有内容,但它对我没有用。
谢谢您的时间。
代码段:
循环向this.timeout = 5000;
添加线程:
FixedThreadPool
//This code is executed recursively (at the end, main(args) is called again)
//Create the threadpool for requests
//Threads is an argument that is set to 750.
ThreadPoolExecutor executor = (ThreadPoolExecutor)Executors.newFixedThreadPool(threads);
Deque<String> proxies = DB.getProxiesToCheck();
while(proxies.isEmpty() == false) {
try {
String[] split = proxies.pop().split(":");
Runnable[] checks = new Runnable[] {
//HTTP check
new RunnableProxyRequest(split[0], split[1], Proxy.Type.HTTP, false),
//SSL check
new RunnableProxyRequest(split[0], split[1], Proxy.Type.HTTP, true),
//SOCKS check
new RunnableProxyRequest(split[0], split[1], Proxy.Type.SOCKS, false)
//Add more checks to this list as time goes...
};
for(Runnable check : checks) {
executor.submit(check);
}
} catch(IndexOutOfBoundsException e) {
continue;
}
}
类:
ProxyRequest
//Proxy details
private String proxyIp;
private int proxyPort;
private Proxy.Type testingType;
//Request details
private boolean useSsl;
public ProxyRequest(String proxyIp, String proxyPort, Proxy.Type testingType, boolean useSsl) {
this.proxyIp = proxyIp;
try {
this.proxyPort = Integer.parseInt(proxyPort);
} catch(NumberFormatException e) {
this.proxyPort = -1;
}
this.testingType = testingType;
this.useSsl = useSsl;
}
public boolean isWorkingProxy() {
//Case of an invalid proxy
if(proxyPort == -1) {
return false;
}
HttpURLConnection con = null;
//Perform checks on URL
//IF any exception occurs here, the proxy is obviously bad.
try {
URL url = new URL(this.getTestingUrl());
//Create proxy
Proxy p = new Proxy(this.testingType, new InetSocketAddress(this.proxyIp, this.proxyPort));
//No redirect
HttpURLConnection.setFollowRedirects(false);
//Open connection with proxy
con = (HttpURLConnection)url.openConnection(p);
//Set the request method
con.setRequestMethod("GET");
//Set max timeout for a request.
con.setConnectTimeout(this.timeout);
} catch(MalformedURLException e) {
System.out.println("The testing URL is bad. Please fix this.");
return false;
} catch(Exception e) {
return false;
}
try(
BufferedReader in = new BufferedReader(new InputStreamReader(con.getInputStream()));
) {
String inputLine = null; StringBuilder response = new StringBuilder();
while((inputLine = in.readLine()) != null) {
response.append(inputLine);
}
//A valid proxy!
return con.getResponseCode() > 0;
} catch(Exception e) {
return false;
}
}
类:
RunnableProxyRequest
public class RunnableProxyRequest extends ProxyRequest implements Runnable {
public RunnableProxyRequest(String proxyIp, String proxyPort, Proxy.Type testingType, boolean useSsl) {
super(proxyIp, proxyPort, testingType, useSsl);
}
@Override
public void run() {
String test = super.getTest();
if(super.isWorkingProxy()) {
System.out.println("-- Working proxy: " + super.getProxy() + " | Test: " + test);
this.updateDB(true, test);
} else {
System.out.println("-- Not working: " + super.getProxy() + " | Test: " + test);
this.updateDB(false, test);
}
}
private void updateDB(boolean success, String testingType) {
switch(testingType) {
case "SSL":
DB.updateSsl(super.getProxyIp(), super.getProxyPort(), success);
break;
case "HTTP":
DB.updateHttp(super.getProxyIp(), super.getProxyPort(), success);
break;
case "SOCKS":
DB.updateSocks(super.getProxyIp(), super.getProxyPort(), success);
break;
default:
break;
}
}
}
类:
DB
答案 0 :(得分:1)
感谢Peter Lawrey指导我解决问题! :)
他的评论:
@ILoveKali我发现网络库在 发生错误时关闭连接。超时倾向 当连接良好时,可以发挥最佳效果。 YMMV
因此,我做了一些研究,发现我还必须使用方法setReadTimeout(this.timeout);
。以前,我只使用setConnectTimeout(this.timeout);
!
感谢这篇帖子(HttpURLConnection timeout defaults)解释了以下内容:
不幸的是,根据我的经验,使用这些默认设置可以 导致不稳定状态,具体取决于您发生的情况 与服务器的连接。如果您使用HttpURLConnection而不 明确设置(至少读取)超时,您的连接可以进入 永久的陈旧状态。默认情况下。因此,始终将setReadTimeout设置为 “某物”,或者您可能孤立连接(甚至可能是线程) 取决于您的应用运行方式。
因此,最后的答案是:GC的运行很好,它对延迟不负责。线程只是永久地固定在一个数字上,因为我没有设置读取超时,因此isWorkingProxy()
方法永远不会得到结果并保持读取状态。