Solicitar proceso de procesamiento en modo Ozone SCM HA

Prefacio


En el artículo anterior, el autor explicó los principios de diseño de Ozone SCM HA . En la actualidad, esta comunidad funcional no se ha completado por completo, pero se han completado la mayoría de las funciones principales. El autor de este artículo habla brevemente sobre el proceso de procesamiento de solicitudes en el modo SCM HA, que es ligeramente diferente de la implementación de Ozone OM HA implementada anteriormente (el principio de implementación de OM HA puede referirse a este artículo: Ozone OM Service HA Principle Analysis ) . Bajo el OM HA existente, después de que Ozone se dé cuenta de SCM HA, la estabilidad general del sistema Ozone mejorará enormemente.

La diferencia entre SCM HA y OM HA


En comparación con el servicio OM, el servicio SCM proporciona el servicio Block subyacente y administrará y mantendrá más cosas, incluidos Block, Container y Pipeline. El OM se centra más plenamente en el nivel de metadatos de KV.

En SCM HA, los tipos de metadatos que se actualizarán incluyen los tres tipos de datos mencionados anteriormente. Debido a que Ozone usa Apache Ratis para el control de consistencia de datos, SCM también usa este mecanismo para sincronizar datos entre el SCM líder y cada SCM seguidor.

Procesamiento de solicitudes SCM HA basado en InvocationHandler


El siguiente autor presenta brevemente cómo SCM HA solicita el procesamiento.

Primero, implementa una clase InvocationHandler personalizada para realizar un procesamiento lógico antes de la invocación del método. El flujo general es el siguiente:

1) Primero, SCM recibe una llamada al método e InvocationHandler obtiene si este método está marcado con la anotación @Replicate.
2) Si se trata de una anotación replicada, utilice el método de comunicación de Ratis; de lo contrario, utilice solo la lógica de
actualización local.3) En la actualización de solicitud de Ratis, la actualización de los metadatos de SCM se escribirá primero en un búfer de transacción llamado SCM. no se puede escribir directamente en el disco. El comportamiento de escritura de vaciado de este búfer de transacción no se activará hasta que SCM tome una instantánea. Esto es para acelerar la velocidad de actualización de metadatos de SCM, que es similar a la tabla de OM + doble búfer.

Los códigos de clase relevantes son los siguientes: Primero, la anotación Replicate se define de la siguiente manera. Tomemos la actualización de Pipeline como ejemplo.

/**
 * Manages the state of pipelines in SCM.
 */
public interface PipelineStateManagerV2 {
    
    

  /**
   * Adding pipeline would be replicated to Ratis.
   * @param pipelineProto
   * @throws IOException
   */
  @Replicate
  void addPipeline(HddsProtos.Pipeline pipelineProto) throws IOException;

  /**
   * Removing pipeline would be replicated to Ratis.
   * @param pipelineIDProto
   * @return Pipeline removed
   * @throws IOException
   */
  @Replicate
  void removePipeline(HddsProtos.PipelineID pipelineIDProto)
      throws IOException;

  /**
   * Updating pipeline state would be replicated to Ratis.
   * @param pipelineIDProto
   * @param newState
   * @throws IOException
   */
  @Replicate
  void updatePipelineState(HddsProtos.PipelineID pipelineIDProto,
                           HddsProtos.PipelineState newState)
      throws IOException;
 ...
}

Replicar significa que la ejecución de estos métodos debe ejecutarse sincrónicamente en Follower SCM para lograr la coherencia de los metadatos de SCM.

Luego, implemente la configuración de proxy dinámico para la clase PipelineStateManagerV2.

    public PipelineStateManagerV2 build() throws IOException {
    
    
      Preconditions.checkNotNull(pipelineStore);

      final PipelineStateManagerV2 pipelineStateManager =
          new PipelineStateManagerV2Impl(pipelineStore, nodeManager);

      final SCMHAInvocationHandler invocationHandler =
          new SCMHAInvocationHandler(SCMRatisProtocol.RequestType.PIPELINE,
              pipelineStateManager, scmRatisServer);

      return (PipelineStateManagerV2) Proxy.newProxyInstance(
          SCMHAInvocationHandler.class.getClassLoader(),
          new Class<?>[]{
    
    PipelineStateManagerV2.class}, invocationHandler);
    }

La lógica de SCMHAInvocationHandler contiene el juicio de la anotación Replicate:

/**
 * InvocationHandler which checks for {@link Replicate} annotation and
 * dispatches the request to Ratis Server.
 */
public class SCMHAInvocationHandler implements InvocationHandler {
    
    


  private static final Logger LOG = LoggerFactory
      .getLogger(SCMHAInvocationHandler.class);

  private final RequestType requestType;
  private final Object localHandler;
  private final SCMRatisServer ratisHandler;

  /**
   * TODO.
   */
  public SCMHAInvocationHandler(final RequestType requestType,
                                final Object localHandler,
                                final SCMRatisServer ratisHandler) {
    
    
    this.requestType = requestType;
    this.localHandler = localHandler;
    this.ratisHandler = ratisHandler;
  }

  @Override
  public Object invoke(final Object proxy, final Method method,
                       final Object[] args) throws Throwable {
    
    
    try {
    
    
      long startTime = Time.monotonicNow();
      // 判断method call 是否带有了Replicate注释的,然后决定此请求是否用Ratis更新的方式
      final Object result = method.isAnnotationPresent(Replicate.class) ?
          invokeRatis(method, args) : invokeLocal(method, args);
      LOG.debug("Call: {} took {} ms", method, Time.monotonicNow() - startTime);
      return result;
    } catch(InvocationTargetException iEx) {
    
    
      throw iEx.getCause();
    }
  }

