基于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 社区非常的活跃,通过社区成员不断的贡献代码和迭代项目,使得 ...
随机推荐
- 检测浏览器是否支持cookie功能
<script> if(navigator.cookieEnabled) { document.write("你的浏览器支持cookie功能!"); } else{ d ...
- android Button、TabLayout英文自动改小写为大写的问题
如果是Button自动大写问题,直接设置Button的 textAllCaps="false" 即可: 如果是TabLayout出现全大写问题,先在style.xml加入属性: & ...
- Hibernate Validator实践之一 入门篇
在后台的业务逻辑中,对数据值的校验在各层都存在(展示层,业务层,数据访问层等),并且各层校验的规则又不尽相同,如下图所示 注:该图片来自于Hibernate Validator官网 在各层中重复的校验 ...
- C++ 将数据转为字符串的几种方法
收集一下: 1\将int 转为 LPCTSTR 其实LPCTSTR可以直接使用CString直接代替,无需类型强制转换 CString str; ; //str="15" str. ...
- 解决ngui挡住粒子的问题
using UnityEngine; using System.Collections; [ExecuteInEditMode] public class ControlParticle : Mono ...
- iOS修改状态栏颜色
application.statusBarStyle = .LightContent // 在APPlication中设置全局状态栏颜色,为白色 application.statusBarHidden ...
- 学JS的心路历程-JS支持面向对象?(一)
昨天在看Prototype看到JS支持面向对象,被前辈问到说那什么是面向对象?JS是面向对象语言吗? 便开始了一连串艰辛爬文过程,今天就来看一下两者有什么差异吧(rgwyjc)! 首先面向对象有三大特 ...
- 数组的es6新方法
1.数组去重 var changeReArr=(arr)=>{ return Array.from(new Set([1,2,2,3,5,4,5]))//利用set将[1,2,2,3,5,4, ...
- ATM--代码
//信1705-2 张小军 20173662 import java.io.*; import java.util.ArrayList; import java.util.Scanner;public ...
- python计算两个数的百分比
a和b是整数,计算a/b的百分比 a=3 b=7 a=float(a) b=float(b) 保留百分比后2位小数 print "%.2f%%" % (a/b*100) '42. ...