directory

  • An overview of the NIO –
  • NIO-Buffer
  • NIO-Channel
  • Nio-channel interface analysis
  • Nio-socketchannel source code analysis
  • Nio-filechannel source code analysis
  • Nio-selector source code analysis
  • NIO-WindowsSelect Impl source code analysis
  • Nio-epollselectoripml source code analysis

preface

Originally I wanted to learn Netty, but Netty is a NIO framework, so before learning Netty, I would like to comb through the knowledge of NIO. Understand the design principles of NIO by analyzing the source code.

This series of articles focuses on the source code for JDK1.8.0.161.

The previous articles examined the common functions of Buffer and Channel source code. This article will parse the Selector source code.

What is the Selector

During network transmission, the client connects to the server from time to time, and in high concurrency scenarios, most connections are virtually idle. Therefore, in order to improve the performance of network transmission with high concurrency, various I/O models have emerged to optimize CPU processing efficiency. Different selectors implement different I/O model algorithms. Synchronous I/O has an EPoll model on Linux, a KQueue model on MAC, and a Select model on Windows.

For details about the I/O model, see High-performance Network Communication Principles.

In order to know which connection has been ready, in the beginning we need regularly polling Socket to receive a new connection, at the same time we also monitor whether receives the data connection has been established, with most cases, most of the network connection is actually free, so each time to iterate through all of the client, then with the increase of concurrency, Performance overhead also increases linearly.

So with the Selector, we can have it do the monitoring for us, and when it’s monitoring the connection and receiving the data, we just have to read the data out, and that improves performance dramatically. To do the “monitor” action for us, we need to tell it which channels we need to monitor.

Note that you only need to monitor a channel with a Selector when you’re communicating with a network. From the code, the Channel AbstractSelectableChannel must succeed.

Create a Selector

First we need to create a Selector from the static method Selector. Open ().

Selector selector = Selector.open();
Copy the code

Note that a Channel must be non-blocking, and we need to manually set the Channel to non-blocking. Call Channel instance methods SelectableChannel. ConfigureBlocking (Boolean block).

Registered channel

You need to tell the Selector which channels to monitor, and use channel.register to register the channels you want to monitor with the Selector

Registration are implemented in AbstractSelectableChannel, when register new channels with the Selector to create a SelectionKey, and save it to the SelectionKey [] keys in the cache.


