从前面文章可以看出,消息总线是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的消息总线是从它继承下来的,并需要传入特定的参数到消息总线的构造函数中:

  1. public RabbitMqEB(IConnectionFactory connectionFactory,IEventHandlerExecutionContext context,
  2. string exchangeName,string exchangeType,string queueName,int publisherorconsumer,
  3. bool autoAck = true) : base(context)
  4. {
  5. this.connectionFactory = connectionFactory;
  6. this.connection = this.connectionFactory.CreateConnection();
  7. this.exchangeName = exchangeName;
  8. this.exchangeType = exchangeType;
  9. this.autoAck = autoAck;
  10. this.queueName = queueName;
  11. if (publisherorconsumer == )
  12. {
  13. this.channel = CreateComsumerChannel();
  14. }
  15. }

connectionFactory:RabbitMq.Client中的类型,用于与RabbitMq服务器建立连接时需要使用的对象。

context:消息与消息处理器之间的关联关系的对象。

exchangeName:生产者或消费者需要连接到的Exchange的名字。

exchangeType:前面所描述的Exchange模式。

queueName:生产者或消费者发送或接收消息时的Queue的名字。

publisherorconsumer:指定连接到消息总线的组件是消息总线的生产者还是消费者,消费者和生产者会有不同,消费者(publisherorconsumer==2)会构建一个消费通道,用于从Queue接收消息并调用父类的ieventHandlerExecutionContext的HandleAsync方法来处理消息。

3.建立到RabbitMq的连接:

  1. //判断是否已经建立了连接
  2. public bool IsConnected
  3. {
  4. get { return this.connection != null && this.connection.IsOpen; }
  5. }
    public bool TryConnect()
  6. {
  7. //出现连接异常时的重试策略,通常通过第三方nuget包实现重试功能,这里出现连接异常时,每个1秒重试一次,共重试5次
  8. var policy = RetryPolicy.Handle<SocketException>().Or<BrokerUnreachableException>()
  9. .WaitAndRetry(, p => TimeSpan.FromSeconds(),(ex,time)=> {
  10. //记录错误日志
  11. });
  12. policy.Execute(() =>
  13. {
  14. //建立RabbitMq Server的连接
  15. this.connection = this.connectionFactory.CreateConnection();
  16. });
  17. if (IsConnected)
  18. {
  19. return true;
  20. }
  21. return false;
  22. }

4.创建消费者通道:

  1. private IModel CreateComsumerChannel()
  2. {
  3. if (!IsConnected)
  4. {
  5. TryConnect();
  6. }
  7. var channel = this.connection.CreateModel();
  8. channel.ExchangeDeclare(exchange: exchangeName, type: exchangeType, durable: true);
  9. channel.QueueDeclare(queue: queueName, durable: true, exclusive: false, autoDelete: false,
  10. arguments: null);
  11. var consumer = new EventingBasicConsumer(channel);
  12. //消费者接收到消息的处理
  13. consumer.Received += async (model, ea) =>
  14. {
  15. var eventbody = ea.Body;
  16. var json = Encoding.UTF8.GetString(eventbody);
  17. var @event = (IEvent)JsonConvert.DeserializeObject(json);
  18. //调用关联对象中消息对应的处理器的处理方法
  19. await this.eventHandlerExecutionContext.HandleAsync(@event);
  20. //向会话通道确认此消息已被处理
  21. channel.BasicAck(ea.DeliveryTag, multiple: false);
  22. };
  23. channel.BasicConsume(queue: this.queueName, autoAck: false, consumer: consumer);
  24.  
  25. channel.CallbackException += (sender, ea) =>
  26. {
  27. this.channel.Dispose();
  28. this.channel = CreateComsumerChannel();
  29. };
  30. return channel;
  31. }

5.对生产者发布消息到交换机队列的支持:

  1. public override void Publish<TEvent>(TEvent @event)
  2. {
  3. if (!IsConnected)
  4. {
  5. TryConnect();
  6. }
  7. using(var channel = this.connection.CreateModel())
  8. {
  9. channel.ExchangeDeclare(exchange: exchangeName, type: exchangeType, durable: true);
  10. var message = JsonConvert.SerializeObject(@event);
  11. var body = Encoding.UTF8.GetBytes(message);
  12. //发布到交换机,根据交换机与队列的绑定以及交换机模式,最终发布到指定的队列中
  13. channel.BasicPublish(this.exchangeName, @event.GetType().FullName,null, body);
  14. }
  15. }

6.对订阅者从交换机队列中订阅消息的支持:

  1. public override void Subscribe<TEvent, TEventHandler>()
  2. {
  3. //注册接收到的消息类型到订阅方的处理器之间的关系
  4. if (!this.eventHandlerExecutionContext.IsRegisterEventHandler < TEvent,TEventHandler>()){
  5. this.eventHandlerExecutionContext.RegisterEventHandler<TEvent, TEventHandler>();
  6. //消费者进行队列绑定
  7. this.channel.QueueBind(this.queueName, this.exchangeName, typeof(TEvent).FullName);
  8. }
  9. }

从上面的6个步骤,我们基本上就完成了基于RabbitMq消息总线的基本功能,这里需要说明的是,上述代码只是演示,在实际生产环境中,不能直接使用以上代码,还需要小心的重构此代码以保证可靠性与性能。

QQ讨论群:309287205 

微服务实战视频请关注微信公众号:

微服务实战(三):落地微服务架构到直销系统(构建基于RabbitMq的消息总线)的更多相关文章

  1. SpringCloud Alibaba微服务实战三 - 服务调用

    导读:通过前面两篇文章我们准备好了微服务的基础环境并让accout-service 和 product-service对外提供了增删改查的能力,本篇我们的内容是让order-service作为消费者远 ...

  2. 庐山真面目之十一微服务架构手把手教你搭建基于Jenkins的企业级CI/CD环境

    庐山真面目之十一微服务架构手把手教你搭建基于Jenkins的企业级CI/CD环境 一.介绍 说起微服务架构来,有一个环节是少不了的,那就是CI/CD持续集成的环境.当然,搭建CI/CD环境的工具很多, ...

  3. 【SpringCloud微服务实战学习系列】服务治理Spring Cloud Eureka

    Spring Cloud Eureka是Spring Cloud Netflix微服务中的一部分,它基于NetFlix Sureka做了二次封装,主要负责完成微服务架构中的服务治理功能. 一.服务治理 ...

  4. 中小型研发团队架构实践三:微服务架构(MSA)

    一.MSA 简介 1.1.MSA 是什么 微服务架构 MSA 是 Microservice Architect 的简称,它是一种架构模式,它提倡将单一应用程序划分成一组小的服务,服务之间互相通讯.互相 ...

  5. 手撕面试官系列(三):微服务架构Dubbo+Spring Boot+Spring Cloud

    文章首发于今日头条:https://www.toutiao.com/i6712696637623370248/ 直接进入主题 Dubbo (答案领取方式见侧边栏) Dubbo 中 中 zookeepe ...

  6. .NET Core微服务架构学习与实践系列文章索引目录

    一.为啥要总结和收集这个系列? 今年从原来的Team里面被抽出来加入了新的Team,开始做Java微服务的开发工作,接触了Spring Boot, Spring Cloud等技术栈,对微服务这种架构有 ...

  7. SpringCloud微服务实战——搭建企业级开发框架(二十二):基于MybatisPlus插件TenantLineInnerInterceptor实现多租户功能

    多租户技术的基本概念:   多租户技术(英语:multi-tenancy technology)或称多重租赁技术,是一种软件架构技术,它是在探讨与实现如何于多用户的环境下共用相同的系统或程序组件,并且 ...

  8. 微服务实战(一):微服务架构的优势与不足 - DockOne.io

    原文:微服务实战(一):微服务架构的优势与不足 - DockOne.io [编者的话]本文来自Nginx官方博客,是微服务系列文章的第一篇,主要探讨了传统的单体式应用的不足,以及微服务架构的优势与挑战 ...

  9. 微服务架构下分布式Session管理

    转载本文需注明出处:EAII企业架构创新研究院(微信号:eaworld),违者必究.如需加入微信群参与微课堂.架构设计与讨论直播请直接回复此公众号:“加群 姓名 公司 职位 微信号”. 一.应用架构变 ...

随机推荐

  1. Connection reset by peer的常见原因

    1,如果一端的Socket被关闭(或主动关闭,或因为异常退出而 引起的关闭),另一端仍发送数据,发送的第一个数据包引发该异常(Connect reset by peer). Socket默认连接60秒 ...

  2. Java 读书笔记 (九) 运算符

    短路逻辑运算符 && 当使用与逻辑运算符时,在两个操作数都为true时,结果才为true,但是当得到第一个操作为false时,其结果就必定是false,这时候就不会再判断第二个操作了. ...

  3. channel.go

    )         c.RLock()         client, ok := c.clients[msg.clientID]         c.RUnlock()         if ok ...

  4. java集合框架之Collections

    参考http://how2j.cn/k/collection/collection-collections/369.html Collections是一个类,容器的工具类,就如同Arrays是数组的工 ...

  5. 谈谈surging引擎的tcp、http、ws协议和如何容器化部署

    1.前言 分布式已经成为了当前最热门的话题,分布式框架也百花齐放,群雄逐鹿.从中心化服务治理框架,到去中心化分布式服务框架,再到分布式微服务引擎,这都是通过技术不断积累改进而形成的结果.esb,网关, ...

  6. RabbitMQ的介绍及使用进阶(Docker+.Net Core)

    目录: 一.什么是RabbitMQ 二.RabbitMQ运用场景 三.RabbitMQ优势及特点 四.Centos7中Docker安装RabbitMQ 五..Net Core 中使用RabbitMQ ...

  7. 基于 websocket 实现的 im 实时通讯案例

    分享利用 redis 订阅与发布特性,巧妙的现实高性能im系统.为表诚意,先贴源码地址:https://github.com/2881099/im 下载源码后的运行方法: 运行环境:.NETCore ...

  8. [翻译 EF Core in Action 1.8] MyFirstEfCoreApp应用程序设置

    Entity Framework Core in Action Entityframework Core in action是 Jon P smith 所著的关于Entityframework Cor ...

  9. 游戏AI之路径规划(3)

    目录 使用路径点(Way Point)作为节点 洪水填充算法创建路径点 使用导航网(Navigation Mesh)作为节点 区域分割 预计算 路径查询表 路径成本查询表 寻路的改进 平均帧运算 路径 ...

  10. 通过模拟JDK中的动态代理,由浅入深讲解动态代理思想.

    目录 场景引入 动态代理引入 动态代理进阶 总结 个人认为动态代理在设计模式中算是比较难的, 本篇文章将从无到有, 从一个简单代码示例开始迭代, 逐步深入讲解动态代理思想. 场景引入 假设现在有一个坦 ...