sender分析之Selector

Selector是kafka自己实现的一个NIO 异步非阻塞的网络IO操作。使用一条单独的线程管理多条网络连接上的连接、读、写操作

一 核心字段

java.nio.channels.Selector nioSelector: 用来监听网络I/O事件

Map<String, KafkaChannel> channels: 维护了NodeId和KafkaChannel之间的映射关系,KafkaChannel是针对SocketChannel的进一步封装

List<Send> completedSends: 保存哪些请求已经完全发送出去

List<String> failedSends:  保存哪些请求发送失败

List<NetworkReceive> completedReceives: 保存已经完全接收到的请求

Map<KafkaChannel,Deque<NetworkReceive>> stagedReceives: 暂存一次OP_READ事件处理过程中读取到的全部请求,当一次OP_READ事件处理完成之后,会将stagedReceives集合中的请求保存到completedReceives集合中

List<String> disconnected: 记录poll过程中断开的连接

List<String> connected:记录poll过程中新建的连接

ChannelBuilder channelBuilder: 用于创建KafkaChannel的工具,根据不同配置创建不同的TransportLayer的子类,然后创建KafkaChannel

int maxReceiveSize: 能够接受请求的最大字节是多大

 

二 重要方法

2.1 connect

public void connect(String id, InetSocketAddress address, int sendBufferSize, int receiveBufferSize) throws IOException {
    if (this.channels.containsKey(id))
        throw new IllegalStateException("There isalready a connection for id " + id);
    // 创建SocketChannel
   
SocketChannel
socketChannel = SocketChannel.open();
    // 设置成非阻塞模式
   
socketChannel.configureBlocking(false);
    // 获取Socket对象
   
Socket socket = socketChannel.socket();
    socket.setKeepAlive(true);// 设置为长连接
    //
设置发送buffer的大小
   
if (sendBufferSize != Selectable.USE_DEFAULT_BUFFER_SIZE)
        socket.setSendBufferSize(sendBufferSize);
    // 设置接收buffe的大小
   
if (receiveBufferSize != Selectable.USE_DEFAULT_BUFFER_SIZE)
        socket.setReceiveBufferSize(receiveBufferSize);
    socket.setTcpNoDelay(true);
    boolean connected;
    try {
        // 因为是非阻塞式的,所以SocketChannel.connect方法是发起一个连接,connect方法在连接正式建立
        //
之前就可能返回,在后面会通过Selector.finishConnect方法确认连接是否真正的建立
       
connected = socketChannel.connect(address);
    } catch (UnresolvedAddressException e) {
        socketChannel.close();
        throw new IOException("Can't resolve address:" + address, e);
    } catch (IOException e) {
        socketChannel.close();
        throw e;
    }
    // 将这个SocketChannel注册到nioSelector上,并关注OP_CONNECT事件
   
SelectionKey
key = socketChannel.register(nioSelector, SelectionKey.OP_CONNECT);
    // 创建KafkaChannel
   
KafkaChannel channel = channelBuilder.buildChannel(id, key, maxReceiveSize);
    // kafkachannel注册到key
   
key.attach(channel);
    // NodeIdKafkaChannel绑定,放到channels中管理
   
this.channels.put(id, channel);

    if (connected) {
        // OP_CONNECTwon't trigger for immediately connected channels
       
log
.debug("Immediately connected tonode {}", channel.id());
        immediatelyConnectedKeys.add(key);
        key.interestOps(0);
    }
}

 

2.2 poll 真正执行网络IO,它会调用nioSelector.select方法等待I/O事件发生

public void poll(long timeout) throws IOException {
    if (timeout < 0)
        throw new IllegalArgumentException("timeout should be >= 0");

    clear(); // 将上一次poll的结果清除掉

    if (hasStagedReceives() || !immediatelyConnectedKeys.isEmpty())
        timeout = 0;

    /* check ready keys */
    long startSelect = time.nanoseconds();
    // 调用nioSelect.select方法等待I/O事件发生
    int readyKeys = select(timeout);
    long endSelect = time.nanoseconds();
    this.sensors.selectTime.record(endSelect - startSelect, time.milliseconds());
    // 处理I/O事件
    if (readyKeys > 0 || !immediatelyConnectedKeys.isEmpty()) {
        pollSelectionKeys(this.nioSelector.selectedKeys(), false, endSelect);
        pollSelectionKeys(immediatelyConnectedKeys, true, endSelect);
    }
    // stagedReceives复制到completedReceives
    addToCompletedReceives();

    long endIo = time.nanoseconds();
    this.sensors.ioTime.record(endIo - endSelect, time.milliseconds());

    // 关闭长期空闲连接
    maybeCloseOldestConnection(endSelect);
}

 

