EventBus In eShop -- 解析微软微服务架构eShopOnContainers(四)

引言

大家好像对分析源码厌倦了,说实在我也会厌倦,不过不看是无法分析其后面的东西,从易到难是一个必要的过程。

今天说下EventBus,前几天园里的大神已经把其解刨事件总线(Event Bus)知多少,我今天就借着大神的肩膀,分析下在eShop项目中EventBus的实现。

最近发觉转发文章不写出处的,特此加上链接:http://inday.cnblogs.com

解析源码

我们知道使用EventBus是为了解除Publisher和Subscriber之间的依赖性,这样我们的Publisher就不需要知道有多少Subscribers,只需要通过EventBus进行注册管理就好了,在eShop项目中,有一个这样的接口IEventBus(eShopOnContainers\src\BuildingBlocks\EventBus\EventBus\Abstractions)


public interface IEventBus
{   
  
void Subscribe<T, TH>(Func<TH> handler)        where T : IntegrationEvent        where TH : IIntegrationEventHandler<T>;    

   void Unsubscribe<T, TH>()        where TH : IIntegrationEventHandler<T> where T : IntegrationEvent;  

  
void Publish(IntegrationEvent @event); }

我们可以看到这个接口定义了EventBus所需的一些操作, 对比大神的EventBus,相关功能都是一致的,我们看下它的实现类:EventBusRabbitMQ,从名字上可以看出,这是一个通过RabbitMQ来进行管理的EventBus,我们可以看到它使用了IEventBusSubscriptionsManager进行订阅存储,也就是大神文中的:

private readonly ConcurrentDictionary<Type, List<Type>> _eventAndHandlerMapping;

微软在Demo中把其提取出了接口,把一些常用方法给提炼了出来,但是核心还是Dictionary<string, List<Delegate>>, 使用Dictionary进行Map映射。通过Subscribe和UnSubscribe进行订阅和取消,使用Publish方法进行发布操作。


public void Subscribe<T, TH>(Func<TH> handler)    where T : IntegrationEvent    where TH : IIntegrationEventHandler<T>{   
 
var eventName = typeof(T).Name;  
 
var containsKey = _subsManager.HasSubscriptionsForEvent<T>();  
 
if (!containsKey){      
       
if (!_persistentConnection.IsConnected){_persistentConnection.TryConnect();}      
      
using (var channel = _persistentConnection.CreateModel()){channel.QueueBind(queue: _queueName,exchange: BROKER_NAME,routingKey: eventName);}}_subsManager.AddSubscription<T, TH>(handler); }

我们看到在订阅的时候,EventBus会检查下在Map中是否有相应的注册,如果没有的话首先回去RabbitMQ中创建一个新的channel进行绑定,随后在Map中进行注册映射。

UnSubscribe则直接从Map中取消映射,通过OnEventRemoved事件判断Map下此映射的subscriber是否为空,为空则从RabbitMQ中关闭channel。

在RabbitMQ的构造方法中,我们看到这样一个创建:CreateConsumerChannel(),这里创建了一个EventingBasicConsumer,当Queue中有新的消息时会通过ProcessEvent执行Map中注册的handler(subscribers),看图可能更清晰些:


在ProcessEvent方法中,回去Map中找寻subscribers,然后通过动态反射进行执行:

private async Task ProcessEvent(string eventName, string message)
{    if (_subsManager.HasSubscriptionsForEvent(eventName)){ var eventType = _subsManager.GetEventTypeByName(eventName);        var integrationEvent = JsonConvert.DeserializeObject(message, eventType);        var handlers = _subsManager.GetHandlersForEvent(eventName);     
   
foreach (var handlerfactory in handlers){          
  
var handler = handlerfactory.DynamicInvoke();      
  
var concreteType = typeof(IIntegrationEventHandler<>).MakeGenericType(eventType);  
     
await (Task)concreteType.GetMethod("Handle").Invoke(handler, new object[] { integrationEvent });}} }

微软通过简单的代码解耦了Publisher和Subscribers之间的依赖关系,我们引用大神的总结:

应用

在catalog.api中,微软出现了EventBus,我在上一篇中也提到了,这是我的一个疑惑,因为在catalog中并没有订阅操作,直接执行了Publish操作,原先以为是一个空操作,后来看了Basket.Api我才知道为何微软要用RabbitMQ。

使用RabbitMQ,我们不仅是从类之间的解耦,更可以跨项目,跨语言,跨平台的解耦,publisher仅仅需要把消息体(IntegrationEvent)传送到RabbitMQ,Consumer从Queue中获取消息体,然后推送到Subscribers执行相应的操作。我们看下Basket.Api.Startup.cs:

