Amazon web services AWS.CloudWatchLogs的PutLogEvents API的下一个序列是什么

Amazon web services AWS.CloudWatchLogs的PutLogEvents API的下一个序列是什么,amazon-web-services,amazon-cloudwatch,Amazon Web Services,Amazon Cloudwatch,这里提到的下一个序列是什么 我不清楚,可能其他人也不清楚。发送到CloudWatch日志的每批事件都必须包含来自以下站点的序列令牌: 响应再次从API文档返回nextSequenceToken: { "nextSequenceToken": "string", "rejectedLogEventsInfo": { "expiredLogEventEndIndex": number, "tooNewLogEventStartIndex": number,

这里提到的下一个序列是什么


我不清楚,可能其他人也不清楚。

发送到CloudWatch日志的每批事件都必须包含来自以下站点的序列令牌:

响应再次从API文档返回nextSequenceToken:

{
   "nextSequenceToken": "string",
   "rejectedLogEventsInfo": { 
      "expiredLogEventEndIndex": number,
      "tooNewLogEventStartIndex": number,
      "tooOldLogEventEndIndex": number
   }
}
因此,对您的问题的简短回答是:如果您有一个生产者在向流写入数据,那么您可以保存nextSequenceToken并使用它为下一个PutLogEvents请求填充sequenceToken

较长的答案是,如果您有多个制作人,则不能使用此技术,因为一个制作人无权访问对另一个制作人请求的响应。相反,您必须在每次请求之前打电话。以下代码是从Java日志框架中提取的,因此包含对此处未显示的函数的引用,并且可能包含语法错误,因为我省略了特定于日志库的内容:

/**
 *  This function retrieves the current information for a specific log stream.
 *  DescribeLogStreams is a paginated operation, which means that we have to
 *  be prepared for a large number of rows in the response, but since we're
 *  passing the full stream name as a prefix this should never happen.
 */
private LogStream findLogStream(String logGroupName, String logStreamName)
{
    DescribeLogStreamsRequest request = new DescribeLogStreamsRequest()
                                        .withLogGroupName(logGroupName)
                                        .withLogStreamNamePrefix(logStreamName);
    DescribeLogStreamsResult result;
    do
    {
        result = client.describeLogStreams(request);
        for (LogStream stream : result.getLogStreams())
        {
            if (stream.getLogStreamName().equals(logStreamName))
                return stream;
        }
        request.setNextToken(result.getNextToken());
    } while (result.getNextToken() != null);
    return null;
}

/**
 *  This function tries to send a batch of messages, retrieving the sequence
 *  number for each batch and handling the data race if another process has
 *  made that sequence number invalid.
 */
private List<LogMessage> attemptToSend(List<LogMessage> batch)
{
    if (batch.isEmpty())
        return batch;

    PutLogEventsRequest request = new PutLogEventsRequest()
                                  .withLogGroupName(config.logGroupName)
                                  .withLogStreamName(config.logStreamName)
                                  .withLogEvents(constructLogEvents(batch));

    for (int ii = 0 ; ii < 5 ; ii++)
    {
        LogStream stream = findLogStream();

        try
        {
            request.setSequenceToken(stream.getUploadSequenceToken());
            client.putLogEvents(request);
            return Collections.emptyList();
        }
        catch (InvalidSequenceTokenException ex)
        {
            stats.updateWriterRaceRetries();
            Utils.sleepQuietly(100);
            // continue retry loop
        }
        catch (DataAlreadyAcceptedException ex)
        {
            reportError("received DataAlreadyAcceptedException, dropping batch", ex);
            return Collections.emptyList();
        }
        catch (Exception ex)
        {
            reportError("failed to send batch", ex);
            return batch;
        }
    }

    reportError("received repeated InvalidSequenceTokenException responses -- increase batch delay?", null);
    stats.updateUnrecoveredWriterRaceRetries();
    return batch;
}
从PutLogEvents请求返回的大多数异常都是不可恢复的,因此此代码将忽略它们。然而,InvalidSequenceTokenException表示两个生产者之间存在竞争,并且另一生产者能够在该生产者检索流描述并尝试写入其批次之间写入批次。这是不可能的,但可能的,因此它会重试几次,然后拒绝再次尝试的批次


最后一点可能对您很重要:CloudWatch对过去或将来不太远的批次中事件的时间戳有规则。如果批处理包含超出该范围的事件,则将删除这些事件,但其余事件将添加到流中。您可以通过查看响应中的rejectedLogEventsInfo对象来查看是否会发生这种情况,如果日志框架中的任何记录被删除,该对象将具有非零索引,这种情况不太可能发生,并且不会对其进行更正,因此,我只忽略响应值。

发送到CloudWatch日志的每批事件都必须包含来自以下位置的序列令牌:

响应再次从API文档返回nextSequenceToken:

{
   "nextSequenceToken": "string",
   "rejectedLogEventsInfo": { 
      "expiredLogEventEndIndex": number,
      "tooNewLogEventStartIndex": number,
      "tooOldLogEventEndIndex": number
   }
}
因此,对您的问题的简短回答是:如果您有一个生产者在向流写入数据,那么您可以保存nextSequenceToken并使用它为下一个PutLogEvents请求填充sequenceToken

较长的答案是,如果您有多个制作人,则不能使用此技术,因为一个制作人无权访问对另一个制作人请求的响应。相反,您必须在每次请求之前打电话。以下代码是从Java日志框架中提取的,因此包含对此处未显示的函数的引用,并且可能包含语法错误,因为我省略了特定于日志库的内容:

/**
 *  This function retrieves the current information for a specific log stream.
 *  DescribeLogStreams is a paginated operation, which means that we have to
 *  be prepared for a large number of rows in the response, but since we're
 *  passing the full stream name as a prefix this should never happen.
 */
private LogStream findLogStream(String logGroupName, String logStreamName)
{
    DescribeLogStreamsRequest request = new DescribeLogStreamsRequest()
                                        .withLogGroupName(logGroupName)
                                        .withLogStreamNamePrefix(logStreamName);
    DescribeLogStreamsResult result;
    do
    {
        result = client.describeLogStreams(request);
        for (LogStream stream : result.getLogStreams())
        {
            if (stream.getLogStreamName().equals(logStreamName))
                return stream;
        }
        request.setNextToken(result.getNextToken());
    } while (result.getNextToken() != null);
    return null;
}

/**
 *  This function tries to send a batch of messages, retrieving the sequence
 *  number for each batch and handling the data race if another process has
 *  made that sequence number invalid.
 */
private List<LogMessage> attemptToSend(List<LogMessage> batch)
{
    if (batch.isEmpty())
        return batch;

    PutLogEventsRequest request = new PutLogEventsRequest()
                                  .withLogGroupName(config.logGroupName)
                                  .withLogStreamName(config.logStreamName)
                                  .withLogEvents(constructLogEvents(batch));

    for (int ii = 0 ; ii < 5 ; ii++)
    {
        LogStream stream = findLogStream();

        try
        {
            request.setSequenceToken(stream.getUploadSequenceToken());
            client.putLogEvents(request);
            return Collections.emptyList();
        }
        catch (InvalidSequenceTokenException ex)
        {
            stats.updateWriterRaceRetries();
            Utils.sleepQuietly(100);
            // continue retry loop
        }
        catch (DataAlreadyAcceptedException ex)
        {
            reportError("received DataAlreadyAcceptedException, dropping batch", ex);
            return Collections.emptyList();
        }
        catch (Exception ex)
        {
            reportError("failed to send batch", ex);
            return batch;
        }
    }

    reportError("received repeated InvalidSequenceTokenException responses -- increase batch delay?", null);
    stats.updateUnrecoveredWriterRaceRetries();
    return batch;
}
从PutLogEvents请求返回的大多数异常都是不可恢复的,因此此代码将忽略它们。然而,InvalidSequenceTokenException表示两个生产者之间存在竞争,并且另一生产者能够在该生产者检索流描述并尝试写入其批次之间写入批次。这是不可能的,但可能的,因此它会重试几次,然后拒绝再次尝试的批次


最后一点可能对您很重要:CloudWatch对过去或将来不太远的批次中事件的时间戳有规则。如果批处理包含超出该范围的事件,则将删除这些事件,但其余事件将添加到流中。您可以通过查看响应中的rejectedLogEventsInfo对象来查看是否会发生这种情况,如果日志框架中的任何记录被删除,该对象将具有非零索引,这种情况不太可能发生,并且不会对其进行更正,所以我忽略了这个响应值。

那么你还有问题吗?谢谢,对不起,我刚才才看到这个答案。你还有问题吗?谢谢,对不起,我刚才才看到这个答案。很好的答案是ty…所以我想获取下一个令牌的最快方法是使用互斥锁,获取锁,请求下一个令牌,写入日志,然后释放锁…但可能会有更快的way@AlexanderMills-我不认为互斥是正确的方式。假设您有多个线程想要编写消息,就像日志框架使用者一样,我认为最好的方法是有一个后台线程发送消息,中间有一个消息队列。如果你有很多独立作家,你会遇到这样的问题。是的,这种方法似乎更好,明白这一点,索取这样的代币有什么意义?为什么API要求我们发送它?@A.Sallai-唯一能回答这个问题的人是API的原始开发者。我不是他们中的一员,但我怀疑这是一个泄露的实现细节。回答不错,泰…所以我想最快的方法是获得下一个
令牌是一些互斥体,获取一个锁,请求下一个令牌,写入日志,然后释放锁…但是可能有一个更快的方法way@AlexanderMills-我不认为互斥是正确的方式。假设您有多个线程想要编写消息,就像日志框架使用者一样,我认为最好的方法是有一个后台线程发送消息,中间有一个消息队列。如果你有很多独立作家,你会遇到这样的问题。是的,这种方法似乎更好,明白这一点,索取这样的代币有什么意义?为什么API要求我们发送它?@A.Sallai-唯一能回答这个问题的人是API的原始开发者。我不是他们中的一员,但我怀疑这是一个泄露的实现细节。