ABP VNext从单体切换到微服务
注:此处的微服务只考虑服务部分,不考虑内外层网关、认证等。
ABP VNext从单体切换到微服务,提供了相当大的便利性,对于各模块内部不要做任何调整,仅需要调整承载体即可。
ABP can help you in that point by offerring a microservice-compatible, strict module architecture where your module is splitted into multiple layers/projects and developed in its own VS solution completely isolated and independent from other modules. Such a developed module is a natural microservice yet it can be easily plugged-in a monolithic application.
分层架构
- ABP VNext自身提供的分层方式如下图,核心部分是Application、Domain与EntityFrameworkCore层,至于HttpApi.Client与HttpApi都是属于将应用去承载到其他系统之上的。
 
![]()
- Controller层在ABP VNext中被削弱了,VNext提供了将Application提供动态Controller,所以可能习惯了将部分逻辑写在控制器中的会有些唐突,但理解就行。
 
![]()
规划层次结构
- 新建空文件夹,取名随意(本次取名GravelService),用于存放整体项目。
 - 在上一步的空文件夹内新建空白解决方案,可以用VS去生成或是按照dotnet的命令生成。
 
![]()
- 习惯上按照ABP VNext给定的微服务Demo中的格式创建三个文件夹,思路清晰。当然提供的微服务Demo还有更多文件夹,但是这次只关注这三个。
 
![]()
增加三个模块
- 设计三个限界上下文并绘制上下文映射。依照常见的订单、产品与客户划分成三个限界上下文。并规划好订单作为下游依赖产品与客户作为的上游。
 
![]()
- 从官网直接下载三个模块,或是按照命令行去生成(前提是已经安装了ABP CLI),无论哪种方式,确保准备好就行,依照模块化思想,将其存到modules文件夹下。
 
![]()
- 如本次准备了三个模块Product,Order,Customer,依照实际需要,可以去精简一下内部文件,此处各模块内部我只留着src和test文件夹。
 
![]()
- 对于customer模块的src内部,具体生成的文件夹如下,对于其中的HttpApi、HttpApi.Client及MongoDB,我采取了剔除,无需这部分(在稍后的承载体中有同等功能)。
 
![]()
清理之后变得简简单单的。
![]()
对于test内部,同样采取剔除无需的HttpApi.CLient、MongoDB文件夹(注:此处的无需只是针对我而言,如果有需要还是留着吧)。
![]()
清理之后,层次划分变得简简单单的。
![]()
- 改造下各模块内给定的Sample案例,改造成各模块命名开头,方便加以区分。以Customer模块为例,将自带的Sample案例更名成Customer,仅作该项改动。
 
![]()
模拟服务调用
- 对于Order与Product模块内增加一个下游访问上游服务的链路,方便验证从单体跨越到微服务,底层服务的无需任何改动。对于下游访问上游服务的调用形式,了解到的有两种方式。
 
- 当前上下文的应用层直接依赖其他上下文的应用服务;
 - 当前上下文设定出防腐层(在限界上下文间增加一层隔离,方便保证依赖限界上下文存在变动时只需更改隔离层),在防腐层的实现中去依赖或远程调用其他上下文的应用服务;
 
- 我个人倾向于增加防腐层,虽然在VNext中并无相关说法。具体使用时,在领域层或应用层增加防腐层接口,及在EF Core层给与防腐层实现,此时的EF Core层,更应该更名为基础设施层,而不单单是作为资源库的形式。
 
![]()
- 在Order中Domain层增加ServiceClients文件夹用于存放防腐层接口,而在EntityFrameworkCore层增加ServiceClients文件夹用于存放防腐层实现。
 
- 在防腐层接口中增加IProductServiceClient。
 
public interface IProductServiceClient : ITransientDependency
{
    Task<int> GetProductId();
}
- 在防腐层实现中增加ProductServiceClient。
 - 在Order中的EntityFrameworkCore层引用Product模块内的Application.Contracts层。
 - 依赖ProductAppService并完成调用。
 