protected virtual void ConfigureEventBus(IApplicationBuilder app)
{  
  
var catalogPriceHandler = app.ApplicationServices        .GetService<IIntegrationEventHandler<ProductPriceChangedIntegrationEvent>>();    var orderStartedHandler = app.ApplicationServices.GetService<IIntegrationEventHandler<OrderStartedIntegrationEvent>>();    var eventBus = app.ApplicationServices.GetRequiredService<IEventBus>();eventBus.Subscribe<ProductPriceChangedIntegrationEvent, ProductPriceChangedIntegrationEventHandler>(() => app.ApplicationServices.GetRequiredService<ProductPriceChangedIntegrationEventHandler>());eventBus.Subscribe<OrderStartedIntegrationEvent, OrderStartedIntegrationEventHandler>(() => app.ApplicationServices.GetRequiredService<OrderStartedIntegrationEventHandler>()); }

在这个方法里,我们看到了Subscribe操作,想想之前的提问有点搞笑,不过研究明白了也不错,对吧!

总结

今天我们看了EventBus在Demo中的应用,总结一下。

1、EventBus可以很好的解耦订阅者和发布者之间的依赖

2、使用RabbitMQ能够跨项目、跨平台、跨语言的解耦订阅者和发布者

虽然在Demo中我们看到对订阅者的管理是通过Dictionary内存的方式,所以我们的Subscribe仅仅只在Basket.Api中看到,但微软是通过IEventBusSubscriptionsManager接口定义的,我们可以通过自己的需求来进行定制,可以做成分布式的,比如使用memcached。

写在最后

每个月到下旬就会比较忙,所以文章发布会比较慢,但我也会坚持学习完eShop的,为了学习,我建了个群,大家可以进来一起学习,有什么建议和问题都可以进来哦。

eShop虽好,但不建议大家放到生产环境,毕竟是一个Demo,而且目前还是ALPHA版本,用来学习是一个很好的教材,这就是一个大杂烩,学习中你会学到很多新的东西,大家如果看好core的发展,可以一起研究下。

QQ群:376248054

相关文章:

原文地址:http://www.cnblogs.com/inday/p/eventbus-in-eshopcontainers.html


.NET社区新闻,深度好文,微信中搜索dotNET跨平台或扫描二维码关注

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/324919.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

基本数据类型、包装类、String三者之间的相互转换

