引入MQ话题

  可能很多人有疑惑:MQ到底是什么?哪些场景下要使用MQ?
前段时间安装了RabbitMQ,现在就记录下自己的学习心得吧。首先看段程序:

class Program
{
static void Main(string[] args)
{
new Thread(Write).Start();
new Thread(Write).Start();
new Thread(Write).Start();
new Thread(Write).Start();
} public static void WriteLog(int i)
{
using (FileStream f = new FileStream(@"d:\\test.txt", FileMode.Append))
{
using (StreamWriter sw = new StreamWriter(f, Encoding.Default))
{
sw.Write(i);
}
}
} public static void Write()
{
for (int i = ; i < ; i++)
{
WriteLog(i);
}
}
}

仅仅从代码上看,没有觉得任何问题对吧?编译也是通过的,但是执行时,出现一个问题:

当然,这仅仅是一个小的案例,类似这种多线程写文件造成的问题, 就应该使用MQ了

MQ的使用场景大概包括解耦,提高峰值处理能力,送达和排序保证,缓冲等。

MQ概述

  消息队列技术是分布式应用间交换信息的一种技术。

  消息队列可驻留在内存或磁盘上,队列存储消息直到它们被应用程序读走。

  通过消息队列,应用程序可独立地执行--它们不需要知道彼此的位置、或在继续执行前不需要等待接收程序接收此消息。

  MQ主要作用是接受和转发消息。你可以想想在生活中的一种场景:当你把信件的投进邮筒,邮递员肯定最终会将信件送给收件人。我们可以把MQ比作 邮局和邮递员。

  MQ和邮局的主要区别是,它不处理消息,但是,它会接受数据、存储消息数据、转发消息。

RabbitMQ术语

生产者:消息发送者,在MQ中被称为生产者(producer),一个发送消息的应用也被叫做生产者,用P表示

消费者:生产者“生产”出消息后,最终由谁消费呢?等待接受消息的应用程序,我们称之为消费者(Consuming ),用C表示

队列:消息只能存储在队列(queue )中。尽管消息在rabbitMQ和应用程序间流通,但是队列却是存在于RabbitMQ内部。

一个队列不受任何限制,它可以存储你想要存储的消息量,它本质上是一个无限的缓冲区。多个生产者可以向同一个队列发送消息,多个消费者可以尝试从同一个消息队列中接收数据。

一个队列像下面这样(上面是它的队列名称)

注意:生产者、消费者、中间件不必在一台机器上,实际应用中也是绝大多数不在一起的。我们可以用一张图表示RabbitMQ的构造:

使用RabbitMQ解决多线程写入文件问题

  多线程写入,产生消息的也就是一个程序(一个生产者P),消费消息的也是一个程序,它的模型应该是:

编写代码

  引入RabbitMQ client DLL     程序包管理nuget:PM> Install-Package RabbitMQ.Client

生产者

  首先,创建一个 connection 通过socket连接 去和服务器连接起来(需要传目的服务器的IP、用户名、密码等)。

  接着 创建一个 channel ,这是大部分的要做的事情所在。要发送消息,我们必须声明一个队列,然后我们可以向队列发布消息。执行一次BasicPublish方法,推送一个消息。

class Program
{
static void Main(string[] args)
{
new Thread(Write).Start();
new Thread(Write).Start();
new Thread(Write).Start();
} public static void Write()
{
var factory = new ConnectionFactory() { HostName = "localhost", UserName = "eric", Password = "", };
using (var connection = factory.CreateConnection())
using (var channel = connection.CreateModel())
{
// QueueDeclare参数含义: https://blog.csdn.net/vbirdbest/article/details/78670550
channel.QueueDeclare(queue: "writeLog", durable: false, exclusive: false, autoDelete: false, arguments: null);
for (int i = ; i < ; i++)
{
string message = i.ToString();
var body = Encoding.UTF8.GetBytes(message); channel.BasicPublish(exchange: "", routingKey: "writeLog", basicProperties: null, body: body);
Console.WriteLine("Program Sent {0}", message);
}
}
}
}

  声明的队列,在服务器中如果不存在了,会自动创建。而消息的内容是字节数组,在使用时,注意编码问题。

