Apache Kafka Consumer未按顺序接收消息

时间:2018-08-16 23:56:09

标签: .net apache-kafka kafka-consumer-api kafka-producer-api confluent-kafka

我正在我的项目中尝试Kafka的POC,并使用Confluent.kafka库在.net core 2.1中创建了两个控制台应用程序。我已经在Ubuntu盒子上安装了Kafka,它运行正常。当我使用Producer控制台应用程序将数千条消息推入Kafka并在消息中附加序列号时。当我在客户控制台应用程序中使用这些消息时,消息顺序不正确。生产者和消费者只有一个,它们都与一个主题相关。下面是我的生产者的代码

public class Kafta
{
    private Dictionary<string, object> config;
    private string topicName;

    public Kafta(string topic)
    {
        config = new Dictionary<string, object>
        {
            {"bootstrap.servers","192.168.60.173:9092" }
        };
        topicName = topic;
    }
    public async void SendMessageAsync(string message)
    {
        using (var producer = new Producer<string, string>(config, new StringSerializer(Encoding.UTF8), new StringSerializer(Encoding.UTF8)))
        {
            var msg = await producer.ProduceAsync(topicName, "userid", message);

            //producer.ProduceAsync("console", null, message);
            producer.Flush(500);
        }
    }
}

生产者的Program.cs静态void main

static void Main(string[] args)
    {
        string topic = "tester2";
        long count = 1;
        Console.WriteLine("Starting to send message");
        Console.WriteLine("Write the message here: ");

        if(args.Length == 2)
        {
            topic = args[0];
            count = long.Parse(args[1]);
        }
        try
        {
            Console.WriteLine("Topic name " + topic);
            var message = Console.ReadLine();            
            var service = new Kafta(topic);

            for(var i = 0; i<count;i++)
            {
                var msg = message + " number " + i.ToString();
                Console.WriteLine("Message to Kafta: " + msg);
                service.SendMessageAsync(msg);
            }

        }
        catch (Exception ex)
        {
            Console.WriteLine("Exception occured " + ex.Message);
        }
        finally
        {
            Console.WriteLine("Press any key to exit");
            Console.Read();
        }
    }

消费者代码

static void Main(string[] args)
    {
        var config = new Dictionary<string, object>
        {
          { "group.id", "sample-consumer" },
          { "bootstrap.servers", "192.168.60.173:9092" },
          { "enable.auto.commit", "false"}
        };
        string topic = "tester2";
        if (args.Length == 1)
            topic = args[0];
        using (var consumer = new Consumer<string, string>(config, new StringDeserializer(Encoding.UTF8), new StringDeserializer(Encoding.UTF8)))
        {
            consumer.Subscribe(new string[] { topic });                
            consumer.OnMessage += (_, msg) =>
            {
                Console.WriteLine($"Topic: {msg.Topic} Partition: {msg.Partition} Offset: {msg.Offset} {msg.Value}");
                consumer.CommitAsync(msg);

            };

            while (true)
            {
                consumer.Poll(100);
            }
        }
    }

生产者的输出

Message to Kafta: message number 0
Message to Kafta: message number 1
Message to Kafta: message number 2
Message to Kafta: message number 3
Message to Kafta: message number 4
Message to Kafta: message number 5
Message to Kafta: message number 6
Message to Kafta: message number 7
Message to Kafta: message number 8
Message to Kafta: message number 9

消费者的输出:

message number 4
message number 7
message number 0
message number 1
message number 2
message number 3
message number 5
message number 6
message number 8
message number 9

我是Kafka的新手,不确定要使它正常工作所缺少的内容。按照Kafka文档的说明,对于我的用例来说,消息的顺序是可以保证的,所以我一定在犯一些愚蠢的错误,无法弄清楚。

我可以使用Kafka以外的其他替代方法吗?

谢谢

2 个答案:

答案 0 :(得分:1)

  

根据Kafka文档中的消息顺序得到保证

每个分区。关于您的问题,您没有提到主题有多少个分区。您正在打印Topic: {msg.Topic} Partition: {msg.Partition},但这不是帖子的输出。

在生产者中,您正在使用SendMessageAsync进行“即发即弃”操作,而不是验证代理是否实际接收到带有该方法返回值的消息。因此,这是一种可能性-您的打印报表会井然有序,但是消息不一定以这种方式到达代理。

如果代码中显示的使用者输出中的分区号始终相同,尽管我对C#API并不熟悉,但您似乎正在使用非阻塞使用者消息侦听器。该OnMessage函数很可能会在单独的线程中被调用,该线程不一定以保证的顺序写入标准输出。更好的测试方法可能是在每条消息中插入时间戳,而不是仅添加计数器

  

我可以使用Kafka以外的其他替代方法吗?

存在大量其他MQ技术,例如RabbitMQ,因此,如果您不关心Kafka的持久性功能和其他API(Streams和Connect),请随时使用这些

答案 1 :(得分:0)

如@ cricket_007所述,具有一个主题和多个分区意味着仅对从一个分区接收的数据进行排序。

创建使用者(仅一个)时,它将使用所有分区来读取消息。然后,来自分区的数据会同步显示为红色(是),但是您收到消息的分区随时间变化。

比方说,您针对100个主题创建了4个分区的消息。为了简洁起见,请说每个分区存储25条消息。当您启动使用者时,它会收到以下消息(示例):

  • 来自分区#1的5条消息
  • 来自分区#2的4条消息
  • 来自分区#3的6条消息
  • 来自分区#4的2条消息
  • 来自分区#1的3条消息
  • ...

那是因为消费者试图均匀读取所有分区。