public class ProductServiceClient : IProductServiceClient
{
    private readonly IProductAppService _productAppService;
    public ProductServiceClient(IProductAppService productAppService)
    {
        _productAppService = productAppService;
    }
    public async Task<int> GetProductId()
    {
        var productDto = await _productAppService.GetAsync();
        return productDto.Value;
    }
}
- 在OrderAppService中依赖防腐层接口,并完成调用。
 
public class OrderAppService : OrderManagementAppService,   IOrderAppService
{
    private readonly IProductServiceClient _productServiceClient;
    public OrderAppService(IProductServiceClient productServiceClient)
    {
        _productServiceClient = productServiceClient;
    }
    public async Task<OrderDto> GetAsync()
    {
        var productId = await _productServiceClient.GetProductId();
        ...
    }
}
- 这样一来,在防腐层实现中便可以使用到Product上下文的应用服务了,对于这个应用服务的请求是当前进程内的还是进程间的,由具体的承载方式而决定。
 
![]()
大单体承载
将依照如下图,将三个模块承载到GravelService.Host上。
![]()
- 新建一个GravelService.Host的项目,采用WebApi类型即可。存放到microservices文件夹下,这样方便理解整个层级划分。
 
![]()
- 其次依赖一些基本的Nuget包,诸如ABP自身的及方便展示Api的Swagger。本次不考虑实体的创建,也就不考虑EF Core包的依赖了。
 
- Volo.Abp
 - Volo.Abp.AspNetCore.MultiTenancy
 - Volo.Abp.AspNetCore.Mvc
 - Volo.Abp.Autofac
 - Swashbuckle.AspNetCore
 
- 引用各模块内的Application层及EntityFrameworkCore层。模仿着给定的微服务Demo,完成一波CV操作。增加一个类出来承担着服务与中间件的配置。
 
![]()
- 在其中依赖引用的各Application层及EntityFrameworkCore层中的Module,这样一来模块依赖就搞定了。
 
[DependsOn(
    typeof(AbpAutofacModule),
    typeof(AbpAspNetCoreMvcModule),
    typeof(AbpAspNetCoreMultiTenancyModule),
    typeof(CustomerManagementApplicationModule),
    typeof(CustomerManagementEntityFrameworkCoreModule),
    typeof(OrderManagementApplicationModule),
    typeof(OrderManagementEntityFrameworkCoreModule),
    typeof(ProductManagementApplicationModule),
    typeof(ProductManagementEntityFrameworkCoreModule)
   )]
public class GravelServiceHostModule : AbpModule
{
  ...
}
- 增加服务配置,将各模块中的应用服务动态转换成Api控制器。
 
Configure<AbpAspNetCoreMvcOptions>(options =>
{
    options.ConventionalControllers
        .Create(typeof(CustomerManagementApplicationModule).Assembly,
        opts =>
        {
            opts.RootPath = "CustomerManagement";
        })
        .Create(typeof(OrderManagementApplicationModule).Assembly,
        opts =>
        {
            opts.RootPath = "OrderManagement";
        })
        .Create(typeof(ProductManagementApplicationModule).Assembly,
        opts =>
        {
            opts.RootPath = "ProductManagement";
        });
});
- 然后,启动即可,所有模块中的接口都承载到了该承载体中。
 
![]()
- 通过访问Order的api/OrderManagement/order接口获得返回的9527,也就验证了,防腐层内去访问Product上下文的应用服务。
 
![]()
此时是在同一进程内,使用到的是ProductAppService的具体实现,断点查看也可看出当前进程内的请求,依赖其他上下文服务的应用服务为ApplicationServiceProxy。
![]()
多服务承载
依照如下图开始切换承载模式,只更改承载体,将一个大的承载体切分成各上下文独自承载体。
![]()
- 新建三个和GravelService.Host同等的WebApi项目。在GravelService.Host上裁剪即可,每个独立的承载体只拥有自身的上下文。
 
