Flink - InputGate

weixin_34259232發表於2017-10-09

初始化

Task

List<InputGateDeploymentDescriptor> consumedPartitions = tdd.getInputGates();

// Consumed intermediate result partitions
this.inputGates = new SingleInputGate[consumedPartitions.size()];
this.inputGatesById = new HashMap<IntermediateDataSetID, SingleInputGate>();

for (int i = 0; i < this.inputGates.length; i++) {
    SingleInputGate gate = SingleInputGate.create(
            taskNameWithSubtaskAndId, jobId, executionId, consumedPartitions.get(i), networkEnvironment, 
            metricGroup.getIOMetricGroup());

    this.inputGates[i] = gate;
    inputGatesById.put(gate.getConsumedResultId(), gate);
}

 

初始化networkEnvironment

network.registerTask(this);

 

NetworkEnvironment

registerTask
// Setup the buffer pool for each buffer reader
final SingleInputGate[] inputGates = task.getAllInputGates();

for (SingleInputGate gate : inputGates) {
    BufferPool bufferPool = null;

    try {
        bufferPool = networkBufferPool.createBufferPool(gate.getNumberOfInputChannels(), false);
        gate.setBufferPool(bufferPool);
    }

 

SingleInputGate

create

/**
 * Creates an input gate and all of its input channels.
 */
public static SingleInputGate create(
        String owningTaskName,
        JobID jobId,
        ExecutionAttemptID executionId,
        InputGateDeploymentDescriptor igdd,
        NetworkEnvironment networkEnvironment,
        IOMetricGroup metrics) {

    final IntermediateDataSetID consumedResultId = checkNotNull(igdd.getConsumedResultId());

    final int consumedSubpartitionIndex = igdd.getConsumedSubpartitionIndex();
    checkArgument(consumedSubpartitionIndex >= 0);

    final InputChannelDeploymentDescriptor[] icdd = checkNotNull(igdd.getInputChannelDeploymentDescriptors());

    final SingleInputGate inputGate = new SingleInputGate( //生成SingleInputGate物件
            owningTaskName, jobId, executionId, consumedResultId, consumedSubpartitionIndex,
            icdd.length, networkEnvironment.getPartitionStateChecker(), metrics);

    // Create the input channels. There is one input channel for each consumed partition.
    final InputChannel[] inputChannels = new InputChannel[icdd.length]; //初始化InputChannel

    for (int i = 0; i < inputChannels.length; i++) {

        final ResultPartitionID partitionId = icdd[i].getConsumedPartitionId();
        final ResultPartitionLocation partitionLocation = icdd[i].getConsumedPartitionLocation();

        if (partitionLocation.isLocal()) { //local 
            inputChannels[i] = new LocalInputChannel(inputGate, i, partitionId,
                    networkEnvironment.getPartitionManager(),
                    networkEnvironment.getTaskEventDispatcher(),
                    networkEnvironment.getPartitionRequestInitialAndMaxBackoff(),
                    metrics
            );
        }
        else if (partitionLocation.isRemote()) { //remote
            inputChannels[i] = new RemoteInputChannel(inputGate, i, partitionId,
                    partitionLocation.getConnectionId(),
                    networkEnvironment.getConnectionManager(),
                    networkEnvironment.getPartitionRequestInitialAndMaxBackoff(),
                    metrics
            );
        }
        else if (partitionLocation.isUnknown()) {
            inputChannels[i] = new UnknownInputChannel(inputGate, i, partitionId,
                    networkEnvironment.getPartitionManager(),
                    networkEnvironment.getTaskEventDispatcher(),
                    networkEnvironment.getConnectionManager(),
                    networkEnvironment.getPartitionRequestInitialAndMaxBackoff(),
                    metrics
            );
        }
        else {
            throw new IllegalStateException("Unexpected partition location.");
        }

        inputGate.setInputChannel(partitionId.getPartitionId(), inputChannels[i]); //將inputChannel設定inputGate
    }

    return inputGate;
}

inputGate的inputChannel,對應於resultPartition的resultSubPartition

 

------------------------------------------------------------------------------------------------------

OneInputStreamTask

if (numberOfInputs > 0) {
    InputGate[] inputGates = getEnvironment().getAllInputGates();
    inputProcessor = new StreamInputProcessor<IN>(inputGates, inSerializer,
            getCheckpointBarrierListener(), 
            configuration.getCheckpointMode(),
            getEnvironment().getIOManager(),
            isSerializingTimestamps());

 

StreamInputProcessor

InputGate inputGate = InputGateUtil.createInputGate(inputGates);

if (checkpointMode == CheckpointingMode.EXACTLY_ONCE) {
    this.barrierHandler = new BarrierBuffer(inputGate, ioManager);
}
else if (checkpointMode == CheckpointingMode.AT_LEAST_ONCE) {
    this.barrierHandler = new BarrierTracker(inputGate);
}

StreamInputProcessor.processInput中

final BufferOrEvent bufferOrEvent = barrierHandler.getNextNonBlocked();
if (bufferOrEvent != null) {
    if (bufferOrEvent.isBuffer()) {
        currentChannel = bufferOrEvent.getChannelIndex();
        currentRecordDeserializer = recordDeserializers[currentChannel]; //SpillingAdaptiveSpanningRecordDeserializer
        currentRecordDeserializer.setNextBuffer(bufferOrEvent.getBuffer()); //將buffer set到SpillingAdaptiveSpanningRecordDeserializer
    }
    
//後續可以從set到SpillingAdaptiveSpanningRecordDeserializer中反序列化出record
DeserializationResult result = currentRecordDeserializer.getNextRecord(deserializationDelegate);
if (result.isFullRecord()) {
    StreamElement recordOrWatermark = deserializationDelegate.getInstance();
final BufferOrEvent bufferOrEvent = barrierHandler.getNextNonBlocked()

 

BarrierBuffer

public BufferOrEvent getNextNonBlocked() throws IOException, InterruptedException {
    while (true) {
        // process buffered BufferOrEvents before grabbing new ones
        BufferOrEvent next;
        if (currentBuffered == null) { //如果currentBuffered為空,說明沒有unblock的buffer資料,直接從inputGate讀取
            next = inputGate.getNextBufferOrEvent();
        }

 

InputGateUtil.createInputGate

public static InputGate createInputGate(InputGate[] inputGates) {

    if (inputGates.length < 2) {
        return inputGates[0];
    } else {
        return new UnionInputGate(inputGates);
    }
}

 

UnionInputGate

/**
 * Input gate wrapper to union the input from multiple input gates.
 *
 * <p> Each input gate has input channels attached from which it reads data. At each input gate, the
 * input channels have unique IDs from 0 (inclusive) to the number of input channels (exclusive).
 *
 * <pre>
 * +---+---+      +---+---+---+
 * | 0 | 1 |      | 0 | 1 | 2 |
 * +--------------+--------------+
 * | Input gate 0 | Input gate 1 |
 * +--------------+--------------+
 * </pre>
 *
 * The union input gate maps these IDs from 0 to the *total* number of input channels across all
 * unioned input gates, e.g. the channels of input gate 0 keep their original indexes and the
 * channel indexes of input gate 1 are set off by 2 to 2--4.
 *
 * <pre>
 * +---+---++---+---+---+
 * | 0 | 1 || 2 | 3 | 4 |
 * +--------------------+
 * | Union input gate   |
 * +--------------------+
 * </pre>
 *
 * It is possible to recursively union union input gates.
 */
public class UnionInputGate implements InputGate {

    /** The input gates to union. */
    private final InputGate[] inputGates;

    private final Set<InputGate> inputGatesWithRemainingData; //沒有結束的inputGate

    /** Data availability listener across all unioned input gates. */
    private final InputGateListener inputGateListener;

    /** The total number of input channels across all unioned input gates. */
    private final int totalNumberOfInputChannels; //所有的inputGates的所有channels的數目

    /**
     * A mapping from input gate to (logical) channel index offset. Valid channel indexes go from 0
     * (inclusive) to the total number of input channels (exclusive).
     */
    private final Map<InputGate, Integer> inputGateToIndexOffsetMap; //每個inputGate的index的base,比如上面的gate1的base就是2

    /** Flag indicating whether partitions have been requested. */
    private boolean requestedPartitionsFlag;

    public UnionInputGate(InputGate... inputGates) {

        for (InputGate inputGate : inputGates) {
            // The offset to use for buffer or event instances received from this input gate.
            inputGateToIndexOffsetMap.put(checkNotNull(inputGate), currentNumberOfInputChannels); //當前InputChannels的總數就代表該inputGate的base
            inputGatesWithRemainingData.add(inputGate); //加入inputGatesWithRemainingData,表示該inputGate沒有結束

            currentNumberOfInputChannels += inputGate.getNumberOfInputChannels(); //channel數累加
        }

        this.totalNumberOfInputChannels = currentNumberOfInputChannels;

        this.inputGateListener = new InputGateListener(inputGates, this); //InputGateListener
    }

將多個實際的inputGates,合成一個抽象的inputGate;這樣做的目的是為了後面處理方便,把多個輸入對後面透明化掉

 

那這樣在BarrierBuffer,呼叫inputGate.getNextBufferOrEvent

其實就是呼叫,UnionInputGate.getNextBufferOrEvent

@Override
public BufferOrEvent getNextBufferOrEvent() throws IOException, InterruptedException {

    if (inputGatesWithRemainingData.isEmpty()) { //如果所有的inputgate都已經結束
        return null;
    }

    // Make sure to request the partitions, if they have not been requested before.
    requestPartitions(); //從相應的resultpartition去request資料

    final InputGate inputGate = inputGateListener.getNextInputGateToReadFrom(); //獲取一個有資料的inputGate

    final BufferOrEvent bufferOrEvent = inputGate.getNextBufferOrEvent(); //真正的取資料,SingleInputGate.getNextBufferOrEvent

    if (bufferOrEvent.isEvent()
            && bufferOrEvent.getEvent().getClass() == EndOfPartitionEvent.class
            && inputGate.isFinished()) { //如果是結束event,則表示該inputGate已經結束

        if (!inputGatesWithRemainingData.remove(inputGate)) { //從佇列內刪除
            throw new IllegalStateException("Couldn't find input gate in set of remaining " +
                    "input gates.");
        }
    }

    // Set the channel index to identify the input channel (across all unioned input gates)
    final int channelIndexOffset = inputGateToIndexOffsetMap.get(inputGate); //取得改inputgate的baseindex

    bufferOrEvent.setChannelIndex(channelIndexOffset + bufferOrEvent.getChannelIndex()); //baseindx + 真實的index = union index

    return bufferOrEvent;
}

 

InputGateListener

/**
 * Data availability listener at all unioned input gates.
 *
 * <p> The listener registers itself at each input gate and is notified for *each incoming
 * buffer* at one of the unioned input gates.
 */
private static class InputGateListener implements EventListener<InputGate> {

    private final UnionInputGate unionInputGate;

    private final BlockingQueue<InputGate> inputGatesWithData = new LinkedBlockingQueue<InputGate>(); //Cache所有有available buffer的inputGate

    @Override
    public void onEvent(InputGate inputGate) { //SingleInputGate.onAvailableBuffer時被觸發
        // This method is called from the input channel thread, which can be either the same
        // thread as the consuming task thread or a different one.
        inputGatesWithData.add(inputGate); //將inputGate加入佇列,等待讀取

        for (int i = 0; i < registeredListeners.size(); i++) {
            registeredListeners.get(i).onEvent(unionInputGate);
        }
    }

    InputGate getNextInputGateToReadFrom() throws InterruptedException { //從佇列頭取一個inputGate
        return inputGatesWithData.take();
    }

 

先看下requestPartitions,如何request resultpartition的?

public void requestPartitions() throws IOException, InterruptedException {
    if (!requestedPartitionsFlag) {//只需要做一次
        for (InputGate inputGate : inputGates) {
            inputGate.requestPartitions();
        }

        requestedPartitionsFlag = true;
    }
}

 

SingleInputGate.requestPartitions

public void requestPartitions() throws IOException, InterruptedException {
    synchronized (requestLock) {
        if (!requestedPartitionsFlag) { //只做一次

            for (InputChannel inputChannel : inputChannels.values()) {
                inputChannel.requestSubpartition(consumedSubpartitionIndex); //呼叫inputChannel.requestSubpartition
            }
        }

        requestedPartitionsFlag = true;
    }
}

 

RemoteInputChannel

@Override
void requestSubpartition(int subpartitionIndex) throws IOException, InterruptedException {
    if (partitionRequestClient == null) {
        // Create a client and request the partition
        partitionRequestClient = connectionManager
                .createPartitionRequestClient(connectionId);

        partitionRequestClient.requestSubpartition(partitionId, subpartitionIndex, this, 0);
    }
}

PartitionRequestClient,先建立,這個負責和resultSubPartition通訊

requestSubpartition

public ChannelFuture requestSubpartition(
        final ResultPartitionID partitionId,
        final int subpartitionIndex,
        final RemoteInputChannel inputChannel,
        int delayMs) throws IOException {

    partitionRequestHandler.addInputChannel(inputChannel); //將inputChannel加入partitionRequestHandler

    final PartitionRequest request = new PartitionRequest( //生成request
            partitionId, subpartitionIndex, inputChannel.getInputChannelId());

    if (delayMs == 0) {
        ChannelFuture f = tcpChannel.writeAndFlush(request); //傳送request
        f.addListener(listener);
        return f;
    }
    else {
        final ChannelFuture[] f = new ChannelFuture[1];
        tcpChannel.eventLoop().schedule(new Runnable() {
            @Override
            public void run() {
                f[0] = tcpChannel.writeAndFlush(request);
                f[0].addListener(listener);
            }
        }, delayMs, TimeUnit.MILLISECONDS);

        return f[0];
    }
}

 

PartitionRequestClientHandler

@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
    try {
        if (!bufferListener.hasStagedBufferOrEvent() && stagedMessages.isEmpty()) { //普遍msg
            decodeMsg(msg);
        }
        else {
            stagedMessages.add(msg);
        }
    }
    catch (Throwable t) {
        notifyAllChannelsOfErrorAndClose(t);
    }
}

decodeMsg

private boolean decodeMsg(Object msg) throws Throwable {
    final Class<?> msgClazz = msg.getClass();

    // ---- Buffer --------------------------------------------------------
    if (msgClazz == NettyMessage.BufferResponse.class) {
        NettyMessage.BufferResponse bufferOrEvent = (NettyMessage.BufferResponse) msg;

        RemoteInputChannel inputChannel = inputChannels.get(bufferOrEvent.receiverId); //獲取對應的inputChannel

        return decodeBufferOrEvent(inputChannel, bufferOrEvent);
    }

 

decodeBufferOrEvent

private boolean decodeBufferOrEvent(RemoteInputChannel inputChannel, NettyMessage.BufferResponse bufferOrEvent) throws Throwable {
    boolean releaseNettyBuffer = true;

    try {
        if (bufferOrEvent.isBuffer()) {
            // ---- Buffer ------------------------------------------------
            BufferProvider bufferProvider = inputChannel.getBufferProvider();

            while (true) {
                Buffer buffer = bufferProvider.requestBuffer(); //從channel的bufferProvider中獲取buffer

                if (buffer != null) {
                    buffer.setSize(bufferOrEvent.getSize());
                    bufferOrEvent.getNettyBuffer().readBytes(buffer.getNioBuffer()); //將資料寫入buffer中

                    inputChannel.onBuffer(buffer, bufferOrEvent.sequenceNumber); //呼叫inputChannel.onBuffer

                    return true;
                }
                else if (bufferListener.waitForBuffer(bufferProvider, bufferOrEvent)) {
                    releaseNettyBuffer = false;

                    return false;
                }
                else if (bufferProvider.isDestroyed()) {
                    return false;
                }
            }
        }
    }

 

RemoteInputChannel

public void onBuffer(Buffer buffer, int sequenceNumber) {
    boolean success = false;

    try {
        synchronized (receivedBuffers) {
            if (!isReleased.get()) {
                if (expectedSequenceNumber == sequenceNumber) {
                    receivedBuffers.add(buffer); //將buffer放入receivedBuffers
                    expectedSequenceNumber++;

                    notifyAvailableBuffer();//通知有available buffer

                    success = true;
                }
            }
        }
    }
}

notifyAvailableBuffer

protected void notifyAvailableBuffer() {
    inputGate.onAvailableBuffer(this);
}

 

SingleInputGate

public void onAvailableBuffer(InputChannel channel) {
    inputChannelsWithData.add(channel); //inputChannelsWithData中表示該channel有資料需要讀
    EventListener<InputGate> listener = registeredListener;
    if (listener != null) {
        listener.onEvent(this); //通知UnionInputGate,該inputGate有data需要讀
    }
}

 

---------------------------------------------------

SingleInputGate.getNextBufferOrEvent

@Override
public BufferOrEvent getNextBufferOrEvent() throws IOException, InterruptedException {

    requestPartitions();

    InputChannel currentChannel = null;
    while (currentChannel == null) { //如果沒有有資料的channel,會迴圈blocking
        currentChannel = inputChannelsWithData.poll(2, TimeUnit.SECONDS); //從inputChannelsWithData poll一個有資料的channel
    }

    final Buffer buffer = currentChannel.getNextBuffer(); //讀出buffer

    if (buffer.isBuffer()) {
        return new BufferOrEvent(buffer, currentChannel.getChannelIndex());
    }
    else {
        final AbstractEvent event = EventSerializer.fromBuffer(buffer, getClass().getClassLoader());

        if (event.getClass() == EndOfPartitionEvent.class) {
            channelsWithEndOfPartitionEvents.set(currentChannel.getChannelIndex());

            if (channelsWithEndOfPartitionEvents.cardinality() == numberOfInputChannels) {
                hasReceivedAllEndOfPartitionEvents = true;
            }

            currentChannel.notifySubpartitionConsumed();

            currentChannel.releaseAllResources();
        }

        return new BufferOrEvent(event, currentChannel.getChannelIndex());
    }
}

 

RemoteInputChannel

Buffer getNextBuffer() throws IOException {
    synchronized (receivedBuffers) {
        Buffer buffer = receivedBuffers.poll();

        numBytesIn.inc(buffer.getSize());
        return buffer;
    }
}

 

 

 

 

 

 

相關文章