YARN-高并发RPC源码实现

905 阅读4分钟

小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。

YARN-高并发RPC源码实现

上一篇文章讲述了RPC相关的基础内容,接下来咱们讲解一下YARN-高并发RPC源码的具体实现

注意:

1.RPC 服务器端源码分析

本文章从NameNode中找RPCServer 创建的位置

NameNode.class->createNameNode->new NameNode->父类构造方法-> initialize(getConf())->rpcServer = createRpcServer(conf)->new NameNodeRpcServer->new RPC.Builder(conf) .build()->return getProtocolEngine(...).getServer (RPC类中)

Server 所在位置: org.apache.hadoop.ipc.Server。

RPC Server 的创建的代码入口是: RPC.getProtocolEngine(....).getServer();

RPC 中有两个方法:

  • 获取 RPC 服务端:getServer()
  • 获取 RPC 客户端:getClient()
RPC.Server server = new RPC.Builder(new Configuration()).setXXX().setXXXX().build(){

    // ProtocolEngine 有两种实现,WritableRpcEngine 和 ProtobufRpcEngine 此处为ProtobufRpcEngine
    return getProtocolEngine(this.protocol, this.conf).getServer(...){
        return new Server(protocol, protocolImpl, conf, bindAddress, port, numHandlers, numReaders, ....){
            super(bindAddress, port, null, numHandlers, numReaders,...){
                super(bindAddress, port, paramClass, handlerCount, numReaders, queueSizePerHandler, conf, serverName, 
...){
                    // 初始化一个 Call 队列
                    this.callQueue = new CallQueueManager<Call>(.....);
                    // 创建一个 Listener 线程: 内部 启动 NIO 服务端,监听链接请求  ServerSocketChannel.open();
                    listener = new Listener(port){
                        // 启动 NIO 服务端
                        address = new InetSocketAddress(bindAddress, port);
                        acceptChannel = ServerSocketChannel.open();
                        acceptChannel.configureBlocking(false);
                        bind(acceptChannel.socket(), address, backlogLength, conf, portRangeConfig);
                        selector = Selector.open();
                        
                        // 初始化一组 Reader 线程,用来处理 IO 
                        readers = new Reader[readThreads];
                        for(int i = 0; i < readThreads; i++) {
                            Reader reader = new Reader("Socket Reader #" + (i + 1) + " for port " + port);
                            readers[i] = reader;
                            reader.start();
                        }
                        
                        // NIO 服务端注册 OP_ACCEPT 事件,用来监听 链接请求
                        acceptChannel.register(selector, SelectionKey.OP_ACCEPT);
                    }
                    
                    // 创建一个链接管理器
                    connectionManager = new ConnectionManager();
                    
                    // 初始化一个响应线程
                    responder = new Responder();
                }
            }
        }
    }
}

到此为止:

  • callQueue初始化完毕
  • Listener初始化完毕
  • Reader初始化完毕;并且启动完毕
  • responder初始化完毕
  • connection创建完毕
  • Handler目前还没有看到创建

RPC Server 的启动的代码入口是:RPC.Server.start()

RPC.Server.start(){
    
    // 负责给 Client 返回响应的线程启动
    responder.start();
    
    // 监听连接请求的线程启动
    listener.start();
    
    // 创建并启动 handlerCount 个 Handler 线程用来执行真正的 RpcCall 的处理
    handlers = new Handler[handlerCount];
    for (int i = 0; i < handlerCount; i++) {
        handlers[i] = new Handler(i);
        handlers[i].start();
    }
}

到此为止:

  • callQueue初始化完毕
  • Listener初始化完毕;启动完毕
  • Reader初始化完毕;启动完毕
  • responder初始化完毕;启动完毕
  • connection创建完毕
  • Handler初始化完毕;启动完毕

当 Rpc Server 接收到一个 RPC 请求:

Listener线程:

Listener.run(){
    // 启动定时任务,用来检测闲置的 Connection
    connectionManager.startIdleScan();

    // 侦听链接请求
    while(running) {
        getSelector().select();
        Iterator<SelectionKey> iter = getSelector().selectedKeys().iterator();
        while(iter.hasNext()) {
            key = iter.next();
            iter.remove();
            try {
                if(key.isValid()) {
                    if(key.isAcceptable()) {

                        // 处理链接请求
                        doAccept(key){
                            ServerSocketChannel server = (ServerSocketChannel) key.channel();
                            SocketChannel channel;
                            
                            // 完成链接
                            while((channel = server.accept()) != null) {
                                
                                // 获取一个 Reader 线程用来完成该 SocketChannel 的处理
                                // 获取策略: 轮询
                                Reader reader = getReader();
                                Connection c = connectionManager.register(channel, this.listenPort, 
this.isOnAuxiliaryPort);
                                key.attach(c);
                                
                                // SocketChannel 对应的 Connection 和 Reader 线程的绑定
                                 reader.addConnection(c);
                            }
                        }
                    }
                }
            }
        }
    }
}

Reader线程:

