Efficiency of while(true) ServerSocket Listen

2019-02-09 10:55发布

问题:

I am wondering if a typical while(true) ServerSocket listen loop takes an entire core to wait and accept a client connection (Even when implementing runnable and using Thread .start())

I am implementing a type of distributed computing cluster and each computer needs every core it has for computation. A Master node needs to communicate with these computers (invoking static methods that modify the algorithm's functioning).

The reason I need to use sockets is due to the cross platform / cross language capabilities. In some cases, PHP will be invoking these java static methods.

I used a java profiler (YourKit) and I can see my running ServerSocket listen thread and it never sleeps and it's always running. Is there a better approach to do what I want? Or, will the performance hit be negligible?

Please, feel free to offer any suggestion if you can think of a better way (I've tried RMI, but it isn't supported cross-language.

Thanks everyone

回答1:

If you mean something like this:

while (true) {
  Socket socket = server.accept();
  /* Do something with socket... */
}

then, no, the call to accept() does not "take an entire core." It's a blocking call that will allow the CPU to be scheduled for another thread until a client actually connects. Once the call to accept() returns, the current thread will be scheduled to run and will consume CPU until it blocks on accept() in the next iteration of the loop.

To avoid the listen backlog of other clients growing too large, another thread usually handles interaction with the newly-accepted Socket, leaving one thread to focus on accepting new clients. The socket handling thread might handle many sockets, using NIO, or it might be dedicated to a single socket, which is much simpler to code but won't scale well past many hundreds of simultaneous connections.



回答2:

You might want to take a look at the Java 1.4 nio libraries and in particular ServerSocketChannel. I use this very successfully to implement an efficient server, the key bits of code are:

Selector selector = Selector.open();

ServerSocketChannel server= ServerSocketChannel.open();
server.socket().bind(new java.net.InetSocketAddress(port));
server.configureBlocking(false);
SelectionKey serverKey = server.register(selector, SelectionKey.OP_ACCEPT);

// start listening thread
new Thread(listener).start();

And the listener is just a loop that runs:

selector.select(1000); // listen for one second max
Set<SelectionKey> keys = selector.selectedKeys();

if (keys.size()>0) {
    handleKeys(keys);
}


回答3:

I used a java profiler (YourKit) and I can see my running ServerSocket listen thread and it never sleeps and it's always running.

Basically, the profiler is misleading you.

I assume your code looks like this:

ServerSocket server = ...
// configure server socket

try {
    while (true) {
        Socket socket = server.accept();
        // do something with socket (and close it afterwards!)
    }
} catch (InterruptedException ex) {
    // we're outta here!
}

This will not consume significant CPU ... unless you have done something pathological, like calling ServerSocket.setSoTimeout(int) with a small timeout.



回答4:

Let the core sleep a little. In your Runnable method, add something like

Thread.sleep(250); // milliseconds 

in every loop.

That should significantly reduce CPU usage

Edit: bad idea, see comments, sorry, my fault


And: don't use while(true). it's awful design, as the semantics suggest that eventually true will not be true anymore. Usually you will want to query some volatile or atomic variable from the main thread

public class MyClass {

class MyRunnable implements Runnable {

    public void run() {
        while (MyClass.this.keepGoing.get()) {
            // listen();
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                // deal with exception
            }

        }
    }

}

private final AtomicBoolean keepGoing = new AtomicBoolean(true);

}

That way the main thread has a way to stop the listener thread.