c#开源消息队列中间件EQueue 教程
一、简介
EQueue是一个参照RocketMQ实现的开源消息队列中间件,兼容Mono,具体可以参看作者的文章《分享一个c#写的开源分布式消息队列equeue》。项目开源地址:https://github.com/tangxuehua/equeue,项目中包含了队列的全部源代码以及如何使用的示例。
二、安装EQueue
Producer、Consumer、Broker支持分布式部署,安装EQueue需要.NET 4, Visual Studio 2010/2012/2013. 目前EQueue是个类库,需要自己实现Broker的宿主,可以参照QuickStart,创建一个QuickStart.BrokerServer项目,通过Visual Studio的Nuget 查找equeue
using System;
using System.Text;
using ECommon.Autofac;
using ECommon.Configurations;
using ECommon.JsonNet;
using ECommon.Log4Net;
using EQueue.Broker;
using EQueue.Configurations;
using EQueue.Protocols; namespace QuickStart.BrokerServer
{
class Program
{
static void Main(string[] args)
{
InitializeEQueue();
var setting = new BrokerSetting();
setting.NotifyWhenMessageArrived = false;
setting.DeleteMessageInterval = 1000;
new BrokerController(setting).Initialize().Start();
Console.ReadLine();
} static void InitializeEQueue()
{
Configuration
.Create()
.UseAutofac()
.RegisterCommonComponents()
.UseLog4Net()
.UseJsonNet()
.RegisterEQueueComponents();
}
}
}
InitializeEQueue方法初始化EQueue的环境,使用了Autofac作为IOC容器,使用log4Net记录日志, 我们看一下RegisterEQueueComponents方法:
public static class ConfigurationExtensions
{
public static Configuration RegisterEQueueComponents(this Configuration configuration)
{
configuration.SetDefault<IAllocateMessageQueueStrategy, AverageAllocateMessageQueueStrategy>();
configuration.SetDefault<IQueueSelector, QueueHashSelector>();
configuration.SetDefault<ILocalOffsetStore, DefaultLocalOffsetStore>();
configuration.SetDefault<IMessageStore, InMemoryMessageStore>();
configuration.SetDefault<IMessageService, MessageService>();
configuration.SetDefault<IOffsetManager, InMemoryOffsetManager>();
return configuration;
}
}
代码中涉及到6个组件:
- IAllocateMessageQueueStrategy
- IQueueSelector
- ILocalOffsetStore
- IMessageStore
- IMessageService
- IOffsetManager
DeleteMessageInterval 这个属性是用来设置equeue的定时删除间隔,单位为毫秒,默认值是一个小时。另外还有ProducerSocketSetting 和 ConsumerSocketSetting 分别用于设置Producer连接Broker和Consumer连接Broker的IP和端口,默认端口是5000和5001。
public class BrokerSetting
{
public SocketSetting ProducerSocketSetting { get; set; }
public SocketSetting ConsumerSocketSetting { get; set; }
public bool NotifyWhenMessageArrived { get; set; }
public int DeleteMessageInterval { get; set; } public BrokerSetting()
{
ProducerSocketSetting = new SocketSetting { Address = SocketUtils.GetLocalIPV4().ToString(), Port = 5000, Backlog = 5000 };
ConsumerSocketSetting = new SocketSetting { Address = SocketUtils.GetLocalIPV4().ToString(), Port = 5001, Backlog = 5000 };
NotifyWhenMessageArrived = true;
DeleteMessageInterval = 1000 * 60 * 60;
}
}
运行项目,如果显示下面类似内容,说明Broker启动成功:
2014-03-23 20:10:30,255 INFO BrokerController - Broker started, producer:[169.254.80.80:5000], consumer:[169.254.80.80:5001]
三、在Visual Studio中开发测试
1.创建一个VS项目 QuickStart.ProducerClient,通过Nuget引用EQueue,编写下面Producer代码
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using ECommon.Autofac;
using ECommon.Configurations;
using ECommon.IoC;
using ECommon.JsonNet;
using ECommon.Log4Net;
using ECommon.Scheduling;
using EQueue.Clients.Producers;
using EQueue.Configurations;
using EQueue.Protocols; namespace QuickStart.ProducerClient
{
class Program
{
static void Main(string[] args)
{
InitializeEQueue(); var scheduleService = ObjectContainer.Resolve<IScheduleService>();
var producer = new Producer().Start();
var total = 1000;
var parallelCount = 10;
var finished = 0;
var messageIndex = 0;
var watch = Stopwatch.StartNew(); var action = new Action(() =>
{
for (var index = 1; index <= total; index++)
{
var message = "message" + Interlocked.Increment(ref messageIndex);
producer.SendAsync(new Message("SampleTopic", Encoding.UTF8.GetBytes(message)), index.ToString()).ContinueWith(sendTask =>
{
var finishedCount = Interlocked.Increment(ref finished);
if (finishedCount % 1000 == 0)
{
Console.WriteLine(string.Format("Sent {0} messages, time spent:{1}", finishedCount, watch.ElapsedMilliseconds));
}
});
}
}); var actions = new List<Action>();
for (var index = 0; index < parallelCount; index++)
{
actions.Add(action);
} Parallel.Invoke(actions.ToArray()); Console.ReadLine();
} static void InitializeEQueue()
{
Configuration
.Create()
.UseAutofac()
.RegisterCommonComponents()
.UseLog4Net()
.UseJsonNet()
.RegisterEQueueComponents();
}
}
}
Producer对象在使用之前必须要调用Start初始化,初始化一次即可, 注意:切记不可以在每次发送消息时,都调用Start方法。Producer 默认连接本机的5000端口,可以通过ProducerSetting 进行设置,可以参看下面的代码:
public class ProducerSetting
{
public string BrokerAddress { get; set; }
public int BrokerPort { get; set; }
public int SendMessageTimeoutMilliseconds { get; set; }
public int UpdateTopicQueueCountInterval { get; set; } public ProducerSetting()
{
BrokerAddress = SocketUtils.GetLocalIPV4().ToString();
BrokerPort = 5000;
SendMessageTimeoutMilliseconds = 1000 * 10;
UpdateTopicQueueCountInterval = 1000 * 5;
}
2、创建一个VS项目 QuickStart.ConsumerClient,通过Nuget引用EQueue,编写下面Consumer代码
using System;
using System.Linq;
using System.Text;
using System.Threading;
using ECommon.Autofac;
using ECommon.Configurations;
using ECommon.IoC;
using ECommon.JsonNet;
using ECommon.Log4Net;
using ECommon.Scheduling;
using EQueue.Broker;
using EQueue.Clients.Consumers;
using EQueue.Configurations;
using EQueue.Protocols; namespace QuickStart.ConsumerClient
{
class Program
{
static void Main(string[] args)
{
InitializeEQueue(); var messageHandler = new MessageHandler();
var consumer1 = new Consumer("Consumer1", "group1").Subscribe("SampleTopic").Start(messageHandler);
var consumer2 = new Consumer("Consumer2", "group1").Subscribe("SampleTopic").Start(messageHandler);
var consumer3 = new Consumer("Consumer3", "group1").Subscribe("SampleTopic").Start(messageHandler);
var consumer4 = new Consumer("Consumer4", "group1").Subscribe("SampleTopic").Start(messageHandler); Console.WriteLine("Start consumer load balance, please wait for a moment.");
var scheduleService = ObjectContainer.Resolve<IScheduleService>();
var waitHandle = new ManualResetEvent(false);
var taskId = scheduleService.ScheduleTask(() =>
{
var c1AllocatedQueueIds = consumer1.GetCurrentQueues().Select(x => x.QueueId);
var c2AllocatedQueueIds = consumer2.GetCurrentQueues().Select(x => x.QueueId);
var c3AllocatedQueueIds = consumer3.GetCurrentQueues().Select(x => x.QueueId);
var c4AllocatedQueueIds = consumer4.GetCurrentQueues().Select(x => x.QueueId);
if (c1AllocatedQueueIds.Count() == 1 && c2AllocatedQueueIds.Count() == 1 && c3AllocatedQueueIds.Count() == 1 && c4AllocatedQueueIds.Count() == 1)
{
Console.WriteLine(string.Format("Consumer load balance finished. Queue allocation result: c1:{0}, c2:{1}, c3:{2}, c4:{3}",
string.Join(",", c1AllocatedQueueIds),
string.Join(",", c2AllocatedQueueIds),
string.Join(",", c3AllocatedQueueIds),
string.Join(",", c4AllocatedQueueIds)));
waitHandle.Set();
}
}, 1000, 1000); waitHandle.WaitOne();
scheduleService.ShutdownTask(taskId); Console.ReadLine();
} static void InitializeEQueue()
{
Configuration
.Create()
.UseAutofac()
.RegisterCommonComponents()
.UseLog4Net()
.UseJsonNet()
.RegisterEQueueComponents();
}
} class MessageHandler : IMessageHandler
{
private int _handledCount; public void Handle(QueueMessage message, IMessageContext context)
{
var count = Interlocked.Increment(ref _handledCount);
if (count % 1000 == 0)
{
Console.WriteLine("Total handled {0} messages.", count);
}
context.OnMessageHandled(message);
}
}
}
使用方式给用户感觉是消息从EQueue服务器推到了应用客户端。
但是实际Consumer内部是使用长轮询Pull方式从EQueue服务器拉消息,然后再回调用户Listener方法。Consumer默认连接本机的5001端口,可以通过ConsumerSetting 进行设置,可以参看下面的代码:
public class ConsumerSetting
{
public string BrokerAddress { get; set; }
public int BrokerPort { get; set; }
public int RebalanceInterval { get; set; }
public int UpdateTopicQueueCountInterval { get; set; }
public int HeartbeatBrokerInterval { get; set; }
public int PersistConsumerOffsetInterval { get; set; }
public PullRequestSetting PullRequestSetting { get; set; }
public MessageModel MessageModel { get; set; }
public MessageHandleMode MessageHandleMode { get; set; } public ConsumerSetting()
{
BrokerAddress = SocketUtils.GetLocalIPV4().ToString();
BrokerPort = 5001;
RebalanceInterval = 1000 * 5;
HeartbeatBrokerInterval = 1000 * 5;
UpdateTopicQueueCountInterval = 1000 * 5;
PersistConsumerOffsetInterval = 1000 * 5;
PullRequestSetting = new PullRequestSetting();
MessageModel = MessageModel.Clustering;
MessageHandleMode = MessageHandleMode.Parallel;
}
EQueue兼容Linux/Mono,下面是CentOS 6.4/Mono 3.2.3 环境下的运行结果:
c#开源消息队列中间件EQueue 教程的更多相关文章
- 常用的消息队列中间件mq对比
原文地址:https://blog.csdn.net/qq_30764991/article/details/80239076 消息队列中间件是分布式系统中重要的组件,主要解决应用耦合,异步消息,流量 ...
- 基于硬件的消息队列中间件 Solace 简介之二
前言...... 前面简单介绍了Solace来自于哪家公司, 主要能做哪些事情. 本篇主要进一步介绍Solace作为消息传递的中间件如何工作的. 传统意义上来讲, 每当我们谈到消息中间件时, 首先想到 ...
- 在 CentOS7 上安装 RabbitMQ 消息队列中间件
RabbitMQ 是流行的开源消息队列系统,是 AMQP(Advanced Message Queuing Protocol 高级消息队列协议)的标准实现,用 erlang 语言开发.RabbitMQ ...
- nodejs一个函数实现消息队列中间件
消息队列中间件(Message Queue)相信大家不会陌生,如Kafka.RabbitMQ.RocketMQ等,已经非常成熟,在大大小小的公司和项目中也已经广泛使用. 有些项目中,如果是只使用初步的 ...
- ActiveMQ RabbitMQ RokcetMQ Kafka实战 消息队列中间件视频教程
附上消息队列中间件百度网盘连接: 链接: https://pan.baidu.com/s/1FFZQ5w17e1TlLDSF7yhzmA 密码: hr63
- Delayer 基于 Redis 的延迟消息队列中间件
Delayer 基于 Redis 的延迟消息队列中间件,采用 Golang 开发,支持 PHP.Golang 等多种语言客户端. 参考 有赞延迟队列设计 中的部分设计,优化后实现. 项目链接:http ...
- 初试kafka消息队列中间件一 (只适合初学者哈)
初试kafka消息队列中间件一 今天闲来有点无聊,然后就看了一下关于消息中间件的资料, 简单一点的理解哈,网上都说的太高大上档次了,字面意思都想半天: 也就是用作消息通知,比如你想告诉某某你喜欢他,或 ...
- 初试kafka消息队列中间件二(采用java代码收发消息)
初试kafka消息队列中间件二(采用java代码收发消息) 上一篇 初试kafka消息队列中间件一 今天的案例主要是将采用命令行收发信息改成使用java代码实现,根据上一篇的接着写: 先启动Zooke ...
- 消息队列中间件(二)使用 ActiveMQ
ActiveMQ 介绍 Active MQ 是由 Apache 出品的一款流行的功能强大的开源消息中间件,它速度快,支持跨语言的客户端,具有易于使用的企业集成模式和许多的高级功能,同时完全支持 JSM ...
随机推荐
- (3)WebApi客户端调用
1.创建一个应用台控制程序,可以把Model的引用,用下面的方法拖拽上来(解决方案里没有这个文件,只是这个文件的引用) 2.Program.cs using System; using System ...
- [Spring] Spring配置文件中特殊字符的规定
今天查找一个错误,发现在xml里面不能包含特殊字符:&,特来总结一下: XML中共有5个特殊的字符,分别是:&<>“’.如果配置文件中的注入值包括这些特殊字符,就需要进行特 ...
- viewgager
CycleRotationView:自定义控件,主要功能是实现类似与各种商城首页的广告轮播图.其实像这种比较常见的自定义控件早就满大街了,虽然说"不要重复发明轮子",但是不代表不用 ...
- for循环每次取出一个字符(不是字节)
python3.5 for循环每次取出一个字符(不是字节) #!/usr/bin/env python # -*- coding:utf-8 -*- my_str = "我是哈哈" ...
- 【手把手教你全文检索】Apache Lucene初探
PS: 苦学一周全文检索,由原来的搜索小白,到初次涉猎,感觉每门技术都博大精深,其中精髓亦是不可一日而语.那小博猪就简单介绍一下这一周的学习历程,仅供各位程序猿们参考,这其中不涉及任何私密话题,因此也 ...
- laravel 框架使用总结 limit
后台开发就是数据的各种处理很多时候需要做到分页,但是在laravel中使用limit做分页的时候会出现问题,偏移量和每页的条数放进去好像不好使了 下面推荐给大家一种在laravel框架中非常好用的写法 ...
- JavaScript(三) 正则表达式 以及实现的功能
RegExp 是正则表达式的缩写.定义RegExp正则表达式 RegExp 对象用于存储检索模式. 通过 new 关键词来定义 RegExp 对象.以下代码定义了名为 p 的 RegExp 对象,其模 ...
- [spring源码学习]八、IOC源码-messageSource
一.代码实例 我们在第八章可以看到,spring的context在初始化的时候,会默认调用系统中的各种约定好的bean,其中第一个bean就是id为messageSource的bean,我们了解这应该 ...
- LeetCode 104. Maximum Depth of Binary Tree
Problem: Given a binary tree, find its maximum depth. The maximum depth is the number of nodes along ...
- 【爬虫】Python2 爬虫初学笔记
爬虫,个人理解就是:利用模拟“操作浏览器”的过程,自动获取我们想要的数据(或者说信息,比如图片啊) 为何要学爬虫:爬取数据,为我所用(相当于可以把一类数据整合起来) 一.简单静态网页爬虫架构: 1.B ...