Selector介绍

Selector是对java nio中Selector的封装,额外提供了对kafka请求或者响应的处理方法

框架图

kafka earliest参数_java

Selectable接口

Selector实现了Selectable接口。下面列举它的定义

public interface Selectable {
    public void close(String id);

    // 发送数据
    public void send(Send send);

    // 等待timeout时间,然后处理就绪的连接
    public void poll(long timeout) throws IOException;

    // 返回已经成功发送的数据
    public List<Send> completedSends();

    // 返回已经成功接收的数据
    public List<NetworkReceive> completedReceives();

    // 返回关闭的连接,ChannelState表示连接关闭前最后的状态,在KafkaChannel会有介绍
    public Map<String, ChannelState> disconnected();

    // 返回已经成功的连接
    public List<String> connected();

    // 取消监听读事件
    public void mute(String id);

    // 开启监听读事件
    public void unmute(String id);

    // 连接是否已经就绪(比如ssl握手过程是否完成)
    public boolean isChannelReady(String id);
}

Selector类

register方法由Processor调用,负责注册该连接

public class Selector implements Selectable, AutoCloseable {
    // nioSelector负责连接的事件监听器
    private final java.nio.channels.Selector nioSelector;
    
    // channels集合
    private final Map<String, KafkaChannel> channels;

    public void register(String id, SocketChannel socketChannel) throws ClosedChannelException {
        // 监听连接的读事件
        SelectionKey key = socketChannel.register(nioSelector, SelectionKey.OP_READ);
        // 构造KafkaChannel,并且绑定到该连接。KafkaChannel负责请求的解析和响应的发送
        KafkaChannel channel = channelBuilder.buildChannel(id, key, maxReceiveSize);
        key.attach(channel);
        // 更新channels
        this.channels.put(id, channel);
    }
}

poll方法,是Selector最重要的方法。它负责监听事件,并做相应的处理

public void poll(long timeout) throws IOException {
        if (timeout < 0)
            throw new IllegalArgumentException("timeout should be >= 0");
        // 清除
        clear();
        // hasStagedReceives返回是否还有receive处理。如果有,则设置timeout为0,表示不等待。
        if (hasStagedReceives() || !immediatelyConnectedKeys.isEmpty())
            timeout = 0;
        // 等待socket事件就绪
        int readyKeys = select(timeout);

        if (readyKeys > 0 || !immediatelyConnectedKeys.isEmpty()) {
            //  处理就绪的连接
            pollSelectionKeys(this.nioSelector.selectedKeys(), false, endSelect);
            // 处理immediatelyConnectedKeys的连接
            pollSelectionKeys(immediatelyConnectedKeys, true, endSelect);
        }
        // 更新请求
        addToCompletedReceives();

        // we use the time at the end of select to ensure that we don't close any connections that
        // have just been processed in pollSelectionKeys
        maybeCloseOldestConnection(endSelect);
    }

clear方法,清除上一次的连接列表

public class Selector implements Selectable, AutoCloseable {
    
    // 完成发送的列表
    private final List<Send> completedSends;
    // 已经成功接收完的请求列表
    private final List<NetworkReceive> completedReceives;
    // 关闭的连接列表
    private final Map<String, ChannelState> disconnected;
    // 已经建立的连接列表
    private final List<String> connected;
    // 准备关闭的连接列表,当这个连接的请求处理完后,就会移除
    private final Map<String, KafkaChannel> closingChannels;
    // 发送send失败的channel列表。用来更新disconnected的状态
    private final List<String> failedSends;

   private void clear() {
        this.completedSends.clear();
        this.completedReceives.clear();
        this.connected.clear();
        this.disconnected.clear();
            
        // 遍历准备关闭的连接,当异常关闭的连接,其请求已全部读取完,就关闭
        for (Iterator<Map.Entry<String, KafkaChannel>> it = closingChannels.entrySet().iterator(); it.hasNext(); ) {
            KafkaChannel channel = it.next().getValue();
            Deque<NetworkReceive> deque = this.stagedReceives.get(channel);
            // 是否发送send失败
            boolean sendFailed = failedSends.remove(channel.id());
            if (deque == null || deque.isEmpty() || sendFailed) {
                // 关闭连接,并且从closingChannels删除
                doClose(channel, true);
                it.remove();
            }
        }
        // 遍历发送失败的连接
        for (String channel : this.failedSends) {
            KafkaChannel failedChannel = closingChannels.get(channel);
            if (failedChannel != null)
                // 更新channel的state
                failedChannel.state(ChannelState.FAILED_SEND);
            // 添加到disconnected列表里
            this.disconnected.put(channel, ChannelState.FAILED_SEND);
        }
        this.failedSends.clear();
    }
}

