基于asp.net core 从零搭建自己的业务框架(一)
前言
asp.net core版本选择2.2,只是因为个人习惯了vs2017,代码以及设计皆可移植到vs2019,用asp.net core 3.0以及以上运行起来
项目类似选择web api,基础设施选择entity frame core + Masstransit + aspectCore
先赘述一下思路业务,中间通讯以及容错/重试交给masstransit,部分流程的解耦交给aspectCore来完成,这部分包括,错误后通过masstransit发布给错误处理的模块,最后落盘到ef core
整个过程,业务处理的层之间可以通过Masstransit的通讯,可采用rpc的模式,也可以同异步的发布订阅通讯
整体设计是可单体可分布式的理念,可以根据项目变化,可以自行配置,拆分成完成单体到分布式的过程,完成业务分解,但是对于业务使用而言,都是一样的,无感知。
刚开始的文章会有很多代码段说明,属于前置知识的铺垫,可能会有些啰嗦,后续文章会忽略掉无关的大片代码段。
业务演示,我们选择传统的银行转账业务以及电商的支付到下单。
整体crud的设计图
包括了业务层和基础设施层的设计
当前文章的设计图如下
整体采用Mq异步的发布订阅,订阅之间也通过发布订阅通知,完成最终一致性
业务实体
public class BaseModel
{
[Key]
public int Id { get; set; }
public DateTime CreateTime { get; set; }
}
public class UserInfo: BaseModel
{ public string NickName { get; set; }
public decimal Money { get; set; }
public DateTime LastOptions { get; set; }
}
public class PayOrder: BaseModel
{
public int SourceId { get; set; }
public int TargetId { get; set; }
public decimal Money { get; set; }
}
配置EF Core
public class TransactionContexts:DbContext
{
public DbSet<PayOrder> PayOrders { get; set; }
public DbSet<UserInfo> UserInfos { get; set; } public TransactionContexts(DbContextOptions<TransactionContexts> options):base(options)
{ }
}
配置数据源偷懒就用InMemory了
services.AddDbContext<TransactionContexts>(build=> {
build.UseInMemoryDatabase("TransactionContexts");
});
实例编写
建立一个交易的Command
internal class PayOrderCommand
{
public int SourceId { get; set; }
public int TargetId { get; set; }
public decimal Money { get; set; }
}
一个交易的Event
internal class PayOrderEvent: PayOrderCommand
{ }
Command对外,Event对内,主要是用于项目区分外部流程和内部流程的区别,代码本身是没有硬编码要求的
构建一个交易的服务,对外公开一个转账的接口
public interface ITransactionService
{
void TransferAccounts(int sourceId, int targetId, decimal money);
}
这个接口完成Publish/Subscribe模式的交易
Publish端
internal class TransactionService: ITransactionService
{
private IBusControl busControl; public TransactionService(IBusControl busControl)
{
this.busControl = busControl;
} public async void TransferAccounts(int sourceId, int targetId, decimal money)
{
await busControl.Publish(
new PayOrderCommand { SourceId = sourceId, TargetId = targetId, Money = money }
);
}
}
很常见的发布一个命令
Subscribe端
internal class TransactionConsumer :
IConsumer<PayOrderCommand>,
IConsumer<PayOrderEvent>
{
private TransactionContexts transactionContexts; public TransactionConsumer(TransactionContexts transactionContexts)
{
this.transactionContexts = transactionContexts;
} public async Task Consume(ConsumeContext<PayOrderCommand> context)
{
var value = context.Message; await Console.Out.WriteLineAsync($"PayOrderCommand Before:{DateTime.Now} SourceId:{value.SourceId} TargetId:{value.TargetId} Money:{value.Money}"); await transactionContexts.PayOrders.AddAsync(new PayOrder
{
Id = ,
SourceId = value.SourceId,
TargetId = value.TargetId,
Money = value.Money
});
await transactionContexts.SaveChangesAsync(); await context.Publish(new PayOrderEvent
{
SourceId = value.SourceId,
TargetId = value.TargetId,
Money = value.Money
}); await Console.Out.WriteLineAsync($"PayOrderCommand After:{DateTime.Now}");
} public async Task Consume(ConsumeContext<PayOrderEvent> context)
{
var value = context.Message; await Console.Out.WriteLineAsync($"PayOrderEvent Before:{DateTime.Now} SourceId:{value.SourceId} TargetId:{value.TargetId} Money:{value.Money}"); var source = transactionContexts.UserInfos.First(user => user.Id == value.SourceId); if (source.Money < value.Money)
throw new Exception(); var target = transactionContexts.UserInfos.First(user => user.Id == value.TargetId); source.Money -= value.Money;
target.Money += value.Money; transactionContexts.UserInfos.Update(source);
transactionContexts.UserInfos.Update(target); await transactionContexts.SaveChangesAsync(); await Console.Out.WriteLineAsync($"PayOrderEvent After:{DateTime.Now}");
}
}
配置依赖注入流程
MassTransit的通讯选择MassTransit.RabbitMQ,这个库,也支持很多MQ,个人图方便就选的rabbitmq,后期要更换,修改一下依赖注入的配置即可
services.AddScoped<TransactionConsumer>(); services.AddMassTransit(c =>
{
c.AddConsumer<TransactionConsumer>();
c.AddBus(serviceProvider =>
{
return Bus.Factory.CreateUsingRabbitMq(cfg =>
{
var host = cfg.Host(new Uri("rabbitmq://localhost/"), hst =>
{
hst.Username("guest");
hst.Password("guest");
}); cfg.ReceiveEndpoint("Transaction", config =>
{
config.ConfigureConsumer<TransactionConsumer>(serviceProvider);
});
});
});
});
这样就完成了引入Masstransit做数据通讯部分,Masstransit支持Rpc模式,也支持Publish/Subscribe,后续的文章会混搭Rpc模式和发布订阅的模式,主要根据业务场景的选择做调整
编写演示例子
在Configure里面配置初始化数据
using (var serviceScoped = app.ApplicationServices.CreateScope())
{
var serviceProvider = serviceScoped.ServiceProvider;
var context = serviceProvider.GetRequiredService<TransactionContexts>(); context.UserInfos.Add(new UserInfo
{
Id = ,
NickName = "Form",
CreateTime = DateTime.Now,
LastOptions = DateTime.Now,
Money =
});
context.UserInfos.Add(new UserInfo
{
Id = ,
NickName = "To",
CreateTime = DateTime.Now,
LastOptions = DateTime.Now,
Money =
}); context.SaveChanges();
}
#endregion
给Configure方法增加一个注入的接口
public void Configure(IApplicationBuilder app, IHostingEnvironment env, IApplicationLifetime lifetime)
{
//。。。
var busControl = app.ApplicationServices.GetRequiredService<IBusControl>(); lifetime.ApplicationStarted.Register(busControl.Start);
lifetime.ApplicationStopped.Register(busControl.Stop);
}
依旧是省事儿的Configure方法里面写的一个管道
app.Run(async (context) =>
{
var serviceProvider = context.RequestServices; var bus = serviceProvider.GetRequiredService<IBusControl>(); await bus.Publish(new PayOrderCommand
{
SourceId = 1,
TargetId = 2,
Money = 2000
}); await context.Response.WriteAsync("Hello World!");
});
后话
写在默认的index管道,会触发一个有意思的BUG,会两次执行,但是主键是唯一的,这样重复执行会抛出异常,提示主键已存在
这个例子是先铺垫一下,很多有意思的实现还没开展
1、某个业务需要执行过程和下游强一致性,要么一起完成,要么当场失败
2、某个业务完成最终一致性,这个过程,如果失败需要重试/限流/熔断
3、某个业务完成最终一致性,失败了则触发补偿
先铺垫一下,后空余时间逐一编写示例演示
现在的代码一点都不优雅,还有很强的面向于具体实现的,后续会逐步高度抽象化,从相对麻烦的代码,变成纯crud拿来主义,业务层 和后续代码的流程无感
打个小广告
如果有技术交流可以加NCC的群 24791014、436035237,我在群里,有任何关于asp.net core/Masstransit的问题或者建议都可以与我交流,非常欢迎
示例代码:
https://github.com/htrlq/Crud.Sample
基于asp.net core 从零搭建自己的业务框架(一)的更多相关文章
- 基于asp.net core 从零搭建自己的业务框架(三)
前言 根据业务处理部分,单体马上就能得知错误与否,快速做出处理,而分布式系统,会因为各种原因,无法如同单体一样立刻处理,所以这个时候需要 处理异常 的,做 补偿.转移.人工干预. 当然也可以直接在消费 ...
- 基于asp.net core 从零搭建自己的业务框架(二)
前言 对于项目的迭代,如何降低复杂性的要求高于性能以及技术细节的 一个易用的项目,才能迭代到比拼性能,最后拼刺刀的阶段 传统单体项目,都是传统三层,直接请求响应的模式,这类称为Rpc模式,易用性上非常 ...
- 基于ASP.Net Core开发的一套通用后台框架
基于ASP.Net Core开发一套通用后台框架 写在前面 这是本人在学习的过程中搭建学习的框架,如果对你有所帮助那再好不过.如果您有发现错误,请告知我,我会第一时间修改. 知其然,知其所以然,并非重 ...
- 基于ASP.NET Core 3.0快速搭建Razor Pages Web应用
前言 虽然说学习新的开发框架是一项巨大的投资,但是作为一个开发人员,不断学习新的技术并快速上手是我们应该掌握的技能,甚至是一个.NET Framework开发人员,学习.NET Core 新框架可以更 ...
- 用VSCode开发一个基于asp.net core 2.0/sql server linux(docker)/ng5/bs4的项目(1)
最近使用vscode比较多. 学习了一下如何在mac上使用vscode开发asp.netcore项目. 这里是我写的关于vscode的一篇文章: https://www.cnblogs.com/cgz ...
- 基于ASP.NET Core 6.0的整洁架构
大家好,我是张飞洪,感谢您的阅读,我会不定期和你分享学习心得,希望我的文章能成为你成长路上的垫脚石,让我们一起精进. 本节将介绍基于ASP.NET Core的整洁架构的设计理念,同时基于理论落地的代码 ...
- 如何基于asp.net core的Identity框架在mysql上作身份验证处理
首先了解这个概念,我一开始也是理解和掌握基本的概念,再去做程序的开发.Identity框架是微软自己提供,基于.net core平台,可拓展.轻量 级.面向多个数据库的身份验证框架.IdentityS ...
- 基于Asp.Net Core的简单社区项目源代码开源
2019年3月27号 更新版本 本项目基于 ASP.NET CORE 3.0+EF CORE 3.0开发 使用vs2019 +sqlserver 2017(数据库脚本最低支持sql server 20 ...
- AServer - 基于Asp.net core Kestrel的超迷你http服务器
AServer是基于ASP.NET Core Kestrel封装的一个超迷你http服务器.它可以集成进你的Core程序里,用来快速的响应Http请求,而不需要集成整个ASP.NET Core MVC ...
随机推荐
- Django---进阶3
目录 无名有名分组反向解析 路由分发 名称空间(了解) 伪静态(了解) 虚拟环境(了解) django版本区别 视图层 三板斧 JsonResponse对象 form表单上传文件及后端如何操作 req ...
- SqlLite用SQLiteTransaction快速导入数据
mysql与sql server都有整表导入的类库,但是查遍了资料发现sqlLite没有,除非自己去写个,发现用SQLiteTransaction导入数据也很快,附上代码 /// <summar ...
- 介绍下重绘和回流(Repaint & Reflow),以及如何进行优化
1. 浏览器渲染机制 浏览器采用流式布局模型(Flow Based Layout) 浏览器会把HTML解析成DOM,把CSS解析成CSSOM,DOM和CSSOM合并就产生了渲染树(Render Tre ...
- web 部署专题(二):gunicore 并发部署(用gunicorn+gevent启动Flask项目)
转自:https://blog.csdn.net/dutsoft/article/details/51452598 Flask,webpy,Django都带着 WSGI server,当然性能都不好, ...
- CSS之Bootstrap(快速布局)
简介 什么是Bootstrap? Bootstrap官网 框架:库 lib library jQuery作为一个框架来讲,提供一套比较便捷的操作DOM的方式 把大家都需要的功能预先写好到一些文件 这就 ...
- 基于.NetCore3.1系列 ——认证授权方案之Swagger加锁
一.前言 在之前的使用Swagger做Api文档中,我们已经使用Swagger进行开发接口文档,以及更加方便的使用.这一转换,让更多的接口可以以通俗易懂的方式展现给开发人员.而在后续的内容中,为了对a ...
- Ethical Hacking - Web Penetration Testing(13)
OWASP ZAP(ZED ATTACK PROXY) Automatically find vulnerabilities in web applications. Free and easy to ...
- Python面试题!百度大牛总结十条Python面试题陷阱
无论是应聘Python web开发,爬虫工程师,或是数据分析,还是自动化运维,都涉及到一些基础的知识!我挑了一些Python的基础面试题,看看你能不能的答上来,也许面试的同学用的着! 问题1:请问如何 ...
- Github下载文件慢试试这款工具吧
https://g.widora.cn 可能随时崩溃哦~~暂时还不支持超过 2GB 的仓库,服务器选自日本 vultr 设计思路:通过在日本的 VPS clone -r 代码,下载后压缩成 zip 再 ...
- Shell基本语法---处理海量数据的awk命令
awk命令 其实是一门编程语言,支持条件判断,数组,循环等功能,与grep,sed被称为linux三剑客 之所以叫AWK是因为取其三位创始人 Alfred Aho,Peter Weinberger, ...