最近这段时间感觉自己松懈了,以前每天下班不管多晚都会坚持看会书或者看会博客,最近学了下微服务,首先还是需要了解一下基本的概念,那些长篇的理论就没有照抄过来了,只是为记录下自己学到了多少。
一、微服务基本概念
1.什么是微服务
微服务顾名思义可以理解为微小的服务
,在理解它之前,我们需要了解什么是服务,我是这样理解的,现实中我们去消费,商家提供商品,那么商家就是为我们服务,但是在软件中服务的定义,我觉得可以这样理解,提供给其他系统数据,提供页面,提供文档的系统,就是服务
,而微服务
就是只做一件事
的服务,
2.微服务的表现形式及特征
那么它是怎么样的形式表现呢?在企业管理系统中,OA提供了公告,考勤,组织人员系统提供了人员信息,组织信息,文件系统提供了图片,视频,文本,上传下载功能,注意这些不是微服务,他们是服务,微服务是在它们之上演变的,例如OA系统拆分公告和考勤作为单独的服务,那么它们就是微服务。至于在现实中我们的系统该如何去拆分,应该根据自己的业务来划分,因为所有的微服务围绕业务领域而生的。
除了上面介绍的方式去认识微服务外,其实微服务还包含一些特征,首先是单一性
,因为微服务遵从单一职责,一个服务之作一件事,从而产生了灵活性
,各个微服务之间互不影响,对比集成在一起的大包大揽的服务,微服务有了灵活和单一的特征后,同样充分保证了健壮性
,和可复用性
二、搭建简单的微服务项目
在这里主要主要是利用ABP vNext创建微服务项目,因为有cli脚手架工具,加上本身的快速开发框架,搭建起来很多操作都继承了,比较事半功倍一些。我们作为练习的微服务主要从电商系统简单抽象而来,按照业务领域分离出4个模块,包含订单、商品、支付和用户模块分别作为单独的微服务项目
1.创建基本项目
1.创建文件夹用于存放项目,然后使用ABP CLI创建一个指定版本的控制台项目得到一个解决方案,我们取名为Fun.Microservices
abp new Fun.Microservices -t console -o -v 4.4.3
2.进入Fun.Microservices目录到解决方案同级目录中,然后创建各个模块,将项目输出到moduls文件夹下
cd Fun.Microservices
//创建订单模块
abp new Fun.Order-t module --dbms mysql --no-ui -o moduls/Fun.Order-v 4.4.3
//创建商品模块
abp new Fun.Product -t module --dbms mysql --no-ui -o moduls/Fun.Product -v 4.4.3
//创建支付模块
abp new Fun.Payment-t module --dbms mysql --no-ui -o moduls/Fun.Payment-v 4.4.3
//创建用户模块
abp new Fun.User -t module --dbms mysql --no-ui -o moduls/Fun.User -v 4.4.3
3.然后使用VS打开项目,首先右键创建一个名为modules的解决方案文件夹,然后在modules模块下面创建4个模块的解决方案文件夹
4.在对应的模块文件夹上手动加入刚才创建的模块,右键添加现有项目,将第二步中创建的模块的src文件夹中,将每一个项目文件引入
5.引入完成后,再次进入解决方案同级目录,创建一个microservices文件夹,然后将模块中host文件夹下的Fun.xxxx.HttpApi.Host
项目拷贝到microservices文件夹下
6.在vs中创建解决方案文件夹microservices,然后右键继续添加现有项目,将刚才拷贝的每一个Fun.xxxx.HttpApi.Host
项目引入进来
7.打开解决方案,因为我们进行文件夹拆分后,依赖项发生了变化,删除原有项目依赖,此时我们应该重新在每一个Host项目引入模块项目,分别引入Fun.xxxx.Application
、Fun.xxxx.EntityFramworkCore
、Fun.xxxx.HttpApi
三个项目依赖
2.编译项目
我们根据上面的步骤创建了一个微服务项目的大概结构,由于分开分开文件夹后,会有一些地方有冲突,要想能重新编译和运行项目,需要手动解决项目中的错误
1.首先删除各个Fun.xxxx.HttpApi.Host
中报错的多租户项目代码。
2.删除Volo.AuditLogging.EntityFramworkCore
日志模块的引用。
3.删除Volo.PermissionManagement.EntityFramworkCore
模块引用。
4.删除Volo.SettingManagement.EntityFramworkCore
模块引用。
5.在各个Fun.xxxx.HttpApi.Host
项目的HttpApiHostModule中删除依赖。
6.在各个Fun.xxxx.HttpApi.Host
项目依赖Mysql驱动,然后将Mysql加入到依赖注入容器。
public override void ConfigureServices(ServiceConfigurationContext context)
{
Configure<AbpDbContextOptions>(options =>
{
//options.UseSqlServer();
options.UseMySQL();
});
}
7.修改OrderHttpApiHostMigrationsDbContextFactory 中的将创建上下文的代码改为mysql
public OrderHttpApiHostMigrationsDbContext CreateDbContext(string[] args)
{
var builder = new DbContextOptionsBuilder<OrderHttpApiHostMigrationsDbContext>()
//.UseSqlServer(configuration.GetConnectionString("Order"));
.UseMySql(configuration.GetConnectionString("Order"),MySqlServerVersion.LatestSupportedServerVersion);
return new OrderHttpApiHostMigrationsDbContext(builder.Options);
}
8.修改appsetting.json中的配置文件
"ConnectionStrings": {
"Default": "Server=localhost;Port=3306;Database=Order;Uid=root;Pwd=myPassword;",
"Order": "Server=121.4.69.34;Database=Order_Module;Uid=root;Pwd=123456789"
}
9.编译解决方案
3.迁移数据
1.在各个Fun.xxxx.HttpApi.Host
下执行迁移命令,步骤为创建迁移,添加迁移文件,执行迁移文件
dotnet ef
dotnet ef migrations add xxxxservice
dotnet ef database update
2.dotnet run 运行微服务项目
3.以订单为例,在领域层Fun.Order.Domain添加一个继承自AggregateRoot</Guid>的领域实体Order和实体 OrderItem
public class Orders : AggregateRoot<Guid>
{
public string OrderType { set; get; }
public string OrderSn { set; get; }
public string OrderTotalPrice { set; get; }
public DateTime Createtime { set; get; }
public DateTime Updatetime { set; get; }
public ICollection<OrderItem> OrderItems { set; get; }
}
public class OrderItem: Entity<Guid>
{
public Guid OrderId { set; get; }
public decimal ItemPrice { set; get; }
}
4.在Fun.Order.EntityFrameworkCore中的上下文中配置领域
public DbSet<Fun.Domain.Order.Orders> Orders { get; set; } // 配置订单领域(以领域为单位)
5.在OrderDbContextModelCreatingExtensions中配置订单到订单明细表的映射一对多关系
//配置实体构建
builder.Entity<Fun.Domain.Order.Orders>(t =>
{
t.ConfigureByConvention();
t.HasMany(u=>u.OrderItems).WithOne().HasForeignKey(u=>u.OrderId).IsRequired();
});
6.删除迁移文件,重新按照步骤执行,会在数据库生成对应的数据表
三、Net Core Api结合ABP创建聚合服务
上面完整的记录了利用ABP vNext
创建单个微服务的过程,下面我们来利用普通的API应用结合ABP vNext 搭建一个商品和订单的聚合服务Fun.OrderDetailServices
1.传统项目改造为ABP项目步骤
1.下载Nuget 包 Volo.Abp.AspNetCore.Mvc
和Volo.Abp.Autofac
2.在类同级目录创建模块类,引入依赖,并重写AbpModule
中的ConfigureServices
和OnApplicationInitialization
[DependsOn(typeof(AbpAspNetCoreMvcModule))]
[DependsOn(typeof(AbpAutofacModule))]
public class OrderDetailsServicesModule : AbpModule
{
}
3.将Startup中添加中间件的代码,剪切到模块类的OnApplicationInitialization
中,将注入容器代码剪切到模块类的ConfigureServices
,并改写原有的代码
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddApplication<OrderDetailsServicesModule>();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.InitializeApplication();
}
}
4.在Program
中添加AutoFac
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
}).UseAutofac();
2.配置Abp微服务通信
1.在聚合服务引入需要的微服务的Fun.xxx.HttpApi.Client
的项目引用
2.在需要接入微服务的聚合服务,appsettings引入远程服务配
- 名称一定要对应
Fun.xxx.HttpApi.Client
模块中的RemoteServiceName
- BaseUrl对应微服务
Fun.xxxx.HttpApi.Host
中Properties下的launchSettings的applicationUrl
"RemoteServices": {
"Order": {
"BaseUrl": "https://localhost:44361/"
},
"Product": {
"BaseUrl": "https://localhost:44372/"
}
}
3.在聚合服务创建的控制器中使用属性注入应用服务,就可以在内部使用了
public IProductAppService _ProductAppService { set; get; }
public IOrderAppService _OrderAppService { set; get; }
四、配置框架问题
1.Swagger不显示Api
运行项目swagger不显示自动api,需要在IOC中配置发现控制器
//IOC 注入 ConfigureConventionalControllers();
private void ConfigureConventionalControllers()
{
Configure<AbpAspNetCoreMvcOptions>(options =>
{
options.ConventionalControllers.Create(typeof(OrderApplicationModule).Assembly, options => {
options.RootPath = "OrderService";
});
});
}
2.AutoMap映射
AutoMap映射字段不一致,领域实体字段多于DTO实体,需要在模块类的IOC中配置Automap时关闭验证代码
public class OrderApplicationModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
context.Services.AddAutoMapperObjectMapper<OrderApplicationModule>();
Configure<AbpAutoMapperOptions>(options =>
{
//验证dto 实体是否必须一致,最好关掉被坑了1个多小时
options.AddMaps<OrderApplicationModule>(validate: false);
});
}
}
3.主从表迁移文件
继承自FullAuditedEntity
目前需要在DbContextModelCreatingExtensions
类的配置方法中 配置对应表字段的默认值
builder.Entity<ProductImage>(b =>
{
b.ConfigureByConvention();
b.Property(m=>m.IsDeleted).HasDefaultValue(false);
});