上个章节我们讲了kafka的环境安装(这里),现在主要来了解下Kafka使用,基于.net实现kafka的消息队列应用,本文用的是Confluent.Kafka,版本0.11.6

1、安装:

在NuGet程序包中搜索“Confluent.Kafka”下载安装即可

2、producer发送消息:

 using System;
using System.Collections.Generic;
using System.Text;
using Confluent.Kafka;
using Confluent.Kafka.Serialization; namespace KafKa
{
/// <summary>
/// Kafka消息生产者
/// </summary>
public sealed class KafkaProducer
{
/// <summary>
/// 生产消息并发送消息
/// </summary>
/// <param name="broker">kafka的服务器地址</param>
/// <param name="topic">kafka的消息主题名称</param>
/// <param name="partion">分区</param>
/// <param name="message">需要传送的消息</param>
public bool Produce(string broker, string topic, int partion, string message)
{
bool result = false;
if (string.IsNullOrEmpty(broker) || string.IsNullOrWhiteSpace(broker) || broker.Length <= )
{
throw new ArgumentNullException("Kafka消息服务器地址不能为空!");
} if (string.IsNullOrEmpty(topic) || string.IsNullOrWhiteSpace(topic) || topic.Length <= )
{
throw new ArgumentNullException("消息所属的主题不能为空!");
} if (string.IsNullOrEmpty(message) || string.IsNullOrWhiteSpace(message) || message.Length <= )
{
throw new ArgumentNullException("消息内容不能为空!");
} var config = new Dictionary<string, object>
{
{ "bootstrap.servers", broker }
};
using (var producer = new Producer<Null, string>(config, null, new StringSerializer(Encoding.UTF8)))
{
var deliveryReport = producer.ProduceAsync(topic, null, message, partion);
deliveryReport.ContinueWith(task =>
{
if (task.Result.Error.Code == ErrorCode.NoError)
{
result = true;
}
//可以在控制台使用以下语句
//Console.WriteLine("Producer:" + producer.Name + "\r\nTopic:" + topic + "\r\nPartition:" + task.Result.Partition + "\r\nOffset:" + task.Result.Offset + "\r\nMessage:" + task.Result.Value);
}); producer.Flush(TimeSpan.FromSeconds());
}
return result;
}
}
}

3、consumer接收消息:

 using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using Confluent.Kafka;
