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


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

    开始搭建#

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

    dotnet new sln -n FunShow
    

    然后在解决方案目录下创建目录

    创建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.0TargetFramework>
        <ImplicitUsings>enableImplicitUsings>
        <Nullable>enableNullable>
        <RootNamespace>FunShow.Shared.HostingRootNamespace>
      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.0TargetFramework>
        <ImplicitUsings>enableImplicitUsings>
        <Nullable>enableNullable>
        <RootNamespace>FunShow.Shared.Hosting.AspNetCoreRootNamespace>
      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.Hosting\FunShow.Shared.Hosting.csproj" />
      ItemGroup>
    Project>
    

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

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

    FunShow.Shared.Hosting.Microservices

    <Project Sdk="Microsoft.NET.Sdk">
    
      <PropertyGroup>
        <TargetFramework>net7.0TargetFramework>
        <ImplicitUsings>enableImplicitUsings>
        <Nullable>enableNullable>
      PropertyGroup>
    
      <ItemGroup>
        <ProjectReference Include="..\FunShow.Shared.Hosting.AspNetCore\FunShow.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.0TargetFramework>
        <ImplicitUsings>enableImplicitUsings>
        <Nullable>enableNullable>
      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(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();
                    var logger = context.ServiceProvider.GetRequiredService>();
                    var proxyConfigProvider = context.ServiceProvider.GetRequiredService();
                    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(options =>
            {
                options.IsEnabled = true;
            });
    
    
            Configure(options =>
            {
                options.KeyPrefix = "FunShow:";
            });
    
    
            var redis = ConnectionMultiplexer.Connect(configuration["Redis:Configuration"]);
    
    
            context.Services
                .AddDataProtection()
                .SetApplicationName("FunShow")
                .PersistKeysToStackExchangeRedis(redis, "FunShow-Protection-Keys");
    
    
            context.Services.AddSingleton(_ =>
                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>FunShowRootNamespace>
    <GenerateEmbeddedFilesManifest>trueGenerateEmbeddedFilesManifest>
    

    如果没有上述配置,系统是无法读取多语言配置的。
    创建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(options =>
            {
                options.FileSets.AddEmbedded();
            });
    
    
            Configure(options =>
            {
                options.Resources
                    .Add("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

  • 相关阅读:
    HTML基础入门
    Windows下的geant4的安装和clion的使用
    基于openEuler系统执行Linux内核性能测试脚本(lkp-tests)的流程
    【超简单-Java设计模式2】简单工厂模式
    三菱FX3U——ST编程IF判断
    LeetCode每日一题——1668. 最大重复子字符串
    2023.11.18 -自用hadoop高可用环境搭建命令
    export default 与export 有什么区别?
    【MySQL事务篇】多版本并发控制(MVCC)
    中国联通5G-NR 900MHz基站设备技术白皮书(2022)
  • 原文地址:https://www.cnblogs.com/fanshaoO/p/17164302.html