【问题标题】:Kafka consume message and then produce to another topic卡夫卡消费消息,然后生产到另一个主题
【发布时间】:2020-07-28 00:26:56
【问题描述】:

我必须从 Kafka 主题中消费,获取消息并执行一些 json 清理和过滤工作,然后我需要将新消息生成到另一个 Kafka 主题,我的代码是这样的:

public static YamlMappingNode configs;
        public static void Main(string[] args)
        {
            using (var reader = new StreamReader(Path.Combine(Directory.GetCurrentDirectory(), ".gitlab-ci.yml")))
            {
                var yaml = new YamlStream();
                yaml.Load(reader);

                //find variables 
                configs = (YamlMappingNode)yaml.Documents[0].RootNode;
                configs = (YamlMappingNode)configs.Children.Where(k => k.Key.ToString() == "variables")?.FirstOrDefault().Value;

            }

            CancellationTokenSource cts = new CancellationTokenSource();
            Console.CancelKeyPress += (_, e) => {
                e.Cancel = true; // prevent the process from terminating.
                cts.Cancel();
            }; 
            Run_ManualAssign(configs, cts.Token);
        }

 public static async void Run_ManualAssign(YamlMappingNode configs, CancellationToken cancellationToken)
        {
            var brokerList = configs.Where(k => k.Key.ToString() == "kfk_broker")?.FirstOrDefault().Value.ToString();
            var topics = configs.Where(k => k.Key.ToString() == "input_kfk_topic")?.FirstOrDefault().Value.ToString();
            var config = new ConsumerConfig
            {
                // the group.id property must be specified when creating a consumer, even 
                // if you do not intend to use any consumer group functionality.
                GroupId = new Guid().ToString(),
                BootstrapServers = brokerList,
                // partition offsets can be committed to a group even by consumers not
                // subscribed to the group. in this example, auto commit is disabled
                // to prevent this from occurring.
                EnableAutoCommit = true
            };

            using (var consumer =
                new ConsumerBuilder<Ignore, string>(config)
                    .SetErrorHandler((_, e) => Console.WriteLine($"Error: {e.Reason}"))
                    .Build())
            {
                //consumer.Assign(topics.Select(topic => new TopicPartitionOffset(topic, 0, Offset.Beginning)).ToList());
                consumer.Assign(new TopicPartitionOffset(topics, 0, Offset.End));
                //var producer = new ProducerBuilder<Null, string>(config).Build();
                try
                {
                    while (true)
                    {
                        try
                        {
                            var consumeResult = consumer.Consume(cancellationToken);
                            /// Note: End of partition notification has not been enabled, so
                            /// it is guaranteed that the ConsumeResult instance corresponds
                            /// to a Message, and not a PartitionEOF event.

                            //filter message  
                            var result = ReadMessage(configs, consumeResult.Message.Value);
                            //send to kafka topic
                            await Run_ProducerAsync(configs, result);
                        }
                        catch (ConsumeException e)
                        {

                            Console.WriteLine($"Consume error: {e.Error.Reason}");
                        }
                    }
                }
                catch (OperationCanceledException)
                {
                    Console.WriteLine("Closing consumer.");
                    consumer.Close();
                }
            }
        }
        #endregion

        #region Run_Producer
        public static async Task Run_ProducerAsync(YamlMappingNode configs, string message)
        {
            var brokerList = configs.Where(k => k.Key.ToString() == "kfk_broker")?.FirstOrDefault().Value.ToString();
            var topicName = configs.Where(k => k.Key.ToString() == "target_kafka_topic")?.FirstOrDefault().Value.ToString();
            var config = new ProducerConfig {
                BootstrapServers = brokerList,
            };

            using (var producer = new ProducerBuilder<Null, string>(config).Build())
            {
                try
                {
                    /// Note: Awaiting the asynchronous produce request below prevents flow of execution
                    /// from proceeding until the acknowledgement from the broker is received (at the 
                    /// expense of low throughput).
                    var deliveryReport = await producer.ProduceAsync(topicName, new Message<Null, string> { Value = message });
                    producer.Flush(TimeSpan.FromSeconds(10));
                    Console.WriteLine($"delivered to: {deliveryReport.TopicPartitionOffset}");
                }
                catch (ProduceException<string, string> e)
                {
                    Console.WriteLine($"failed to deliver message: {e.Message} [{e.Error.Code}]");
                }
            }
        }
        #endregion

我在这里做错了吗?程序在执行var deliveryReport = await producer.ProduceAsync(topicName, new Message&lt;Null, string&gt; { Value = message });时立即存在,没有错误信息,没有错误代码。

同时我使用 Python 并为Producer 进行了相同的配置,效果很好。

【问题讨论】:

  • 顺便说一下,我使用的是 .net core 2.1,以防你想知道。

标签: c# .net-core apache-kafka kafka-consumer-api kafka-producer-api


【解决方案1】:

Run_ManualAssign(configs, cts.Token);

对于 Main 函数中的这一行,您在同步函数中调用 async 而不等待。因此程序在此调用开始后立即退出(未完成,因为它是异步的)

你可以有两个选择

  1. 使用 async Main 函数并在此调用前添加 await。

    https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/proposals/csharp-7.1/async-main

  2. 如果你真的想在同步函数中调用异步函数

    Run_ManualAssign(configs, ts.Token).ConfigureAwait(false).GetAwaiter().GetResult();

【讨论】:

  • 你不能等待一个 void 函数。
  • Run_ManualAssign 需要定义为公共静态异步任务 Run_ManualAssign(YamlMappingNode configs, CancellationToken cancelToken)
【解决方案2】:

我解决了这个问题,但实际上我不知道为什么。我在这里开了一个issue

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-07-24
    • 1970-01-01
    • 2020-02-20
    • 1970-01-01
    • 2021-01-26
    • 1970-01-01
    • 2017-04-05
    • 2019-08-06
    相关资源
    最近更新 更多