HiveBrain v1.2.0
Get Started
← Back to all entries
patternjavaMinor

Concurrent multi-server pinging in Java

Submitted by: @import:stackexchange-codereview··
0
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.

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:

  • 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 round


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.

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 round

Context

StackExchange Code Review Q#38557, answer score: 6

Revisions (0)

No revisions yet.