Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/apache-kafka/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 重新使用给定时间的卡夫卡消息_C#_Apache Kafka_Kafka Consumer Api - Fatal编程技术网

C# 重新使用给定时间的卡夫卡消息

C# 重新使用给定时间的卡夫卡消息,c#,apache-kafka,kafka-consumer-api,C#,Apache Kafka,Kafka Consumer Api,我正在使用Confluent.Kafka.NET客户端版本1.3.0。我想从给定的时间开始使用消息 为此,我可以使用OffsetsForTimes获取所需的偏移量,并Commit提交该分区的偏移量: private void SetOffset() { const string Topic = "myTopic"; const string BootstrapServers = "server1, server2"; var adminClient = new Admi

我正在使用Confluent.Kafka.NET客户端版本1.3.0。我想从给定的时间开始使用消息

为此,我可以使用
OffsetsForTimes
获取所需的偏移量,并
Commit
提交该分区的偏移量:

private void SetOffset()
{
    const string Topic = "myTopic";
    const string BootstrapServers = "server1, server2";

    var adminClient = new AdminClientBuilder(
        new Dictionary<string, string>
        {
            { "bootstrap.servers", BootstrapServers },
            { "security.protocol", "sasl_plaintext" },
            { "sasl.mechanisms", "PLAIN" },
            { "sasl.username", this.kafkaUsername },
            { "sasl.password", this.kafkaPassword }
        }).Build();

    var consumer = new ConsumerBuilder<byte[], byte[]>(
        new Dictionary<string, string>
        {
            { "bootstrap.servers", BootstrapServers },
            { "group.id", this.groupId },
            { "enable.auto.commit", "false" },
            { "security.protocol", "sasl_plaintext" },
            { "sasl.mechanisms", "PLAIN" },
            { "sasl.username", this.kafkaUsername },
            { "sasl.password", this.kafkaPassword }
        }).Build();

    // Timestamp to which the offset should be set to
    var timeStamp = new DateTime(2020, 3, 1, 0, 0, 0, DateTimeKind.Utc);

    var newOffsets = new List<TopicPartitionOffset>();
    var metadata = adminClient.GetMetadata(Topic, TimeSpan.FromSeconds(30));
    foreach (var topicMetadata in metadata.Topics)
    {
        if (topicMetadata.Topic == Topic)
        {
            foreach (var partitionMetadata in topicMetadata.Partitions.OrderBy(p => p.PartitionId))
            {
                var topicPartition = new TopicPartition(topicMetadata.Topic, partitionMetadata.PartitionId);

                IEnumerable<TopicPartitionOffset> found = consumer.OffsetsForTimes(
                    new[] { new TopicPartitionTimestamp(topicPartition, new Timestamp(timeStamp, TimestampType.CreateTime)) },
                    TimeSpan.FromSeconds(5));

                newOffsets.Add(new TopicPartitionOffset(topicPartition, new Offset(found.First().Offset)));
            }
        }
    }

    consumer.Commit(newOffsets);

    // Consume messages
    consumer.Subscribe(Topic);
    var consumerResult = consumer.Consume();
    // process message
    //consumer.Commit(consumerResult);
}
private void SetOffset()
{
const string Topic=“myTopic”;
常量字符串bootstrapserver=“server1,server2”;
var adminClient=new AdminClientBuilder(
新词典
{
{“bootstrap.servers”,bootstrapserver},
{“安全协议”,“sasl_明文”},
{“sasl.mechaniss”,“PLAIN”},
{“sasl.username”,this.kafkaUsername},
{“sasl.password”,this.kafkaPassword}
}).Build();
var consumer=新的ConsumerBuilder(
新词典
{
{“bootstrap.servers”,bootstrapserver},
{“group.id”,this.groupId},
{“enable.auto.commit”,“false”},
{“安全协议”,“sasl_明文”},
{“sasl.mechaniss”,“PLAIN”},
{“sasl.username”,this.kafkaUsername},
{“sasl.password”,this.kafkaPassword}
}).Build();
//偏移量应设置为的时间戳
var timeStamp=新的日期时间(2020,3,1,0,0,0,DateTimeKind.Utc);
var newOffsets=新列表();
var metadata=adminClient.GetMetadata(主题,TimeSpan.FromSeconds(30));
foreach(metadata.Topics中的var topicMetadata)
{
if(topicMetadata.Topic==Topic)
{
foreach(topicMetadata.Partitions.OrderBy(p=>p.PartitionId)中的var partitionMetadata)
{
var topicPartition=新的topicPartition(topicMetadata.Topic,partitionMetadata.PartitionId);
已找到IEnumerable=consumer.OffsetsForTimes(
new[]{new-TopicPartitionTimestamp(topicPartition,new-Timestamp(Timestamp,TimestampType.CreateTime))},
时间跨度从秒(5)开始;
添加(新TopicPartitionOffset(topicPartition,新偏移量(found.First().Offset));
}
}
}
consumer.Commit(newoffset);
//使用消息
消费者。订阅(主题);
var consumerResult=consumer.consumer();
//处理消息
//consumer.Commit(consumerResult);
}
如果我想跳过消息并跳转到给定的偏移量(如果我想跳转到的偏移量在最后一条提交的消息之后),那么这种方法很有效

但是,如果给定的时间戳早于最后提交的消息的时间戳,则上述方法将不起作用。在上述代码中,如果
时间戳
在最后提交的消息的时间戳之前,则
OffsetsForTimes
将返回最后提交的消息的偏移量+1。即使我手动将偏移量设置为较低的偏移量,那么
consumer.Commit(newOffsets)
似乎也没有效果,在消费时我会收到第一条未提交的消息


有没有办法从代码中实现这一点?

我不是专家,但我将尝试解释您如何做到这一点

首先,我们必须提到subscribe和assign方法

使用subscribe时,传递一个或多个主题。这样,每个主题的分区列表将根据其组中的使用者数量分配给使用者。主题分区是由主题名称和分区号组成的对象

consumer.Subscribe(Topic);
您可以使用assign传递使用者将读取的分区。此方法不使用使用者的组管理功能(不需要group.id) 如果我没有错,在assign方法中可以指定初始偏移量

consumer.Assign(topicName,0,新偏移量(lastConsumedOffset));
分配(topicPartition,新偏移量(lastConsumedOffset));
另一个选项是使用seek()方法设置偏移量

consumer.Seek(topicPartitionOffset);
如果您要混合订阅和分配,请记住,您必须先使用“取消订阅”

如果要重新使用所有消息,另一个选项是创建 新的不同消费群体中的消费者

示例(待审查) 我现在就离开你和我的例子,稍后我会检查它。 我用java编写了这个示例,因为我对它比较熟悉。 在本例中,我不使用subscribe,而是使用assign。 首先检索主题分区,我们设置从中读取消息的开始日期时间,我们为每个分区创建一个指定该日期时间的映射

通过创建的映射,我们使用offsetsForTimes方法获得指定日期时间内每个分区的偏移量。利用每个分区的偏移量,我们使用seek移动到每个分区上的偏移量,最后使用消息

我现在没有时间检查代码,但我会做的。 我希望有帮助

AdminClient=AdminClient.create(getAdminClientProperties());
卡夫卡消费者=新卡夫卡消费者(
getConsumerProperties());
字符串TOPIC=“TOPIC”;
//获取主题的所有分区的信息
List partitionsInfo=消费者。用于(主题)的分区;
//创建主题分区列表
Set partitions=new HashSet();
for(PartitionInfo p:partitionsInfo){
添加(新的主题分区(p.topic(),p.partition());
}
//使用者将从所有分区读取数据
消费者。分配(分区);
DateTime timeToStartReadMessagesFrom=新的日期时间(2020,3,1,0,0);
Map timestamps=newhashmap();
for(主题分区tp:分区){
timestamps.put(tp,timeToStartReadMessagesFrom.getMillis());
}
//获取每个分区中该时间的偏移量
映射偏移=消费者.offsetsForTimes(时间戳);
for(主题分区tp:分区){
消费者
public static List<TopicPartition> GetTopicPartitions(string bootstrapServers, string topicValue) {
    var tp = new List<TopicPartition>();
    using (var adminClient = new AdminClientBuilder(new AdminClientConfig { BootstrapServers = bootstrapServers }).Build()) {
        var meta = adminClient.GetMetadata(TimeSpan.FromSeconds(20));
        meta.Topics.ForEach(topic => {
            if (topic.Topic == topicValue) {
                foreach (PartitionMetadata partition in topic.Partitions) {
                    tp.Add(new TopicPartition(topic.Topic, partition.PartitionId));
                }
            }
        });
    }
    return tp;
}
List<TopicPartition> topic_partitions = frmMain.GetTopicPartitions(mBootstrapServers, txtTopic.Text);

using (var consumer = new ConsumerBuilder<Ignore, string>(cfg).Build()) {
    consumer.Assign(topic_partitions);

    List<TopicPartitionTimestamp> new_times = new List<TopicPartitionTimestamp>();
    foreach (TopicPartition tp in topic_partitions) {
        new_times.Add(new TopicPartitionTimestamp(tp, new Timestamp(dtpNewTime.Value)));
    }

    List<TopicPartitionOffset> seeked_offsets = consumer.OffsetsForTimes(new_times, TimeSpan.FromSeconds(40));
    string s = "";
    foreach (TopicPartitionOffset tpo in seeked_offsets) {
        s += $"{tpo.TopicPartition}: {tpo.Offset.Value}\n";
    }
    Console.WriteLine(s);
    consumer.Close();
}
using (var consumer =
    new ConsumerBuilder<string, string>(config)
        .SetErrorHandler((_, e) => Log($"Error: {e.Reason}"))
        .Build()) {
    consumer.Assign(seeked_offsets);

    try {
        while (true) {
            try {
                var r = consumer.Consume(cancellationToken);
                // do something with r
            } catch (ConsumeException e) {
                //Log($"Consume error: {e.Error.Reason}");
            }
        }
    } catch (OperationCanceledException) {
        //Log("Closing consumer.");
        consumer.Close();
    }
}