pollSelectionKeys方法,处理连接相关的事件

public class Selector implements Selectable, AutoCloseable {
   private void pollSelectionKeys(Iterable<SelectionKey> selectionKeys,
                                   boolean isImmediatelyConnected,
                                   long currentTimeNanos) {
        // 遍历Keys
        Iterator<SelectionKey> iterator = selectionKeys.iterator();
         while (iterator.hasNext()) {
            SelectionKey key = iterator.next();
            iterator.remove();
            // 获取与key绑定的KafkaChannel
            KafkaChannel channel = channel(key);
            try {
                // connect事件
                if (isImmediatelyConnected || key.isConnectable()) {
                    if (channel.finishConnect()) {
                        // 必须等到KafkaChannel完成连接,才加入到connected列表
                        this.connected.add(channel.id());
                    } else
                        continue;
                }
                // 如果channel还没有ready 
                if (channel.isConnected() && !channel.ready())
                    channel.prepare();
                //读事件
                if (channel.ready() && key.isReadable() && !hasStagedReceive(channel)) {
                    // channel已经准备好,并且还没有没有读取完的Receive
                    NetworkReceive networkReceive;
                    while ((networkReceive = channel.read()) != null)
                        // channel.read如果返回null,表示一条请求的数据还没接收完整,需要继续监听读事件
                        // 加入到未处理的Receive队列
                        addToStagedReceives(channel, networkReceive);
                }

                // 写事件
                if (channel.ready() && key.isWritable()) {
                    // channel.write如果发送完,将会send。如没有,则会返回null
                    Send send = channel.write();
                    if (send != null) {
                        // 更新completedSends列表
                        this.completedSends.add(send);
                    }
                }
                if (!key.isValid())
                    //如果连接失效,则关闭连接
                    close(channel, true);
            } catch (Exception e) {
                // 关闭连接
                close(channel, true);
            } finally {
                .......
            }
    }
}

addToCompletedReceives方法,从stagedReceives取出Receive到completedReceives列表里

private void addToCompletedReceives() {
        if (!this.stagedReceives.isEmpty()) {
            // 遍历stagedReceives map,
            Iterator<Map.Entry<KafkaChannel, Deque<NetworkReceive>>> iter = this.stagedReceives.entrySet().iterator();
            while (iter.hasNext()) {
                Map.Entry<KafkaChannel, Deque<NetworkReceive>> entry = iter.next();
                KafkaChannel channel = entry.getKey();
                if (!channel.isMute()) {
                    // 如果channel还在监听读事件,则将Receive添加到completedReceives队列
                    Deque<NetworkReceive> deque = entry.getValue();
                    addToCompletedReceives(channel, deque);
                    if (deque.isEmpty())
                        iter.remove();
                }
            }
        }
    }
    
    // 从stagedDeque取出一个Receive,添加到completedReceives里面
    private void addToCompletedReceives(KafkaChannel channel, Deque<NetworkReceive> stagedDeque) {
        NetworkReceive networkReceive = stagedDeque.poll();
        this.completedReceives.add(networkReceive);
    }

close方法

private void close(KafkaChannel channel, boolean processOutstanding) {
        // 关闭连接
        channel.disconnect();

       // 获取channel有哪些未处理的Receive
        Deque<NetworkReceive> deque = this.stagedReceives.get(channel);
        if (processOutstanding && deque != null && !deque.isEmpty()) {
            // processOutstanding表示是否需要处理未完成的请求
            if (!channel.isMute()) {
                // 如果连接还在监听读事件
                addToCompletedReceives(channel, deque);
                if (deque.isEmpty())
                    this.stagedReceives.remove(channel);
            }
            // 添加到closingChannels列表,表示即将关闭
            closingChannels.put(channel.id(), channel);
        } else
            // 否则关闭channel
            doClose(channel, processOutstanding);
        this.channels.remove(channel.id());
    }

send 方法

public void send(Send send) {
        String connectionId = send.destination();
        if (closingChannels.containsKey(connectionId))
            this.failedSends.add(connectionId);
        else {
            KafkaChannel channel = channelOrFail(connectionId, false);
            try {
                // 调用channel setSend方法
                channel.setSend(send);
            } catch (CancelledKeyException e) {
                this.failedSends.add(connectionId);
                close(channel, false);
            }
        }
    }

概括

Selector实现了Selectable的接口。

Processor会从Selector获取 请求列表completedReceives,发送列表completedSends, disconnected 连接关闭列表.

当连接被意外关掉后,Selector仍然会处理完所有的请求