微服务实战(三):落地微服务架构到直销系统(构建基于RabbitMq的消息总线)
从前面文章可以看出,消息总线是EDA(事件驱动架构)与微服务架构的核心部件,没有消息总线,就无法很好的实现微服务之间的解耦与通讯。通常我们可以利用现有成熟的消息代理产品或云平台提供的消息服务来构建自己的消息总线;也可以自己完全写一个消息代理产品,然后基于它构建自己的消息总线。通常我们不用重复造轮子(除非公司有特殊的要求,比如一些大型互联网公司考虑到自主可控的白盒子),可以利用比如像RabbitMq这样成熟的消息代理产品作为消息总线的底层支持。
RabbitMq核心组件解释:
Connection:消息的发送方或订阅方通过它连接到RabbitMq服务器。
Channel:消息的发送方或订阅方通过Connection连接到RabbitMq服务器后,通过Channel建立会话通道。
Exchange:消息的发送方向Exchange发送消息,通过RabbitMq服务器中Exchange与Queue的绑定关系,Exchange会将消息路由到匹配的Queue中。
Queue:消息的承载者,消息的发送者的消息最终通过Exchange路由到匹配的Queue,消息的接收者从Queue接收消息并进行处理。
Exchange模式:在消息发送到Exchange时,需要路由到匹配的Queue中,至于如何路由,则是由Exchange模式决定的。
1.Direct模式:特定的路由键(消息类型)转发到该Exchange的指定Queue中。
2.Fanout模式:发送到该Exchange的消息,被同时发送到Exchange下绑定的所有Queue中。
3.Topic模式:具有某种特征的消息转发到该Exchange的指定Queue中。
我们最常见的使用是Direct模式,如果消息要被多个消费者消费,则可以使用Fanout模式。
实现基于RabbitMq的消息总线:
我们首先需要安装Erlang与RabbitMq到服务器上,然后就可以进行基于RabbitMq的消息总线的开发了,开发的总体思路与步骤如下:
1.首先建立一个项目作为消息总线,然后引入Rabbitmq.Client 这个nuget包,这样就有了RabbitMq开发的支持。
2.前面实现了基本的消息总线,所有基于RabbitMq的消息总线是从它继承下来的,并需要传入特定的参数到消息总线的构造函数中:
public RabbitMqEB(IConnectionFactory connectionFactory,IEventHandlerExecutionContext context,
string exchangeName,string exchangeType,string queueName,int publisherorconsumer,
bool autoAck = true) : base(context)
{
this.connectionFactory = connectionFactory;
this.connection = this.connectionFactory.CreateConnection();
this.exchangeName = exchangeName;
this.exchangeType = exchangeType;
this.autoAck = autoAck;
this.queueName = queueName;
if (publisherorconsumer == )
{
this.channel = CreateComsumerChannel();
}
}
connectionFactory:RabbitMq.Client中的类型,用于与RabbitMq服务器建立连接时需要使用的对象。
context:消息与消息处理器之间的关联关系的对象。
exchangeName:生产者或消费者需要连接到的Exchange的名字。
exchangeType:前面所描述的Exchange模式。
queueName:生产者或消费者发送或接收消息时的Queue的名字。
publisherorconsumer:指定连接到消息总线的组件是消息总线的生产者还是消费者,消费者和生产者会有不同,消费者(publisherorconsumer==2)会构建一个消费通道,用于从Queue接收消息并调用父类的ieventHandlerExecutionContext的HandleAsync方法来处理消息。
3.建立到RabbitMq的连接:
//判断是否已经建立了连接
public bool IsConnected
{
get { return this.connection != null && this.connection.IsOpen; }
}
public bool TryConnect()
{
//出现连接异常时的重试策略,通常通过第三方nuget包实现重试功能,这里出现连接异常时,每个1秒重试一次,共重试5次
var policy = RetryPolicy.Handle<SocketException>().Or<BrokerUnreachableException>()
.WaitAndRetry(, p => TimeSpan.FromSeconds(),(ex,time)=> {
//记录错误日志
});
policy.Execute(() =>
{
//建立RabbitMq Server的连接
this.connection = this.connectionFactory.CreateConnection();
});
if (IsConnected)
{
return true;
}
return false;
}
4.创建消费者通道:
private IModel CreateComsumerChannel()
{
if (!IsConnected)
{
TryConnect();
}
var channel = this.connection.CreateModel();
channel.ExchangeDeclare(exchange: exchangeName, type: exchangeType, durable: true);
channel.QueueDeclare(queue: queueName, durable: true, exclusive: false, autoDelete: false,
arguments: null);
var consumer = new EventingBasicConsumer(channel);
//消费者接收到消息的处理
consumer.Received += async (model, ea) =>
{
var eventbody = ea.Body;
var json = Encoding.UTF8.GetString(eventbody);
var @event = (IEvent)JsonConvert.DeserializeObject(json);
//调用关联对象中消息对应的处理器的处理方法
await this.eventHandlerExecutionContext.HandleAsync(@event);
//向会话通道确认此消息已被处理
channel.BasicAck(ea.DeliveryTag, multiple: false);
};
channel.BasicConsume(queue: this.queueName, autoAck: false, consumer: consumer); channel.CallbackException += (sender, ea) =>
{
this.channel.Dispose();
this.channel = CreateComsumerChannel();
};
return channel;
}
5.对生产者发布消息到交换机队列的支持:
public override void Publish<TEvent>(TEvent @event)
{
if (!IsConnected)
{
TryConnect();
}
using(var channel = this.connection.CreateModel())
{
channel.ExchangeDeclare(exchange: exchangeName, type: exchangeType, durable: true);
var message = JsonConvert.SerializeObject(@event);
var body = Encoding.UTF8.GetBytes(message);
//发布到交换机,根据交换机与队列的绑定以及交换机模式,最终发布到指定的队列中
channel.BasicPublish(this.exchangeName, @event.GetType().FullName,null, body);
}
}
6.对订阅者从交换机队列中订阅消息的支持:
public override void Subscribe<TEvent, TEventHandler>()
{
//注册接收到的消息类型到订阅方的处理器之间的关系
if (!this.eventHandlerExecutionContext.IsRegisterEventHandler < TEvent,TEventHandler>()){
this.eventHandlerExecutionContext.RegisterEventHandler<TEvent, TEventHandler>();
//消费者进行队列绑定
this.channel.QueueBind(this.queueName, this.exchangeName, typeof(TEvent).FullName);
}
}
从上面的6个步骤,我们基本上就完成了基于RabbitMq消息总线的基本功能,这里需要说明的是,上述代码只是演示,在实际生产环境中,不能直接使用以上代码,还需要小心的重构此代码以保证可靠性与性能。
QQ讨论群:309287205
微服务实战视频请关注微信公众号:
微服务实战(三):落地微服务架构到直销系统(构建基于RabbitMq的消息总线)的更多相关文章
- SpringCloud Alibaba微服务实战三 - 服务调用
导读:通过前面两篇文章我们准备好了微服务的基础环境并让accout-service 和 product-service对外提供了增删改查的能力,本篇我们的内容是让order-service作为消费者远 ...
- 庐山真面目之十一微服务架构手把手教你搭建基于Jenkins的企业级CI/CD环境
庐山真面目之十一微服务架构手把手教你搭建基于Jenkins的企业级CI/CD环境 一.介绍 说起微服务架构来,有一个环节是少不了的,那就是CI/CD持续集成的环境.当然,搭建CI/CD环境的工具很多, ...
- 【SpringCloud微服务实战学习系列】服务治理Spring Cloud Eureka
Spring Cloud Eureka是Spring Cloud Netflix微服务中的一部分,它基于NetFlix Sureka做了二次封装,主要负责完成微服务架构中的服务治理功能. 一.服务治理 ...
- 中小型研发团队架构实践三:微服务架构(MSA)
一.MSA 简介 1.1.MSA 是什么 微服务架构 MSA 是 Microservice Architect 的简称,它是一种架构模式,它提倡将单一应用程序划分成一组小的服务,服务之间互相通讯.互相 ...
- 手撕面试官系列(三):微服务架构Dubbo+Spring Boot+Spring Cloud
文章首发于今日头条:https://www.toutiao.com/i6712696637623370248/ 直接进入主题 Dubbo (答案领取方式见侧边栏) Dubbo 中 中 zookeepe ...
- .NET Core微服务架构学习与实践系列文章索引目录
一.为啥要总结和收集这个系列? 今年从原来的Team里面被抽出来加入了新的Team,开始做Java微服务的开发工作,接触了Spring Boot, Spring Cloud等技术栈,对微服务这种架构有 ...
- SpringCloud微服务实战——搭建企业级开发框架(二十二):基于MybatisPlus插件TenantLineInnerInterceptor实现多租户功能
多租户技术的基本概念: 多租户技术(英语:multi-tenancy technology)或称多重租赁技术,是一种软件架构技术,它是在探讨与实现如何于多用户的环境下共用相同的系统或程序组件,并且 ...
- 微服务实战(一):微服务架构的优势与不足 - DockOne.io
原文:微服务实战(一):微服务架构的优势与不足 - DockOne.io [编者的话]本文来自Nginx官方博客,是微服务系列文章的第一篇,主要探讨了传统的单体式应用的不足,以及微服务架构的优势与挑战 ...
- 微服务架构下分布式Session管理
转载本文需注明出处:EAII企业架构创新研究院(微信号:eaworld),违者必究.如需加入微信群参与微课堂.架构设计与讨论直播请直接回复此公众号:“加群 姓名 公司 职位 微信号”. 一.应用架构变 ...
随机推荐
- python3[爬虫实战] 使用selenium,xpath爬取京东手机
使用selenium ,可能感觉用的并不是很深刻吧,可能是用scrapy用多了的缘故吧.不过selenium确实强大,很多反爬虫的都可以用selenium来解决掉吧. 思路: 入口: 关键字搜索入口 ...
- Java Script 读书笔记 (四) 面向对象编程
1. 对象,属性 前面看到对象里删除属性一直疑惑,什么是对象,为什么属性可以删除, 我印象里的属性还是停留在property, 总想不明白为什么属性竟然能够删除.直到看到标准库才明白,原来对象就是py ...
- Scrapy爬虫框架(实战篇)【Scrapy框架对接Splash抓取javaScript动态渲染页面】
(1).前言 动态页面:HTML文档中的部分是由客户端运行JS脚本生成的,即服务器生成部分HTML文档内容,其余的再由客户端生成 静态页面:整个HTML文档是在服务器端生成的,即服务器生成好了,再发送 ...
- HTTP VISUAL HTTP请求可视化工具、HTTP快照工具(公测)
先啰嗦几句,最近工作比较忙,再加上自己又开设了一个小站(简单点),没时间写博客,都快憋坏了,趁着周末有时间,抓紧来一篇~ HTTP VISUAL是一款HTTP可视化工具,它可以记录HTTP请求,包括请 ...
- Python数据结构应用2——Queue
Reference: Problem Solving with Algorithms and Data Structures, Release 3.0 队列 Queue 建立 class Queue: ...
- Java基础练习4(内存管理)
请根据如下程序代码,画出对应的内存管理图(不需要画方法区),并写出输出结果. 1. public class Cell{ int row; int col; public Cell(int row,i ...
- Web前后端分离
第一篇博客:见谅 用自己的通俗语言讲web工程的前后端分离: 只是从自己的角度去分析,我眼中的前后端分离(可能不对) 首先要明白我们服务器和浏览器之前传输和接受的是什么: 静态文件(html,css, ...
- OO第二单元作业小结
前言 转眼已是第九周,第二单元的电梯系列作业已经结束,终于体验了一番多线程电梯之旅. 第一次作业是单电梯的傻瓜调度,虽然是第一次写多线程,但在课程PPT的指引下,写起来还是非常容易:第二次作业是单电梯 ...
- 【学习笔记Part 1 ● Linux】
本章目标 Linux简介 Linux发展 Linux界面熟悉 Linux常用命令 Linux简介 经常看到的或者熟悉的操作系统: Windows操作系统:Window 10或者Window 7 Mac ...
- headfirst设计模式(7)—命令模式
一.前言 什么是命令模式? 在软件系统中,“行为请求者”与“行为实现者”通常呈现一种“紧耦合”.但在某些场合,比如要对行为进行“记录.撤销/重做.事务”等处理,这种无法抵御变化的紧耦合是不合适的.在这 ...