近期要实现一个图片异步上传的需求,需要用到RabbitMQ,辅助客户端完成对高并发请求的处理。
由于RabbitMQ服务器是用Erlang语言编写,所以我们需要先安装Erlang环境,本文使用的版本是erl-24.1
下载地址: https://github.com/erlang/otp/releases/download/OTP-24.1/otp_win64_24.1.exe
下载完成后,双击exe完成安装。记录安装目录,默认路径:C:\Program Files\erl-24.1
配置环境变量
路径:计算机–右键–属性–高级系统设置–环境变量–系统变量
step 1 新建:
变量名:ERLANG_HOME
变量值:C:\Program Files\erl-24.1
step 2 在系统变量栏找到Path
新建值%ERLANG_HOME%\bin
默认情况下,如果只有guest账户,RabbitMQ是不允许用户通过远程ip访问管理页面的。因此需要配置自定义账户,才能通过远程方式(即IP+端口号)访问RabbitMQ。
--------------------------------------------------------------------以下操作非必要---------------------------------------------------------------
感兴趣可以先了解一下RabbitMQ中 exchange、route、queue的关系
using System;
using System.Text;
using System.Threading;
using RabbitMQ.Client;
namespace publisher
{
class Program
{
static void Main(string[] args)
{
//基本登录信息
var factory = new ConnectionFactory()
{
HostName = "192.168.123.29",
VirtualHost = "visual_knitting_machine",
UserName = "admin",
Password = "123456",
Port = 5672
};
//创建连接
using (var connection = factory.CreateConnection())
{
//分配channel
using (var channel = connection.CreateModel())
{
for (int i = 1; i < 1000; i++)
{
//描述队列
channel.QueueDeclare(
queue: "TestQueue1",
durable: true,
exclusive: false,
autoDelete: false,
arguments: null
);
//要发送的消息
string message = "Hello World! -- " + i.ToString();
var body = Encoding.UTF8.GetBytes(message);
//发布消息
channel.BasicPublish(
exchange: "myTestExchange",
routingKey: "queue1_routingKey",
basicProperties: null,
body: body
);
Console.WriteLine(" {0} Sent \'{1}\'", i.ToString(), message);
Thread.Sleep(150);
}
}
}
}
}
}
4. 消息订阅者代码(consumer.cs):每隔200ms,从队列消费一条消息。消费者可以有多个。
using System;
using System.Text;
using System.Threading;
using RabbitMQ.Client;
using RabbitMQ.Client.Events;
namespace consumer
{
class Program
{
static EventingBasicConsumer consumer;
static IConnection connection;
static IModel channel;
static void Main(string[] args)
{
// 禁用控制台快速编辑模式,防止窗口假死。
// 假死后会阻塞程序,导致无法接受消息
// 可通过按回车解除假死状态
ConsoleUtil.DisbleQuickEditMode();
try
{
var factory = new ConnectionFactory()
{
HostName = "192.168.123.29",
VirtualHost = "visual_knitting_machine",
UserName = "admin",
Password = "123456",
Port = 5672
};
connection = factory.CreateConnection();
channel = connection.CreateModel();
//队列声明
channel.QueueDeclare("TestQueue1", true, false, false, null);
//公平分发,一次只为某个消费者发送一条消息,
//否则会出现其他消费者拿不到消息的情况
//也可设置一次接受多条消息,将1设置n即可。
channel.BasicQos(0, 1, false);
//构建消费者实例
consumer = new EventingBasicConsumer(channel);
//消息接收后的事件委托,异步,非阻塞。
consumer.Received += OnReceiveMessage;
//启动消费者
//关闭自动确认,避免一次性将消息全部消费
channel.BasicConsume("TestQueue1", false, consumer);
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
//此循环无特殊意义,用来防止控制台自动关闭。
while (true) { }
}
static void OnReceiveMessage(object sender, BasicDeliverEventArgs e)
{
//手动确认消息被消费,只有确认消费后,RabbitMQ才会重新为该消费者发送新消息
channel.BasicAck(e.DeliveryTag, false);
var body = e.Body.ToArray();
var message = Encoding.UTF8.GetString(body);
if (!string.IsNullOrEmpty(message))
{
Console.WriteLine(" Received '{0}'", message);
}
Thread.Sleep(200);
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
public class ConsoleUtil
{
#region 设置控制台标题 禁用关闭按钮
[DllImport("user32.dll", EntryPoint = "FindWindow")]
extern static IntPtr FindWindow(string lpClassName, string lpWindowName);
[DllImport("user32.dll", EntryPoint = "GetSystemMenu")]
extern static IntPtr GetSystemMenu(IntPtr hWnd, IntPtr bRevert);
[DllImport("user32.dll", EntryPoint = "RemoveMenu")]
extern static IntPtr RemoveMenu(IntPtr hMenu, uint uPosition, uint uFlags);
static void DisbleClosebtn()
{
IntPtr windowHandle = FindWindow(null, "控制台标题");
IntPtr closeMenu = GetSystemMenu(windowHandle, IntPtr.Zero);
uint SC_CLOSE = 0xF060;
RemoveMenu(closeMenu, SC_CLOSE, 0x0);
}
protected static void CloseConsole(object sender, ConsoleCancelEventArgs e)
{
Environment.Exit(0);
}
#endregion
#region 关闭控制台 快速编辑模式、插入模式
const int STD_INPUT_HANDLE = -10;
const uint ENABLE_QUICK_EDIT_MODE = 0x0040;
const uint ENABLE_INSERT_MODE = 0x0020;
[DllImport("kernel32.dll", SetLastError = true)]
internal static extern IntPtr GetStdHandle(int hConsoleHandle);
[DllImport("kernel32.dll", SetLastError = true)]
internal static extern bool GetConsoleMode(IntPtr hConsoleHandle, out uint mode);
[DllImport("kernel32.dll", SetLastError = true)]
internal static extern bool SetConsoleMode(IntPtr hConsoleHandle, uint mode);
public static void DisbleQuickEditMode()
{
IntPtr hStdin = GetStdHandle(STD_INPUT_HANDLE);
uint mode;
GetConsoleMode(hStdin, out mode);
mode &= ~ENABLE_QUICK_EDIT_MODE;//移除快速编辑模式
mode &= ~ENABLE_INSERT_MODE; //移除插入模式
SetConsoleMode(hStdin, mode);
}
#endregion
#region 设置控制台颜色
static void WriteColorLine(string str, ConsoleColor colorF, ConsoleColor colorB)
{
ConsoleColor currentForegroundColor = Console.ForegroundColor;
ConsoleColor currentBackgroundColor = Console.BackgroundColor;
Console.ForegroundColor = colorF;
Console.BackgroundColor = colorB;
//Console.WriteLine(str);
Console.ForegroundColor = currentForegroundColor;
Console.BackgroundColor = currentBackgroundColor;
}
#endregion
}