![]()
- 依据上下文映射关系,对处于下游的Order承载体增加对上游Product的依赖,在Order承载体中增加对Product模块中Application.Contracts层的依赖。并在服务配置中完成依赖(第7行)。
 
[DependsOn(
    typeof(AbpAutofacModule),
    typeof(AbpAspNetCoreMvcModule),
    typeof(AbpAspNetCoreMultiTenancyModule),
    typeof(OrderManagementApplicationModule),
    typeof(OrderManagementEntityFrameworkCoreModule),
    typeof(ProductManagementApplicationContractsModule)
    )]
public class OrderServiceHostModule : AbpModule
{
...
}
- 配置远程服务代理,在服务配置中设置从Order承载体到Product承载体的远程服务请求。在Order承载体中增加Volo.Abp.Http.Client包并在服务配置中完成依赖。
 
[DependsOn(
    ...
    typeof(AbpHttpClientModule),
    ...
    )]
public class OrderServiceHostModule : AbpModule
{
    public override void ConfigureServices(ServiceConfigurationContext context)
    {
        var configuration = context.Services.GetConfiguration();
        ...   
        context.Services.AddHttpClientProxies(         typeof(ProductManagementApplicationContractsModule).Assembly);
        ...
    }
}
- 在配置源appsettings文件中加入对Product承载体的远程调用地址
 
 {
  "RemoteServices": {
    "Default": {
      "BaseUrl": "http://localhost:57687"
    }
}
- 更改项目配置,启动多个文件,一并启动三个独立承载体,再次调用Order中的Get请求,同样获得了从Product上下文的应用服务中的数据
 
![]()
- 这次采用的不同进程间的承载方式,通过防腐层,然后经ABP提供的Http代理服务调用Product承载体,可以断点查看当前在防腐层中的AppService类型,已经不再是直接使用ProductAppService了。
 
![]()
真香
从大单体承载切换多服务承载,Modules部分不需要做任何更改,着实方便。至于内部的远程调用,本身ABP VNext还存在一些问题像类似集合的Get请求在3.1的版本中才做了修复,但是这丝毫不影响这一巨人的前行。
2020-09-26,望技术有成后能回来看见自己的脚步
ABP VNext从单体切换到微服务的更多相关文章
- 持续提升程序员幸福指数——使用abp vnext设计一款面向微服务的单体架构
		
可能你会面临这样一种情况,在架构设计之前,你对业务不甚了解,需求给到的也模棱两可,这个时候你既无法明确到底是要使用单体架构还是使用微服务架构,如果使用单体,后续业务扩展可能带来大量修改,如果使用微服务 ...
 - 使用SpringCloud将单体迁移至微服务
		
使用SpringBoot构建单体项目有一段时间了,准备对一个老项目重构时引入SpringCloud微服务,以此奠定后台服务能够应对未知的业务需求. 现在SOA架构下的服务管理面临很多挑战,比如面临一个 ...
 - 系列免费课程汇总(Java、单体应用、微服务、物联网、SaaS)
		
概述 2020年春节尽在眼前,又忙碌了一年的你一定有很多收获:是升职加薪,还是收获爱情?是买房置业,还是新添人口? 我在2019年的最大收获是:我的第二枚千金诞生,使我顺利加入富豪行列! 新年伊始我们 ...
 - 学习一下 SpringCloud (一)-- 从单体架构到微服务架构、代码拆分(maven 聚合)
		
一.架构演变 1.系统架构.集群.分布式系统 简单理解 (1)什么是系统架构? [什么是系统架构?] 系统架构 描述了 在应用程序内部,如何根据 业务.技术.灵活性.可扩展性.可维护性 等因素,将系统 ...
 - Service Mesh架构的持续演进  单体模块化 SOA 微服务 Service Mesh
		
架构不止-严选Service Mesh架构的持续演进 网易严选 王育松 严选技术团队 2019-11-25 前言同严选的业务一样,在下层承载它的IT系统架构一样要生存.呼吸.增长和发展,否则过时的.僵 ...
 - Abp vNext 切换MySql数据库
		
Abp vNext是Abp的下一代版本,目前还在经一步完善,代码已经全部重写了,好的东西保留了下来,去除了很多笨重的东西,从官宣来看,Abp vNext主要是为了以后微服务架构而诞生的. 从源码来看, ...
 - ABP vNext
		
一.简要介绍# ABP vNext 是 ABP 框架作者所发起的新项目,截止目前 (2019 年 8 月 20 日) 已经拥有 1400 多个 Star,最新版本号为 v 0.19.0,可以尝试用于生 ...
 - abp vnext 开发快速入门 1 认识框架
		
最近在做一个项目,用的框架是Abp vnext ,不是Abp, 我自己也是刚开始用这个框架来做项目,难免要查资料,这个框架官方有中文文档,可以到官网www.abp.io 去查看,国内也有一些写了相关的 ...
 - Chris Richardson微服务翻译:重构单体服务为微服务
		
Chris Richardson 微服务系列翻译全7篇链接: 微服务介绍 构建微服务之使用API网关 构建微服务之微服务架构的进程通讯 微服务架构中的服务发现 微服务之事件驱动的数据管理 微服务部署 ...
 
随机推荐
- SAP ABAP RFC接口通用日志工具:abap fm logger
			
很早之前就想写个能记录函数模块日志的通用工具,最早尝试时,没有想清楚插入代码的体积问题.在一些群友的提醒下,了解到可以用宏来处理这一问题.不过当时比较忙,就没有动笔.最近又想起这件事,花了2天完成了一 ...
 - Java并发必知必会第三弹:用积木讲解ABA原理
			
Java并发必知必会第三弹:用积木讲解ABA原理 可落地的 Spring Cloud项目:PassJava 本篇主要内容如下 一.背景 上一节我们讲了程序员深夜惨遭老婆鄙视,原因竟是CAS原理太简单? ...
 - 跟我一起学.NetCore之日志(Log)模型核心
			
前言 鲁迅都说:没有日志的系统不能上线(鲁迅说:这句我没说过,但是在理)!日志对于一个系统而言,特别重要,不管是用于事务审计,还是用于系统排错,还是用于安全追踪.....都扮演了很重要的角色:之前有很 ...
 - C++ int与char[]的相互转换
			
C++ int与char[]的相互转换 一.itoa函数与atio函数①把int类型数字转成char类型,可以使用itoa函数. itoa函数原型: char*itoa(int value,char* ...
 - Matrix4x4
			
Unity3D开发之Matrix4x4矩阵变换 https://www.cnblogs.com/hewei2012/p/4190282.html Matrix4x4 4x4矩阵 http://wiki ...
 - 浅析一个lua文件窥slua工作机制
			
slua的东西不是几句话能讲得完,这里只说结论不说原因,原因有空写个Little Slua工程来解释,下面注释中有几个关键点:LuaVar系列类:LuaFunction,LuaTable,LuaDel ...
 - <string name="xxx"> 的复杂用法:格式化及使用html标签
			
1.官方文档: https://developer.android.com/guide/topics/resources/string-resource 2.格式化字符串 2.1 示例 <res ...
 - 敏捷转型谁先动:老总,项目经理or团队
			
摘要: 敏捷转型成功的企业究竟是从老总开始?还是从项目经理开始?还是团队本身具有这种意识?相信还有很多想要转型敏捷的公司都存在这样的疑问. 从06年首届敏捷中国开发者大会召开到现在,敏捷方法在国内的应 ...
 - 绝世好题(线性dp)
			
给定一个长度为n的数列ai,求ai的子序列bi的最长长度,满足bi&bi-1!=0(2<=i<=len). Input 输入文件共2行. 第一行包括一个整数n. 第二行包括n个 ...
 - 二.spring boot第一个web服务
			
通过<一.spring boot初始化项目>我们已经会初始化spring boot项目,那本篇文章就说明下初始化项目的具体内容,并编写第一个Hello页面. 项目结构 mvnw.mvnw. ...