簡體   English   中英

對於AWS Kinesis的KCL Java庫,如何使用requestShutdown和shutdown來執行正常關閉

[英]How do I use the requestShutdown and shutdown to do graceful shutdown in the case of KCL Java library for AWS Kinesis

我正在嘗試使用Java中的KCL庫的新功能來為AWS Kinesis通過注冊shutdown hook來優雅地關閉以停止所有記錄處理器,然后優雅地停止工作。 新庫提供了一個新的接口,需要實現記錄處理器。 但它如何被調用?

嘗試首先調用worker.requestShutdown()然后調用worker.shutdown()並且它可以正常工作。 但它是否有任何預期的使用方式。 那么使用它們有什么用處及其好處?

開始消費者

正如您可能知道的那樣,當您創建一個Worker ,它

1)在dynamodb中創建消費者偏移表

2)按照配置的時間間隔創建租約, 安排租賃接受者和租賃續訂者

如果您有兩個分區,則同一個dynamodb表中將有兩條記錄,這意味着分區需要租約。

例如。

{
  "checkpoint": "TRIM_HORIZON",
  "checkpointSubSequenceNumber": 0,
  "leaseCounter": 38,
  "leaseKey": "shardId-000000000000",
  "leaseOwner": "ComponentTest_Consumer_With_Two_Partitions_Consumer_192.168.1.83",
  "ownerSwitchesSinceCheckpoint": 0
}

{
  "checkpoint": "49570828493343584144205257440727957974505808096533676050",
  "checkpointSubSequenceNumber": 0,
  "leaseCounter": 40,
  "leaseKey": "shardId-000000000001",
  "leaseOwner": "ComponentTest_Consumer_With_Two_Partitions_Consumer_192.168.1.83",
  "ownerSwitchesSinceCheckpoint": 0
}
  • 租賃協調員ScheduledExecutorService(稱為leaseCoordinatorThreadPool )負責接收和續訂租約的時間表

3)然后,對於流中的每個分區, Worker創建一個內部PartitionConsumer ,它實際上獲取事件 ,並調度到RecordProcessor#processRecords 請參閱ProcessTask #call

4)關於你的問題,你必須將IRecordProcessorFactory impl注冊到worker ,這將為每個PartitionConsumer提供一個ProcessorFactoryImpl

例如。 看這里的例子,這可能會有所幫助

KinesisClientLibConfiguration streamConfig = new KinesisClientLibConfiguration(
 "consumerName", "streamName", getAuthProfileCredentials(), "consumerName-" + "consumerInstanceId")
            .withKinesisClientConfig(getHttpConfiguration())
            .withInitialPositionInStream(InitialPositionInStream.TRIM_HORIZON); // "TRIM_HORIZON" = from the tip of the stream

Worker consumerWorker = new Worker.Builder()
            .recordProcessorFactory(new DavidsEventProcessorFactory())
            .config(streamConfig)
            .dynamoDBClient(new DynamoDB(new AmazonDynamoDBClient(getAuthProfileCredentials(), getHttpConfiguration())))
            .build();


public class DavidsEventProcessorFactory implements IRecordProcessorFactory {

    private Logger logger = LogManager.getLogger(DavidsEventProcessorFactory.class);

    @Override
    public IRecordProcessor createProcessor() {
        logger.info("Creating an EventProcessor.");
        return new DavidsEventPartitionProcessor();
    }
}

class DavidsEventPartitionProcessor implements IRecordProcessor {

    private Logger logger = LogManager.getLogger(DavidsEventPartitionProcessor.class);

    //TODO add consumername ?

    private String partitionId;

    private ShutdownReason RE_PARTITIONING = ShutdownReason.TERMINATE;

    public KinesisEventPartitionProcessor() {
    }

    @Override
    public void initialize(InitializationInput initializationInput) {
        this.partitionId = initializationInput.getShardId();
        logger.info("Initialised partition {} for streaming.", partitionId);
    }

    @Override
    public void processRecords(ProcessRecordsInput recordsInput) {
        recordsInput.getRecords().forEach(nativeEvent -> {
            String eventPayload = new String(nativeEvent.getData().array());
            logger.info("Processing an event {} : {}" , nativeEvent.getSequenceNumber(), eventPayload);

            //update offset after configured amount of retries
            try {
                recordsInput.getCheckpointer().checkpoint();
                logger.debug("Persisted the consumer offset to {} for partition {}",
                        nativeEvent.getSequenceNumber(), partitionId);
            } catch (InvalidStateException e) {
                logger.error("Cannot update consumer offset to the DynamoDB table.", e);
                e.printStackTrace();
            } catch (ShutdownException e) {
                logger.error("Consumer Shutting down", e);
                e.printStackTrace();
            }
        });
    }

    @Override
    public void shutdown(ShutdownInput shutdownReason) {
        logger.debug("Shutting down event processor for {}", partitionId);

        if(shutdownReason.getShutdownReason() == RE_PARTITIONING) {
            try {
                shutdownReason.getCheckpointer().checkpoint();
            } catch (InvalidStateException e) {
                logger.error("Cannot update consumer offset to the DynamoDB table.", e);
                e.printStackTrace();
            } catch (ShutdownException e) {
                logger.error("Consumer Shutting down", e);
                e.printStackTrace();
            }
        }
    }

}

//然后開始消費者

consumerWorker.run();

阻止消費者

現在,當你想要停止你的Consumer實例( Worker )時,你不需要對每個PartitionConsumer做太多的處理,一旦你要求它關閉,這將由Worker

  • shutdown ,它要求 leaseCoordinatorThreadPool停止,它負責續訂和租賃,並等待終止。

  • 另一方面, requestShutdown取消租賃者, 通知PartitionConsumer關閉。

requestShutdown更重要的是,如果你想在RecordProcessor上得到通知,那么你也可以實現IShutdownNotificationAware 這種情況如果在RecordProcessor處理事件但工作人員即將關閉的競爭條件下,您仍應該能夠提交偏移然后關閉。

requestShutdown返回ShutdownFuture ,然后調用worker.shutdown

您必須在RecordProcessor上實現以下方法才能在requestShutdown上收到通知,

class DavidsEventPartitionProcessor implements IRecordProcessor, IShutdownNotificationAware {

   private String partitionId;

   // few implementations

    @Override
    public void shutdownRequested(IRecordProcessorCheckpointer checkpointer) {
        logger.debug("Shutdown requested for {}", partitionId);
    }

}

但是如果你在通知之前松開租約那么它可能不會被調用。

總結你的問題

新庫提供了一個新的接口,需要實現記錄處理器。 但它如何被調用?

  • 實現一個IRecordProcessorFactoryIRecordProcessor
  • 然后將RecordProcessorFactory到您的Worker

嘗試首先調用worker.requestShutdown()然后調用worker.shutdown()並且它可以正常工作。 但它是否有任何預期的使用方式?

您應該使用requestShutdown()進行正常關閉 ,這將處理競爭條件。 它是在kinesis-client-1.7.1中引入的

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM