今天有点时间,我就来说两句。最近接触的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实现的可以长链接的消息生产者的更多相关文章

  1. 基于Confluent.Kafka实现的KafkaConsumer消费者类和KafkaProducer消息生产者类型

    一.引言 研究Kafka有一段时间了,略有心得,基于此自己就写了一个Kafka的消费者的类和Kafka消息生产者的类,进行了单元测试和生产环境的测试,还是挺可靠的. 二.源码 话不多说,直接上代码,代 ...

  2. 基于Kafka的实时计算引擎如何选择?Flink or Spark?

    1.前言 目前实时计算的业务场景越来越多,实时计算引擎技术及生态也越来越成熟.以Flink和Spark为首的实时计算引擎,成为实时计算场景的重点考虑对象.那么,今天就来聊一聊基于Kafka的实时计算引 ...

  3. 基于Kafka的实时计算引擎如何选择?(转载)

    1.前言 目前实时计算的业务场景越来越多,实时计算引擎技术及生态也越来越成熟.以Flink和Spark为首的实时计算引擎,成为实时计算场景的重点考虑对象.那么,今天就来聊一聊基于Kafka的实时计算引 ...

  4. 纯Socket(BIO)长链接编程的常见的坑和填坑套路

    本文章纯属个人经验总结,伪代码也是写文章的时候顺便白板编码的,可能有逻辑问题,请帮忙指正,谢谢. Internet(全球互联网)是无数台机器基于TCP/IP协议族相互通信产生的.TCP/IP协议族分了 ...

  5. DataPipeline丨瓜子二手车基于Kafka的结构化数据流

    文 |彭超 瓜子大数据架构师 交流微信 | datapipeline2018 一.为什么选择Kafka   为什么选Kafka?鉴于庞大的数据量,需要将其做成分布式,这时需要将Q里面的数据分到许多机器 ...

  6. 微信公众号开发C#系列-10、长链接转短链接

    1.概述 短网址的好处众多,便于记忆,占用字符少等,现在市面上出现了众多的将长网址转变为短网址的方法,但是由于他们都是小的公司在幕后运营,所以很不靠谱,面对随时关闭服务的可能,这样也导致我们将转换好了 ...

  7. 深入浅出理解基于 Kafka 和 ZooKeeper 的分布式消息队列

    消息队列中间件是分布式系统中重要的组件,主要解决应用耦合,异步消息,流量削锋等问题.实现高性能,高可用,可伸缩和最终一致性架构,是大型分布式系统不可缺少的中间件. 本场 Chat 主要内容: Kafk ...

  8. Knative 实战:基于 Kafka 实现消息推送

    作者 | 元毅 阿里云智能事业群高级开发工程师 导读:当前在 Knative 中已经提供了对 Kafka 事件源的支持,那么如何基于 Kafka 实现消息推送呢?本文作者将以阿里云 Kafka 产品为 ...

  9. 基于 Kafka 的实时数仓在搜索的实践应用

    一.概述 Apache Kafka 发展至今,已经是一个很成熟的消息队列组件了,也是大数据生态圈中不可或缺的一员.Apache Kafka 社区非常的活跃,通过社区成员不断的贡献代码和迭代项目,使得 ...

随机推荐

  1. Android Studio 3.0.1 又见恶心爆的bug。。。xiete

    写了个AIDL的东西,结果一直编译不通过: Error:Execution failed for task ':app:compileDebugAidl'. > java.io.IOExcept ...

  2. 一秒去除Win7快捷方式箭头

    我相信有无数的小盆友跟我一样很讨厌Win7快捷方式图标上的箭头,实在太丑陋了,尤其是带有强迫症滴.现在介绍去除箭头的方式. 1. 打开编辑器,将以下代码粘贴进去,然后保存为.bat后缀的文件,然后双击 ...

  3. 18.异常.md

    目录 1.try...catch 2.异常了的继承机制 2.1基本概念 2.2常用异常 2.3多异常捕获 2.4获取异常信息 2.5finally回收资源 2.6Checked异常和Runtime异常 ...

  4. Javascript中call、apply之我见

    一.call和apply定义. 1.Call 语法:call([thisObj[,arg1[, arg2[, [,.argN]]]]]) 参数 thisObj 可选项.将被用作当前对象的对象. arg ...

  5. 使用Python抓取猫眼近10万条评论并分析

    <一出好戏>讲述人性,使用Python抓取猫眼近10万条评论并分析,一起揭秘“这出好戏”到底如何? 黄渤首次导演的电影<一出好戏>自8月10日在全国上映,至今已有10天,其主演 ...

  6. Github好桑心,慢慢来吧,等待中

    等了大半天还是没办法注册,在线求助...

  7. Kotlin系列之序列(Sequences)源码完全解析

    Kotlin系列之序列(Sequences)源码完全解析 2018年06月05日 22:04:50 mikyou 阅读数:179 标签: Kotlin序列(sequence)源码解析Androidja ...

  8. 学习-HTML5

    @@ 学习HTML5发现对我们开发工作者来说要方便很多,它现在还在发展阶段,在未来肯定会是主流. 我们知道HTML5目的是取代HTML4.01和XHTML1.0标准,他希望能够减少互联网富应用(RIA ...

  9. 【C++】SGI-STL空间配置器

    第一级配置器是对C的内存分配函数malloc,free,realloc的简单封装,用来分配大于128bytes的区块. 第二级配置器管理16个free-lists链表,各自管理8-128bytes的小 ...

  10. 使用Fiddler查看APP的请求接口、接口参数和返回值的方法

    1.下载Fiddler,然后安装成功后. 2.开启代理的设置 3.查看电脑的ip, 4.建立一个wifi局域网,什么360wifi,猎豹wifi,腾讯wifi都可以,用安装手机接入到这个局域网的wif ...