package com.wdl.day13;import org.junit.Test;/** 包装类的使用:* 1.java提供了8种基本数据类型对应的包装类&#xff0c;使得基本数据类型的变量具有类的特征** 2.掌握的&#xff1a;基本数据类型、包装类、String三者之间的相互转换****/ public class WrapperTest {//Strin…

很简单很简单的DBHelper类

记录一个简单的DBHelper类吧&#xff0c;用的时候在上来拿&#xff01; /// <summary>/// 数据库连接工具类/// </summary>public class DBHelper{string constr "Data Source.;Initial CatalogschoolDB;Integrated SecurityTrue";private SqlConnecti…

DDD理论学习系列(1)-- 通用语言

1.引言 在开始之前&#xff0c;我想我们有必要先了解以下DDD的主要参与者。因为毕竟语言是人说的吗&#xff0c;就像我们面向对象编程一样&#xff0c;那通用语言面向的是&#xff1f;DDD的主要参与者&#xff1a;领域专家开发人员领域专家&#xff1a;精通业务的任何人。开发…

定了!对于本周四(7.16日)抽奖活动取消简要说明,新抽奖活动暂定下周三(7.22日)...

大家好&#xff0c;我是雄雄&#xff0c;对于本周四&#xff08;7.16日&#xff09;抽奖活动取消简要说明&#xff0c;新抽奖活动暂定下周三&#xff08;7.22日&#xff09;&#xff0c;欢迎各位粉丝积极参与&#xff0c;奖品已经准备好了&#xff0c;你&#xff01;准备好了吗…

RabbitMQ系列教程之二:工作队列(Work Queues)

今天开始RabbitMQ教程的第二讲&#xff0c;废话不多说&#xff0c;直接进入话题。 (使用.NET 客户端 进行事例演示) 在第一个教程中&#xff0c;我们编写了一个从命名队列中发送和接收消息的程序。在本教程中&#xff0c;我们将创建一个工作队列&#xff0c;这个队列将用于在…

面试中经常会问的智力题,来看看你会做几道

转载自 面试中经常会问的智力题&#xff0c;来看看你会做几道 下面是大部分题目来自滴滴出行2017秋招题。开始头脑风暴吧~~~ 问题 question one 有50家人家&#xff0c;每家一条狗。有一天警察通知&#xff0c;50条狗当中有病狗&#xff0c;行为和正常狗不一样。每人只能通…

你喜欢什么样的课堂?

最近看了一本书《让课堂充满幽默》&#xff0c;里面有些内容还是觉得挺有道理的。书中开头就说&#xff1a;“大量的课件、复杂的网络&#xff0c;使得老师似乎成了信息管理员和媒体播放机&#xff0c;学生则成为了被灌输的对象”&#xff0c;看后&#xff0c;内心深处反问自己…

main()方法

main()方法的使用说明&#xff1a; main()方法作为程序的入口main()方法也是一个普通的静态方法main()方法可以作为我们与控制台交互的方式。&#xff08;之前&#xff1a;使用Scanner&#xff09;

实现自己的.NET Core配置Provider之EF

《10分钟就能学会.NET Core配置》里详细介绍了.NET Core配置的用法&#xff0c;另外我还开源了自定义的配置Provider&#xff1a;EF配置Provider和Yaml配置Provider。本文先来聊聊EF配置Provider的实现&#xff0c;其中会涉及到EntityFramework Core的知识&#xff0c;不熟悉也…

今天的雪糕格外好吃!

赤日炎炎&#xff0c;室外的温度超过30℃。午休罢&#xff0c;教室里一片寂静&#xff0c;大家都有一个目的——等待老师的进来&#xff0c;继续上课。偶尔有几位同学貌似等待焦急&#xff0c;遂将目光瞥向窗外&#xff0c;若有所思。还有几位好动同学在自己的座位上左右摇动&a…

洛谷P2296-寻找道路【日常图论,最短路,SPFA】

题目 一个有向图&#xff0c;要求满足要求的最短路径&#xff0c;要求为&#xff1a; 路径上的所有点的出边所指向的点都直接或间接与终点连通。 输入1 3 2 (3个点,2条边) 1 2 (1和2之间可以连接) 2 1 1 3 (从1到3) 输出1 -1 输入2 6 6 1 2 1 3 2 6 2 5 4 5 3…

活动 | Unity带你亲临王者荣耀KPL总决赛,领略电竞的魅力

《王者荣耀》是由腾讯出品的 Made with Unity的现象级游戏。在Unite Shanghai 2017中的案例专场&#xff0c;我们也曾邀请了《王者荣耀》项目组的技术总监邓君&#xff0c;分享过《王者技术修炼之路》。 从移动游戏到电子竞技&#xff0c;KPL(King Pro League)也是《王者荣耀》…

Java的并发编程中的多线程问题到底是怎么回事儿?

转载自 Java的并发编程中的多线程问题到底是怎么回事儿&#xff1f; 在我之前的一篇《再有人问你Java内存模型是什么&#xff0c;就把这篇文章发给他。》文章中&#xff0c;介绍了Java内存模型&#xff0c;通过这篇文章&#xff0c;大家应该都知道了Java内存模型的概念以及作…

DDD理论学习系列(2)-- 领域

1. 引言 领域一词&#xff0c;主要有以下两个意思&#xff1a; 一国主权所达之地。学术思想或社会活动的范围。 不管是指国家的主权范围也好还是学术活动范围&#xff0c;都是在讲一个范围&#xff0c;一个界限。比如我们常说的&#xff0c;学术领域、思想领域、技术领域、语…

最全、最详细的配置jdk十步法!

求关注求转发大家好&#xff0c;我是雄雄。今天给大家分享的是 如何配置jdk &#xff0c;还记得我当年刚开始自学编程时&#xff0c;jdk配置了好几天才配置好&#xff0c;说来也是惭愧啊。并且配置成功的步骤我自己都不知道是啥&#xff0c;因为当时是东找一个方法&#xff0c;…

RabbitMQ系列教程之三:发布\/订阅(Publish\/Subscribe)

在前一个教程中&#xff0c;我们创建了一个工作队列。工作队列背后的假设是每个任务会被交付给一个【工人】。在这一部分我们将做一些完全不同的事情--我们将向多个【消费者】传递信息。这种模式被称为“发布/订阅”。 为了说明这种模式&#xff0c;我们将构建一个简单的日志…

路径使用场景

路径的使用场景 路径适用的范围&#xff1a;jsp页面中link script img a form 等 如果访问的是其他服务器中的项目文件只可以使用绝对路径 如果访问的是同一个服务器中的其他项目推荐使用根路径 如果访问的是同一个服务器中的同一个项目中的文件推荐使用相对base标签的路径 修改…

【jzoj】2018.1.30NOIP普及组——模拟赛D组

_ 前言 写博客时间 2018/1/30 22:36。 感想&#xff1a;出数据的dalao我服&#xff01; 正题 题目1&#xff1a;二项式展开式&#xff08;jzoj2254&#xff09; 输入一个整数&#xff0c;求展开(ab)^n。展开方式为 (ab)^n?a^n?a^(n-1)b?a^(n-2)b^2…?b^n 其中” ? “…

开发人员也要懂点的测试知识

转载自 开发人员也要懂点的测试知识 本文来自于作者投稿&#xff0c;作者陈彩华&#xff0c;贝聊后端开发工程师。 最近参加了保利威测试总监李乐的《互联网测试姿势》为主题的分享交流会&#xff0c;收获颇丰&#xff0c;作为一个开放&#xff0c;秉承“不懂产品和测试的开…

转发和重定向

需求说明 用户输入并提交登录信息 登录成功&#xff0c;跳转到success.jsp页面 登录失败&#xff0c;跳转回login.jsp页面 理解图 区别详解