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) ...
随机推荐
- Windows群集之NLB【转】
本文转自:http://www.talkwithtrend.com/Article/31746 网络负载平衡群集(Network Load balancing) 在Internet快速发展的今天,为了 ...
- 前端:table、thead、th、tr、td
table:表格:thead:表头:tr:行:td:单元格:th:一行的首个单元格 tbody包含行的内容下载完优先显示,不必等待表格结束.另外,还需要注意一个地方.表格行本来是从上向下显示的.但是, ...
- PHP程序功能设计
以留言板为例. 数据表设计 分析数据表结构:有哪些信息需要存储:留言信息:ID,留言标题,留言内容,留言时间,留言人 CREATE TABLE message( id INT UNSIGNED NOT ...
- ffmpeg 命令的使用
当然先安装了 gentoo 下一条命令搞定 emerge ffmpeg 格式转换 (将file.avi 转换成output.flv) ffmpeg -i file.avi output.flv ...
- 【ASE模型组】Hint::neural 模型与case study
模型 基于搜索的提示系统 我们的系统用Pycee针对语法错误给出提示.然而,对于语法正确.结果错误的代码,我们需要另外的解决方式.因此,我们维护一些 (错误代码, 相应提示) 的数据,该数据可以由我们 ...
- ASE —— 第二次结对作业
目录 重现基线模型 基线模型原理 模型的优缺点 模型重现结果 提出改进 改进动机 新模型框架 评价合作伙伴 重现基线模型 基线模型原理 我们选用的的模型为DeepCS,接下来我将解释一下它的原理. 我 ...
- Spark学习笔记1——第一个Spark程序:单词数统计
Spark学习笔记1--第一个Spark程序:单词数统计 笔记摘抄自 [美] Holden Karau 等著的<Spark快速大数据分析> 添加依赖 通过 Maven 添加 Spark-c ...
- spider _其他库的简单操作与方法
PHP : 网络IO java : 代码笨重,代码量很大 C/C++ :虽然效率高,但是代码成型很慢 1 通用网络爬虫(搜索引擎引用,需要遵守robots协议) 1 搜索引擎如何获取一个新网站的 UR ...
- sqlserver 拼接字符串分割
CREATE FUNCTION [dbo].[fnQuerySplit] ( @string VARCHAR(MAX) ,--待分割字符串 )--分割符 ) ) ) AS BEGIN DECLARE ...
- 剑指Offer的学习笔记(C#篇)-- 左旋转字符串
题目描述 汇编语言中有一种移位指令叫做循环左移(ROL),现在有个简单的任务,就是用字符串模拟这个指令的运算结果.对于一个给定的字符序列S,请你把其循环左移K位后的序列输出.例如,字符序列S=”abc ...