ABP微服务系列学习-搭建自己的微服务结构(一)

在原本的结构里面,由于默认服务引用的都是ABP原生的模块,所以结构目录里面没有包含modules目录,这里我们添加一个modules目录,用于存放我们的自定义模块。
在shared里面,我们再抽一个EventData的模块,用于消息队列共用数据实体。修改后结构如下图所示:
ABP微服务系列学习-搭建自己的微服务结构(一)

开始搭建

由于我们没有商业版的代码生成器,那就纯手工搭建这个结构了。这里我们使用VS Code作为编辑器配合dotnet cli操作
创建新的空白解决方案,后续通过再VS来编辑解决方案的内容。

dotnet new sln -n FunShow 

然后在解决方案目录下创建目录
ABP微服务系列学习-搭建自己的微服务结构(一)

创建Shared项目

使用dotnet cli创建shared目录下的项目

dotnet new classlib -n FunShow.Shared.Hosting -f net7.0 dotnet new classlib -n FunShow.Shared.Hosting.AspNetCore -f net7.0 dotnet new classlib -n FunShow.Shared.Hosting.Gateways -f net7.0 dotnet new classlib -n FunShow.Shared.Hosting.Microservices -f net7.0 dotnet new classlib -n FunShow.Shared.Localization -f net7.0 dotnet new classlib -n FunShow.Shared.EventData -f net7.0 dotnet new console -n FunShow.DbMigrator -f net7.0 

编辑.csproj文件

FunShow.Shared.Hosting

<Project Sdk="Microsoft.NET.Sdk">    <PropertyGroup>     <TargetFramework>net7.0</TargetFramework>     <ImplicitUsings>enable</ImplicitUsings>     <Nullable>enable</Nullable>     <RootNamespace>FunShow.Shared.Hosting</RootNamespace>   </PropertyGroup>      <ItemGroup>         <PackageReference Include="Serilog.Extensions.Logging" Version="3.1.0" />         <PackageReference Include="Serilog.Sinks.Async" Version="1.5.0" />         <PackageReference Include="Serilog.Sinks.File" Version="5.0.0" />         <PackageReference Include="Serilog.Sinks.Console" Version="4.1.0" />     </ItemGroup>      <ItemGroup>         <PackageReference Include="Volo.Abp.Autofac" Version="7.0.0" />         <PackageReference Include="Volo.Abp.Data" Version="7.0.0" />     </ItemGroup> </Project> 

FunShow.Shared.Hosting.AspNetCore

<Project Sdk="Microsoft.NET.Sdk">    <PropertyGroup>     <TargetFramework>net7.0</TargetFramework>     <ImplicitUsings>enable</ImplicitUsings>     <Nullable>enable</Nullable>     <RootNamespace>FunShow.Shared.Hosting.AspNetCore</RootNamespace>   </PropertyGroup>   <ItemGroup>     <PackageReference Include="Serilog.AspNetCore" Version="6.1.0" />     <PackageReference Include="Serilog.Sinks.Async" Version="1.5.0" />     <PackageReference Include="Serilog.Sinks.ElasticSearch" Version="8.4.1" />     <PackageReference Include="prometheus-net.AspNetCore" Version="4.1.1" />   </ItemGroup>    <ItemGroup>     <PackageReference Include="Volo.Abp.Swashbuckle" Version="7.0.0" />     <PackageReference Include="Volo.Abp.AspNetCore.Serilog" Version="7.0.0" />     <ProjectReference Include="..FunShow.Shared.HostingFunShow.Shared.Hosting.csproj" />   </ItemGroup> </Project> 

FunShow.Shared.Hosting.Gateways
这里网关我们使用yarp

<Project Sdk="Microsoft.NET.Sdk">    <PropertyGroup>     <TargetFramework>net7.0</TargetFramework>     <ImplicitUsings>enable</ImplicitUsings>     <Nullable>enable</Nullable>   </PropertyGroup>    <ItemGroup>     <PackageReference Include="Yarp.ReverseProxy" Version="1.1.1" />   </ItemGroup>    <ItemGroup>     <ProjectReference Include="..FunShow.Shared.Hosting.AspNetCoreFunShow.Shared.Hosting.AspNetCore.csproj" />   </ItemGroup> </Project>  