Reader.run(){
    doRunLoop(){
        while(running) {

            // 给每个刚完成链接的 Connection 注册 OP_READ 事件
            int size = pendingConnections.size();
            for(int i = size; i > 0; i--) {
                Connection conn = pendingConnections.take();
                conn.channel.register(readSelector, SelectionKey.OP_READ, conn);
            }

            // 进行 select 此处是阻塞的
            readSelector.select();

            // 选择有 OP_READ 响应的客户端,执行 read 动作
            Iterator<SelectionKey> iter = readSelector.selectedKeys().iterator();
            while(iter.hasNext()) {
                key = iter.next();
                iter.remove();

                if(key.isReadable()) {

                    // 执行 read 动作
                    doRead(key){
                        // 记录最近一次活跃时间
                        Connection c = (Connection) key.attachment();
                        c.setLastContact(Time.now());

                        // 处理 read
                        count = c.readAndProcess(){

                            // 如果之前没有读取过请求头,则先读取请求头
                            if(!connectionHeaderRead) {
                                count = channelRead(channel, connectionHeaderBuf);
                            }

                            // 读取请求数据
                            count = channelRead(channel, data);

                            // 处理一次 RPC 请求
                            processOneRpc(requestData){
                                processRpcRequest(header, buffer){
                                    // 反序列化得到 rpcRequest
                                    Writable rpcRequest;
                                    rpcRequest = buffer.newInstance(rpcRequestClass, conf);

                                    // 构建一个 RpcCall 请求对象
                                    RpcCall call = new RpcCall(this, ....);

                                    // 加入 callQueue 队列
                                    internalQueueCall(call){
                                        internalQueueCall(call, true){
                                            if(blocking) {
                                                callQueue.put(call);
                                            } else {
                                                callQueue.add(call);
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}

Handler 线程消耗callQueue:

Handler.run(){

    // 从队列获取 RpcCall 实例
    call = callQueue.take();

    // 执行
    call.run(){

        // 执行 Call 的处理
        value = call(rpcKind, connection.protocolName, rpcRequest, timestampNanos){
            // RpcInvoker 有两种: WritableRpcInvoker 和 ProtoBufRpcInvoker
            return getRpcInvoker(rpcKind).call(this, protocol, rpcRequest, receiveTime){

                // 处理请求头,还有各种参数等

                // 调用 RPC 方法执行处理
                result = service.callBlockingMethod(methodDescriptor, null, param);

                // 返回 RPC 方法执行结果
                return RpcWritable.wrap(result);
            }
        }

        // 设置 RPC 处理结果
        setResponseFields(value, responseParams);

        // 开始发送响应
        sendResponse(){
            doResponse(null){
                doResponse(t, RpcStatusProto.FATAL){

                    // 设置该 Call 的响应结果
                    setupResponse(call, ....);

                    // 处理该 Call 的响应
                    connection.sendResponse(call){

                        // 最终调用 responder 线程来完成最终结果的输出
                        responder.doRespond(call){

                            // 将 call 加入响应队列
                            call.connection.responseQueue.addLast(call);

                            // 处理 Call 的响应
                            if(call.connection.responseQueue.size() == 1) {
                                
                                // 最终,无论是同步写,还是异步写,都是这个方法
                                // 同步写: processResponse 在 Handler 线程中执行
                                // 异步写: processResponse 在 Responder 线程中执行
                                processResponse(call.connection.responseQueue, true){

                                    // 写出响应
                                    int numBytes = channelWrite(channel, call.rpcResponse);
                                    //
                                    if(numBytes < 0) {
                                        return true;
                                    }
                                    
                                    // TODO_MA 注释: 一次写完所有数据了
                                    if(!call.rpcResponse.hasRemaining()) {
                                        ...
                                    }  else {
                                        
                                        // 注册 OP_WRITE 执行多步异步写出
                                        writeSelector.wakeup();
                                        channel.register(writeSelector, SelectionKey.OP_WRITE, call);
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}

Responder 线程:

Responder.run() {
        doRunLoop(){
            while(running) {
                writeSelector.select(TimeUnit.NANOSECONDS.toMillis(PURGE_INTERVAL_NANOS));
                Iterator<SelectionKey> iter = writeSelector.selectedKeys().iterator();

                while(iter.hasNext()) {
                    SelectionKey key = iter.next();
                    iter.remove();

                    // 处理 Write
                    if(key.isWritable()) {
                        doAsyncWrite(key){

                            // 又回到跟之前一样的回路
                            processResponse(call.connection.responseQueue, false);
                        }
                    }
                }
            }
        }
    }
}

注意:

  1. Listener线程是只有一个的,它处理的速度是非常快的
  2. Reader线程初始为1个,可以调节
  3. Handler线程初始为10个,可以调节
  4. 在Hadoop高并发调优的时候可以适当的调节Reader线程和Handler线程(Call队列的消费者和生产者的消费水平和生产水平达到一个平衡的状态)

RPC称为是高并发的网络通信框架的主要原因就是因为Reader和Handler线程

至此,下图的所有流程都已经讲解完毕,包括:

  • Listener、Reader、Handler、Responder各个线程的创建初始化,以及启动的流程
  • 针对各个线程的具体内容细节进行说明

image-20210922205256788.png

YARN 是一个分布式资源管理系统,它包含了分布的多个组件,我们可以通过这些组件之间设计的交互协议来说明。

本人是该领域的小白,在学习的路上,上述文章如有错误还请指出批评。