public final SelectionKey register(Selector sel, int ops, Object att) throws ClosedChannelException
{
    synchronized (regLock) {
        if(! isOpen())throw new ClosedChannelException();
            // Whether the current Channel supports operations
        if((ops & ~validOps()) ! =0)
            throw new IllegalArgumentException();
            // Blocking is not supported
        if (blocking)
            throw new IllegalBlockingModeException();
        SelectionKey k = findKey(sel);
        if(k ! =null) {
            // If it already exists, register it for supported operations
            k.interestOps(ops);
            // Save the parameters
            k.attach(att);
        }
        if (k == null) {
            // New registration
            synchronized (keyLock) {
                if(! isOpen())throw new ClosedChannelException();
                / / register
                k = ((AbstractSelector)sel).register(this, ops, att);
                // Add to cacheaddKey(k); }}returnk; }}Copy the code

New SelectionKey will call to AbstractSelector. The register, will first create a SelectionKeyImpl first, and then calls the method implRegister perform the actual registration, This functionality is implemented in the SelectorImpl implementation class for each platform.

k = ((AbstractSelector)sel).register(this, ops, att);
protected final SelectionKey register(AbstractSelectableChannel ch, int ops, Object attachment)
{
    if(! (chinstanceof SelChImpl))
        throw new IllegalSelectorException();
        / / create a SelectionKey
    SelectionKeyImpl k = new SelectionKeyImpl((SelChImpl)ch, this);
    k.attach(attachment);
    synchronized (publicKeys) {
        / / register
        implRegister(k);
    }
    // Set the event
    k.interestOps(ops);
    return k;
}
Copy the code

After the SelectionKey is created, it is added to the keys cache. When the keys cache is insufficient, it is doubled in size.

private void addKey(SelectionKey k) {
    assert Thread.holdsLock(keyLock);
    int i = 0;
    if((keys ! =null) && (keyCount < keys.length)) {
        // Find empty element of key array
        for (i = 0; i < keys.length; i++)
            if (keys[i] == null)
                break;
    } else if (keys == null) {
        keys =  new SelectionKey[3];
    } else {
        // Double the size
        int n = keys.length * 2;
        SelectionKey[] ks =  new SelectionKey[n];
        for (i = 0; i < keys.length; i++)
            ks[i] = keys[i];
        keys = ks;
        i = keyCount;
    }
    keys[i] = k;
    keyCount++;
}

Copy the code

SelectorProvider

Before we talk about how a Selector works, let’s take a look at how a Selector is created. We create a Selector using the Selector. Open () static method. Internal actual is through SelectorProvider openSelector () method to create the Selector.

public static Selector open(a) throws IOException {
    return SelectorProvider.provider().openSelector();
}
Copy the code

Create SelectorProvider

Through SelectorProvider provider () static method, get SelectorProvider, for the first time when they get through configuration methods such as injection, if no configuration, use the DefaultSelectorProvider generated.

public static SelectorProvider provider(a) {
    synchronized (lock) {
        if(provider ! =null)
            return provider;
        return AccessController.doPrivileged(
            new PrivilegedAction<SelectorProvider>() {
                public SelectorProvider run(a) {
                        / / by configuring Java. Nio. Channels. The spi. Into custom SelectorProvider SelectorProvider values
                        if (loadProviderFromProperty())
                            return provider;
                        // Inject through ServiceLoad and get the first service configured
                        if (loadProviderAsService())
                            return provider;
                        provider = sun.nio.ch.DefaultSelectorProvider.create();
                        returnprovider; }}); }}Copy the code

If we did not do any special configuration, we would create the SelectorProvider using the default DefaultSelectorProvider. The implementation of DefaultSelectorProvider varies from platform to platform. Can be found in the JDK \ SRC \ [macosx | | solaris Windows] \ classes \ sun \ nio \ ch find realize DefaultSelectorProvider Java. Here is the implementation of SelectorProvider.

//windows
public class DefaultSelectorProvider {
    private DefaultSelectorProvider(a) {}public static SelectorProvider create(a) {
        return newsun.nio.ch.WindowsSelectorProvider(); }}//linux
public class DefaultSelectorProvider {

    private DefaultSelectorProvider(a) {}@SuppressWarnings("unchecked")
    private static SelectorProvider createProvider(String cn) {
        Class<SelectorProvider> c;
        try {
            c = (Class<SelectorProvider>)Class.forName(cn);
        } catch (ClassNotFoundException x) {
            throw new AssertionError(x);
        }
        try {
            return c.newInstance();
        } catch (IllegalAccessException | InstantiationException x) {
            throw newAssertionError(x); }}public static SelectorProvider create(a) {
        String osname = AccessController
            .doPrivileged(new GetPropertyAction("os.name"));
        if (osname.equals("SunOS"))
            return createProvider("sun.nio.ch.DevPollSelectorProvider");
        if (osname.equals("Linux"))
            return createProvider("sun.nio.ch.EPollSelectorProvider");
        return newsun.nio.ch.PollSelectorProvider(); }}Copy the code

Create a Selector

Now that I’ve got the SelectorProvider, I’ve created the Selector. Through SelectorProvider. OpenSelector instance () method creates a Selector

//windows
public class WindowsSelectorProvider extends SelectorProviderImpl {

    public AbstractSelector openSelector(a) throws IOException {
        return new WindowsSelectorImpl(this); }}//linux
public class EPollSelectorProvider
    extends SelectorProviderImpl
{
    public AbstractSelector openSelector(a) throws IOException {
        return new EPollSelectorImpl(this); }... }Copy the code

Under Windows created WindowsSelectorImpl, founded EPollSelectorImpl under Linux.

All XXXSelectorImpl inherited from SelectorImpl, can be in JDK \ SRC \ [macosx | | Windows solaris | share] \ classes \ sun \ nio \ ch find realize XXXSelectorImpl Java. The inheritance relationship is shown in the following figure.

Next, we’ll discuss the main features that Selector provides, and then we’ll look at the implementation of Selector for Windows and Linux.

SelectorImpl

The SelectorImpl is created by initializing two hashSets, publicKeys for all registered Selectionkeys, and selectedKeys for ready selectionkeys.

protected SelectorImpl(SelectorProvider sp) {
    super(sp);
    keys = new HashSet<SelectionKey>();
    selectedKeys = new HashSet<SelectionKey>();
    if (Util.atBugLevel("1.4")) {
        publicKeys = keys;
        publicSelectedKeys = selectedKeys;
    } else {
        // Create a collection that cannot be modified
        publicKeys = Collections.unmodifiableSet(keys);
        // Create a collection that can only be deleted but cannot be addedpublicSelectedKeys = Util.ungrowableSet(selectedKeys); }}Copy the code

I found an article on util. atBugLevel that mentions this method. It seems to be related to a null pointer exception in EPoll. This bug was introduced in NIO bugLevel=1.4 and existed in JDK1.5 until JDK1.7.

Having registered the channels with the Selector earlier, we now need to call the Selector.select() instance method to load the ready file descriptor from system memory.


public int select(a) throws IOException {
    return select(0);
}
public int select(long timeout)
    throws IOException
{
    if (timeout < 0)
        throw new IllegalArgumentException("Negative timeout");
    return lockAndDoSelect((timeout == 0)? -1 : timeout);
}

private int lockAndDoSelect(long timeout) throws IOException {
    synchronized (this) {
        if(! isOpen())throw new ClosedSelectorException();
        synchronized (publicKeys) {
            synchronized (publicSelectedKeys) {
                returndoSelect(timeout); }}}}protected abstract int doSelect(long timeout) throws IOException;
Copy the code

The result is a call to doSelect with a specific SelectorImpl that essentially does two things internally

  1. Call native methods to get ready file descriptors.
  2. callupdateSelectedKeysUpdate the ready eventSelectorKey

Once we get the selectionkeys ready, we can walk through them. The specific logic that needs to be executed depends on the event type of the SelectionKey.

// Get the ready Key to traverse
Set<SelectionKey> selectKeys = selector.selectedKeys();
Iterator<SelectionKey> it = selectKeys.iterator();
while (it.hasNext()) {
    SelectionKey key = it.next();
    // Handle events.
    if(key.isAcceptable()){
        doAccept(key);
    }
    else if(key.isReadable()) { doRead(key); }... it.remove(); }Copy the code

conclusion

This article analyzes the creation of Selector and SelectorProvider. The general process can be seen in the following figure

The EpollArrayWarpper() of the following steps will be explained in the SelectorImpl platform implementation. The following will be divided into two WindowsSelectorImpl and EpollSelectorImpl analysis.

Related literature

  1. ServiceLoader,
  2. SelectorImpl analysis
  3. NIO source code Analysis (1)

  • Wechat scan the QR code to follow the subscription number Jie Ge technology share
  • Source: www.cnblogs.com/Jack-Blog/p…
  • Author: Jay is very busy
  • This article uses the “CC BY 4.0” authoring sharing protocol. Welcome to reprint, please give the source and link in the obvious place.