.Net Core 商城微服务项目系列(十一):MQ消费端独立为Window服务+消息处理服务
之前使用MQ的时候是通过封装成dll发布Nuget包来使用,消息的发布和消费都耦合在使用的站点和服务里,这样会造成两个问题:
1.增加服务和站点的压力,因为每次消息的消费就意味着接口的调用,这部分的压力都加在了使用的站点和服务的机器上。
2.增加修改的复杂性,如果我们需要加两条消费日志,都需要再发布一个版本重新通过dll引用。
所以我们需要做以下两方面的工作:
1.MQ的接收拆分为Windows服务,通过zokeerper实现主从防止单点故障。
2.MQ的消费这里做成单独的WebApi服务。
这样做的好处有以下几方面:
1.解耦。MQ的消费从使用的站点和服务中被拆分出来,减轻服务压力。
2.增加程序的可维护和可调试性。
3.单独部署提高吞吐量。
首先我们先来看下MQ的消费服务端,其实就是把之前调接口的方法单独放到了WebApi中,这样可以单独部署,减轻服务器压力:
/// <summary>
/// MQ消费到指定的服务接口
/// </summary>
[HttpPost]
public async Task<ConsumerProcessEventResponse> ConsumerProcessEventAsync([FromBody]ConsumerProcessEventRequest request)
{
ConsumerProcessEventResponse response = new ConsumerProcessEventResponse();
try
{
_logger.LogInformation($"MQ准备执行ConsumerProcessEvent方法,RoutingKey:{request.RoutingKey} Message:{request.MQBodyMessage}");
using (var scope = _autofac.BeginLifetimeScope(AUTOFAC_SCOPE_NAME))
{
//获取绑定该routingKey的服务地址集合
var subscriptions = await StackRedis.Current.GetAllList(request.RoutingKey);
if (!subscriptions.Any())
{
//如果Redis中不存在 则从数据库中查询 加入Redis中
var queryRoutingKeyApiUrlResponse = _apiHelperService.PostAsync<QueryRoutingKeyApiUrlResponse>(ServiceAddress.QueryRoutingKeyApiUrlAsync, new QueryRoutingKeyApiUrlRequest { RoutingKey = request.RoutingKey });
if (queryRoutingKeyApiUrlResponse.Result != null && queryRoutingKeyApiUrlResponse.Result.ApiUrlList.Any())
{
subscriptions = queryRoutingKeyApiUrlResponse.Result.ApiUrlList;
Task.Run(() =>
{
StackRedis.Current.SetLists(request.RoutingKey, queryRoutingKeyApiUrlResponse.Result.ApiUrlList);
});
}
}
if(subscriptions!=null && subscriptions.Any())
{
foreach (var apiUrl in subscriptions)
{
Task.Run(() =>
{
_logger.LogInformation(request.MQBodyMessage);
}); //这里需要做判断 假如MQ要发送到多个服务接口 其中一个消费失败 应该将其单独记录到数据库 而不影响这个消息的确认
//先做个备注 以后添加这个处理
await _apiHelperService.PostAsync(apiUrl, request.MQBodyMessage);
}
_logger.LogInformation($"MQ执行ProcessEvent方法完成,RoutingKey:{request.RoutingKey} Message:{request.MQBodyMessage}");
}
}
}
catch(Exception ex)
{
response.Successful = false;
response.Message = ex.Message;
_logger.LogError(ex, $"MQ消费失败 RoutingKey:{request.RoutingKey} Message:{request.MQBodyMessage}");
} return response;
}
这个WebApi只有这一个方法,就是根据RoutingKey查找对应的MQ配置,然后根据配置的接口地址调用指定的接口,比较简单哈,之前也写过,就不细说了。
我们来看接收MQ消息的Windows服务端,MQ首次使用都需要重新绑定Routingkey、队列和交换器,所以我在Monitor服务里写了一个绑定的方法,在Windows服务端启动的时候调用一次:
public class MQConsumerService
{
private readonly IApiHelperService _apiHelperService;
private ILog _logger; public MQConsumerService(IApiHelperService apiHelperService,ILog logger)
{
_apiHelperService = apiHelperService;
_logger = logger;
} /// <summary>
/// 发送MQ到MQ消费服务端
/// </summary>
/// <param name="routingKey"></param>
/// <param name="message"></param>
public void ProcessEvent(string routingKey, string message)
{
try
{
_logger.Info($"MQ准备执行ProcessEvent方法,RoutingKey:{routingKey} Message:{message}");
_apiHelperService.PostAsync<ConsumerProcessEventResponse>(ServiceUrls.ConsumerProcessEvent,new ConsumerProcessEventRequest { RoutingKey=routingKey,MQBodyMessage=message});
}
catch(Exception ex)
{
_logger.Error($"MQ发送消费服务端失败 RoutingKey:{routingKey} Message:{message}",ex);
}
} /// <summary>
/// MQ初始化 调用队列交换器绑定接口
/// </summary>
/// <returns></returns>
public async Task MQSubscribeAsync()
{
try
{
var response= await _apiHelperService.PostAsync<MQSubscribeResponse>(ServiceUrls.MQSubscribe, new MQSubscribeRequest());
if(!response.Successful)
{
_logger.Error($"MQ绑定RoutingKey队列失败: {response.Message}");
}
}
catch(Exception ex)
{
_logger.Error($"MQ绑定RoutingKey队列失败",ex);
}
}
}
这里为了简单起见,交换器和队列使用的都是同一个,路由方式是“direct”模式,之后会继续修改的,先跑起来再说:
static void Main(string[] args)
{
//交换器(Exchange)
const string BROKER_NAME = "mi_event_bus";
//队列(Queue)
var SubscriptionClientName = "RabbitMQ_Bus_MI";
//log4net日志加载
ILoggerRepository repository = LogManager.CreateRepository("MI.WinService.MQConsumer");
XmlConfigurator.Configure(repository, new FileInfo("log4net.config"));
ILog log = LogManager.GetLogger(repository.Name, "MI.WinService.MQConsumer");
//依赖注入加载
IServiceCollection serviceCollection = new ServiceCollection();
//WebApi调用类
serviceCollection.AddTransient<IApiHelperService, ApiHelperService>();
var serviceProvider = serviceCollection.AddHttpClient().BuildServiceProvider();
serviceProvider.GetService<ILogger>();
var apiHelperService = serviceProvider.GetService<IApiHelperService>();
//MQ消费类(发送MQ消息调用接口、绑定队列交换器)
MQConsumerService consumerService = new MQConsumerService(apiHelperService,log); //MQ连接类
ConnectionFactory factory = new ConnectionFactory
{
UserName = "",
Password = "",
HostName = ""
}; var connection = factory.CreateConnection();
var channel = connection.CreateModel(); channel.ExchangeDeclare(exchange: BROKER_NAME, type: "direct"); channel.QueueDeclare(queue: SubscriptionClientName, durable: true, exclusive: false, autoDelete: false, arguments: null); var consumer = new EventingBasicConsumer(channel);
consumer.Received += (ch, ea) =>
{
//发送到MQ消费服务端
var message = Encoding.UTF8.GetString(ea.Body);
log.Info($"MQ准备消费消息 RoutingKey:{ea.RoutingKey} Message:{message}"); //发送到MQ消费服务端MQStationServer
Task result= Task.Run(() =>
{
consumerService.ProcessEvent(ea.RoutingKey, message);
});
if(!result.IsFaulted)
{
//确认ack
channel.BasicAck(ea.DeliveryTag, false);
}
};
channel.BasicConsume(SubscriptionClientName, false, consumer);
Console.WriteLine("消费者已启动!"); //绑定队列RoutingKey
Task taskResult= Task.Run(async() =>
{
await consumerService.MQSubscribeAsync();
}); taskResult.Wait(); Console.WriteLine("队列RoutingKey绑定完成!"); Console.ReadKey();
channel.Dispose();
connection.Close();
}
最后梳理下消费端消费MQ流程:
MQ发布后,Windows服务端会受到MQ消息,然后通过调用接口将消息发送到MQ消费服务端,通过RoutingKey从数据库查找对应的MQ和接口配置,调用指定接口,当然,这里只是简单的代码列子,想用在生产中必须要做好完善的日志调用记录、性能监控、健康检查以及服务器层面的集群防止单点故障。
.Net Core 商城微服务项目系列(十一):MQ消费端独立为Window服务+消息处理服务的更多相关文章
- .Net Core 商城微服务项目系列(七):使用消息队列(RabbitMQ)实现服务异步通信
RabbitMQ是什么,怎么使用我就不介绍了,大家可以到园子里搜一下教程.本篇的重点在于实现服务与服务之间的异步通信. 首先说一下为什么要使用消息队列来实现服务通信:1.提高接口并发能力. 2.保证 ...
- .Net Core 商城微服务项目系列(二):使用Ocelot + Consul构建具备服务注册和发现功能的网关
1.服务注册 在上一篇的鉴权和登录服务中分别通过NuGet引用Consul这个包,同时新增AppBuilderExtensions类: public static class AppBuilderEx ...
- .Net Core 商城微服务项目系列(八):购物车
最近加班有点多,一周五天,四天加班到11点+,心很累.原因是我当前在的这个组比较特殊,相当于业务的架构组,要为其它的开发组提供服务和监控.所以最近更新的也少,不过这个元旦三天假应该会更新三篇. 这篇是 ...
- .Net Core 商城微服务项目系列(一):使用IdentityServer4构建基础登录验证
这里第一次搭建,所以IdentityServer端比较简单,后期再进行完善. 1.新建API项目MI.Service.Identity,NuGet引用IdentityServer4,添加类InMemo ...
- .Net Core 商城微服务项目系列(十三):搭建Log4net+ELK+Kafka日志框架
之前是使用NLog直接将日志发送到了ELK,本篇将会使用Docker搭建ELK和kafka,同时替换NLog为Log4net. 一.搭建kafka 1.拉取镜像 //下载zookeeper docke ...
- .Net Core 商城微服务项目系列(六):搭建自己的Nuget包服务器
当我们使用微服务架构之后,紧接而来的问题便是服务之间的程序集引用问题,可能没接触过的同学不太理解这句话,都已经微服务化了为什么还要互相引用程序集,当然可以不引用.但是我们会有这样一种情况,我们的每个接 ...
- .Net Core 商城微服务项目系列(十四):分布式部署携程Apollo构建配置中心
一.开场白 在系统设计里我们有很多配置希望独立于系统之外,而又能够被系统实时读取.但是在传统的系统设计里,配置信息通常是耦合在系统内的,比如.net里通常会放在App.config或者web.conf ...
- .Net Core 商城微服务项目系列(五):使用Polly处理服务错误
项目进行微服务化之后,随之而来的问题就是服务调用过程中发生错误.超时等问题的时候我们该怎么处理,比如因为网络的瞬时问题导致服务超时,这在我本人所在公司的项目里是很常见的问题,当发生请求超时问题的时候, ...
- .Net Core 商城微服务项目系列(十):使用SkyWalking构建调用链监控(2019-02-13 13:25)
SkyWalking的安装和简单使用已经在前面一篇介绍过了,本篇我们将在商城中添加SkyWalking构建调用链监控. 顺带一下怎么把ES设置为Windows服务,cd到ES的bin文件夹,运行ela ...
随机推荐
- 一张图了解.Net Core和.NetFx和.Net Standard和Xamarin关系
一张图了解 .Net Core和.Net Framework和.Net Standard和Xamarin关系 总结 .NET Standard是一项API规范,每一个特定的版本,都定义了必须实现的基类 ...
- centos7.2+jdk7.9搭建haddoop2.7.0伪分布式环境(亲测成功)
最近想研究下hadoop,玩一玩大数据,废话不多说,就此开始! 所用环境: xshell 5.0(ssh连接工具,支持ftp,可向虚拟机传文件) CentOS-7-x86_64-DVD-1511. ...
- HDU 4719Oh My Holy FFF 线段树+DP
/* ** 日期: 2013-9-12 ** 题目大意:有n个数,划分为多个部分,假设M份,每份不能多于L个.每个数有一个h[i], ** 每份最右边的那个数要大于前一份最右边的那个数.设每份最右边的 ...
- HDU - 4370 0 or 1 最短路
HDU - 4370 参考:https://www.cnblogs.com/hollowstory/p/5670128.html 题意: 给定一个矩阵C, 构造一个A矩阵,满足条件: 1.X12+X1 ...
- 左偏树 P3377【模板】左偏树(可并堆)
题目传送门 代码: /* code by: zstu wxk time: 2019/03/01 */ #include<bits/stdc++.h> using namespace std ...
- 详解RMQ-ST算法 ST模板
RMQ问题是求解区间最值的问题. 这里分析的是ST算法,它可以对所有要处理的数据做到O(nlogn)的预处理,对每个区间查询做到O(1)查询 ST算法本质是一个DP的过程 这里通过举一个求最大值实例来 ...
- Go语言基础之网络编程
现在我们几乎每天都在使用互联网,我们前面已经学习了如何编写Go语言程序,但是如何才能让我们的程序通过网络互相通信呢?本章我们就一起来学习下Go语言中的网络编程. 关于网络编程其实是一个很庞大的领域,本 ...
- 学习笔记-Unity3d代码实现Windows10加载圈圈的效果
最近在写一个Unity3d的模仿windows10的桌面的程序,由于Unity3d本身不支持Gif图片,所以突发奇想使用代码来实现接近的. 接下来是代码部分:不一一解析,很简单,看的懂原理就Okly了 ...
- 本地代码上传github失败常见错误
1.上传失败 解决办法如下: 可以通过如下命令进行github与本地代码合并: git pull --rebase origin master 重新执行上传命令: git push -u origin ...
- IBM MQ reason code list
The reason code parameter (Reason) is a qualification to the completion code parameter (CompCode). I ...