FunShow.Shared.Hosting.Microservices

<Project Sdk="Microsoft.NET.Sdk">    <PropertyGroup>     <TargetFramework>net7.0</TargetFramework>     <ImplicitUsings>enable</ImplicitUsings>     <Nullable>enable</Nullable>   </PropertyGroup>    <ItemGroup>     <ProjectReference Include="..FunShow.Shared.Hosting.AspNetCoreFunShow.Shared.Hosting.AspNetCore.csproj" />   </ItemGroup>    <ItemGroup>       <PackageReference Include="Microsoft.AspNetCore.DataProtection.StackExchangeRedis" Version="7.0.1" />       <PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="7.0.1" />       <PackageReference Include="DistributedLock.Redis" Version="1.0.2" />   </ItemGroup>    <ItemGroup>       <PackageReference Include="Volo.Abp.AspNetCore.MultiTenancy" Version="7.0.0" />       <PackageReference Include="Volo.Abp.EventBus.RabbitMQ" Version="7.0.0" />       <PackageReference Include="Volo.Abp.BackgroundJobs.RabbitMQ" Version="7.0.0" />       <PackageReference Include="Volo.Abp.Caching.StackExchangeRedis" Version="7.0.0" />       <PackageReference Include="Volo.Abp.DistributedLocking" Version="7.0.0" />       <PackageReference Include="Volo.Abp.EntityFrameworkCore" Version="7.0.0" />   </ItemGroup>  </Project>  

FunShow.Shared.EventData

<Project Sdk="Microsoft.NET.Sdk">    <PropertyGroup>     <TargetFramework>net7.0</TargetFramework>     <ImplicitUsings>enable</ImplicitUsings>     <Nullable>enable</Nullable>   </PropertyGroup>    <ItemGroup>     <PackageReference Include="Volo.Abp.EventBus.Abstractions" Version="7.0.0" />   </ItemGroup> </Project>  

DbMigrator我们后续到数据迁移时再去完善

实现FunShow.Shared.Hosting

添加类FunShowSharedHostingModule.cs

