.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 ...
随机推荐
- ssh延迟加载问题的解决方案
1. 什么是延迟加载问题 ? 业务层查询的数据 关闭session 之后...web层获取延迟加载的数据失败. 例如:查询订单没有查询客户,需要显示客户,session已经关闭,无法查询 2. 如何解 ...
- 消息中间件——RabbitMQ(九)RabbitMQ整合Spring AMQP实战!(全)
前言 1. AMQP 核心组件 RabbitAdmin SpringAMQP声明 RabbitTemplate SimpleMessageListenerContainer MessageListen ...
- POJ3321 - Apple Tree DFS序 + 线段树或树状数组
Apple Tree:http://poj.org/problem?id=3321 题意: 告诉你一棵树,每棵树开始每个点上都有一个苹果,有两种操作,一种是计算以x为根的树上有几个苹果,一种是转换x这 ...
- git bash下添加忽略文件列表
转载自:https://blog.csdn.net/weixin_42808389/article/details/81232119 在用KEIL 5(MDK ARM)开发项目时需要用到GIT管理代码 ...
- Erlcron分析学习
介绍 Erlcron实现类似Linux下面的cron的计划任务. 源码下载地址:https://github.com/erlware/erlcron 原理 Erlcron进程监控树 ecrn_cron ...
- mybatis转义
SELECT * FROM test WHERE 1 = 1 AND start_date <= CURRENT_DATE AND end_date >= CURRENT_DATE 在执行 ...
- 【Offer】[21] 【调整数组顺序使奇数位于偶数前面】
题目描述 思路分析 测试用例 Java代码 代码链接 题目描述 输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有的奇数位于数组的前半部分,所有的偶数位于数组的后半部分,(偶数之间和奇数 ...
- LeetCode探索初级算法 - 动态规划
LeetCode探索初级算法 - 动态规划 今天在LeetCode上做了几个简单的动态规划的题目,也算是对动态规划有个基本的了解了.现在对动态规划这个算法做一个简单的总结. 什么是动态规划 动态规划英 ...
- springmvc两种非注解的处理器映射器
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.sp ...
- zookeeper的未授权访问漏洞解决
zookeeper的基本情况 zookeeper是分布式协同管理工具,常用来管理系统配置信息,提供分布式协同服务.zookeeper官网下载软件包,bin目录下有客户端脚本和服务端脚本.另外还有个工具 ...