引言

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

今天说下EventBus,前几天园里的大神已经把其解刨,我今天就借着大神的肩膀,分析下在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

EventBus In eShop -- 解析微软微服务架构Demo(四)的更多相关文章

  1. Health Check in eShop -- 解析微软微服务架构Demo(五)

    引言 What is the Health Check Health Check(健康状态检查)不仅是对自己应用程序内部检测各个项目之间的健康状态(各项目的运行情况.项目之间的连接情况等),还包括了应 ...

  2. 开篇有益-解析微软微服务架构eShopOnContainers(一)

    为了推广.Net Core,微软为我们提供了一个开源Demo-eShopOnContainers,这是一个使用Net Core框架开发的,跨平台(几乎涵盖了所有平台,windows.mac.linux ...

  3. Identity Service - 解析微软微服务架构eShopOnContainers(二)

    接上一篇,众所周知一个网站的用户登录是非常重要,一站式的登录(SSO)也成了大家讨论的热点.微软在这个Demo中,把登录单独拉了出来,形成了一个Service,用户的注册.登录.找回密码等都在其中进行 ...

  4. Catalog Service - 解析微软微服务架构eShopOnContainers(三)

    上一篇我们说了Identity Service,因为其基于IdentityServer4开发的,所以知识点不是很多,今天我们来看下Catalog Service,今后的讲解都会把不同的.重点的拿出来讲 ...

  5. 微软微服务架构eShopOnContainers

    为了推广.Net Core,微软为我们提供了一个开源Demo-eShopOnContainers,这是一个使用Net Core框架开发的,跨平台(几乎涵盖了所有平台,windows.mac.linux ...

  6. 基于docker部署的微服务架构(四): 配置中心

    原文:http://www.jianshu.com/p/b17d65934b58%20 前言 在微服务架构中,由于服务数量众多,如果使用传统的配置文件管理方式,配置文件分散在各个项目中,不易于集中管理 ...

  7. Spring Cloud构建微服务架构(四)分布式配置中心(续)

    先来回顾一下,在前文中我们完成了什么: 构建了config-server,连接到Git仓库 在Git上创建了一个config-repo目录,用来存储配置信息 构建了config-client,来获取G ...

  8. Spring Cloud构建微服务架构(四)分布式配置中心

    Spring Cloud Config为服务端和客户端提供了分布式系统的外部化配置支持.配置服务器为各应用的所有环境提供了一个中心化的外部配置.它实现了对服务端和客户端对Spring Environm ...

  9. 基于微服务架构、运行于容器中的.NET Core示例应用eShopOnContainers

    eShopOnContainers 是 <.NET Microservices – Architecture for Containerized .NET Applications>这本微 ...

随机推荐

  1. Java8新特性-Lambda表达式

    1.  什么是Lambda表达式? Lambda表达式就是可以把函数作为参数传递,或者说把代码作为数据传递给函数. 2. Lambda表达式的语法格式 基本语法格式如下: 基本语法下多个变体的说明: ...

  2. HAProxy的三种不同类型配置方案

    haproxy是一款功能强大.灵活好用反向代理软件,提供了高可用.负载均衡.后端服务器代理的功能,它在7层负载均衡方面的功能很强大(支持 cookie track, header rewrite等等) ...

  3. 【JAVAWEB学习笔记】03_JavaScript

    javascript简单介绍 ECMAScript 1.语法 2.变量:只能使用var定义,如果在函数的内容使用var定义,那么它是一个局部变量,如果没有使用var它是一个全局的.弱类型! 3.数据类 ...

  4. jQ实现的一个轮播图

    众所周知,轮播图是被广泛的运用的. 轮播图我们在很多的网站上都可以看到,例如淘宝.京东这些网站都很常见. 下面开始我们的轮播之旅: 搭建我们的骨架: <!DOCTYPE html> < ...

  5. Linux如何安装VMware Tools

    第一步:安装VMware Tools 桌面上会出现一个光盘,并且会弹出一个框框,里面是VMware Tools的安装包,将其拖到桌面上 就像下面这样 进入终端,将文件拷贝到/tmp目录下面,并解压(也 ...

  6. 4.docker学习之镜像

    镜像 我们知道,我们想在Windows操作系统上跑Linux,需要安装一个虚拟机程序,然后下载一个Linux镜像,在该虚拟机程序中创建一个虚拟机,并使用该镜像安装对应的Linux操作系统,安装好之后, ...

  7. C语言之变量和数据类型

    常量:程序在运行过程中无法对值进行更改. 变量:是在计算机内存空间一种表示,声明变量将会向计算机内存申请存储空间,用于保存数据,计算机的CPU会从内存中加载数据. 声明变量: 数据类型  变量名[=值 ...

  8. nfs+rsync+inotify实现文件的实时同步

    准备三台服务器进行测试: nfs_server:192.168.12.110 web_server:192.168.12.111 rsync_server:192.168.12.112 网络规划图: ...

  9. C/C++遍历目录下的所有文件(Windows/Linux篇,超详细)

    本文可转载,转载请注明出处:http://www.cnblogs.com/collectionne/p/6815924.html. 前面的一篇文章我们讲了用Windows API遍历一个目录下的所有文 ...

  10. Spring+SpringMVC+MyBatis深入学习及搭建(六)——MyBatis关联查询

    转载请注明出处:http://www.cnblogs.com/Joanna-Yan/p/6923464.html 前面有将到:Spring+SpringMVC+MyBatis深入学习及搭建(五)--动 ...