基于kafka-net实现的可以长链接的消息生产者
今天有点时间,我就来说两句。最近接触的Kafka相关的东西要多一些,其实以前也接触过,但是在项目使用中的经验不是很多。最近公司的项目里面使用了Kafka消息中间件,由于以前的人员编写的客户端的类不是很好,没有设计的概念,就是一个简单类的功能罗列,没有考虑到后期的扩展和维护(以后可能会兼容其他形式的消息队列,要做到无缝衔接),所以这个重构的任务就落到我的身上。
先说说我的感受,然后再贴出代码的实现吧。我第一次是基于Confluent.Kafka编写的Kafka消息生产者,后来经过测试,同步操作的时间比较长,要完成20万数据发送消息并更新到数据库的时间大概是16-18分钟,这个结果有点让人不能接受。为了提高性能,也做了很多测试,都没有办法解决这个问题。后来抱着试试看的想法,我又基于kafka-net重新实现了Kafka消息的生产者。经过测试,完成同样的任务,时间大概需要3分钟左右。两种实现方法完成同样的任务,都是以同步的方式生产消息,并将消息成功发送到Broker后,再将数据插入到数据库做记录。大家不要纠结为什么这样使用消息队列,这是上头的做法,我还不能做大的改动,我也无奈。
目前看,基于kafka-net实现的消息生产者在生产消息并发送成功所需要的时间要比基于Confluent.Kafka实现的消息生产者的所需要的时间要少,尤其是发送的数据越多,这个时间的差距越大。具体的原因还不清楚,如果有高手可以不吝赐教。好了,我该上代码了。
开始代码之前,要说明一点:Confluent.Kafka的Broker是不需要带Http://这个前缀的,但是 kafka-net 的Broker是有http://这个前缀的,大家要注意这个,刚开始的时候我也被坑了一下子。
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks; namespace Enterprise.Framework.MessageQueue
{
/// <summary>
/// 消息生产者的接口定义,所有消息生产者的实现必须继承该接口
/// </summary>
public interface IMessageProducer
{
/// <summary>
/// 将指定的消息内容发送到消息服务器并存放在指定的主题名称里
/// </summary>
/// <param name="topic">发送消息的主题名称,这个主题就是对消息的分类,不同的主题存放不同的消息,该参数不能为空,空值会抛出异常</param>
/// <param name="message">需要发送的消息内容,该参数不能为空,空值会抛出异常</param>
void Produce(string topic, string message);
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks; namespace Enterprise.Framework.MessageQueue
{
/// <summary>
/// Kafka消息生产者的接口定义,所有Kafka消息生产者的实现必须继承该接口
/// </summary>
public interface IKafkaMessageProducer : IMessageProducer
{
/// <summary>
/// 将指定的消息内容发送到消息服务器并存放在指定的主题名称里
/// </summary>
/// <param name="topic">发送消息的主题名称,这个主题就是对消息的分类,不同的主题存放不同的消息,该参数不能为空,空值会抛出异常</param>
/// <param name="message">需要发送的消息内容,该参数不能为空,空值会抛出异常</param>
/// <param name="producedAction">当消息生产完成并成功发送到服务器后,可以对成功生产并发送的消息执行代理所封装方法的操作,默认值为空</param>
void Produce(string topic, string message, Action<MessageResult> producedAction = null);
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text; namespace Enterprise.Framework.AbstractInterface
{
/// <summary>
/// 该抽象类定义了所有需要释放资源类型的抽象类
/// </summary>
public abstract class DisposableBase : IDisposable
{
private bool disposed = false; /// <summary>
/// 实现IDisposable中的Dispose方法
/// </summary>
public void Dispose()
{
//必须为true
Dispose(true);
//通知垃圾回收机制不再调用终结器(析构器)
GC.SuppressFinalize(this);
} /// <summary>
/// 不是必要的,提供一个Close方法仅仅是为了更符合其他语言(如C++)的规范
/// </summary>
public void Close()
{
Dispose();
} /// <summary>
/// 必须,以备程序员忘记了显式调用Dispose方法
/// </summary>
~DisposableBase()
{
//必须为false
Dispose(false);
} /// <summary>
/// 非密封类修饰用protected virtual
/// 密封类修饰用private
/// </summary>
/// <param name="disposing">是否要清理托管资源,true表示需要清理托管资源,false表示不需要清理托管资源</param>
protected virtual void Dispose(bool disposing)
{
if (disposed)
{
return;
}
if (disposing)
{
// 清理托管资源
DisposeManagedResources();
}
// 清理非托管资源
DisposeUnmanagedResource();
//让类型知道自己已经被释放
disposed = true;
} /// <summary>
/// 释放托管资源
/// </summary>
protected abstract void DisposeManagedResources(); /// <summary>
/// 释放非托管资源
/// </summary>
protected abstract void DisposeUnmanagedResource();
}
}
using KafkaNet;
using KafkaNet.Model;
using KafkaNet.Protocol;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using ThreeSoft.Framework.AbstractInterface; namespace Enterprise.Framework.MessageQueue
{
/// <summary>
/// Kafka消息生产者具体的实现类,可以针对长链接进行消息发送处理,不用频繁进行消息组件的创建和销毁的工作
/// </summary>
public sealed class KafkaMessageKeepAliveProducer : DisposableBase, IDisposable, IKafkaMessageProducer
{
#region 私有字段 private KafkaNet.Producer _producer;
private BrokerRouter _brokerRouter;
private string _broker; #endregion #region 构造函数 /// <summary>
/// 通过构造函数初始化消息队列的服务器
/// </summary>
/// <param name="broker">消息队列服务器地址,该值不能为空</param>
public KafkaMessageKeepAliveProducer(string broker)
{
if (string.IsNullOrEmpty(broker) || string.IsNullOrWhiteSpace(broker))
{
throw new ArgumentNullException("消息队列服务器的地址不可以为空!");
} #region kafka-net实现 Uri[] brokerUriList = null; if (broker.IndexOf(',') >= )
{
string[] brokers = broker.Split(',');
brokerUriList = new Uri[brokers.Length]; for (int i = ; i < brokers.Length; i++)
{
brokerUriList[i] = new Uri(brokers[i]);
}
}
else
{
brokerUriList = new Uri[] { new Uri(broker) };
} var kafkaOptions = new KafkaOptions(brokerUriList);
_brokerRouter = new BrokerRouter(kafkaOptions);
_producer = new KafkaNet.Producer(_brokerRouter); #endregion _broker = broker;
} #endregion #region 实例属性 /// <summary>
/// 获取消息服务器的地址
/// </summary>
public string Broker
{
get { return _broker; }
} #endregion #region 发送消息的方法 /// <summary>
/// 将指定的消息内容发送到消息服务器并存放在指定的主题名称里
/// </summary>
/// <param name="topic">发送消息的主题名称,这个主题就是对消息的分类,不同的主题存放不同的消息,该参数不能为空,空值会抛出异常</param>
/// <param name="message">需要发送的消息内容,该参数不能为空,空值会抛出异常</param>
/// <param name="producedAction">当消息生产完成并成功发送到服务器后,可以对成功生产并发送的消息执行代理所封装方法的操作</param>
public void Produce(string topic, string message, Action<MessageResult> producedAction = null)
{
#region 同步实现 var currentDatetime = DateTime.Now;
var key = currentDatetime.Second.ToString();
var events = new[] { new KafkaNet.Protocol.Message(message, key) };
List<ProduceResponse> result = _producer.SendMessageAsync(topic, events).Result; if (producedAction != null && result != null && result.Count > )
{
MessageResult messageResult = new MessageResult { Broker = Broker, GroupID = null, Message = message, Offset = result[].Offset, Partition = result[].PartitionId, Topic = result[].Topic };
producedAction(messageResult);
} #endregion
} /// <summary>
/// 将指定的消息内容发送到消息服务器并存放在指定的主题名称里
/// </summary>
/// <param name="topic">发送消息的主题名称,这个主题就是对消息的分类,不同的主题存放不同的消息,该参数不能为空,空值会抛出异常</param>
/// <param name="message">需要发送的消息内容,该参数不能为空,空值会抛出异常</param>
public void Produce(string topic, string message)
{
Produce(topic, message, null);
} #endregion #region 实现消息队列资源的释放 /// <summary>
/// 析构函数释放资源
/// </summary>
~KafkaMessageKeepAliveProducer()
{
Dispose(false);
} /// <summary>
/// 释放托管资源
/// </summary>
protected override void DisposeManagedResources()
{
if (_producer != null)
{
_producer.Dispose();
}
if (_brokerRouter != null)
{
_brokerRouter.Dispose();
}
} /// <summary>
/// 释放非托管资源
/// </summary>
protected override void DisposeUnmanagedResource(){} #endregion
}
}
好了,今天就写到这里了,每天进步一点点,努力坚持。不忘初心,继续努力吧,欢迎大家前来讨论。
基于kafka-net实现的可以长链接的消息生产者的更多相关文章
- 基于Confluent.Kafka实现的KafkaConsumer消费者类和KafkaProducer消息生产者类型
一.引言 研究Kafka有一段时间了,略有心得,基于此自己就写了一个Kafka的消费者的类和Kafka消息生产者的类,进行了单元测试和生产环境的测试,还是挺可靠的. 二.源码 话不多说,直接上代码,代 ...
- 基于Kafka的实时计算引擎如何选择?Flink or Spark?
1.前言 目前实时计算的业务场景越来越多,实时计算引擎技术及生态也越来越成熟.以Flink和Spark为首的实时计算引擎,成为实时计算场景的重点考虑对象.那么,今天就来聊一聊基于Kafka的实时计算引 ...
- 基于Kafka的实时计算引擎如何选择?(转载)
1.前言 目前实时计算的业务场景越来越多,实时计算引擎技术及生态也越来越成熟.以Flink和Spark为首的实时计算引擎,成为实时计算场景的重点考虑对象.那么,今天就来聊一聊基于Kafka的实时计算引 ...
- 纯Socket(BIO)长链接编程的常见的坑和填坑套路
本文章纯属个人经验总结,伪代码也是写文章的时候顺便白板编码的,可能有逻辑问题,请帮忙指正,谢谢. Internet(全球互联网)是无数台机器基于TCP/IP协议族相互通信产生的.TCP/IP协议族分了 ...
- DataPipeline丨瓜子二手车基于Kafka的结构化数据流
文 |彭超 瓜子大数据架构师 交流微信 | datapipeline2018 一.为什么选择Kafka 为什么选Kafka?鉴于庞大的数据量,需要将其做成分布式,这时需要将Q里面的数据分到许多机器 ...
- 微信公众号开发C#系列-10、长链接转短链接
1.概述 短网址的好处众多,便于记忆,占用字符少等,现在市面上出现了众多的将长网址转变为短网址的方法,但是由于他们都是小的公司在幕后运营,所以很不靠谱,面对随时关闭服务的可能,这样也导致我们将转换好了 ...
- 深入浅出理解基于 Kafka 和 ZooKeeper 的分布式消息队列
消息队列中间件是分布式系统中重要的组件,主要解决应用耦合,异步消息,流量削锋等问题.实现高性能,高可用,可伸缩和最终一致性架构,是大型分布式系统不可缺少的中间件. 本场 Chat 主要内容: Kafk ...
- Knative 实战:基于 Kafka 实现消息推送
作者 | 元毅 阿里云智能事业群高级开发工程师 导读:当前在 Knative 中已经提供了对 Kafka 事件源的支持,那么如何基于 Kafka 实现消息推送呢?本文作者将以阿里云 Kafka 产品为 ...
- 基于 Kafka 的实时数仓在搜索的实践应用
一.概述 Apache Kafka 发展至今,已经是一个很成熟的消息队列组件了,也是大数据生态圈中不可或缺的一员.Apache Kafka 社区非常的活跃,通过社区成员不断的贡献代码和迭代项目,使得 ...
随机推荐
- [C语言]使用函数
------------------------------------------------------------------------------------------ //函数原型声明: ...
- 第一个Python脚本!
# hello.py print 'Hello Pythons'
- Structs复习 简单数据校验
jar包 web.XML <?xml version="1.0" encoding="UTF-8"?> <web-app version=&q ...
- 吴裕雄 27-MySQL 元数据
你可能想知道MySQL以下三种信息:查询结果信息: SELECT, UPDATE 或 DELETE语句影响的记录数.数据库和数据表的信息: 包含了数据库及数据表的结构信息.MySQL服务器信息: 包含 ...
- ADO.Net 综合练习题
题目: 第一部分: 新建一个数据库:ADO测试,包含下面两个数据表,使用代码创建,并保留创建的代码文本. 专业表Subject: 专业编号(SubjectCode):nvarchar类型,不能为空,主 ...
- XML 可扩展标记语言
因 为XML实在是太重要了,而且被广泛应用!不论是数据存储,还是其他方面,如配置文件等.XML是一种对独立于任何编程语言的数据进行编码的机制.在数据 交换领域,正在变得非常流行!因为他的基于节点的存储 ...
- kotlin函数api
原 Kotlin学习(4)Lambda 2017年09月26日 21:00:03 gwt0425 阅读数:551 记住Lambda的本质,还是一个对象.和JS,Python等不同的是,Kotlin ...
- pip 离线安装
pip download ansible -d . --trusted-host mirrors.aliyun.com pip install ansible-2.7.5.tar.gz --user ...
- vue router 跳转到新的窗口方法
在CreateSendView2.vue 组件中的方法定义点击事件,vue router 跳转新的窗口通过采用如下的方法可以实现传递参数跳转相应的页面goEditor: function (index ...
- apt-get出现的问题
报的错 E: 无法获得锁 /var/cache/apt/archives/lock – open (11 资源临时不可用) E: 无法锁定下载目录 解决方法一: #:ps -aux (列出进程,形式如 ...