今天有点时间,我就来说两句。最近接触的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. 最邻近算法(KNN)识别数字验证码

    应用场景   对于简单的数字型验证码的自动识别.前期已经完成的工作是通过切割将验证码图片切割成一个一个的单个数字的图片,并按照对应的数字表征类别进行分类(即哪些图片表示数字7,哪些表示8),将各种数字 ...

  2. struts2与spring整合时需要注意的点

    首先我们需要明白spring整合struts2中的什么东西,spring中的核心就是IOC和AOP,IOC是对象的容器,AOP是处理动态代理的;比如spring与hibernate整合时就要用到aop ...

  3. 吴裕雄 31-MySQL 导出数据

    MySQL中你可以使用SELECT...INTO OUTFILE语句来简单的导出数据到文本文件上. show global variables like '%secure%';SHOW VARIABL ...

  4. kotlin string

    Kotlin String split 操作实践   内容 此文章展示kotlin中对String字符串的split操作,如果你有遇到这方面的需求,希望对你有用. 1. split + 正则 先看下系 ...

  5. 安装 Java Cryptography Extension (JCE) Unlimited Strength

    http://www.oracle.com/technetwork/java/javase/downloads/jce8-download-2133166.html cp ./* /usr/java/ ...

  6. Excel批量修改文件

    [1]把下图片放在一个文件目录下面,如E:\SVM_Class\airplanes [2]点击“开始”→“运行”(或按快捷键win+R),在弹出框中输入“cmd”,进入dos操作界面.   [3]do ...

  7. Building Projects with Native Code

    [Building Projects with Native Code] 1.安装Node(v4.0以上).Python2.JDK(v8.0以上). 添加 JAVA_HOME环境变量,指向 JDK 的 ...

  8. python 文件操作: 文件操作的函数, 模式及常用操作.

    1.文件操作的函数: open("文件名(路径)", mode = '模式', encoding = "字符集") 2.模式: r , w , a , r+ , ...

  9. 把图片上的文字转换成word文字?

    转换后的文字不是很如意,但是免费方便. 1.打开Office办公软件自带的OneNote工具.随便新建一个笔记页面,以方便我们接下来的操作. 2.插入图片.在菜单栏里点击[插入],选择插入[图片],找 ...

  10. Centos 7 下 Corosync + Pacemaker + psc + HA-proxy 实现业务高可用

    一.介绍: 1.本博客Corosync + Pacemaker + psc + HA-proxy 实现业务高可用,以httpd 服务实现高可用为例. 2.架构思路 a.三台web 节点,功能:全部安装 ...