using Confluent.Kafka.Serialization; namespace KafKa
{
/// <summary>
/// Kafka消息消费者
/// </summary>
public sealed class KafkaConsumer
{
#region 私有字段 private bool isCancelled; #endregion #region 构造函数 /// <summary>
/// 构造函数,初始化IsCancelled属性
/// </summary>
public KafkaConsumer()
{
isCancelled = false;
} #endregion #region 属性 /// <summary>
/// 是否应该取消继续消费Kafka的消息,默认值是false,继续消费消息
/// </summary>
public bool IsCancelled
{
get { return isCancelled; }
set { isCancelled = value; }
} #endregion #region 同步版本 /// <summary>
/// 指定的组别的消费者开始消费指定主题的消息
/// </summary>
/// <param name="broker">Kafka消息服务器的地址</param>
/// <param name="topic">Kafka消息所属的主题</param>
/// <param name="groupID">Kafka消费者所属的组别</param>
/// <param name="action">可以对已经消费的消息进行相关处理</param>
public void Consume(string broker, string topic, string groupID, Action<ConsumerResult> action = null)
{
if (string.IsNullOrEmpty(broker) || string.IsNullOrWhiteSpace(broker) || broker.Length <= )
{
throw new ArgumentNullException("Kafka消息服务器的地址不能为空!");
} if (string.IsNullOrEmpty(topic) || string.IsNullOrWhiteSpace(topic) || topic.Length <= )
{
throw new ArgumentNullException("消息所属的主题不能为空!");
} if (string.IsNullOrEmpty(groupID) || string.IsNullOrWhiteSpace(groupID) || groupID.Length <= )
{
throw new ArgumentNullException("用户分组ID不能为空!");
} var config = new Dictionary<string, object>
{
{ "bootstrap.servers", broker },
{ "group.id", groupID },
{ "enable.auto.commit", true }, // this is the default
{ "auto.commit.interval.ms", },
{ "statistics.interval.ms", },
{ "session.timeout.ms", },
{ "auto.offset.reset", "smallest" }
}; using (var consumer = new Consumer<Ignore, string>(config, null, new StringDeserializer(Encoding.UTF8)))
{
if (action != null)
{
consumer.OnMessage += (_, message) => {
ConsumerResult messageResult = new ConsumerResult();
messageResult.Broker = broker;
messageResult.Topic = message.Topic;
messageResult.Partition = message.Partition;
messageResult.Offset = message.Offset.Value;
messageResult.Message = message.Value; //执行外界自定义的方法
action(messageResult);
};
} consumer.OnPartitionEOF += (_, end) => Console.WriteLine("Reached end of topic " + end.Topic + " partition " + end.Partition + ", next message will be at offset " + end.Offset); consumer.OnError += (_, error) => Console.WriteLine("Error:" + error); //引发反序列化错误或消费消息出现错误!= NoError。
consumer.OnConsumeError += (_, message) => Console.WriteLine("Error consuming from topic/partition/offset " + message.Topic + "/" + message.Partition + "/" + message.Offset + ": " + message.Error); consumer.OnOffsetsCommitted += (_, commit) => Console.WriteLine(commit.Error ? "Failed to commit offsets:" + commit.Error : "Successfully committed offsets:" + commit.Offsets); // 当消费者被分配一组新的分区时引发。
consumer.OnPartitionsAssigned += (_, partitions) =>
{
Console.WriteLine("Assigned Partitions:" + partitions + ", Member ID:" + consumer.MemberId);
//如果您未向OnPartitionsAssigned事件添加处理程序,则会自动执行以下.Assign调用。 如果你为它添加了事件处理程序,你必须明确地调用.Assign以便消费者开始消费消息。
consumer.Assign(partitions);
}; // Raised when the consumer's current assignment set has been revoked.
//当消费者的当前任务集已被撤销时引发。
consumer.OnPartitionsRevoked += (_, partitions) =>
{
Console.WriteLine("Revoked Partitions:" + partitions);
// If you don't add a handler to the OnPartitionsRevoked event,the below .Unassign call happens automatically. If you do, you must call .Unassign explicitly in order for the consumer to stop consuming messages from it's previously assigned partitions.
//如果您未向OnPartitionsRevoked事件添加处理程序,则下面的.Unassign调用会自动发生。 如果你为它增加了事件处理程序,你必须明确地调用.Usessign以便消费者停止从它先前分配的分区中消费消息。
consumer.Unassign();
}; //consumer.OnStatistics += (_, json) => Console.WriteLine("Statistics: " + json); consumer.Subscribe(topic); //Console.WriteLine("Subscribed to:" + consumer.Subscription); while (!IsCancelled)
{
consumer.Poll(TimeSpan.FromMilliseconds());
}
}
} #endregion #region 异步版本 /// <summary>
/// 指定的组别的消费者开始消费指定主题的消息
/// </summary>
/// <param name="broker">Kafka消息服务器的地址</param>
/// <param name="topic">Kafka消息所属的主题</param>
/// <param name="groupID">Kafka消费者所属的组别</param>
/// <param name="action">可以对已经消费的消息进行相关处理</param>
public void ConsumeAsync(string broker, string topic, string groupID, Action<ConsumerResult> action = null)
{
if (string.IsNullOrEmpty(broker) || string.IsNullOrWhiteSpace(broker) || broker.Length <= )
{
throw new ArgumentNullException("Kafka消息服务器的地址不能为空!");
} if (string.IsNullOrEmpty(topic) || string.IsNullOrWhiteSpace(topic) || topic.Length <= )
{
throw new ArgumentNullException("消息所属的主题不能为空!");
} if (string.IsNullOrEmpty(groupID) || string.IsNullOrWhiteSpace(groupID) || groupID.Length <= )
{
throw new ArgumentNullException("用户分组ID不能为空!");
} ThreadPool.QueueUserWorkItem(KafkaAutoCommittedOffsets, new ConsumerSetting() { Broker = broker, Topic = topic, GroupID = groupID, Action = action });
} #endregion #region 两种提交Offsets的版本 /// <summary>
/// Kafka消息队列服务器自动提交offset
/// </summary>
/// <param name="state">消息消费者信息</param>
private void KafkaAutoCommittedOffsets(object state)
{
ConsumerSetting setting = state as ConsumerSetting; var config = new Dictionary<string, object>
{
{ "bootstrap.servers", setting.Broker },
{ "group.id", setting.GroupID },
{ "enable.auto.commit", true }, // this is the default
{ "auto.commit.interval.ms", },
{ "statistics.interval.ms", },
{ "session.timeout.ms", },
{ "auto.offset.reset", "smallest" }
}; using (var consumer = new Consumer<Ignore, string>(config, null, new StringDeserializer(Encoding.UTF8)))
{
if (setting.Action != null)
{
consumer.OnMessage += (_, message) =>
{
ConsumerResult messageResult = new ConsumerResult();
messageResult.Broker = setting.Broker;
messageResult.Topic = message.Topic;
messageResult.Partition = message.Partition;
messageResult.Offset = message.Offset.Value;
messageResult.Message = message.Value; //执行外界自定义的方法
setting.Action(messageResult);
};
} //consumer.OnStatistics += (_, json)=> Console.WriteLine("Statistics: {json}"); //可以写日志
//consumer.OnError += (_, error)=> Console.WriteLine("Error:"+error); //可以写日志
//consumer.OnConsumeError += (_, msg) => Console.WriteLine("Error consuming from topic/partition/offset {msg.Topic}/{msg.Partition}/{msg.Offset}: {msg.Error}"); consumer.Subscribe(setting.Topic); while (!IsCancelled)
{
consumer.Poll(TimeSpan.FromMilliseconds());
}
}
} /// <summary>
/// Kafka消息队列服务器手动提交offset
/// </summary>
/// <param name="state">消息消费者信息</param>
private void KafkaManuallyCommittedOffsets(object state)
{
ConsumerSetting setting = state as ConsumerSetting; var config = new Dictionary<string, object>
{
{ "bootstrap.servers", setting.Broker },
{ "group.id", setting.GroupID },
{ "enable.auto.commit", false },//不是自动提交的
{ "auto.commit.interval.ms", },
{ "statistics.interval.ms", },
{ "session.timeout.ms", },
{ "auto.offset.reset", "smallest" }
}; using (var consumer = new Consumer<Ignore, string>(config, null, new StringDeserializer(Encoding.UTF8)))
{
//可以写日志
//consumer.OnError += (_, error) => Console.WriteLine("Error:"+error); //可以写日志
// Raised on deserialization errors or when a consumed message has an error != NoError.
//consumer.OnConsumeError += (_, error)=> Console.WriteLine("Consume error:"+error); consumer.Subscribe(setting.Topic); Message<Ignore, string> message = null; while (!isCancelled)
{
if (!consumer.Consume(out message, TimeSpan.FromMilliseconds()))
{
continue;
} if (setting.Action != null)
{
ConsumerResult messageResult = new ConsumerResult();
messageResult.Broker = setting.Broker;
messageResult.Topic = message.Topic;
messageResult.Partition = message.Partition;
messageResult.Offset = message.Offset.Value;
messageResult.Message = message.Value; //执行外界自定义的方法
setting.Action(messageResult);
} if (message.Offset % == )
{
var committedOffsets = consumer.CommitAsync(message).Result;
//Console.WriteLine("Committed offset:"+committedOffsets);
}
}
}
} #endregion
}
}

