第二节:老杨集成事件框架Zack.EventBus的使用和基于源码的剖析、改造、升级
一. Zack.EventBus简介
1. 说明
【Zack.EventBus】是老杨开发的一个基于Rabbitmq消息队列的集成事件框架,简化了原生连接Rabbitmq繁琐的代码,常用于多个微服务间通信。
RabbitMQ等消息中间件的消息发布和消费的过程是异步的,也就是消息发布者将消息放入消息中间件就返回了,并不会等待消息的消费过程,因此集成事件不仅能够降低微服务之间的耦合度,
也还能够起到削峰填谷的作用,避免一个微服务中的突发请求导致其他微服务雪崩的情况出现,而且消息中间件的失败重发机制可以提高消息处理的成功率,从而保证事务的最终一致性。
2. 最终一致性的事务
需要开发人员对流程进行精细的设计,甚至有时候需要引入人工补偿操作。不像强一致性事务那样是纯技术方案。
PS: 同类产品还有Cap框架,详见:https://www.cnblogs.com/yaopengfei/p/13776361.html
二. 快速上手
1. 搭建项目
(1). 准备两个6.0版本的WebApi项目,分别充当消息的发布者、消息的接收者角色,安装 【Zack.EventBus 1.1.12】程序集
(2). 启动Rabbitmq服务,保证默认的 账号 guest、密码guest 能正常使用。
2. 在program中注册EventBus服务
(1). 配置请求地址、交换机名称, 核心类: IntegrationEventRabbitMQOptions
(2). 当前程序集添加队列(没有则创建)
(3). 开启EventBus,app.UseEventBus();
//注册 基于Rabbitmq的EventBus服务 (无法配置账号密码)
builder.Services.Configure<IntegrationEventRabbitMQOptions>(u =>
{
u.HostName = "localhost";
u.ExchangeName = "ypfExchange1";
});
builder.Services.AddEventBus("ypfQueue1", Assembly.GetExecutingAssembly());
var app = builder.Build();
//开启
app.UseEventBus();
缺陷: 没有配置账号密码的地方,分析源码得知,ServicesCollectionExtensions类中的 var factory = new ConnectionFactory()默认并没有传递账号和密码 , 通过F12查看ConnectionFactory的源码,发现UserName和PassWord都有默认值,都是guest,显然这不合理。账号密码写死了,均为guest,后续将自己升级改造。
3.在消息发布者中控制器中编写代码
(1). 注入IEventBus eventBus
(2). 在AddUser接口中通过Publish进行消息的一对多发送,需要标记事件名称。
[Route("api/[controller]/[action]")]
[ApiController]
public class HomeController : ControllerBase
{
private IEventBus eventBus;
public HomeController(IEventBus eventBus)
{
this.eventBus = eventBus;
}
[HttpPost]
public string AddUser(string userName,int userAge)
{
//1. 模拟数据库操作
//2. DB成功后,执行消息的发布
eventBus.Publish("userAdd",new UserData(userName,userAge));
return "ok";
}
}
4.消息接收者中通过特性[EventName("userAdd")]声明接受的事件名称,一个handle支持接受多个事件,另外这里支持三种形式
(1). IIntegrationEventHandler,将发送的消息序列化成json字符串
(2). JsonIntegrationEventHandler,支持泛型的形式
(3). DynamicIntegrationEventHandler,支持dynamic的形式
另外可以通过eventName来判断事件的名称。
EventHandle1
/// <summary>
/// 事件接收类1
/// IIntegrationEventHandler,将发送的消息序列化成json字符串
/// </summary>
[EventName("userAdd")]
[EventName("userEdit")]
public class EventHandle1 : IIntegrationEventHandler
{
public Task Handle(string eventName, string eventData)
{
if (eventName=="userAdd")
{
Console.WriteLine($"收到了{eventName}消息,消息为:{eventData}");
}
return Task.CompletedTask;
}
}
EventHandle2
/// <summary>
/// 事件接收类2
/// JsonIntegrationEventHandler,支持泛型的形式
/// </summary>
[EventName("userAdd")]
[EventName("userEdit")]
public class EventHandle2 : JsonIntegrationEventHandler<UserData>
{
public override Task HandleJson(string eventName, UserData eventData)
{
if (eventName == "userAdd")
{
Console.WriteLine($"收到了{eventName}消息,消息为:{eventData}");
}
return Task.CompletedTask;
}
}
EventHandle3
/// <summary>
/// 事件接收类3
/// DynamicIntegrationEventHandler,支持dynamic的形式
/// </summary>
[EventName("userAdd")]
[EventName("userEdit")]
public class EventHandle3 : DynamicIntegrationEventHandler
{
public override Task HandleDynamic(string eventName, dynamic eventData)
{
if (eventName == "userAdd")
{
Console.WriteLine($"收到了{eventName}消息,消息为:{eventData}");
}
return Task.CompletedTask;
}
}
5. 测试
三. 源码剖析、改造、升级
1. 改造使其支持账号密码
(1). Zack.EventBus/Models/IntegrationEventRabbitMQOptions新增两个属性 UserName和PassWord,并且赋默认值, admin1 123456
/// <summary>
/// 注册EventBus服务的参数类
/// </summary>
public class IntegrationEventRabbitMQOptions
{
/// <summary>
/// 访问url
/// </summary>
public string HostName { get; set; }
/// <summary>
/// 交换机名称
/// </summary>
public string ExchangeName { get; set; }
/************************* 下面是ypf添加的 ***************************************/
/// <summary>
/// 账号
/// </summary>
public string UserName { get; set; } = "admin1";
/// <summary>
/// 密码
/// </summary>
public string Password { get; set; } = "123456";
}
(2). Zack.EventBus/ServicesCollectionExtensions 添加传入账号和密码的代码,如下:
var factory = new ConnectionFactory()
{
HostName = optionMQ.HostName,
DispatchConsumersAsync = true,
//下面是ypf自己添加的(用来配置账号和密码)
UserName= optionMQ.UserName,
Password= optionMQ.Password
};
(3). 调用,可以根据需要传入账号和密码了
services.Configure<IntegrationEventRabbitMQOptions>(u =>
{
u.HostName = "localhost";
u.ExchangeName = "ypfExchange1";
u.UserName = "admin"; //账号
u.Password = "123456"; //密码
});
!
- 作 者 : Yaopengfei(姚鹏飞)
- 博客地址 : http://www.cnblogs.com/yaopengfei/
- 声 明1 : 如有错误,欢迎讨论,请勿谩骂^_^。
- 声 明2 : 原创博客请在转载时保留原文链接或在文章开头加上本人博客地址,否则保留追究法律责任的权利。