  /**
   * TODO.
   */
  private Object invokeLocal(Method method, Object[] args)
      throws InvocationTargetException, IllegalAccessException {
    
    
    LOG.trace("Invoking method {} on target {}", method, localHandler);
    return method.invoke(method, args);
  }

  /**
   * TODO.
   */
  private Object invokeRatis(Method method, Object[] args)
      throws Exception {
    
    
    LOG.trace("Invoking method {} on target {}", method, ratisHandler);
    final SCMRatisResponse response =  ratisHandler.submitRequest(
        SCMRatisRequest.of(requestType, method.getName(), args));
    if (response.isSuccess()) {
    
    
      return response.getResult();
    }
    // Should we unwrap and throw proper exception from here?
    throw response.getException();
  }

En la actualización de metadatos de SCM, los datos se almacenan en caché primero y no se conservan directamente en la tienda. El código es el siguiente

  public void updatePipelineState(
      HddsProtos.PipelineID pipelineIDProto, HddsProtos.PipelineState newState)
      throws IOException {
    
    
    PipelineID pipelineID = PipelineID.getFromProtobuf(pipelineIDProto);
    Pipeline.PipelineState oldState =
        getPipeline(pipelineID).getPipelineState();
    lock.writeLock().lock();
    try {
    
    
      ...
      if (pipelineStore != null) {
    
    
        // 在内存中更新了Pipeline的信息
        pipelineStateMap.updatePipelineState(pipelineID,
            Pipeline.PipelineState.fromProtobuf(newState));
        // 然后更新操作首先是被保存到了SCM transactionBuffer里面的BatchOperation里面
        pipelineStore.putWithBatch(transactionBuffer.getCurrentBatchOperation(),
            pipelineID, getPipeline(pipelineID));
      }
    } catch (IOException ex) {
    
    
      ...;
    }
  }

Se mantiene una lista de operaciones en BatchOperation para guardar las operaciones de actualización de datos

/**
 * An utility class to store a batch of DB write operations.
 */
public class BatchOperation {
    
    

  /**
   * Enum for write operations.
   */
  public enum Operation {
    
    
    DELETE, PUT
  }

  private List<SingleOperation> operations =
      Lists.newArrayList();
...
}

Estas operaciones de BatchOperation, solo en el modo SCM HA, cuando SCM toma instantáneas con regularidad, los datos de BatchOperation se enviarán al almacén de almacenamiento subyacente.

Código del método SCMStateMachine # takeSnapsho,

  @Override
  public long takeSnapshot() throws IOException {
    
    
    long startTime = Time.monotonicNow();
    TermIndex lastTermIndex = getLastAppliedTermIndex();
    long lastAppliedIndex = lastTermIndex.getIndex();
    SCMTransactionInfo lastAppliedTrxInfo =
        SCMTransactionInfo.fromTermIndex(lastTermIndex);
    if (transactionBuffer.getLatestTrxInfo()
        .compareTo(lastAppliedTrxInfo) < 0) {
    
    
      transactionBuffer.updateLatestTrxInfo(
          SCMTransactionInfo.builder()
              .setCurrentTerm(lastTermIndex.getTerm())
              .setTransactionIndex(lastTermIndex.getIndex())
              .build());
      transactionBuffer.setLatestSnapshot(
          transactionBuffer.getLatestTrxInfo().toSnapshotInfo());
    } else {
    
    
      lastAppliedIndex =
          transactionBuffer.getLatestTrxInfo().getTransactionIndex();
    }
    // Flush transactionBuffer中的数据出去
    transactionBuffer.flush();
    LOG.info("Current Snapshot Index {}, takeSnapshot took {} ms",
        lastAppliedIndex, Time.monotonicNow() - startTime);
    return lastAppliedIndex;
  }

SCMDBTransactionBuffer # flush 方法 ,

  @Override
  public void flush() throws IOException {
    
    
    // write latest trx info into trx table in the same batch
    Table<String, SCMTransactionInfo> transactionInfoTable
        = metadataStore.getTransactionInfoTable();
    transactionInfoTable.putWithBatch(currentBatchOperation,
        TRANSACTION_INFO_KEY, latestTrxInfo);
    // commit BatchOperation里的数据更新操作到底层store里
    metadataStore.getStore().commitBatchOperation(currentBatchOperation);
    currentBatchOperation.close();
    this.latestSnapshot = latestTrxInfo.toSnapshotInfo();
    // reset batch operation
    currentBatchOperation = metadataStore.getStore().initBatchOperation();

    DeletedBlockLog deletedBlockLog = scm.getScmBlockManager()
        .getDeletedBlockLog();
    Preconditions.checkArgument(
        deletedBlockLog instanceof DeletedBlockLogImplV2);
    ((DeletedBlockLogImplV2) deletedBlockLog).onFlush();
  }

Diagrama de proceso de procesamiento de solicitudes de SCM HA


El diagrama del proceso de procesamiento de solicitudes de SCM HA es el siguiente: Los
Inserte la descripción de la imagen aquí
anteriores ContainerStateManagerV2, PipelineManagerV2 y DeletedBlockLogStateManager corresponden a la actualización de la información de SCM Container, Pipeline y Block respectivamente, por lo que las solicitudes de SCM finalmente se envían a estas 3 clases.

Link de referencia


[1] .https: //blog.csdn.net/Androidlushangderen/article/details/105669525
[2] .https: //blog.csdn.net/Androidlushangderen/article/details/103997315

Supongo que te gusta

Origin blog.csdn.net/Androidlushangderen/article/details/113799763
Recomendado
Clasificación