一个生产者将生产出来的数据发布到mq中,又多个消费者消费数据,并将消费结果反馈给mq,另外一个消费者消费结果数据。
一个Topic的mq,生产者将数据发给交换机,消费者绑定交换机,然后将结果上报给一个简单的mq,另外一个消费者消费结果。
using System;
using System.Text;
using Newtonsoft.Json;
using RabbitMQ.Client;
using RabbitMQ.Client.Events;
namespace Common.MQ
{
public class QueueOperator : IDisposable
{
///
/// 消息到达事件
///
public event Action<ulong, bool> MessageArrived;
///
/// 消息被拒绝事件
///
public event Action<ulong, bool> MessageDenied;
///
/// 当不能执行触发异常时的回调
///
public event Action<QueueCanNotDoException> QueueCanNotDoneed;
private ConnectionFactory factory;
private IConnection connection;
private bool isFirst = true;
public QueueOperator(string username, string pwd, string db, string hostname, int port)
{
factory = new ConnectionFactory()
{
UserName = username,
Password = pwd,
VirtualHost = db,
HostName = hostname,
Port = port
};
}
///
/// 异步发送消息
///
///
///
///
///
public void SendTopicAsnyc<TData>(string exchangeName, string routingkey, TData data) where TData : class, new()
{
using (var connection = factory.CreateConnection())
using (var channel = connection.CreateModel())
{
CheckIsFirst(exchangeName, channel);
channel.BasicAcks += Channel_BasicAcks;
channel.BasicNacks += Channel_BasicNacks;
channel.ConfirmSelect();
var body = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(data));
channel.BasicPublish(exchange: exchangeName,
routingKey: routingkey,
basicProperties: null,
body: body);
}
}
public void SendSampleAsnyc<TData>(string queueName, TData data)
{
using (var connection = factory.CreateConnection())
using (var channel = connection.CreateModel())
{
CreateQueue(queueName, channel);
channel.BasicAcks += Channel_BasicAcks;
channel.BasicNacks += Channel_BasicNacks;
channel.ConfirmSelect();
var body = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(data));
channel.BasicPublish(exchange: "",
routingKey: queueName,
basicProperties: null,
body: body);
}
}
private void CheckIsFirst(string exchangeName, IModel channel)
{
if (isFirst)
{
channel.BasicQos(0, 1, true);
CreateExchage(exchangeName, "topic", channel);
// CreateQueue(routingkey, channel);
// channel.QueueBind(routingkey, exchangeName, routingkey, null);
isFirst = false;
}
}
///
/// 绑定队列
///
///
///
///
/// 回调事件,收到数据要做的事情
public void BindQueue<TData>(string exchangeName, string routingkey, Action<TData> callback) where TData : class, new()
{
connection = factory.CreateConnection();
var channel = connection.CreateModel();
CheckIsFirst(exchangeName, channel);
var queueName = channel.QueueDeclare().QueueName;
channel.QueueBind(queueName, exchangeName, routingkey);
var consumer = new EventingBasicConsumer(channel);
consumer.Received += (model, ea) =>
{
var body = ea.Body.ToArray();
var message = Encoding.UTF8.GetString(body);
callback?.Invoke(JsonConvert.DeserializeObject<TData>(message));
//反馈处理完成信号
channel.BasicAck(ea.DeliveryTag, false);
};
channel.BasicConsume(queue: queueName,
autoAck: false,
consumer: consumer);
}
///
/// 绑定队列
///
///
///
/// 回调事件,收到数据要做的事情
public void BindQueue<TData>(string queueName, Action<TData> callback) where TData : class, new()
{
if (connection == null)
connection = factory.CreateConnection();
var channel = connection.CreateModel();
CreateQueue(queueName, channel);
var consumer = new EventingBasicConsumer(channel);
consumer.Received += (model, ea) =>
{
var body = ea.Body.ToArray();
var message = Encoding.UTF8.GetString(body);
callback?.Invoke(JsonConvert.DeserializeObject<TData>(message));
//反馈处理完成信号
channel.BasicAck(ea.DeliveryTag, false);
};
channel.BasicConsume(queue: queueName,
autoAck: false,
consumer: consumer);
}
private void CreateExchage(string name, string type, IModel channel)
{
try
{
channel.ExchangeDeclare(exchange: name,
type: type);
}
catch (Exception ex)
{
QueueCanNotDoneed?.Invoke(new QueueCanNotDoException(ex) { CanNotType = "交换机创建异常" });
}
}
private void CreateQueue(string name, IModel channel)
{
try
{
channel.QueueDeclare(name, false, false, false, null);
}
catch (Exception ex)
{
QueueCanNotDoneed?.Invoke(new QueueCanNotDoException(ex) { CanNotType = $"Queue[{name}]创建异常" });
}
}
///
/// 拒绝消息
///
///
///
private void Channel_BasicNacks(object sender, RabbitMQ.Client.Events.BasicNackEventArgs e)
{
MessageDenied?.Invoke(e.DeliveryTag, e.Multiple);
}
///
/// 接收消息
///
///
///
private void Channel_BasicAcks(object sender, RabbitMQ.Client.Events.BasicAckEventArgs e)
{
MessageArrived?.Invoke(e.DeliveryTag, e.Multiple);
}
public void Dispose()
{
if (connection.IsOpen)
connection.Close();
}
}
}
using System;
using System.Collections.Generic;
using System.Text;
namespace Common.MQ
{
public class QueueCanNotDoException : Exception
{
public QueueCanNotDoException(Exception ex)
{
new Exception(ex?.Message??"", ex);
}
public string CanNotType { get; set; }
public override string ToString()
{
return $"{CanNotType} 不能被执行,详细信息:\r\n{Message}";
}
}
}
生产者这里没有好记录的,正常操作mq就行了
.net 5 的 work service
public static IHostBuilder CreateHostBuilder(string[] args)
{
var ihostbuilder = Host.CreateDefaultBuilder(args);
//window和linux的路径表示方式不一样,虽然/在win10中也能识别,但是尽量保持系统原有风格
var configFile = AppDomain.CurrentDomain.BaseDirectory;
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
ihostbuilder.UseWindowsService();
configFile += "conf\\appsettings.json";
}
else
{
ihostbuilder.UseSystemd();
configFile += "conf/appsettings.json";
}
ihostbuilder.ConfigureAppConfiguration((hostingContext, config) =>
{
config.Sources.Clear();
//更改配置文件的路径,方便docker进行外部映射 Volume
config.AddJsonFile(configFile);
})
.ConfigureServices((hostContext, services) =>
{
services.AddHostedService<Worker>();
})
.ConfigureLogging(logging =>
{
logging.ClearProviders();
logging.SetMinimumLevel(LogLevel.Trace);
})
.UseNLog();
return ihostbuilder;
}
public class Worker : BackgroundService
{
private readonly ILogger<Worker> _logger;
private static QueueOperator queueOperator;
private MQSetting setting;
public Worker(ILogger<Worker> logger, IConfiguration configuration)
{
_logger = logger;
setting = configuration.GetSection("MQSetting").Get<MQSetting>();
}
public override Task StartAsync(CancellationToken cancellationToken)
{
try
{
queueOperator = new QueueOperator(setting.UserName, setting.Password, setting.DBName, setting.HostName, setting.Port);
queueOperator.BindQueue<CarlingData<string>>(setting.ExchangeName, setting.RoutingKey, data =>
{
Console.WriteLine(data.Step);
queueOperator.SendSampleAsnyc<CarlingResultData<string>>(setting.ResultRoutingKey, new CarlingResultData<string>() { Step = data.Step, Key = data.Key });
});
Console.WriteLine("初始化mq完成");
}
catch (Exception ex)
{
Console.WriteLine("初始化mq异常:" + ex.Message);
}
return base.StartAsync(cancellationToken);
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
Console.WriteLine($"Worker running at: { DateTimeOffset.Now}");
// _logger.LogInformation("Worker running at: {time}", DateTimeOffset.Now);
await Task.Delay(TimeSpan.FromMinutes(1), stoppingToken);
}
}
}
#See https://aka.ms/containerfastmode to understand how Visual Studio uses this Dockerfile to build your images for faster debugging.
# 这里要注意,vs生成的默认dockerfile中这里的runtime:5.0的版本,这个版本会导致做成镜像无法运行,因为缺少.net 5 库。
FROM mcr.microsoft.com/dotnet/aspnet:5.0-buster-slim AS base
WORKDIR /app
FROM mcr.microsoft.com/dotnet/sdk:5.0-buster-slim AS build
WORKDIR /src
RUN dotnet restore
COPY . .
#FROM mcr.microsoft.com/dotnet/sdk:5.0-buster-slim AS build
#WORKDIR /src
#COPY ["service/Shimada.CarlingService/Shimada.CarlingService.csproj", "service/Shimada.CarlingService/"]
#RUN dotnet restore "service/Shimada.CarlingService/Shimada.CarlingService.csproj"
#COPY . .
#WORKDIR "/src/service/Shimada.CarlingService"
#RUN dotnet build "Shimada.CarlingService.csproj" -c Release -o /app/build
#
#FROM build AS publish
#RUN dotnet publish "Shimada.CarlingService.csproj" -c Release -o /app/publish
#FROM base AS final
#WORKDIR /app
#COPY --from=publish /app/publish .
#ENTRYPOINT ["dotnet", "Shimada.CarlingService.dll"]
FROM base AS debuge
WORKDIR /app
COPY "bin/Debug/net5.0/" "/app"
EXPOSE 8080/tcp
EXPOSE 8080/udp
# 这里将conf配置文件目录和Logs日志文件目录映射出来,方便在docker外面修改和查看数据。
VOLUME ["/app/conf","/app/Logs"]
ENTRYPOINT ["dotnet", "Shimada.CarlingService.dll"]
docker build -t carlingservice:0.0.5.5 C:\shimada\SY-VIS-2101\src\SY_VIS_2101\service\Shimada.CarlingService
命令语法
Usage: docker build [OPTIONS] PATH | URL | -
Build an image from a Dockerfile
Options:
--add-host list Add a custom host-to-IP mapping (host:ip)
--build-arg list Set build-time variables
--cache-from strings Images to consider as cache sources
--disable-content-trust Skip image verification (default true)
-f, --file string Name of the Dockerfile (Default is
'PATH/Dockerfile')
--iidfile string Write the image ID to the file
--isolation string Container isolation technology
--label list Set metadata for an image
--network string Set the networking mode for the RUN
instructions during build (default "default")
--no-cache Do not use cache when building the image
-o, --output stringArray Output destination (format:
type=local,dest=path)
--platform string Set platform if server is multi-platform
capable
--progress string Set type of progress output (auto, plain,
tty). Use plain to show container output
(default "auto")
--pull Always attempt to pull a newer version of
the image
-q, --quiet Suppress the build output and print image
ID on success
--secret stringArray Secret file to expose to the build (only
if BuildKit enabled):
id=mysecret,src=/local/secret
--ssh stringArray SSH agent socket or keys to expose to the
build (only if BuildKit enabled) (format:
default|<id>[=<socket>|<key>[,<key>]])
-t, --tag list Name and optionally a tag in the
'name:tag' format
--target string Set the target build stage to build.
docker run --name debug2 -d carlingservice:0.0.5.9 -v /C/shimada/SY-VIS-2101/src/SY_VIS_2101/service/Shimada.CarlingService/bin/Logs:/app/Logs
https://blog.csdn.net/ap10062kai/article/details/79232582
互联网解决办法,在我这里没有解决
我的解决办法:
使用docker desktop工具运行的镜像,指定的挂在路径就可以了,有谁知道用命令怎么写的可以告诉我
2021年5月13日 补充
使用命令可以实现挂载
docker run -v C:\shimada\SY-VIS-2101\src\SY_VIS_2101\service\Shimada.CarlingService\bin\conf:/app/conf -v C:\shimada\SY-VIS-2101\src\SY_VIS_2101\service\Shimada.CarlingService\bin\Logs:/app/Logs --name debug3 -d carlingservice:0.0.5.5
重点: -v参数一定要紧跟着run 如果在run和-v中间添加了其他参数会导致执行失败。
desktop版本的docker在添加挂载的时候会有确认提示框,提示是否分享目录。(也可以提前在Setting中配置好File Sharing)
可以按照 https://www.cnblogs.com/microestc/p/10784877.html 的办法解决
个人觉得上面这个办法太麻烦了,直接开了个代理,因为国内的墙无法下载一些地址的文件导致的。
在dockerfile中进行修改 (看上面的dockerfile源码)
# 这里要注意,vs生成的默认dockerfile中这里的runtime:5.0的版本,这个版本会导致做成镜像无法运行,因为缺少.net 5 库。
FROM mcr.microsoft.com/dotnet/aspnet:5.0-buster-slim AS base
https://docs.docker.com/engine/reference/builder/#usage (dockerfile 文档)
https://docs.microsoft.com/zh-cn/aspnet/core/fundamentals/configuration/?view=aspnetcore-5.0#file-configuration-provider (appsettings.json 文档)
https://www.cnblogs.com/microestc/p/10784877.html (使用vs生成镜像失败或者非常慢的解决办法)
https://blog.csdn.net/shanghaibao123/article/details/108278639
https://www.rabbitmq.com/tutorials/tutorial-one-dotnet.html