4、新建Producer控制台发送消息

 using System;
using KafKa; namespace ConsoleProducer
{
class Program
{
static void Main(string[] args)
{
while (true)
{
var message = Console.ReadLine();
var producer = new KafkaProducer();
producer.Produce("localhost:9092", "test", , message);
} Console.ReadKey();
}
}
}

5、新建Consumer控制台接收消息

 using System;
using System.Collections.Generic;
using KafKa; namespace ConsoleConsumer
{
class Program
{
static void Main(string[] args)
{
var dts = new List<TimeSpan>(); var consumer = new KafkaConsumer();
consumer.ConsumeAsync("localhost:9092", "test", "", result =>
{
Console.WriteLine(result.Message);
}); Console.ReadKey();
}
}
}

通过以上步骤运行producer控制台,发送消息回车,在consumer控制台就可以接收到消息了。

那么我们如何通过多个consumer来消费消息呢,kafka默认采用的是range分配方法,即平均分配分区。首先注意在创建topic的命令行时创建多个分区(--partitions 5),这里我们创建了5个分区,在发送消息时选择不同的分区发送(0-5),打开5个consumer控制台(注意要同一个分组),我们会发现5个consumer会分别消费对应分区的消息

kafka - Confluent.Kafka的更多相关文章

  1. DataPipeline联合Confluent Kafka Meetup上海站

    Confluent作为国际数据“流”处理技术领先者,提供实时数据处理解决方案,在市场上拥有大量企业客户,帮助企业轻松访问各类数据.DataPipeline作为国内首家原生支持Kafka解决方案的“iP ...

  2. 基于Confluent.Kafka实现的Kafka客户端操作类使用详解

    一.引言 有段时间没有写东西了,当然不是没得写,还有MongoDB的系列没有写完呢,那个系列还要继续.今天正好是周末,有点时间,来写新东西吧.最近公司用了Kafka做为消息的中间件,最开始写的那个版本 ...

  3. Kafka Confluent

    今天我们要讲的大数据公司叫作Confluent,这个公司是前LinkedIn员工出来后联合创办的,而创业的基础是一款叫作Apache Kafka的开源软件. Confluen联合创始人Jun Rao即 ...

  4. c# .net 使用Confluent.Kafka针对kafka进行生产和消费

    首先说明一点,像Confluent.Kafka这种开源的组件,三天两头的更新.在搜索引擎搜索到的结果往往用不了,浪费时间.建议以后遇到类似的情况直接看官网给的Demo. 因为搜索引擎搜到的文章,作者基 ...

  5. python confluent kafka客户端配置kerberos认证

    kafka的认证方式一般有如下3种: 1. SASL/GSSAPI  从版本0.9.0.0开始支持 2. SASL/PLAIN   从版本0.10.0.0开始支持 3. SASL/SCRAM-SHA- ...

  6. Apache Kafka安全| Kafka的需求和组成部分

    1.目标 - 卡夫卡安全 今天,在这个Kafka教程中,我们将看到Apache Kafka Security 的概念  .Kafka Security教程包括我们需要安全性的原因,详细介绍加密.有了这 ...

  7. CentOS 7部署Kafka和Kafka集群

    CentOS 7部署Kafka和Kafka集群 注意事项 需要启动多个shell脚本交互客户端进行验证,运行中的客户端不要停止. 准备工作: 安装java并设置java环境变量,在`/etc/prof ...

  8. Kafka(3)--kafka消息的存储及Partition副本原理

    消息的存储原理: 消息的文件存储机制: 前面我们知道了一个 topic 的多个 partition 在物理磁盘上的保存路径,那么我们再来分析日志的存储方式.通过 [root@localhost ~]# ...

  9. Kafka记录-Kafka简介与单机部署测试

    1.Kafka简介 kafka-分布式发布-订阅消息系统,开发语言-Scala,协议-仿AMQP,不支持事务,支持集群,支持负载均衡,支持zk动态扩容 2.Kafka的架构组件 1.话题(Topic) ...

随机推荐

  1. 微信小程序wx:key以及wx:key=" *this"详解:

    今天写微信小程序无意中看到控制台给出了这样一行提示: 求解百度才知道,给大家分享一下: 1.wx:for定义 官方文档:在组件上使用 wx:for 控制属性绑定一个数组,即可使用数组中各项的数据重复渲 ...

  2. 【es6】es6使用集锦

    一.查找数组中是否包含某个元素  使用includes  ,返回值为布尔值 arr.includes(searchElement, fromIndex): 解析:searchElement   查询元 ...

  3. 2019 WebRtc AudioMixer混音流程

    本文简要说明最新版WebRtc AudioMixer混音流程. 本程序使用4个16KHz 单声道时长均大于10秒的Wav文件作为混音源,只合成前10秒的音频,输出也是16KHz单声道音频. 输入和输出 ...

  4. Unity 宽度适配 NGUI

    这是很久之前写的一篇Note,现在移到Blog上来,可能有些参数,NGUI插件等等不和现在版本相同.不过大概的思路应该不会错. ps: 可能有部分內容是摘抄自其他作者,没办法考证了,如有请务必联系我. ...

  5. 面试想拿20K,HR说你只值10K,这个时候你该怎样回答或者反驳

    正确的做法是: 不动声色,继续跟HR谈下去.把面试流程走完,并且争取拿到这个10K的offer. 这里有一个小技巧: 如果拿到offer,礼貌地跟HR表示感谢,不过不要马上入职.可以找个由头,比方说自 ...

  6. Java学习第一天之简单了解java语言及开发环境的安装

    一.初步了解Java语言 Java语言是由Sun公司的James Gosling创造的一门面向对象的高级语言. 2009年4月20日,Sun公司被Oracle以总价值约为74亿美元的价格收购,Java ...

  7. 《浏览器工作原理与实践》<10>作用域链和闭包 :代码中出现相同的变量,JavaScript引擎是如何选择的?

    在上一篇文章中我们讲到了什么是作用域,以及 ES6 是如何通过变量环境和词法环境来同时支持变量提升和块级作用域,在最后我们也提到了如何通过词法环境和变量环境来查找变量,这其中就涉及到作用域链的概念. ...

  8. 【转】PS1应用-修改linux终端命令行字体颜色

    原文链接:https://www.jianshu.com/p/4239d3ea72fe cd ls -la vim .bashrc 在.bashrc中加入这一行: PS1="\[\e[37; ...

  9. tcpdump和windump

    Tcpdump简介 tcpdump命令是一款sniffer工具,它可以打印所有经过网络接口的数据包的头信息, tcpdump,就是:dump the traffic on a network,根据使用 ...

  10. KubeEdge v0.2发布,全球首个K8S原生的边缘计算平台开放云端代码

    KubeEdge开源背景 KubeEdge在18年11月24日的上海KubeCon上宣布开源,技术圈曾掀起一阵讨论边缘计算的风潮,从此翻开了边缘计算和云计算联动的新篇章. KubeEdge即Kube+ ...