.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}"); }
代码实例(发布/订阅模式)