kafka-net
基于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
{
///
/// 消息生产者的接口定义,所有消息生产者的实现必须继承该接口
///
public interface IMessageProducer
{
///
/// 将指定的消息内容发送到消息服务器并存放在指定的主题名称里
///
/// 发送消息的主题名称,这个主题就是对消息的分类,不同的主题存放不同的消息,该参数不能为空,空值会抛出异常
/// 需要发送的消息内容,该参数不能为空,空值会抛出异常
void Produce(string topic, string message);
}
}
复制代码
复制代码
1 using System;
2 using System.Collections.Generic;
3 using System.Linq;
4 using System.Text;
5 using System.Threading.Tasks;
6
7 namespace Enterprise.Framework.MessageQueue
8 {
9 ///
10 /// Kafka消息生产者的接口定义,所有Kafka消息生产者的实现必须继承该接口
11 ///
12 public interface IKafkaMessageProducer : IMessageProducer
13 {
14 ///
15 /// 将指定的消息内容发送到消息服务器并存放在指定的主题名称里
16 ///
17 /// 发送消息的主题名称,这个主题就是对消息的分类,不同的主题存放不同的消息,该参数不能为空,空值会抛出异常
18 /// 需要发送的消息内容,该参数不能为空,空值会抛出异常
19 /// 当消息生产完成并成功发送到服务器后,可以对成功生产并发送的消息执行代理所封装方法的操作,默认值为空
20 void Produce(string topic, string message, Action producedAction = null);
21 }
22 }
复制代码
复制代码
1 using System;
2 using System.Collections.Generic;
3 using System.Linq;
4 using System.Runtime.InteropServices;
5 using System.Text;
6
7 namespace Enterprise.Framework.AbstractInterface
8 {
9 ///
10 /// 该抽象类定义了所有需要释放资源类型的抽象类
11 ///
12 public abstract class DisposableBase : IDisposable
13 {
14 private bool disposed = false;
15
16 ///
17 /// 实现IDisposable中的Dispose方法
18 ///
19 public void Dispose()
20 {
21 //必须为true
22 Dispose(true);
23 //通知垃圾回收机制不再调用终结器(析构器)
24 GC.SuppressFinalize(this);
25 }
26
27 ///
28 /// 不是必要的,提供一个Close方法仅仅是为了更符合其他语言(如C++)的规范
29 ///
30 public void Close()
31 {
32 Dispose();
33 }
34
35 ///
36 /// 必须,以备程序员忘记了显式调用Dispose方法
37 ///
38 ~DisposableBase()
39 {
40 //必须为false
41 Dispose(false);
42 }
43
44 ///
45 /// 非密封类修饰用protected virtual
46 /// 密封类修饰用private
47 ///
48 /// 是否要清理托管资源,true表示需要清理托管资源,false表示不需要清理托管资源
49 protected virtual void Dispose(bool disposing)
50 {
51 if (disposed)
52 {
53 return;
54 }
55 if (disposing)
56 {
57 // 清理托管资源
58 DisposeManagedResources();
59 }
60 // 清理非托管资源
61 DisposeUnmanagedResource();
62 //让类型知道自己已经被释放
63 disposed = true;
64 }
65
66 ///
67 /// 释放托管资源
68 ///
69 protected abstract void DisposeManagedResources();
70
71 ///
72 /// 释放非托管资源
73 ///
74 protected abstract void DisposeUnmanagedResource();
75 }
76 }
复制代码
复制代码
1 using KafkaNet;
2 using KafkaNet.Model;
3 using KafkaNet.Protocol;
4 using System;
5 using System.Collections.Generic;
6 using System.IO;
7 using System.Linq;
8 using System.Text;
9 using System.Threading;
10 using System.Threading.Tasks;
11 using ThreeSoft.Framework.AbstractInterface;
12
13 namespace Enterprise.Framework.MessageQueue
14 {
15 ///
16 /// Kafka消息生产者具体的实现类,可以针对长链接进行消息发送处理,不用频繁进行消息组件的创建和销毁的工作
17 ///
18 public sealed class KafkaMessageKeepAliveProducer : DisposableBase, IDisposable, IKafkaMessageProducer
19 {
20 #region 私有字段
21
22 private KafkaNet.Producer _producer;
23 private BrokerRouter _brokerRouter;
24 private string _broker;
25
26 #endregion
27
28 #region 构造函数
29
30 ///
31 /// 通过构造函数初始化消息队列的服务器
32 ///
33 /// 消息队列服务器地址,该值不能为空
34 public KafkaMessageKeepAliveProducer(string broker)
35 {
36 if (string.IsNullOrEmpty(broker) || string.IsNullOrWhiteSpace(broker))
37 {
38 throw new ArgumentNullException("消息队列服务器的地址不可以为空!");
39 }
40
41 #region kafka-net实现
42
43 Uri[] brokerUriList = null;
44
45 if (broker.IndexOf(',') >= 0)
46 {
47 string[] brokers = broker.Split(',');
48 brokerUriList = new Uri[brokers.Length];
49
50 for (int i = 0; i < brokers.Length; i++)
51 {
52 brokerUriList[i] = new Uri(brokers[i]);
53 }
54 }
55 else
56 {
57 brokerUriList = new Uri[] { new Uri(broker) };
58 }
59
60 var kafkaOptions = new KafkaOptions(brokerUriList);
61 _brokerRouter = new BrokerRouter(kafkaOptions);
62 _producer = new KafkaNet.Producer(_brokerRouter);
63
64 #endregion
65
66 _broker = broker;
67 }
68
69 #endregion
70
71 #region 实例属性
72
73 ///
74 /// 获取消息服务器的地址
75 ///
76 public string Broker
77 {
78 get { return _broker; }
79 }
80
81 #endregion
82
83 #region 发送消息的方法
84
85 ///
86 /// 将指定的消息内容发送到消息服务器并存放在指定的主题名称里
87 ///
88 /// 发送消息的主题名称,这个主题就是对消息的分类,不同的主题存放不同的消息,该参数不能为空,空值会抛出异常
89 /// 需要发送的消息内容,该参数不能为空,空值会抛出异常
90 /// 当消息生产完成并成功发送到服务器后,可以对成功生产并发送的消息执行代理所封装方法的操作
91 public void Produce(string topic, string message, Action producedAction = null)
92 {
93 #region 同步实现
94
95 var currentDatetime = DateTime.Now;
96 var key = currentDatetime.Second.ToString();
97 var events = new[] { new KafkaNet.Protocol.Message(message, key) };
98 List result = _producer.SendMessageAsync(topic, events).Result;
99
100 if (producedAction != null && result != null && result.Count > 0)
101 {
102 MessageResult messageResult = new MessageResult { Broker = Broker, GroupID = null, Message = message, Offset = result[0].Offset, Partition = result[0].PartitionId, Topic = result[0].Topic };
103 producedAction(messageResult);
104 }
105
106 #endregion
107 }
108
109 ///
110 /// 将指定的消息内容发送到消息服务器并存放在指定的主题名称里
111 ///
112 /// 发送消息的主题名称,这个主题就是对消息的分类,不同的主题存放不同的消息,该参数不能为空,空值会抛出异常
113 /// 需要发送的消息内容,该参数不能为空,空值会抛出异常
114 public void Produce(string topic, string message)
115 {
116 Produce(topic, message, null);
117 }
118
119 #endregion
120
121 #region 实现消息队列资源的释放
122
123 ///
124 /// 析构函数释放资源
125 ///
126 ~KafkaMessageKeepAliveProducer()
127 {
128 Dispose(false);
129 }
130
131 ///
132 /// 释放托管资源
133 ///
134 protected override void DisposeManagedResources()
135 {
136 if (_producer != null)
137 {
138 _producer.Dispose();
139 }
140 if (_brokerRouter != null)
141 {
142 _brokerRouter.Dispose();
143 }
144 }
145
146 ///
147 /// 释放非托管资源
148 ///
149 protected override void DisposeUnmanagedResource(){}
150
151 #endregion
152 }
153 }
复制代码
好了,今天就写到这里了,每天进步一点点,努力坚持。不忘初心,继续努力吧,欢迎大家前来讨论。
天下国家,可均也;爵禄,可辞也;白刃,可蹈也;中庸不可能也
分类: 消息队列
kafka-net的更多相关文章
- Spark踩坑记——Spark Streaming+Kafka
[TOC] 前言 在WeTest舆情项目中,需要对每天千万级的游戏评论信息进行词频统计,在生产者一端,我们将数据按照每天的拉取时间存入了Kafka当中,而在消费者一端,我们利用了spark strea ...
- 消息队列 Kafka 的基本知识及 .NET Core 客户端
前言 最新项目中要用到消息队列来做消息的传输,之所以选着 Kafka 是因为要配合其他 java 项目中,所以就对 Kafka 了解了一下,也算是做个笔记吧. 本篇不谈论 Kafka 和其他的一些消息 ...
- kafka学习笔记:知识点整理
一.为什么需要消息系统 1.解耦: 允许你独立的扩展或修改两边的处理过程,只要确保它们遵守同样的接口约束. 2.冗余: 消息队列把数据进行持久化直到它们已经被完全处理,通过这一方式规避了数据丢失风险. ...
- .net windows Kafka 安装与使用入门(入门笔记)
完整解决方案请参考: Setting Up and Running Apache Kafka on Windows OS 在环境搭建过程中遇到两个问题,在这里先列出来,以方便查询: 1. \Jav ...
- kafka配置与使用实例
kafka作为消息队列,在与netty.多线程配合使用时,可以达到高效的消息队列
- kafka源码分析之一server启动分析
0. 关键概念 关键概念 Concepts Function Topic 用于划分Message的逻辑概念,一个Topic可以分布在多个Broker上. Partition 是Kafka中横向扩展和一 ...
- Kafka副本管理—— 为何去掉replica.lag.max.messages参数
今天查看Kafka 0.10.0的官方文档,发现了这样一句话:Configuration parameter replica.lag.max.messages was removed. Partiti ...
- Kafka:主要参数详解(转)
原文地址:http://kafka.apache.org/documentation.html ############################# System ############### ...
- kafka
2016-11-13 20:48:43 简单说明什么是kafka? Apache kafka是消息中间件的一种,我发现很多人不知道消息中间件是什么,在开始学习之前,我这边就先简单的解释一下什么是消息 ...
- Spark Streaming+Kafka
Spark Streaming+Kafka 前言 在WeTest舆情项目中,需要对每天千万级的游戏评论信息进行词频统计,在生产者一端,我们将数据按照每天的拉取时间存入了Kafka当中,而在消费者一端, ...
随机推荐
- 【CSS】position relative 用法
Relative是position的一个属性,是相对定位. position的默认值是static,(也就是说对于任意一个元素,如果没有定义它的position属性,那么它的position:stat ...
- HDU 2222 最简单的AC自动机套模板应用
HDU 2222 题意:给出N(N<=10,000)个单词,每个单词长度不超过50.再给出一个字符串S,字符串长度不超过1,000,000.问有多少个单词出现在了字符串S中.(单词可能重复,单词 ...
- hdu4514(非连通图的环判断与图中最长链)(树的直径)
湫湫系列故事——设计风景线 随着杭州西湖的知名度的进一步提升,园林规划专家湫湫希望设计出一条新的经典观光线路,根据老板马小腾的指示,新的风景线最好能建成环形,如果没有条件建成环形,那就建的越长越好. ...
- 【ZJOI2017 Round1练习】D2T1 river(二分图)
题意: 思路:这道题并没有官方题解 没有羊驼在所有三元组中出现就是NO 现在考虑不少于1只的情况 删去其中一只,我们得到了两组点和一些边 我们只要判断这是否为一张二分图,使用暴力染色的方法就有60分了 ...
- 1597: [Usaco2008 Mar]土地购买 [ dp+斜率优化 ] 未完
传送门 1597: [Usaco2008 Mar]土地购买 Time Limit: 10 Sec Memory Limit: 162 MBSubmit: 1979 Solved: 705[Subm ...
- 关于HTML文件、JS文件、CSS文件
把JS和CSS脚本写在html里和写在独立文件里有什么区别? 1. 都写在html里是性能最优的方案. 2. 都写在html里是可维护性最差的方案. 3. 分开写在js.css.html是可维护性最有 ...
- 洛谷——P1951 收费站_NOI导刊2009提高(2)
https://www.luogu.org/problem/show?pid=1951 题目描述 在某个遥远的国家里,有n个城市.编号为1,2,3,…,n. 这个国家的政府修建了m条双向的公路.每条公 ...
- json三种类型小笔记
JSON基本语法与图例 Object(对象类型)用{ }包含一系列无序的key–Value键值对表示,其中Key和Value之间用冒号分割,每个key-value之间用逗号分割. Array(数组类型 ...
- JAVA 比较两张图片的相似度的代码
原文:http://www.open-open.com/code/view/1448334323079 import java.awt.image.BufferedImage; import java ...
- ios开发 MPMoviePlayerController 视频播放器
项目中用到视频播放功能, 写点视频基础部分 MPMoviePlayerController是通过MediaPlayer.frame引入的,可用于播放在iOS支持的所有格式的视频,用起来很简单!!! M ...