2.3 pollSelectionKeys 处理OP_CONNECT、OP_READ、OP_WRITE事件,并且会检测连接状态

 

private void pollSelectionKeys(Iterable<SelectionKey> selectionKeys,
        boolean isImmediatelyConnected, long currentTimeNanos) {
    Iterator<SelectionKey> iterator = selectionKeys.iterator();
    // 遍历SelectionKey
    while (iterator.hasNext()) {
        // 获取每一个SelectionKey
        SelectionKey key = iterator.next();
        // Iterator中移除
        iterator.remove();
        // 之前创建连接时,将kafkachannel注册到key上,就是为了在这里获取
        KafkaChannel channel = channel(key);
        sensors.maybeRegisterConnectionMetrics(channel.id());
        if (idleExpiryManager != null)
            idleExpiryManager.update(channel.id(), currentTimeNanos);

        try {
            // connect方法返回true或者OP_CONNECTION事件进行处理
            if (isImmediatelyConnected || key.isConnectable()) {
                // 会检测SocketChannel是否建立完成,建立后会取消对OP_CONNECT事件的关注,开始
                // 关注OP_READ事件
                if (channel.finishConnect()) {
                    // 添加到已连接的集合中
                    this.connected.add(channel.id());
                    this.sensors.connectionCreated.record();
                    SocketChannel socketChannel = (SocketChannel) key.channel();
                    log.debug("Created socket with SO_RCVBUF = {}, SO_SNDBUF = {}, SO_TIMEOUT = {} to node {}",
                            socketChannel.socket().getReceiveBufferSize(),
                            socketChannel.socket().getSendBufferSize(),
                            socketChannel.socket().getSoTimeout(),
                            channel.id());
                } else
                    continue;
            }

            // 调用KafkaChannelprepare方法进行身份验证
            if (channel.isConnected() && !channel.ready())
                channel.prepare();
            // 处理OP_READ事件
            if (channel.ready() && key.isReadable() && !hasStagedReceive(channel)) {
                NetworkReceive networkReceive;
                while ((networkReceive = channel.read()) != null)
                    // read方法读到一个完整的NetworkReceive,则将其添加到stagedReceives中保存
                    // 如读取不到一个完整的NetworkReceive,则返回null,下次处理OP_READ事件时,继续读取
                    // 直到读取到一个完整的NetworkReceive
                    addToStagedReceives(channel, networkReceive);
            }

            // 处理OP_WRITE事件
            if (channel.ready() && key.isWritable()) {
                Send send = channel.write();
                // 上面的write方法将KafkaChannelsend字段发送出去,如果发送未完成,则返回null
                // 如果发送完成则返回Send,并添加到completedSends集合中带后续处理
                if (send != null) {
                    this.completedSends.add(send);
                    this.sensors.recordBytesSent(channel.id(), send.size());
                }
            }
            // 如果key无效。则关闭KafkaChannel.并且添加这个channel到断开的连接的集合中
            if (!key.isValid()) {
                close(channel);
                this.disconnected.add(channel.id());
            }

        } catch (Exception e) {
            String desc = channel.socketDescription();
            if (e instanceof IOException)
                log.debug("Connection with {} disconnected", desc, e);
            else
                log.warn("Unexpected error from {}; closing connection", desc, e);
            close(channel);
            this.disconnected.add(channel.id());
        }
    }
}

 

2.4 具体的读写操作交给了KafkaChannel

private boolean send(Send send) throws IOException {
    // 如果send在一次write调用时没有发送完,SelectionKeyOP_WRITE事件还没有取消,还会继续监听此channelop_write事件
    // 直到整个send请求发送完毕才取消
    send.writeTo(transportLayer);
    // 判断发送是否完成是通过ByteBuffer中是否还有剩余的字节来判断的
    if (send.completed())
        transportLayer.removeInterestOps(SelectionKey.OP_WRITE);
    return send.completed();
}

 

