CAP实现EventBus
分布式事务一致性,使用的是 CAP,那么我们在什么情况下使用它呢。
EventBus
:事件总线,是一个基于观察者模块的事件发布/订阅框架,可以将一些复杂的,重复的操作,异步的操作,交给系统去处理,当前系统直接返回成功的操作。
简单的说就是解耦,
比如用户发布文章后,需要将消息推送给关注的用户,这时候,推送会浪费许多时间,本来这个操作与当前登录的用户没有关系,用户也只关注文章发布是否成功,。后续,将推送等操作,交给另一件事件去处理,这样速度更快,屏蔽了一些细节。就类似一种消息通知,我不关心后续是什么操作,只将自己的一些状态传递过去。
我们先来了解一下CAP
中的EventBus
,主要特点是发布订阅模式,内部使用RabbitMQ
实现异构系统的消息同步,通过本地消息表对信息持久化,保证由EventBus
发出的消息是可靠的,即高可用。
如果系统启动后在log/log200xxx.txt日志错误原因,RabbitMQ
DotNetCore.CAP.BrokerConnectionException: Broker Unreachable
---> RabbitMQ.Client.Exceptions.BrokerUnreachableException: None of the specified endpoints were reachable
---> RabbitMQ.Client.Exceptions.OperationInterruptedException: The AMQP operation was interrupted: AMQP close-reason, initiated by Peer, code=530, text="NOT_ALLOWED - access to vhost '/admin' refused for user 'admin'", classId=10, methodId=40, cause=
at RabbitMQ.Client.Impl.SimpleBlockingRpcContinuation.GetReply(TimeSpan timeout)
at RabbitMQ.Client.Impl.ModelBase.ConnectionOpen(String virtualHost, String capabilities, Boolean insist)
at RabbitMQ.Client.Framing.Impl.Connection.Open(Boolean insist)
at RabbitMQ.Client.Framing.Impl.Connection..ctor(IConnectionFactory factory, Boolean insist, IFrameHandler frameHandler, String clientProvidedName)
at RabbitMQ.Client.Framing.Impl.AutorecoveringConnection.Init(IFrameHandler fh)
at RabbitMQ.Client.Framing.Impl.AutorecoveringConnection.Init(IEndpointResolver endpoints)
at RabbitMQ.Client.ConnectionFactory.CreateConnection(IEndpointResolver endpointResolver, String clientProvidedName)
--- End of inner exception stack trace ---
at RabbitMQ.Client.ConnectionFactory.CreateConnection(IEndpointResolver endpointResolver, String clientProvidedName)
at RabbitMQ.Client.ConnectionFactory.CreateConnection(String clientProvidedName)
at DotNetCore.CAP.RabbitMQ.ConnectionChannelPool.<>c__DisplayClass19_0.<CreateConnection>b__1()
at DotNetCore.CAP.RabbitMQ.ConnectionChannelPool.GetConnection()
at DotNetCore.CAP.RabbitMQ.RabbitMQConsumerClient.Connect()
at DotNetCore.CAP.RabbitMQ.RabbitMQConsumerClientFactory.Create(String groupId)
--- End of inner exception stack trace ---
at DotNetCore.CAP.RabbitMQ.RabbitMQConsumerClientFactory.Create(String groupId)
at DotNetCore.CAP.Internal.ConsumerRegister.<>c__DisplayClass17_0.<Start>b__0()
2020-04-26 15:59:54.280 +08:00 [DBG] Transport connection checking...
2020-04-26 15:59:54.281 +08:00 [WRN] Transport connection is unhealthy, reconnection...
2020-04-26 15:59:54.467 +08:00 [ERR] Broker Unreachable
安装下载
我们就需要安装rabbitmq
了后,配置好用户,密码,virtual_hosts
,因为rabbimq是erlang
写的,所以
前提要安装erlang,官网下载比百度网盘还慢,也可自行去官网下载。
erlang 安装包 链接:https://pan.baidu.com/s/1T1_dI7iPMpEgiP8QC5tlaw
提取码:86jm
rabbitmq 安装包 链接:https://pan.baidu.com/s/1wD22_gKFX2LW9oOx69rnCA
提取码:ohea
配置RabbitMQ
服务开启后,管理地址:
http://localhost:15672/
- 帐号:
guest
密码 :guest
如果遇到RabbitmMQ安装后,web管理端口http://localhost:15672/
无法访问的解决
先进入rabbitMQ
安装目录下的sbin
目录,在目录下shift+右键
打开命令行
使用rabbitmq-plugins.bat enable rabbitmq_management
开启网页管理界面,然后重启rabbitMQ
-
-
此命令执行要先定位到
rabbitmq
的安装目录下的sbin
文件夹下。
rabbitmq-plugins enable rabbitmq_management
docker下安装rabbitmq
通过docker
来安装rabbitmq
,并启动
- 拉取镜像
docker pull rabbitmq:3.7.7-management
- 创建和启动容器
docker run -d --name rabbitmq3.7.7 -p 5672:5672 -p 15672:15672 -v `pwd`/data:/var/lib/rabbitmq --hostname myRabbit -e RABBITMQ_DEFAULT_VHOST=my_vhost -e RABBITMQ_DEFAULT_USER=admin -e RABBITMQ_DEFAULT_PASS=admin df80af9ca0c9
说明:
-d
后台运行容器;
--name
指定容器名;
-p
指定服务运行的端口(5672
:应用访问端口;15672
:控制台Web端口号);
-v
映射目录或文件;
--hostname
主机名(RabbitMQ的一个重要注意事项是它根据所谓的 “节点名称” 存储数据,默认为主机名);
-e
指定环境变量;(RABBITMQ_DEFAULT_VHOST
:默认虚拟机名;RABBITMQ_DEFAULT_USER
:默认的用户名;RABBITMQ_DEFAULT_PASS
:默认用户名的密码)
其他,可查看正在运行的容器
docker ps
打开浏览器,进入web管理端:http://Server-IP:15672
,用户名密码都是admin
开发配置项
lin-cms-dotnetcore 中 appsettings.json
中配置如下,默认guest
,不太安全,所以需要我们稍微学习一下rabbitmq
的工具的使用。
"RabbitMQ": {
"HostName": "localhost",
"UserName": "admin",
"Password": "123456",
"Port": 5672,
"VirtualHost": "/admin"
}
-
新增一个
admin
用户,配置这个账号的密码是123455
(好像也不安全),配置tag
,这里我们选择management
-
新建一个
virtual hosts
,啥意思就不BB了。
3.给admin
这个用户配置virtual hosts
先选择第一个图中的admin
用户。
然后set permission
选择virtual hosts /admin
点击set permission
,万事大吉
重新启动项目,日志不会显示错误。
CAP
概念
微服务分布式事务解决方案
可以搭建不同站点,使用CAP
,连接同一个RabbitMQ
,部署在不同的服务器上,实现分布式部署
需要一个数据库来记录事件
需要一个队列来存放事件消息
安装包 DotNetCore.CAP
安装包 DotNetCore.CAP.RabbitMQ
在Startup.cs
的ConfigureServices
方法中注入
services.AddCap(o=>{
o.UseSqlServer("");
o.UseRabbitMQ(mq => {
mq.HostName = "";//RabbitMQ服务器地址
mq.Port=5672;
mq.UserName = "admin";
mq.Password = "admin";
});
o.UseDashboard(); //添加监控仪表盘,通过http://localhost/cap访问
o.FailedRetryInterval = 30;//失败后的重拾间隔,默认60秒
o.FailedRetryCount = 10;//失败后的重试次数,默认50次;在FailedRetryInterval默认60秒的情况下,即默认重试50*60秒(50分钟)之后放弃失败重试
o.SucceedMessageExpiredAfter = 60 * 60; //设置成功信息的删除时间默认24*3600秒
});
发布与订阅代码示例
public class TestCap{
private readonly ICapPublisher _capPublisher;
public TestCap(ICapPublisher capPublisher){
_capPublisher = capPublisher;
}
//发布事件
public void TestPublish(){
await _capPublisher.Publish<string>("Test.Event","hello world","回调事件")
}
//订阅事件
[CapSubscribe("Test.Event")]
public string TestCapSubscribe(string message){
Console.WriteLine(message);
return "OK";
}
[CapSubscribe("回调事件")]
public void TestCallback(string result){
Console.WriteLine(result);
}
}