消费者

  当队列里有消息时,消费者能够从队列里获取消息。

  就像我们打篮球进行传球,需要事先确认要传给的那个队友位置一样,生产者要发送消息,一定要事先知道消费消息的程序的对列是哪个。

由此,声明对列,就应该在消费者程序中完成。

class Program
{
public static void Main()
{
var factory = new ConnectionFactory() { HostName = "localhost", UserName = "eric", Password = "", VirtualHost ="/"};
using (var connection = factory.CreateConnection())
using (var channel = connection.CreateModel())
{
channel.QueueDeclare(queue: "writeLog",
durable: false,
exclusive: false,
autoDelete: false,
arguments: null);
//提供一个回调事件EventingBasicConsumer,用于处理接收到的消息。
var consumer = new EventingBasicConsumer(channel);
consumer.Received += (model, ea) =>
{
var body = ea.Body;
var message = Encoding.UTF8.GetString(body);
ExcuateWriteFile(message);
Console.WriteLine(" Receiver Received {0}", message);
};
//启动一个基本的内容类消费者
channel.BasicConsume(queue: "writeLog",
noAck: true,
consumer: consumer); Console.WriteLine(" Press [enter] to exit.");
Console.ReadLine();
}
}
public static void ExcuateWriteFile(string i)
{
using (FileStream fs = new FileStream(@"d:\\test.txt", FileMode.Append))
{
using (StreamWriter sw = new StreamWriter(fs, Encoding.Unicode))
{
sw.Write(i);
}
}
}
}

执行程序

先执行 消费者程序,让它一直保持监听。

1、未启动消费者前

2、消费者启动后,可以看到界面处

然后运行 生产者程序。我们先开着 Receive ,当生产者运行时

消费者自动触发执行 :

直到所有的 指定的 queue 里面的消息完全消费完为止。(此时消费者程序仍然在监听中)

异常

1、None of the specified endpoints were reachable

  参考:https://www.cnblogs.com/gossip/p/4573056.html

这个异常在创建连接时抛出(CreateConnection()),原因一般是ConnectionFactory参数设置不对,比如HostName、UserName、Password

标准设置:

var factory = new ConnectionFactory();

factory.UserName = QueueSetttiong.UserName;   //用户名

factory.Password = QueueSetttiong.Password;      //密码

factory.HostName = QueueSetttiong.HostName;  //Rabbitmq服务IP,不包含端口

factory.Port = AmqpTcpEndpoint.UseDefaultPort;

factory.VirtualHost = QueueSetttiong.VirtualHost;  //默认为"/"

factory.Protocol = Protocols.DefaultProtocol;

部署生产后,factory配置都ok,但是还是抛异常None of the specified endpoints were reachable,最后发现原因是生产机器防火墙未打开RabbitMQ的端口,RabbitMQ的默认端口是:5672

另外一个可能的原因:未设置VirtualHost的权限

设置方法:RabbitmqWeb管理网站-->Admin。未设置权限时:

设置权限:(点击admin进入设置页面)

