Un estudio preliminar de Apache Hudi (2) (combinado con flink): flink escribe la operación hudi (operación de envío en el lado de JobManager)

fondo

En la exploración preliminar de Apache Hudi (1) (combinado con flink) , mencionamos Pipelines.hoodieStreamWrite 写hudi文件que esta operación en realidad escribe hudi bajo Pipelines.hoodieStreamWriteel método transform(opName("stream_write", conf), TypeInformation.of(Object.class), operatorFactory)y analiza específicamente el proceso de escritura.

analizar

Para transform(opName("stream_write", conf), TypeInformation.of(Object.class), operatorFactory)este fragmento de código, nos fijamos principalmente en operatorFactoryeste objeto ( transformesta operación es la operación del marco Flink):

public class StreamWriteOperator<I> extends AbstractWriteOperator<I> {

  public StreamWriteOperator(Configuration conf) {
    super(new StreamWriteFunction<>(conf));
  }

  public static <I> WriteOperatorFactory<I> getFactory(Configuration conf) {
    return WriteOperatorFactory.instance(conf, new StreamWriteOperator<>(conf));
  }
}

hudiEl operador más importante es StreamWriteOperator, donde la operación más importante la StreamWriteFunctionrealiza :

// StreamWriteFunction
  

   @Override
  public void initializeState(FunctionInitializationContext context) throws Exception {
    this.taskID = getRuntimeContext().getIndexOfThisSubtask();
    this.metaClient = StreamerUtil.createMetaClient(this.config);
    this.writeClient = FlinkWriteClients.createWriteClient(this.config, getRuntimeContext());
    this.writeStatuses = new ArrayList<>();
    this.writeMetadataState = context.getOperatorStateStore().getListState(
        new ListStateDescriptor<>(
            "write-metadata-state",
            TypeInformation.of(WriteMetadataEvent.class)
        ));

    this.ckpMetadata = CkpMetadata.getInstance(this.metaClient.getFs(), this.metaClient.getBasePath());
    this.currentInstant = lastPendingInstant();
    if (context.isRestored()) {
      restoreWriteMetadata();
    } else {
      sendBootstrapEvent();
    }
    // blocks flushing until the coordinator starts a new instant
    this.confirming = true;
  }

  @Override
  public void open(Configuration parameters) throws IOException {
    this.tracer = new TotalSizeTracer(this.config);
    initBuffer();
    initWriteFunction();
  }

  @Override
  public void snapshotState(FunctionSnapshotContext functionSnapshotContext) throws Exception {
    if (inputEnded) {
      return;
    }
    snapshotState();
    // Reload the snapshot state as the current state.
    reloadWriteMetaState();
  }

  @Override
  public void snapshotState() {
    // Based on the fact that the coordinator starts the checkpoint first,
    // it would check the validity.
    // wait for the buffer data flush out and request a new instant
    flushRemaining(false);
  }

  @Override
  public void processElement(I value, ProcessFunction<I, Object>.Context ctx, Collector<Object> out) throws Exception {
    bufferRecord((HoodieRecord<?>) value);
  }

  • initializeStateOperación, principalmente para realizar algunas operaciones de inicialización.

    • this.taskID = getRuntimeContext().getIndexOfThisSubtask();
      Obtenga el subíndice de índice de la tarea actual, que se utiliza para operator coordinatorenviar el evento operator coordinator, y luego StreamWriteOperatorCoordinator( operator coordinator) lo procesará, y hablaremos de StreamWriteOperatorCoordinator más adelante.

    • metaClient = StreamerUtil.createMetaClient(this.config)
      writeClient = FlinkWriteClients.createWriteClient
      Inicialice el cliente de metadatos de Hudi (aquí HoodieTableMetaClient) y escriba el cliente (aquí HoodieFlinkWriteClient)

    • writeStatuses = new ArrayList<>()
      Registre la información posterior escrita en el archivo hudi

    • writeMetadataState = context.getOperatorStateStore().getListState
      eventRegistre el evento de metadatos escrito en hudi, que se empaquetará y enviará a operator coordinator(StreamWriteOperatorCoordinator) en operaciones posteriores.

    • ckpMetadata = CkpMetadata.getInstance
      La ruta de información de metadatos del punto de control de Flink,默认的路径是/${hoodie.basePath}/.hoodie/.aux/ckp_meta

    • currentInstant = lastPendingInstant()
      Obtenga el compromiso que no se completó la última vez

    • restoreWriteMetadata或者sendBootstrapEvent, De acuerdo con si se recupera del punto de control para enviar mensajes diferentes, el (StreamWriteOperatorCoordinator)
      aquí operator coordinatorrealizará un procesamiento unificado e inicializará una confirmación.

  • openOperación
    La operación previa antes de escribir en hudi, por ejemplo, seleccione la operación de seguimiento 初始化TotalSizeTracer记录maxBufferSize便于flush操作
    de acuerdo con el valor (el valor predeterminado es upsert) , aquí estáwrite.operationinsert或upsert或overwriteupsert

  • processElementOperación
    Aquí, los datos entrantes HoodieRecordse almacenan en caché, principalmente para bufferRecordhacer cosas,

    • Primero, se obtendrá el ID del depósito y luego los datos se insertarán en el depósito correspondiente.
    • Si excede write.batch.size(el valor predeterminado es 128 MB), se realizará la operación flushBucket, que consiste principalmente en escribir en hudi //TODO: escritura específica en hudi
      • Primero, se obtendrán las nuevas confirmaciones que deben enviarse.
      • Luego realice la operación real de escritura.
      • La información de metadatos del archivo escrito se envía de vuelta operator coordinatoral
  • snapshotStatefuncionar

    • Llame para flushRemainingescribir los datos restantes en el almacenamiento hudi
    • Vuelva a cargar la información de metadatos del archivo hudi actualmente escrita en el estado actual de flink

