.net6 操作kafka

使用docker部署kafka(单服务)

  本人使用的docker部署kafka 具体的部署流程可以参考如下地址:
  https://blog.csdn.net/steve_frank/article/details/109782212
  https://www.jianshu.com/p/e642793cd5de

 

详解Kafka消息队列的两种模式

  Kafka是一种高吞吐量的分布式发布订阅消息系统,有如下特性

    • 通过O的磁盘数据结构提供消息的持久化,这种结构对于即使数以TB的消息存储也能够保持长时间的稳定性能。
    • 高吞吐量:即使是非常普通的硬件Kafka也可以支持每秒数百万 [2] 的消息。
    • 支持通过Kafka服务器和消费机集群来分区消息。
    • 支持Hadoop并行数据加载。
      Kafka通过官网发布了最新版本2.3.0

  1. 点对点模式(一对一)

  1)模式特点:
      消息生产者生产消息发送到Queue中,然后消息消费者从Queue中取出并且消费消息。消息被消费以后,queue中不再存储该条消息,所以消息消费者不可能消费到已经被消费的消息。Queue支持存在多个消费者,但是对一个消息而言,只会有一个消费者消费。

  2)数据拉取方式:消费者主动拉取。

  3)模式缺点:消息不能被重复消费。

  点对点模式下包括三个角色:

  消息队列

  发送者 (生产者)

  接收者(消费者)

 

 

 

  2. 发布/订阅模式(一对多)
  1)模式特点:
      消息生产者(发布)将消息发布到topic中,同时有多个消息消费者(订阅)消费该消息。和点对点方式不同,发布到topic的消息会被所有订阅者消费。

  2)数据拉取方式:消费者主动拉取、消费者被动接受(类似微信公众号)

  3)模式缺点:当数据拉取方式为消费者被动接受时,消费者的消费速度可能跟不上生产者的生产速度。

  发布/订阅模式下包括三个角色:

  角色主题(Topic)

  发布者(Publisher)

  订阅者(Subscriber)

 

 

   kafka更多详细资料:https://lotabout.me/2018/kafka-introduction/

  

代码实例(点对点模式)  

   public interface IKafkaService
    {
        /// <summary>
        /// 发生消息至指定主题
        /// </summary>
        /// <typeparam name="TMessage"></typeparam>
        /// <param name="topicName"></param>
        /// <param name="message"></param>
        /// <returns></returns>
        Task PublishAsync<TMessage>(string host,string topicName, TMessage message) where TMessage : class;


        /// <summary>
        /// 从指定主题订阅消息
        /// </summary>
        /// <typeparam name="TMessage"></typeparam>
        /// <param name="topics"></param>
        /// <param name="messageFunc"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        Task SubscribeAsync<TMessage>(IEnumerable<string> topics,Action<TMessage> messageFunc,CancellationToken cancellationToken) where TMessage : class;
    }

//封装

public class KafkaService : IKafkaService
    {
        public readonly string host = "120.79.77.91:9092";
        public async Task PublishAsync<TMessage>(string host, string topicName, TMessage message) where TMessage : class
        {
            var config = new ProducerConfig
            {
                BootstrapServers = host
            };
            using var producer = new ProducerBuilder<string, string>(config).Build();
            var data = new Message<string, string> { Key = Guid.NewGuid().ToString(), Value = Newtonsoft.Json.JsonConvert.SerializeObject(message) };
            await producer.ProduceAsync(topicName, data);
        }

        public async Task SubscribeAsync<TMessage>(IEnumerable<string> topics, Action<TMessage> messageFunc, CancellationToken cancellationToken) where TMessage : class
        {
            var config = new ConsumerConfig
            {
                BootstrapServers = host,
                GroupId = "consumer",
                EnableAutoCommit = false,
                StatisticsIntervalMs = 5000,
                SessionTimeoutMs = 6000,
                AutoOffsetReset = AutoOffsetReset.Earliest,
                EnablePartitionEof = true
            };
            //const int commitPeriod = 5;
            using var consumer = new ConsumerBuilder<Ignore, string>(config)
             .SetErrorHandler((_, e) =>
             {
                 Console.WriteLine($"Error: {e.Reason}");
             })
             .SetStatisticsHandler((_, json) =>
             {
                 Console.WriteLine($" - {DateTime.Now:yyyy-MM-dd HH:mm:ss} > 消息监听中..");
             })
             .SetPartitionsAssignedHandler((c, partitions) =>
             {
                 string partitionsStr = string.Join(", ", partitions);
                 Console.WriteLine($" - 分配的 kafka 分区: {partitionsStr}");
             })
             .SetPartitionsRevokedHandler((c, partitions) =>
             {
                 string partitionsStr = string.Join(", ", partitions);
                 Console.WriteLine($" - 回收了 kafka 的分区: {partitionsStr}");
             })
             .Build();
            consumer.Subscribe(topics);
            try
            {
                while (true)
                {
                    try
                    {
                        var consumeResult = consumer.Consume(cancellationToken);
                        Console.WriteLine($"Consumed message '{consumeResult.Message?.Value}' at: '{consumeResult?.TopicPartitionOffset}'.");
                        if (consumeResult.IsPartitionEOF)
                        {
                            Console.WriteLine($" - {DateTime.Now:yyyy-MM-dd HH:mm:ss} 已经到底了:{consumeResult.Topic}, partition {consumeResult.Partition}, offset {consumeResult.Offset}.");
                            continue;
                        }
                        TMessage messageResult = null;
                        try
                        {
                            messageResult = JsonConvert.DeserializeObject<TMessage>(consumeResult.Message.Value);
                        }
                        catch (Exception ex)
                        {
                            var errorMessage = $" - {DateTime.Now:yyyy-MM-dd HH:mm:ss}【Exception 消息反序列化失败,Value:{consumeResult.Message.Value}】 :{ex.StackTrace?.ToString()}";
                            Console.WriteLine(errorMessage);
                            messageResult = null;
                        }
                        if (messageResult != null/* && consumeResult.Offset % commitPeriod == 0*/)
                        {
                            messageFunc(messageResult);
                            try
                            {
                                consumer.Commit(consumeResult);
                            }
                            catch (KafkaException e)
                            {
                                Console.WriteLine(e.Message);
                            }
                        }
                    }
                    catch (ConsumeException e)
                    {
                        Console.WriteLine($"Consume error: {e.Error.Reason}");
                    }
                }
            }
            catch (OperationCanceledException)
            {
                Console.WriteLine("Closing consumer.");
                consumer.Close();
            }
            await Task.CompletedTask;
        }
    }

//生产者

string[] configList = { "xxx.xx.xx.xx:9092", "sun" };

//调用接口定义的方法
KafkaService kafkaService = new KafkaService();
while (true)
{
    var data = Console.ReadLine();
    await kafkaService.PublishAsync<string>(configList.First(), configList.Last(), data);
}

//消费者

KafkaService kafkaService = new KafkaService();
await kafkaService.SubscribeAsync<string>(new string[] { "sun" }, like, new CancellationToken());

static void like(string like)
{
    Console.WriteLine($"这个是接受到的结果:{like}");
}

代码实例(发布/订阅模式) 

 

posted on 2022-01-11 11:28  白码一号  阅读(1336)  评论(3编辑  收藏  举报