RabbitMQ引入的更多相关文章

  1. RabbitMQ 概念

    RabbitMQ快速概念入门 转(http://blog.csdn.net/qq_16414307/article/details/50585630) 本文适有一定消息队列基础的,但没有接触过Ra ...

  2. RabbitMQ防止消息丢失

    转载请注明出处 0.目录 RabbitMQ-从基础到实战(1)— Hello RabbitMQ RabbitMQ-从基础到实战(3)— 消息的交换 1.简介 RabbitMQ中,消息丢失可以简单的分为 ...

  3. RabbitMQ (十二) 消息确认机制 - 发布者确认

    消费者确认解决的问题是确认消息是否被消费者"成功消费". 它有个前提条件,那就是生产者发布的消息已经"成功"发送出去了. 因此还需要一个机制来告诉生产者,你发送 ...

  4. RabbitMQ学习之Work Queues(2)

    目录: 轮询调度(Round-robin dispatching):即依次分配分配任务给worker. 消息答复(Message acknowledgement):在consumer处理完之后,进行消 ...

  5. RabbitMQ学习笔记(五、RabbitMQ集群)

    目录: RabbitMQ集群 镜像队列 RabbitMQ服务日志 RabbitMQ分布式部署 高可用集群 RabbitMQ集群: 1.集群中组件的状态 首先MQ一定要是一个高可用的中间件所以集群肯定是 ...

  6. RabbitMQ学习笔记(四、RabbitMQ队列)

    目录: 消息路由失败了会怎样 备份交换器 TTL与DLX 如何实现延迟队列 RabbitMQ的RPC实现 持久化 事务 发送方确认机制 消息路由失败了会怎样: 在RabbitMQ中,如果消息路由失败了 ...

  7. 1.RabbitMQ工作模型与基本原理

        1.了解 MQ 的本质和 RabbitMQ 的特性: 2.掌握 RabbitMQ 的 Java API 编程和 Spring 集成 RabbitMQ 1. MQ 了解 1.1. 消息队列简介 ...

  8. SpringCloud之RabbitMQ消息队列原理及配置

    本篇章讲解RabbitMQ的用途.原理以及配置,RabbitMQ的安装请查看SpringCloud之RabbitMQ安装 一.MQ用途 1.同步变异步消息 场景:用户下单完成后,发送邮件和短信通知. ...

  9. 01 . RabbitMQ简介及部署

    RabbitMQ简介 ​ MQ全称为Message Queue, 消息队列(MQ)是一种应用程序对应用程序的通信方法.应用程序通过读写出入队列的消息(针对应用程序的数据)来通信,而无需专用连接来链接它 ...

随机推荐

  1. 问题:ORA-28000: the account is locked 用户锁住了。

    打开cmd: 执行 sqlplus /nolog 执行conn / as sysdba 执行 ALTER USER  username  ACCOUNT UNLOCK;

  2. idea中使用插件Grep Console在IDEA的log的不同的级别,可以设置不同的颜色。

    一.安装. 1. 2. 3. 二,使用. 1. 2.

  3. VS2010/MFC编程入门之前言

    鸡啄米的C++编程入门系列给大家讲了C++的编程入门知识,大家对C++语言在语法和设计思想上应该有了一定的了解了.但是教程中讲的例子只是一个个简单的例程,并没有可视化窗口.鸡啄米在这套VS2010/M ...

  4. G.Finding the Radius for an Inserted Circle 2017 ACM-ICPC 亚洲区(南宁赛区)网络赛

    地址:https://nanti.jisuanke.com/t/17314 题目: Three circles C_{a}C​a​​, C_{b}C​b​​, and C_{c}C​c​​, all ...

  5. MOPSO 多目标粒子群优化算法

    近年来,基于启发式的多目标优化技术得到了很大的发展,研究表明该技术比经典方法更实用和高效.有代表性的多目标优化算法主要有NSGA.NSGA-II.SPEA.SPEA2.PAES和PESA等.粒子群优化 ...

  6. Git冲突:commit your changes or stash them before you can merge. 解决办法

    用git pull来更新代码的时候,遇到了下面的问题: 1 2 3 4 error: Your local changes to the following files would be overwr ...

  7. 20145204《java程序设计》课程总结

    ---恢复内容开始--- 20145204<java程序设计>课程总结 每周读书笔记链接汇总: · 20145204<java程序设计>第一周总结 · 20145204< ...

  8. HDU 5950 Recursive sequence(矩阵快速幂)题解

    思路:一开始不会n^4的推导,原来是要找n和n-1的关系,这道题的MOD是long long 的,矩阵具体如下所示 最近自己总是很坑啊,代码都瞎吉坝写,一个long long的输入写成%d一直判我TL ...

  9. POJ 1159 Palindrome(最长公共子序列)

    http://poj.org/problem?id=1159 题意: 给出一个字符串,计算最少要插入多少个字符可以使得该串变为回文串. 思路: 计算出最长公共子序列,再用原长-LCS=所要添加的字符数 ...

  10. 使用git一张图就够了

    现在,版本控制工具中,git逐步成为主流.他的分散式的特性是它超越svn渐渐独霸江湖.如果你还不熟悉git,通过本文,你有个最基本最实用的理解:如果你熟悉git,温故而知新,为你加深对git的理解 g ...