patternjavaMinor
Concurrent multi-server pinging in Java
Viewed 0 times
pingingmultijavaserverconcurrent
Problem
I have an application that essentially "pings" all of the servers on my network. I have about 100 servers, and this ping will happen every 10 seconds.
So, do you think this code can be improved? I really feel like opening a new thread every time I make a connection isn't a good thing.
EDIT:
So looking over all the proposed solutions, they are all very good, and I will over the next week try and all and see which one appears to be the most efficient.
public class HealthChecker {
private static List servers = new ArrayList();
public static void main(String[] args){
// not shown: populating servers list
new Thread(){
public void run(){
while (true){
try {
Thread.sleep(10*1000);
for (final InetSocketAddress server : servers){
new Thread(){
public void run(){
Socket connection = new Socket();
try {
connection.connect(server, 5*1000);
} catch (IOException e) {
servers.remove(server);
}
}
}.start();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}.start();
}So, do you think this code can be improved? I really feel like opening a new thread every time I make a connection isn't a good thing.
EDIT:
So looking over all the proposed solutions, they are all very good, and I will over the next week try and all and see which one appears to be the most efficient.
Solution
Here's an example adapted to your case which uses ThreadPoolExecutor.
This example allows a configurable throughput while establishing TCP connections to servers.
Checking Future.get() for each submitted worker thread ensures that no servers are checked more than others.
What happens here:
Here's the source
Here's the output of 30 domain sample
You can increase the maximumPoolSize of ThreadPoolExecutor in order to get all domains into queue in just one iteration. However I left that part for you.
This example allows a configurable throughput while establishing TCP connections to servers.
Checking Future.get() for each submitted worker thread ensures that no servers are checked more than others.
What happens here:
- Target domains are added to a list
- If the domain is an unknown host (DNS unreachable) than it's skipped.
- Resolvable domains are submitted as 1 domain per 1 thread into the pool.
- If the pool gets overflown by PingWork threads, that submission is delayed for 3 seconds until the pool is ready to accept.
- Each TCP port 80 connection has a timeout of 5 seconds.
- When there are no exceptions thrown by a TCP connection to a certain host, it is assumed reachable and not reported.
- When all PingWork threads finish, main thread waits for 10 seconds until next round.
Here's the source
// package
// imports
public class Main {
private static List servers =
new ArrayList();
public static void main(String[] args)
throws InterruptedException, ExecutionException {
String[] domains = new String[]{
"3Com.com",
// etc. etc.
"Kai.com"
};
for (String hostname : domains) {
try {
servers.add(
new InetSocketAddress(
InetAddress.getByName(hostname),
80
)
);
} catch (UnknownHostException e) {
System.out.println("Unknown host: " + hostname);
}
}
System.out.println(
"Total number of target servers: " + servers.size()
);
BlockingQueue work =
new ArrayBlockingQueue(5);
ThreadPoolExecutor pool = new ThreadPoolExecutor(
5, // corePoolSize
10, // maximumPoolSize
5000, // keepAliveTime
TimeUnit.MILLISECONDS, // TimeUnit
work // workQueue
);
pool.prestartAllCoreThreads();
pool.setRejectedExecutionHandler(
new RejectedExecutionHandler() {
@Override
public void rejectedExecution(
Runnable r,
ThreadPoolExecutor executor
) {
System.out.println("Work queue is currently full");
try {
Thread.sleep(3000);
} catch (InterruptedException ignore) {
}
executor.submit(r);
}
}
);
Collection> futures = new LinkedList>();
while (true) {
for (InetSocketAddress server : servers) {
futures.add(pool.submit(new PingWork(server)));
}
for (Future future : futures) {
future.get();
}
System.out.println(
"All servers checked. Will wait for 10 seconds until next round"
);
Thread.sleep(10000);
}
}
private static class PingWork implements Runnable {
private static final int TIMEOUT = 5000;
private InetSocketAddress target;
private PingWork(InetSocketAddress target) {
this.target = target;
}
@Override
public void run() {
Socket connection = new Socket();
boolean reachable;
try {
try {
connection.connect(target, TIMEOUT);
} finally {
connection.close();
}
reachable = true;
} catch (Exception e) {
reachable = false;
}
if (!reachable) {
System.out.println(
String.format(
"%s:%d was UNREACHABLE",
target.getAddress(),
target.getPort()
)
);
}
}
}
}Here's the output of 30 domain sample
Total number of target servers: 30
Work queue is currently full
Work queue is currently full
Amdahl.com/129.212.11.21:80 was UNREACHABLE
All servers checked. Will wait for 10 seconds until next round
Work queue is currently full
Work queue is currently full
Amdahl.com/129.212.11.21:80 was UNREACHABLE
All servers checked. Will wait for 10 seconds until next roundYou can increase the maximumPoolSize of ThreadPoolExecutor in order to get all domains into queue in just one iteration. However I left that part for you.
Code Snippets
// package
// imports
public class Main {
private static List<InetSocketAddress> servers =
new ArrayList<InetSocketAddress>();
public static void main(String[] args)
throws InterruptedException, ExecutionException {
String[] domains = new String[]{
"3Com.com",
// etc. etc.
"Kai.com"
};
for (String hostname : domains) {
try {
servers.add(
new InetSocketAddress(
InetAddress.getByName(hostname),
80
)
);
} catch (UnknownHostException e) {
System.out.println("Unknown host: " + hostname);
}
}
System.out.println(
"Total number of target servers: " + servers.size()
);
BlockingQueue<Runnable> work =
new ArrayBlockingQueue<Runnable>(5);
ThreadPoolExecutor pool = new ThreadPoolExecutor(
5, // corePoolSize
10, // maximumPoolSize
5000, // keepAliveTime
TimeUnit.MILLISECONDS, // TimeUnit
work // workQueue
);
pool.prestartAllCoreThreads();
pool.setRejectedExecutionHandler(
new RejectedExecutionHandler() {
@Override
public void rejectedExecution(
Runnable r,
ThreadPoolExecutor executor
) {
System.out.println("Work queue is currently full");
try {
Thread.sleep(3000);
} catch (InterruptedException ignore) {
}
executor.submit(r);
}
}
);
Collection<Future<?>> futures = new LinkedList<Future<?>>();
while (true) {
for (InetSocketAddress server : servers) {
futures.add(pool.submit(new PingWork(server)));
}
for (Future<?> future : futures) {
future.get();
}
System.out.println(
"All servers checked. Will wait for 10 seconds until next round"
);
Thread.sleep(10000);
}
}
private static class PingWork implements Runnable {
private static final int TIMEOUT = 5000;
private InetSocketAddress target;
private PingWork(InetSocketAddress target) {
this.target = target;
}
@Override
public void run() {
Socket connection = new Socket();
boolean reachable;
try {
try {
connection.connect(target, TIMEOUT);
Total number of target servers: 30
Work queue is currently full
Work queue is currently full
Amdahl.com/129.212.11.21:80 was UNREACHABLE
All servers checked. Will wait for 10 seconds until next round
Work queue is currently full
Work queue is currently full
Amdahl.com/129.212.11.21:80 was UNREACHABLE
All servers checked. Will wait for 10 seconds until next roundContext
StackExchange Code Review Q#38557, answer score: 6
Revisions (0)
No revisions yet.