.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}"); }
代码实例(发布/订阅模式)
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库