using Volo.Abp.Autofac; using Volo.Abp.Data; using Volo.Abp.Modularity;  namespace FunShow.Shared.Hosting;  [DependsOn(     typeof(AbpAutofacModule),     typeof(AbpDataModule) )] public class FunShowSharedHostingModule : AbpModule {     public override void PreConfigureServices(ServiceConfigurationContext context)     {         // https://www.npgsql.org/efcore/release-notes/6.0.html#opting-out-of-the-new-timestamp-mapping-logic         System.AppContext.SetSwitch("Npgsql.EnableLegacyTimestampBehavior", true);          }     public override void ConfigureServices(ServiceConfigurationContext context)     {     	ConfigureDatabaseConnections();     }          private void ConfigureDatabaseConnections()     {         Configure<AbpDbConnectionOptions>(options =>         {                      options.Databases.Configure("AdministrationService", database =>             {                 database.MappedConnections.Add("AbpAuditLogging");                 database.MappedConnections.Add("AbpPermissionManagement");                 database.MappedConnections.Add("AbpSettingManagement");                 database.MappedConnections.Add("AbpFeatureManagement");             });                      options.Databases.Configure("IdentityService", database =>             {                 database.MappedConnections.Add("AbpIdentity");                 database.MappedConnections.Add("OpenIddict");             });         });     } } 

这里我打算用PGSQL,所以需要配置一下

System.AppContext.SetSwitch("Npgsql.EnableLegacyTimestampBehavior", true); 

ConfigureDatabaseConnections方法里面作用是设置数据库连接字符串映射关系,把ABP基础模块的数据库映射到微服务对应数据库。目前配置2个基础服务相关的链接字符串。

实现FunShow.Shared.Hosting.AspNetCore

添加类FunShowSharedHostingAspNetCoreModule.cs

using System; using Volo.Abp.AspNetCore.Serilog; using Volo.Abp.Modularity; using Volo.Abp.Swashbuckle;   namespace FunShow.Shared.Hosting.AspNetCore;   [DependsOn(     typeof(FunShowSharedHostingModule),     typeof(AbpAspNetCoreSerilogModule),     typeof(AbpSwashbuckleModule) )] public class FunShowSharedHostingAspNetCoreModule : AbpModule {   } 

添加类SwaggerConfigurationHelper.cs

using System.Collections.Generic; using Microsoft.Extensions.DependencyInjection; using Microsoft.OpenApi.Models; using Volo.Abp.Modularity;   namespace FunShow.Shared.Hosting.AspNetCore {     public static class SwaggerConfigurationHelper     {         public static void Configure(             ServiceConfigurationContext context,             string apiTitle         )         {             context.Services.AddAbpSwaggerGen(options =>             {                 options.SwaggerDoc("v1", new OpenApiInfo { Title = apiTitle, Version = "v1" });                 options.DocInclusionPredicate((docName, description) => true);                 options.CustomSchemaIds(type => type.FullName);             });         }                  public static void ConfigureWithAuth(             ServiceConfigurationContext context,             string authority,             Dictionary<string, string> scopes,             string apiTitle,             string apiVersion = "v1",             string apiName = "v1"         )         {             context.Services.AddAbpSwaggerGenWithOAuth(                 authority: authority,                 scopes: scopes,                 options =>                 {                     options.SwaggerDoc(apiName, new OpenApiInfo { Title = apiTitle, Version = apiVersion });                     options.DocInclusionPredicate((docName, description) => true);                     options.CustomSchemaIds(type => type.FullName);                 });         }     } } 

添加类SerilogConfigurationHelper.cs

using System; using System.IO; using Microsoft.Extensions.Configuration; using Serilog; using Serilog.Events; using Serilog.Sinks.Elasticsearch;   namespace FunShow.Shared.Hosting.AspNetCore {     public static class SerilogConfigurationHelper     {         public static void Configure(string applicationName)         {             var configuration = new ConfigurationBuilder()                 .SetBasePath(Directory.GetCurrentDirectory())                 .AddJsonFile("appsettings.json")                 .AddEnvironmentVariables()                 .Build();               Log.Logger = new LoggerConfiguration()     #if DEBUG                     .MinimumLevel.Debug()     #else                     .MinimumLevel.Information()     #endif                     .MinimumLevel.Override("Microsoft", LogEventLevel.Information)                 .MinimumLevel.Override("Microsoft.EntityFrameworkCore", LogEventLevel.Warning)                 .Enrich.FromLogContext()                 .Enrich.WithProperty("Application", $"{applicationName}")                 .WriteTo.Async(c => c.File("Logs/logs.txt"))                 // .WriteTo.Elasticsearch(                 //     new ElasticsearchSinkOptions(new Uri(configuration["ElasticSearch:Url"]))                 //     {                 //         AutoRegisterTemplate = true,                 //         AutoRegisterTemplateVersion = AutoRegisterTemplateVersion.ESv6,                 //         IndexFormat = "Walk-log-{0:yyyy.MM}"                 //     })                 .WriteTo.Async(c => c.Console())                 .CreateLogger();         }     } } 

这里我们先注释掉写入ES的配置。先预留,后续有需要可以放开注释,或者配置其他日志记录方式。

实现FunShow.Shared.Hosting.Gateways

添加类FunShowSharedHostingGatewaysModule.cs

using FunShow.Shared.Hosting.AspNetCore; using Microsoft.Extensions.DependencyInjection; using Volo.Abp.Modularity;   namespace FunShow.Shared.Hosting.Gateways;   [DependsOn(         typeof(FunShowSharedHostingAspNetCoreModule)     )] public class FunShowSharedHostingGatewaysModule : AbpModule {     public override void ConfigureServices(ServiceConfigurationContext context)     {         var configuration = context.Services.GetConfiguration();                  context.Services.AddReverseProxy()             .LoadFromConfig(configuration.GetSection("ReverseProxy"));     } } 

添加类GatewayHostBuilderExtensions.cs

using Microsoft.Extensions.Configuration;   namespace Microsoft.Extensions.Hosting {     public static class GatewayHostBuilderExtensions     {         public const string AppYarpJsonPath = "yarp.json";           public static IHostBuilder AddYarpJson(             this IHostBuilder hostBuilder,             bool optional = true,             bool reloadOnChange = true,             string path = AppYarpJsonPath)         {             return hostBuilder.ConfigureAppConfiguration((_, builder) =>             {                 builder.AddJsonFile(                         path: AppYarpJsonPath,                         optional: optional,                         reloadOnChange: reloadOnChange                     )                     .AddEnvironmentVariables();             });         }     } } 

这个类用于扩展IHostBuilder方法,配置网关读取配置文件,这里采用yarp作为网关组件,原商业版微服务模板采用的是ocelot。
添加类YarpSwaggerUIBuilderExtensions.cs,用于配置swagger

using System.Linq; using Microsoft.AspNetCore.Builder; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Volo.Abp; using Yarp.ReverseProxy.Configuration;   namespace FunShow.Shared.Hosting.Gateways {     public static class YarpSwaggerUIBuilderExtensions     {         public static IApplicationBuilder UseSwaggerUIWithYarp(this IApplicationBuilder app,             ApplicationInitializationContext context)         {             app.UseSwagger();             app.UseSwaggerUI(options =>             {                 var configuration = context.ServiceProvider.GetRequiredService<IConfiguration>();                 var logger = context.ServiceProvider.GetRequiredService<ILogger<ApplicationInitializationContext>>();                 var proxyConfigProvider = context.ServiceProvider.GetRequiredService<IProxyConfigProvider>();                 var yarpConfig = proxyConfigProvider.GetConfig();                   var routedClusters = yarpConfig.Clusters                     .SelectMany(t => t.Destinations,                         (clusterId, destination) => new {clusterId.ClusterId, destination.Value});                   var groupedClusters = routedClusters                     .GroupBy(q => q.Value.Address)                     .Select(t => t.First())                     .Distinct()                     .ToList();                   foreach (var clusterGroup in groupedClusters)                 {                     var routeConfig = yarpConfig.Routes.FirstOrDefault(q =>                         q.ClusterId == clusterGroup.ClusterId);                     if (routeConfig == null)                     {                         logger.LogWarning($"Swagger UI: Couldn't find route configuration for {clusterGroup.ClusterId}...");                         continue;                     }                       options.SwaggerEndpoint($"{clusterGroup.Value.Address}/swagger/v1/swagger.json", $"{routeConfig.RouteId} API");                     options.OAuthClientId(configuration["AuthServer:SwaggerClientId"]);                     options.OAuthClientSecret(configuration["AuthServer:SwaggerClientSecret"]);                 }             });                          return app;         }     } } 

实现FunShow.Shared.Hosting.Microservices

添加类FunShowSharedHostingMicroservicesModule.cs

using Medallion.Threading; using Medallion.Threading.Redis; using Microsoft.AspNetCore.DataProtection; using Microsoft.Extensions.DependencyInjection; using FunShow.Shared.Hosting.AspNetCore; using StackExchange.Redis; using Volo.Abp.AspNetCore.MultiTenancy; using Volo.Abp.BackgroundJobs.RabbitMQ; using Volo.Abp.Caching; using Volo.Abp.Caching.StackExchangeRedis; using Volo.Abp.DistributedLocking; using Volo.Abp.EventBus.RabbitMq; using Volo.Abp.Modularity; using Volo.Abp.MultiTenancy; using Volo.Abp.EntityFrameworkCore;   namespace FunShow.Shared.Hosting.Microservices;   [DependsOn(     typeof(AbpEntityFrameworkCoreModule),     typeof(FunShowSharedHostingAspNetCoreModule),     typeof(AbpBackgroundJobsRabbitMqModule),     typeof(AbpAspNetCoreMultiTenancyModule),     typeof(AbpDistributedLockingModule),     typeof(AbpEventBusRabbitMqModule),     typeof(AbpCachingStackExchangeRedisModule) )] public class FunShowSharedHostingMicroservicesModule: AbpModule {     public override void ConfigureServices(ServiceConfigurationContext context)     {         var configuration = context.Services.GetConfiguration();         var hostingEnvironment = context.Services.GetHostingEnvironment();           Configure<AbpMultiTenancyOptions>(options =>         {             options.IsEnabled = true;         });           Configure<AbpDistributedCacheOptions>(options =>         {             options.KeyPrefix = "FunShow:";         });           var redis = ConnectionMultiplexer.Connect(configuration["Redis:Configuration"]);           context.Services             .AddDataProtection()             .SetApplicationName("FunShow")             .PersistKeysToStackExchangeRedis(redis, "FunShow-Protection-Keys");           context.Services.AddSingleton<IDistributedLockProvider>(_ =>             new RedisDistributedSynchronizationProvider(redis.GetDatabase()));     } } 

添加JWT配置类JwtBearerConfigurationHelper.cs

using System; using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.Extensions.DependencyInjection; using Volo.Abp.Modularity;   namespace FunShow.Shared.Hosting.Microservices {     public static class JwtBearerConfigurationHelper     {         public static void Configure(             ServiceConfigurationContext context,             string audience)         {             var configuration = context.Services.GetConfiguration();               context.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)                 .AddJwtBearer(options =>                 {                     options.Authority = configuration["AuthServer:Authority"];                     options.RequireHttpsMetadata = Convert.ToBoolean(configuration["AuthServer:RequireHttpsMetadata"]);                     options.Audience = audience;                 });         }     } } 

实现FunShow.Shared.Localization

在项目中添加nuget包Microsoft.Extensions.FileProviders.Embedded,此包是实现访问内嵌资源文件的根本。
然后在项目文件的标签中添加xml配置

<RootNamespace>FunShow</RootNamespace> <GenerateEmbeddedFilesManifest>true</GenerateEmbeddedFilesManifest> 

如果没有上述配置,系统是无法读取多语言配置的。
创建Localization目录,添加类FunShowResource.cs

using Volo.Abp.Localization;   namespace FunShow.Localization {     [LocalizationResourceName("FunShow")]     public class FunShowResource     {              } } 

在Localization目录创建FunShow子目录,添加en.json和zh-Hans.json文件

{     "culture": "en",     "texts": {       "Menu:Home": "Home",       "Login": "Login",       "Menu:Dashboard": "Dashboard"     }   } 
{     "culture": "zh-Hans",     "texts": {       "Menu:Home": "家",       "Login": "登录",       "Menu:Dashboard": "仪表盘"     }   } 

添加类FunShowSharedLocalizationModule.cs

using FunShow.Localization; using Volo.Abp.Localization; using Volo.Abp.Modularity; using Volo.Abp.Validation; using Volo.Abp.Validation.Localization; using Volo.Abp.VirtualFileSystem;   namespace FunShow.Shared.Localization;   [DependsOn(     typeof(AbpValidationModule)     )] public class FunShowSharedLocalizationModule : AbpModule {     public override void ConfigureServices(ServiceConfigurationContext context)     {         Configure<AbpVirtualFileSystemOptions>(options =>         {             options.FileSets.AddEmbedded<FunShowSharedLocalizationModule>();         });           Configure<AbpLocalizationOptions>(options =>         {             options.Resources                 .Add<FunShowResource>("en")                 .AddBaseTypes(                     typeof(AbpValidationResource)                 ).AddVirtualJson("/Localization/FunShow");               options.DefaultResourceType = typeof(FunShowResource);         });     } } 

实现FunShow.Shared.EventData

添加类FunShowSharedEventDataModule.cs

using Volo.Abp.Modularity;   namespace FunShow.Shared.EventData;   public class FunShowSharedEventDataModule: AbpModule {   } 

至此我们完成了Shared项目的初始化,后续有一些共用的修改再返回来修改对应的项目。

下一章我们来实现基础的AdministrationService和IdentityService

发表评论

相关文章