Acción hudi StreamWriteOperatorCoordinator

En general, la función de StreamWriteOperatorCoordinator es la misma que la del controlador en Spark, que es enviar información de metadatos al huid al final.
El rol específico aún depende del método específico:

  @Override
  public void handleEventFromOperator(int i, OperatorEvent operatorEvent) {
    ValidationUtils.checkState(operatorEvent instanceof WriteMetadataEvent,
        "The coordinator can only handle WriteMetaEvent");
    WriteMetadataEvent event = (WriteMetadataEvent) operatorEvent;

    if (event.isEndInput()) {
      // handle end input event synchronously
      // wrap handleEndInputEvent in executeSync to preserve the order of events
      executor.executeSync(() -> handleEndInputEvent(event), "handle end input event for instant %s", this.instant);
    } else {
      executor.execute(
          () -> {
            if (event.isBootstrap()) {
              handleBootstrapEvent(event);
            } else {
              handleWriteMetaEvent(event);
            }
          }, "handle write metadata event for instant %s", this.instant
      );
    }
  }
  ...
  @Override
  public void notifyCheckpointComplete(long checkpointId) {
    executor.execute(
        () -> {
          // The executor thread inherits the classloader of the #notifyCheckpointComplete
          // caller, which is a AppClassLoader.
          Thread.currentThread().setContextClassLoader(getClass().getClassLoader());
          // for streaming mode, commits the ever received events anyway,
          // the stream write task snapshot and flush the data buffer synchronously in sequence,
          // so a successful checkpoint subsumes the old one(follows the checkpoint subsuming contract)
          final boolean committed = commitInstant(this.instant, checkpointId);

          if (tableState.scheduleCompaction) {
            // if async compaction is on, schedule the compaction
            CompactionUtil.scheduleCompaction(metaClient, writeClient, tableState.isDeltaTimeCompaction, committed);
          }

          if (tableState.scheduleClustering) {
            // if async clustering is on, schedule the clustering
            ClusteringUtil.scheduleClustering(conf, writeClient, committed);
          }

          if (committed) {
            // start new instant.
            startInstant();
            // sync Hive if is enabled
            syncHiveAsync();
          }
        }, "commits the instant %s", this.instant
    );
  }
  • El método handleEventFromOperator se utiliza para aceptar el mensaje enviado por la tarea.

    • Para BootStrapel tipo de WriteMetadataEvent (en StreamWriteFunction方法initializeState中), es equivalente a la inicialización de la función y activará el procesamiento de
      este tipo de mensaje (asumimos que cada operador de tarea ha completado la operación de inicialización), y el flujo de datos correspondiente es el siguiente:handleBootstrapEvent

      initInstant
         ||
         \/
      reset => startInstant
      

      startInstant aquí inicializará la información de confirmación de una operación de escritura de hudi

    • Para el evento de información de escritura general (por ejemplo, en la función flushBucket de ProcessElement), se procesa handleWriteMetaEventmediante :

       if (this.eventBuffer[event.getTaskID()] != null) {
       this.eventBuffer[event.getTaskID()].mergeWith(event);
       } else {
         this.eventBuffer[event.getTaskID()] = event;
       }
      

      Aquí simplemente se agrega a la matriz de tipo WriteMetadataEvent con nombre de variable eventBuffer, que se procesará más adelante.

    • Para isEndInputel trueevento, este tipo de fuente general se basa en el archivo, que no se discutirá aquí.

  • notifyCheckpointComplete Cuando se complete el checkpointId correspondiente, se llamará a este método

    • commitInstant envía los metadatos de hudi, si ocurre una excepción, revierte la confirmación correspondiente al hudi actual
    • agendaCompacción && agendaAgrupación para la Compcationsuma de huiClustering
    • Si se envía correctamente, se iniciará una nueva confirmación. Si la sincronización de Hive está habilitada (el hive_sync.enabledvalor predeterminado es falso), la información de metadatos se sincronizará con Hive.

Resumir

Utilice una imagen para resumir el modo de interacción, de la siguiente manera:
inserte la descripción de la imagen aquí

Supongo que te gusta

Origin blog.csdn.net/monkeyboy_tech/article/details/132417885
Recomendado
Clasificación