kafka - Confluent.Kafka
上个章节我们讲了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的更多相关文章
- DataPipeline联合Confluent Kafka Meetup上海站
Confluent作为国际数据“流”处理技术领先者,提供实时数据处理解决方案,在市场上拥有大量企业客户,帮助企业轻松访问各类数据.DataPipeline作为国内首家原生支持Kafka解决方案的“iP ...
- 基于Confluent.Kafka实现的Kafka客户端操作类使用详解
一.引言 有段时间没有写东西了,当然不是没得写,还有MongoDB的系列没有写完呢,那个系列还要继续.今天正好是周末,有点时间,来写新东西吧.最近公司用了Kafka做为消息的中间件,最开始写的那个版本 ...
- Kafka Confluent
今天我们要讲的大数据公司叫作Confluent,这个公司是前LinkedIn员工出来后联合创办的,而创业的基础是一款叫作Apache Kafka的开源软件. Confluen联合创始人Jun Rao即 ...
- c# .net 使用Confluent.Kafka针对kafka进行生产和消费
首先说明一点,像Confluent.Kafka这种开源的组件,三天两头的更新.在搜索引擎搜索到的结果往往用不了,浪费时间.建议以后遇到类似的情况直接看官网给的Demo. 因为搜索引擎搜到的文章,作者基 ...
- python confluent kafka客户端配置kerberos认证
kafka的认证方式一般有如下3种: 1. SASL/GSSAPI 从版本0.9.0.0开始支持 2. SASL/PLAIN 从版本0.10.0.0开始支持 3. SASL/SCRAM-SHA- ...
- Apache Kafka安全| Kafka的需求和组成部分
1.目标 - 卡夫卡安全 今天,在这个Kafka教程中,我们将看到Apache Kafka Security 的概念 .Kafka Security教程包括我们需要安全性的原因,详细介绍加密.有了这 ...
- CentOS 7部署Kafka和Kafka集群
CentOS 7部署Kafka和Kafka集群 注意事项 需要启动多个shell脚本交互客户端进行验证,运行中的客户端不要停止. 准备工作: 安装java并设置java环境变量,在`/etc/prof ...
- Kafka(3)--kafka消息的存储及Partition副本原理
消息的存储原理: 消息的文件存储机制: 前面我们知道了一个 topic 的多个 partition 在物理磁盘上的保存路径,那么我们再来分析日志的存储方式.通过 [root@localhost ~]# ...
- Kafka记录-Kafka简介与单机部署测试
1.Kafka简介 kafka-分布式发布-订阅消息系统,开发语言-Scala,协议-仿AMQP,不支持事务,支持集群,支持负载均衡,支持zk动态扩容 2.Kafka的架构组件 1.话题(Topic) ...
随机推荐
- 动手实现CNN卷积神经网络
数据集采用的是手写数据集(http://yann.lecun.com/exdb/mnist/): 本文构建的CNN网络图如下: 像素点:28*28 = 784,55000张手写数字图片. # -*- ...
- 字体图标iconfont
字体图标的作用: 1. 可以做出图片一样可以做的事情, 改变透明度, 旋转度,等... 2. 但是本质其实是文字,可以很随意的改变颜色, 产生阴影,透明效果等 3. 本身体积更小,但携带的信息并没有消 ...
- 2 webpack 4 加vue搭建开发环境最终配置
1 package.json { "name": "c", "version": "1.0.0", "desc ...
- linux内存管理初学
虚拟内存模型 Linux 内核本身并不运行在虚拟空间中,其使用的是物理寻址模式. 物理内存被分割为界面,一个内存页面的大小由PAGE_SIZE宏决定. 虚拟地址空间的方式使程序员可以将巨大的结构用于连 ...
- 好用的redis客户端和Mongo客户端推荐
Another Redis resktop manager electron页面 github地址 MongoCompass 社区版 下载地址
- 查看postgresql的日志show queries log in PostgreSQL?
原文:https://tableplus.io/blog/2018/10/how-to-show-queries-log-in-postgresql.html -------------------- ...
- 1113 form表单与css选择器
目录 1.form表单 form元素 特点 参数 form元素内的控件 1.input的使用 2.select标签 3.textarea元素 4.autofocus属性 2.CSS 1.基础语法 cs ...
- js 正则表达式将 p标签替换 span标签
<!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8&quo ...
- 0010Springboot整合thymeleaf
1.pom.xml中添加thymeleaf的起步依赖 2.编写html文件并放在classpath:/templates/路径下 3.编写controller并返回字符串,会到classpath:/t ...
- 微信PC版Hook研究思维导图