public NetworkReceive read() throws IOException {
    NetworkReceive result = null;
    // 初始化NetworkReceive
    if (receive == null) {
        receive = new NetworkReceive(maxReceiveSize, id);
    }
    // TransportLayer中读取数据到NetworkReceive对象中,如果没有读完一个完整的NetworkReceive
    // 则下次触发OP_READ事件时将继续填充此NetworkReceive对象;如果读完了则将此receive置为空,下次
    // 触发读操作的时候,创建新的NetworkReceive对象
    receive(receive);
    if (receive.complete()) {
        receive.payload().rewind();
        result = receive;
        receive = null;
    }
    return result;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
为下列代码实现可暂停效果: import UIKit class ViewController: UIViewController { private let radarAnimation = "radarAnimation" private var animationLayer: CALayer? private var animationGroup: CAAnimationGroup? private var opBtn: UIButton! override func viewDidLoad() { super.viewDidLoad() let first = makeRadarAnimation(showRect: CGRect(x: 120, y: 100, width: 100, height: 100), isRound: true) view.layer.addSublayer(first) opBtn = UIButton(frame: CGRect(x: 100, y: 450, width: 80, height: 80)) opBtn.backgroundColor = UIColor.red opBtn.clipsToBounds = true opBtn.setTitle("Hsu", for: .normal) opBtn.layer.cornerRadius = 10 view.addSubview(opBtn) let second = makeRadarAnimation(showRect: opBtn.frame, isRound: false) view.layer.insertSublayer(second, below: opBtn.layer) } @IBAction func startAction(_ sender: UIButton) { animationLayer?.add(animationGroup!, forKey: radarAnimation) } @IBAction func stopAction(_ sender: UIButton) { animationLayer?.removeAnimation(forKey: radarAnimation) } private func makeRadarAnimation(showRect: CGRect, isRound: Bool) -> CALayer { // 1. 一个动态波 let shapeLayer = CAShapeLayer() shapeLayer.frame = showRect // showRect 最大内切圆 if isRound { shapeLayer.path = UIBezierPath(ovalIn: CGRect(x: 0, y: 0, width: showRect.width, height: showRect.height)).cgPath } else { // 矩形 shapeLayer.path = UIBezierPath(roundedRect: CGRect(x: 0, y: 0, width: showRect.width, height: showRect.height), cornerRadius: 10).cgPath } shapeLayer.fillColor = UIColor.orange.cgColor // 默认初始颜色透明度 shapeLayer.opacity = 0.0 animationLayer = shapeLayer // 2. 需要重复的动态波,即创建副本 let replicator = CAReplicatorLayer() replicator.frame = shapeLayer.bounds replicator.instanceCount = 4 replicator.instanceDelay = 1.0 replicator.addSublayer(shapeLayer) // 3. 创建动画组 let opacityAnimation = CABasicAnimation(keyPath: "opacity") opacityAnimation.fromValue = NSNumber(floatLiteral: 1.0) // 开始透明度 opacityAnimation.toValue = NSNumber(floatLiteral: 0) // 结束时透明底 let scaleAnimation = CABasicAnimation(keyPath: "transform") if isRound { scaleAnimation.fromValue = NSValue.init(caTransform3D: CATransform3DScale(CATransform3DIdentity, 0, 0, 0)) // 缩放起始大小 } else { scaleAnimation.fromValue = NSValue.init(caTransform3D: CATransform3DScale(CATransform3DIdentity, 1.0, 1.0, 0)) // 缩放起始大小 } scaleAnimation.toValue = NSValue.init(caTransform3D: CATransform3DScale(CATransform3DIdentity, 1.5, 1.5, 0)) // 缩放结束大小 let animationGroup = CAAnimationGroup() animationGroup.animations = [opacityAnimation, scaleAnimation] animationGroup.duration = 3.0 // 动画执行时间 animationGroup.repeatCount = HUGE // 最大重复 animationGroup.autoreverses = false self.animationGroup = animationGroup shapeLayer.add(animationGroup, forKey: radarAnimation) return replicator } }
最新发布
06-03
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

莫言静好、

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值