有梦想的鱼
写代码我得再认真点,当我最终放下键盘时,我实在不想仍有太多疑惑

最近这段时间感觉自己松懈了,以前每天下班不管多晚都会坚持看会书或者看会博客,最近学了下微服务,首先还是需要了解一下基本的概念,那些长篇的理论就没有照抄过来了,只是为记录下自己学到了多少。

一、微服务基本概念

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.ApplicationFun.xxxx.EntityFramworkCoreFun.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.MvcVolo.Abp.Autofac

2.在类同级目录创建模块类,引入依赖,并重写AbpModule中的ConfigureServicesOnApplicationInitialization

 [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);
});

posted on 2022-07-06 22:04  有梦想的鱼i  阅读(758)  评论(0编辑  收藏  举报