erdeni 学无止境

C#订阅Kafka消息一直不能消费的情况怎么处理?

最近跟数据部门对接时对方提供的kafka订阅服务,于是找了资料,写了个C#控制台程序消费了这个服务。

本文主要记录的内容是C#消费Kafka消息时选用kafka-net组件,遇到offset不是从0开始的情况时处理方法。

按照入门教程搭建测试环境并调试一切正常。

在生产环境中部署后遇到一直闪烁却无法消费的问题,看日志是可以连接成功的,后来跟生产方沟通后得知运维在部署kafka的时候设置了消息保留3天。

那么三天前的消息自动清除后,前面的offset在kafka里已经不存在了,这时候我们把offset设置成0时就会出现上述情况,那怎么获得起始的offset呢?

一、首先在本地安装jdk、Kafka和zookeeper。

1、jdk建议使用1.7以上版本。

2、下载安装并运行zookeeper (下载地址 http://zookeeper.apache.org/releases.html)

3、安装并运行Kafka (下载地址 http://kafka.apache.org/downloads.html

如上安装步骤在车江毅的博客《.net windows Kafka 安装与使用入门(入门笔记)》中写的很详细,我就不重复了。

二、在kafka中创建topic、生产者、消费者,上述博客中有详细命令,这里跳过。

三、根据上述博客中推荐,选用了kafka-net。源码地址: https://github.com/Jroland/kafka-net

1、生产者代码

private static void Produce(string broker, string topic,string message)
        {
            var options = new KafkaOptions(new Uri(broker));
            var router = new BrokerRouter(options);
            var client = new Producer(router);

            var currentDatetime = DateTime.Now;
            var key = currentDatetime.Second.ToString();
            var events = new[] { new Message(message) };
            client.SendMessageAsync(topic, events).Wait(1500);
            Console.WriteLine("Produced: Key: {0}. Message: {1}", key, events[0].Value.ToUtf8String());

            using (client) { }
        }

//调用示例:
Produce("http://地址:端口", "TopicName", "Hello World");

2、消费者代码,上面都是为了搭建测试环境,这次的任务是消费其他组提供的kafka消息

private static void Consume(string broker, string topic)
       {
            Uri[] UriArr = Array.ConvertAll<string, Uri>(broker.Split(','), delegate (string s) { return new Uri(s); });
            var options = new KafkaOptions(UriArr);
            var router = new BrokerRouter(options);
            var consumer = new Consumer(new ConsumerOptions(topic, router));

       //实际生产环境中消费不是每次都从0 offset开始的,查了资料发现java可以设置自动参数配置从最小offset开始或者最大offset开始
       //但kafka-net这个sdk中没有找到,所以就下面这段代码配置了offset的起始位置 Task<List<OffsetResponse>> offTask = consumer.GetTopicOffsetAsync(topic); var offsetResponseList = offTask.Result; var positionArr = new OffsetPosition[offsetResponseList.Count]; for (int i = 0; i < offsetResponseList.Count; i++) { //LogHelper.WriteLog(string.Format("获取到Kafka OffsetPosition信息 Partition:{0} maxOffset:{1} minOffset:{2}", offsetResponseList[i].PartitionId, offsetResponseList[i].Offsets[0], offsetResponseList[i].Offsets[1])); positionArr[i] = new OffsetPosition(offsetResponseList[i].PartitionId, offsetResponseList[i].Offsets[1]);//这里Offsets[0]是最大值,我们从头消费用的最小值Offsets[1] } Console.WriteLine("开始执行"); //如果作业执行一段时间后重启,下面可以从数据库中读取后再配置开始读取的位置 /*  try { using (IDbConnection dbConn = SingleDataBase.DBFactory.OpenRead<MessageQueue>()) { string sql = string.Format("SELECT [Partition], MAX(Offset)Offset FROM [表名] WITH(NOLOCK) WHERE topic='{0}' GROUP BY [Partition]", topic); var r = dbConn.SqlList<OffsetPositionEntity>(sql); if (r.Count > 0) { positionArr = Array.ConvertAll<OffsetPositionEntity, OffsetPosition>(r.ToArray(), delegate (OffsetPositionEntity p) { return new OffsetPosition(p.Partition, p.Offset + 1); }); } } } catch { //读取上次获取位置失败,从头开始获取,可能会导致重复获取 //LogHelper.WriteLog("没有获取到上次截至节点,将从头开始获取"); }        */ consumer.SetOffsetPosition(positionArr); //Consume returns a blocking IEnumerable (ie: never ending stream) foreach (var message in consumer.Consume()) { try { string messageStr = message.Value.ToUtf8String(); Console.WriteLine(messageStr); long r = 0; /*这里已经删掉了写入数据库部分*/ //LogHelper.WriteLog(string.Format("Response: Partition {0},Offset {1} : {2} ,Insert : {3}", message.Meta.PartitionId, message.Meta.Offset, messageStr, r)); } catch (Exception ex) { //LogHelper.WriteLog("消费Kafka消息保存到数据库时报错:" + ex.Message); } } } //调用示例: Consume("http://100.162.136.70:9092,http://100.162.136.71:9092,http://100.162.136.72:9092", "YourTopicName");

  以上代码中LogHelper.WriteLog部分用Console.WriteLine替换后可以直接输出到屏幕上,这里没有提供写日志相关方法。

 

  这篇文章写出来,就是为了记录高亮显示的那部分片段,其他都是为了让文章尽可能的完整。如果有朋友对kafka-net比较熟悉,有更方便的配置方式请留言分享,多谢~~~

 

posted on 2018-06-06 10:17  erdeni  阅读(2711)  评论(0编辑  收藏  举报

导航