Browse Source

feat: Optimize API Gateway

pull/1472/head
colin 4 months ago
parent
commit
26a5a53e2a
  1. 46
      gateways/internal/LINGYUN.MicroService.Internal.ApiGateway/LINGYUN.MicroService.Internal.ApiGateway.slnx
  2. 12
      gateways/internal/LINGYUN.MicroService.Internal.ApiGateway/src/LINGYUN.MicroService.Internal.ApiGateway/.config/dotnet-tools.json
  3. 48
      gateways/internal/LINGYUN.MicroService.Internal.ApiGateway/src/LINGYUN.MicroService.Internal.ApiGateway/Controllers/ApiGatewayController.cs
  4. 11
      gateways/internal/LINGYUN.MicroService.Internal.ApiGateway/src/LINGYUN.MicroService.Internal.ApiGateway/Dockerfile
  5. 242
      gateways/internal/LINGYUN.MicroService.Internal.ApiGateway/src/LINGYUN.MicroService.Internal.ApiGateway/InternalApiGatewayModule.Configure.cs
  6. 87
      gateways/internal/LINGYUN.MicroService.Internal.ApiGateway/src/LINGYUN.MicroService.Internal.ApiGateway/InternalApiGatewayModule.cs
  7. 18
      gateways/internal/LINGYUN.MicroService.Internal.ApiGateway/src/LINGYUN.MicroService.Internal.ApiGateway/InternalApiGatewayOptions.cs
  8. 41
      gateways/internal/LINGYUN.MicroService.Internal.ApiGateway/src/LINGYUN.MicroService.Internal.ApiGateway/LINGYUN.MicroService.Internal.ApiGateway.csproj
  9. 9
      gateways/internal/LINGYUN.MicroService.Internal.ApiGateway/src/LINGYUN.MicroService.Internal.ApiGateway/Localization/ApiGatewayResource.cs
  10. 10
      gateways/internal/LINGYUN.MicroService.Internal.ApiGateway/src/LINGYUN.MicroService.Internal.ApiGateway/Localization/Resources/en.json
  11. 10
      gateways/internal/LINGYUN.MicroService.Internal.ApiGateway/src/LINGYUN.MicroService.Internal.ApiGateway/Localization/Resources/zh-Hans.json
  12. 8
      gateways/internal/LINGYUN.MicroService.Internal.ApiGateway/src/LINGYUN.MicroService.Internal.ApiGateway/Models/DownstreamOpenApi.cs
  13. 132
      gateways/internal/LINGYUN.MicroService.Internal.ApiGateway/src/LINGYUN.MicroService.Internal.ApiGateway/Ocelot/Multiplexer/AbpResponseMergeAggregator.cs
  14. 73
      gateways/internal/LINGYUN.MicroService.Internal.ApiGateway/src/LINGYUN.MicroService.Internal.ApiGateway/Program.cs
  15. 20
      gateways/internal/LINGYUN.MicroService.Internal.ApiGateway/src/LINGYUN.MicroService.Internal.ApiGateway/Properties/launchSettings.json
  16. 21
      gateways/internal/LINGYUN.MicroService.Internal.ApiGateway/src/LINGYUN.MicroService.Internal.ApiGateway/Utils/ILoadBalancerFinder.cs
  17. 58
      gateways/internal/LINGYUN.MicroService.Internal.ApiGateway/src/LINGYUN.MicroService.Internal.ApiGateway/Utils/LoadBalancerFinder.cs
  18. 87
      gateways/internal/LINGYUN.MicroService.Internal.ApiGateway/src/LINGYUN.MicroService.Internal.ApiGateway/appsettings.Development.json
  19. 80
      gateways/internal/LINGYUN.MicroService.Internal.ApiGateway/src/LINGYUN.MicroService.Internal.ApiGateway/appsettings.json
  20. 1
      gateways/internal/LINGYUN.MicroService.Internal.ApiGateway/src/LINGYUN.MicroService.Internal.ApiGateway/ocelot.Development.json
  21. 4349
      gateways/internal/LINGYUN.MicroService.Internal.ApiGateway/src/LINGYUN.MicroService.Internal.ApiGateway/ocelot.json
  22. 2
      gateways/internal/LINGYUN.MicroService.Internal.ApiGateway/src/LINGYUN.MicroService.OpenApi.Gateway/OpenApiGatewayModule.cs
  23. 14
      gateways/web/LY.MicroService.ApiGateway/InternalApiGatewayModule.cs

46
gateways/internal/LINGYUN.MicroService.Internal.ApiGateway/LINGYUN.MicroService.Internal.ApiGateway.slnx

@ -0,0 +1,46 @@
<Solution>
<Folder Name="/framework/" />
<Folder Name="/framework/auditing/">
<Project Path="../../../aspnet-core/framework/auditing/LINGYUN.Abp.AuditLogging.Elasticsearch/LINGYUN.Abp.AuditLogging.Elasticsearch.csproj" />
<Project Path="../../../aspnet-core/framework/auditing/LINGYUN.Abp.AuditLogging/LINGYUN.Abp.AuditLogging.csproj" />
</Folder>
<Folder Name="/framework/common/">
<Project Path="../../../aspnet-core/framework/common/LINGYUN.Abp.AspNetCore.Wrapper/LINGYUN.Abp.AspNetCore.Wrapper.csproj" />
<Project Path="../../../aspnet-core/framework/common/LINGYUN.Abp.IdGenerator/LINGYUN.Abp.IdGenerator.csproj" />
<Project Path="../../../aspnet-core/framework/common/LINGYUN.Abp.IP2Region/LINGYUN.Abp.IP2Region.csproj" />
<Project Path="../../../aspnet-core/framework/common/LINGYUN.Abp.Wrapper/LINGYUN.Abp.Wrapper.csproj" />
<Project Path="../../../aspnet-core/framework/mvc/LINGYUN.Abp.AspNetCore.Mvc.Wrapper/LINGYUN.Abp.AspNetCore.Mvc.Wrapper.csproj" />
</Folder>
<Folder Name="/framework/elasticsearch/">
<Project Path="../../../aspnet-core/framework/elasticsearch/LINGYUN.Abp.Elasticsearch/LINGYUN.Abp.Elasticsearch.csproj" />
</Folder>
<Folder Name="/framework/identity/">
<Project Path="../../../aspnet-core/modules/identity/LINGYUN.Abp.Identity.Session.AspNetCore/LINGYUN.Abp.Identity.Session.AspNetCore.csproj" />
<Project Path="../../../aspnet-core/modules/identity/LINGYUN.Abp.Identity.Session/LINGYUN.Abp.Identity.Session.csproj" />
</Folder>
<Folder Name="/framework/localization/">
<Project Path="../../../aspnet-core/framework/localization/LINGYUN.Abp.Localization.CultureMap/LINGYUN.Abp.Localization.CultureMap.csproj" />
</Folder>
<Folder Name="/framework/logging/">
<Project Path="../../../aspnet-core/framework/logging/LINGYUN.Abp.Logging.Serilog.Elasticsearch/LINGYUN.Abp.Logging.Serilog.Elasticsearch.csproj" />
<Project Path="../../../aspnet-core/framework/logging/LINGYUN.Abp.Logging/LINGYUN.Abp.Logging.csproj" />
<Project Path="../../../aspnet-core/framework/logging/LINGYUN.Abp.Serilog.Enrichers.Application/LINGYUN.Abp.Serilog.Enrichers.Application.csproj" />
<Project Path="../../../aspnet-core/framework/logging/LINGYUN.Abp.Serilog.Enrichers.UniqueId/LINGYUN.Abp.Serilog.Enrichers.UniqueId.csproj" />
</Folder>
<Folder Name="/framework/open-api/">
<Project Path="../../../aspnet-core/framework/open-api/LINGYUN.Abp.OpenApi.Authorization/LINGYUN.Abp.OpenApi.Authorization.csproj" />
<Project Path="../../../aspnet-core/framework/open-api/LINGYUN.Abp.OpenApi/LINGYUN.Abp.OpenApi.csproj" />
<Project Path="../../../aspnet-core/framework/open-api/OpenApi.Sdk/OpenApi.Sdk.csproj" />
</Folder>
<Folder Name="/Solution Items/">
<File Path="../../../common.props" />
<File Path="../../../common.secrets.props" />
<File Path="../../../configureawait.props" />
<File Path="../../../Directory.Build.props" />
<File Path="../../../Directory.Packages.props" />
</Folder>
<Folder Name="/src/">
<Project Path="src/LINGYUN.MicroService.Internal.Gateway/LINGYUN.MicroService.Internal.Gateway.csproj" />
<Project Path="src/LINGYUN.MicroService.OpenApi.Gateway/LINGYUN.MicroService.OpenApi.Gateway.csproj" />
</Folder>
</Solution>

12
gateways/internal/LINGYUN.MicroService.Internal.ApiGateway/src/LINGYUN.MicroService.Internal.ApiGateway/.config/dotnet-tools.json

@ -1,12 +0,0 @@
{
"version": 1,
"isRoot": true,
"tools": {
"dotnet-ef": {
"version": "7.0.12",
"commands": [
"dotnet-ef"
]
}
}
}

48
gateways/internal/LINGYUN.MicroService.Internal.ApiGateway/src/LINGYUN.MicroService.Internal.ApiGateway/Controllers/ApiGatewayController.cs

@ -1,48 +0,0 @@
using LINGYUN.MicroService.Internal.ApiGateway.Utils;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.DependencyInjection;
using Ocelot.Multiplexer;
using System;
using System.Linq;
using System.Threading.Tasks;
using Volo.Abp.Application.Dtos;
namespace LINGYUN.MicroService.Internal.ApiGateway.Controllers
{
[Area("ApiGateway")]
[Route("api/ApiGateway/Basic")]
public class ApiGatewayController : Controller
{
protected IServiceProvider ServiceProvider { get; }
protected ILoadBalancerFinder LoadBalancerFinder { get; }
public ApiGatewayController(
IServiceProvider serviceProvider,
ILoadBalancerFinder loadBalancerFinder)
{
ServiceProvider = serviceProvider;
LoadBalancerFinder = loadBalancerFinder;
}
[HttpGet]
[Route("Aggregators")]
public Task<JsonResult> GetAggregatorsAsync()
{
var aggregators = ServiceProvider.GetServices<IDefinedAggregator>();
var aggregatorDtos = new ListResultDto<string>(aggregators.Select(agg => agg.GetType().Name).ToList());
return Task.FromResult(Json(aggregatorDtos));
}
[HttpGet]
[Route("LoadBalancers")]
public async Task<JsonResult> GetLoadBalancersAsync()
{
var loadBalancers = await LoadBalancerFinder.GetLoadBalancersAsync();
var loadBalancerDtos = new ListResultDto<LoadBalancerDescriptor>(loadBalancers);
return Json(loadBalancerDtos);
}
}
}

11
gateways/internal/LINGYUN.MicroService.Internal.ApiGateway/src/LINGYUN.MicroService.Internal.ApiGateway/Dockerfile

@ -1,11 +0,0 @@
FROM mcr.microsoft.com/dotnet/aspnet:8.0
LABEL maintainer="colin.in@foxmail.com"
WORKDIR /app
COPY . /app
EXPOSE 80/tcp
VOLUME [ "./app/Logs" ]
VOLUME [ "./app/Modules" ]
ENTRYPOINT ["dotnet", "LINGYUN.MicroService.Internal.ApiGateway.dll"]

242
gateways/internal/LINGYUN.MicroService.Internal.ApiGateway/src/LINGYUN.MicroService.Internal.ApiGateway/InternalApiGatewayModule.Configure.cs

@ -1,242 +0,0 @@
using LINGYUN.Abp.Serilog.Enrichers.Application;
using LINGYUN.MicroService.Internal.ApiGateway.Localization;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Cors;
using Microsoft.AspNetCore.DataProtection;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Routing;
using Microsoft.AspNetCore.Server.Kestrel.Core;
using Microsoft.Extensions.Caching.StackExchangeRedis;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.IdentityModel.Logging;
using Microsoft.OpenApi.Models;
using Ocelot.DependencyInjection;
using Ocelot.Multiplexer;
using Ocelot.Provider.Polly;
using StackExchange.Redis;
using System;
using System.Linq;
using System.Text.Encodings.Web;
using System.Text.Unicode;
using Volo.Abp.Caching;
using Volo.Abp.Json;
using Volo.Abp.Json.SystemTextJson;
using Volo.Abp.Localization;
using Volo.Abp.VirtualFileSystem;
namespace LINGYUN.MicroService.Internal.ApiGateway
{
public partial class InternalApiGatewayModule
{
protected const string DefaultCorsPolicyName = "Default";
private void PreConfigureApp(IConfiguration configuration)
{
AbpSerilogEnrichersConsts.ApplicationName = "Internal-ApiGateWay";
if (configuration.GetValue<bool>("App:ShowPii"))
{
IdentityModelEventSource.ShowPII = true;
}
}
//private void PreForwardedHeaders()
//{
// PreConfigure<AbpForwardedHeadersOptions>(options =>
// {
// options.ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto;
// options.KnownNetworks.Clear();
// options.KnownProxies.Clear();
// });
//}
private void ConfigureApiGateway(IConfiguration configuration)
{
Configure<InternalApiGatewayOptions>(configuration.GetSection("ApiGateway"));
}
private void ConfigureKestrelServer(IConfiguration configuration, bool isDevelopment = true)
{
// fix: 不限制请求体大小,解决上传文件问题
Configure<KestrelServerOptions>(options =>
{
options.Limits.MaxRequestBodySize = null;
options.Limits.MaxRequestBufferSize = null;
});
if (!isDevelopment)
{
// Ssl证书
var sslOptions = configuration.GetSection("App:SslOptions");
if (sslOptions.Exists())
{
var fileName = sslOptions["FileName"];
var password = sslOptions["Password"];
Configure<KestrelServerOptions>(options =>
{
options.ConfigureEndpointDefaults(cfg =>
{
cfg.UseHttps(fileName, password);
});
});
}
}
}
private void ConfigureJsonSerializer()
{
// 统一时间日期格式
Configure<AbpJsonOptions>(options =>
{
// options.DefaultDateTimeFormat = "yyyy-MM-dd HH:mm:ss";
});
// 中文序列化的编码问题
Configure<AbpSystemTextJsonSerializerOptions>(options =>
{
options.JsonSerializerOptions.Encoder = JavaScriptEncoder.Create(UnicodeRanges.All);
});
}
private void ConfigureVirtualFileSystem()
{
Configure<AbpVirtualFileSystemOptions>(options =>
{
options.FileSets.AddEmbedded<InternalApiGatewayModule>("LINGYUN.MicroService.Internal.ApiGateway");
});
}
private void ConfigureLocalization()
{
Configure<AbpLocalizationOptions>(options =>
{
options.Languages.Add(new LanguageInfo("en", "en", "English"));
options.Languages.Add(new LanguageInfo("zh-Hans", "zh-Hans", "简体中文"));
options.Resources
.Add<ApiGatewayResource>()
.AddVirtualJson("/Localization/Resources");
});
}
private void ConfigureMvc(IServiceCollection services)
{
var mvcBuilder = services.AddMvc();
mvcBuilder.AddApplicationPart(typeof(InternalApiGatewayModule).Assembly);
Configure<AbpEndpointRouterOptions>(options =>
{
options.EndpointConfigureActions.Add(endpointContext =>
{
endpointContext.Endpoints.MapControllerRoute("defaultWithArea", "{area}/{controller=Home}/{action=Index}/{id?}");
endpointContext.Endpoints.MapControllerRoute("default", "{controller=Home}/{action=Index}/{id?}");
});
});
}
private void ConfigureOcelot(IServiceCollection services, IConfiguration configuration)
{
// 需要合并自定义配置取消注释
// services.AddSingleton<IFileConfigurationRepository, DiskFileConfigurationAggragatorRepository>();
services
.AddOcelot(configuration)
.AddPolly()
.AddSingletonDefinedAggregator<AbpResponseMergeAggregator>();
}
private void ConfigureCaching(IConfiguration configuration)
{
Configure<AbpDistributedCacheOptions>(options =>
{
// 最好统一命名,不然某个缓存变动其他应用服务有例外发生
options.KeyPrefix = "LINGYUN.Abp.Application";
// 滑动过期30天
options.GlobalCacheEntryOptions.SlidingExpiration = TimeSpan.FromDays(30d);
// 绝对过期60天
options.GlobalCacheEntryOptions.AbsoluteExpiration = DateTimeOffset.Now.AddDays(60d);
});
Configure<RedisCacheOptions>(options =>
{
var redisConfig = ConfigurationOptions.Parse(options.Configuration);
options.ConfigurationOptions = redisConfig;
options.InstanceName = configuration["Redis:InstanceName"];
});
}
private void ConfigureSwagger(IServiceCollection services)
{
services.AddSwaggerGen(
options =>
{
options.SwaggerDoc("v1", new OpenApiInfo { Title = "Open API Document", Version = "v1" });
options.DocInclusionPredicate((docName, description) => true);
options.CustomSchemaIds(type => type.FullName);
options.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
{
Description = "JWT Authorization header using the Bearer scheme. Example: \"Authorization: Bearer {token}\"",
Name = "Authorization",
In = ParameterLocation.Header,
Scheme = "bearer",
Type = SecuritySchemeType.Http,
BearerFormat = "JWT"
});
options.AddSecurityRequirement(new OpenApiSecurityRequirement
{
{
new OpenApiSecurityScheme
{
Reference = new OpenApiReference { Type = ReferenceType.SecurityScheme, Id = "Bearer" }
},
new string[] { }
}
});
});
}
private void ConfigureCors(IServiceCollection services, IConfiguration configuration)
{
services.AddCors(options =>
{
options.AddPolicy(DefaultCorsPolicyName, builder =>
{
builder
.WithOrigins(
configuration["App:CorsOrigins"]
.Split(",", StringSplitOptions.RemoveEmptyEntries)
.Select(o => o.RemovePostFix("/"))
.ToArray()
)
.WithAbpExposedHeaders()
// 引用 LINGYUN.Abp.AspNetCore.Mvc.Wrapper 包时可替换为 WithAbpWrapExposedHeaders
.WithExposedHeaders("_AbpWrapResult", "_AbpDontWrapResult")
.SetIsOriginAllowedToAllowWildcardSubdomains()
.AllowAnyHeader()
.AllowAnyMethod()
.AllowCredentials();
});
});
}
private void ConfigureSecurity(IServiceCollection services, IConfiguration configuration, bool isDevelopment = false)
{
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.Authority = configuration["AuthServer:Authority"];
options.RequireHttpsMetadata = false;
options.Audience = configuration["AuthServer:ApiName"];
});
if (!isDevelopment)
{
var redis = ConnectionMultiplexer.Connect(configuration["Redis:Configuration"]);
services
.AddDataProtection()
.SetApplicationName("LINGYUN.Abp.Application")
.PersistKeysToStackExchangeRedis(redis, "LINGYUN.Abp.Application:DataProtection:Protection-Keys");
}
}
}
}

87
gateways/internal/LINGYUN.MicroService.Internal.ApiGateway/src/LINGYUN.MicroService.Internal.ApiGateway/InternalApiGatewayModule.cs

@ -1,87 +0,0 @@
using LINGYUN.Abp.Serilog.Enrichers.Application;
using LINGYUN.Abp.Serilog.Enrichers.UniqueId;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Options;
using Ocelot.Middleware;
using Volo.Abp;
using Volo.Abp.AspNetCore.Serilog;
using Volo.Abp.Autofac;
using Volo.Abp.Caching.StackExchangeRedis;
using Volo.Abp.Modularity;
namespace LINGYUN.MicroService.Internal.ApiGateway
{
[DependsOn(
typeof(AbpAutofacModule),
typeof(AbpSerilogEnrichersApplicationModule),
typeof(AbpSerilogEnrichersUniqueIdModule),
typeof(AbpCachingStackExchangeRedisModule),
typeof(AbpAspNetCoreSerilogModule)
)]
public partial class InternalApiGatewayModule : AbpModule
{
public override void PreConfigureServices(ServiceConfigurationContext context)
{
var configuration = context.Services.GetConfiguration();
PreConfigureApp(configuration);
}
public override void ConfigureServices(ServiceConfigurationContext context)
{
var hostingEnvironment = context.Services.GetHostingEnvironment();
var configuration = context.Services.GetConfiguration();
ConfigureLocalization();
ConfigureJsonSerializer();
ConfigureVirtualFileSystem();
ConfigureCaching(configuration);
ConfigureApiGateway(configuration);
ConfigureKestrelServer(configuration, hostingEnvironment.IsDevelopment());
ConfigureSecurity(context.Services, configuration, hostingEnvironment.IsDevelopment());
ConfigureMvc(context.Services);
ConfigureSwagger(context.Services);
ConfigureCors(context.Services, configuration);
ConfigureOcelot(context.Services, configuration);
}
public override void OnApplicationInitialization(ApplicationInitializationContext context)
{
var app = context.GetApplicationBuilder();
app.UseAuditing();
app.UseStaticFiles();
app.UseRouting();
app.UseCors(DefaultCorsPolicyName);
app.UseAuthentication();
app.UseAbpClaimsMap();
app.MapWhen(
ctx => ctx.Request.Path.ToString().StartsWith("/api/ApiGateway/Basic/"),
appNext =>
{
// 仅针对属于网关自己的控制器进入MVC管道
appNext.UseRouting();
appNext.UseConfiguredEndpoints();
});
app.UseSwagger();
app.UseSwaggerUI(c =>
{
c.SwaggerEndpoint("/swagger/v1/swagger.json", "Open API Document");
var options = app.ApplicationServices.GetRequiredService<IOptions<InternalApiGatewayOptions>>().Value;
foreach (var api in options.DownstreamOpenApis)
{
c.SwaggerEndpoint(api.EndPoint, api.Name);
}
});
// 启用ws协议
app.UseWebSockets();
app.UseAbpSerilogEnrichers();
app.UseOcelot().Wait();
}
}
}

18
gateways/internal/LINGYUN.MicroService.Internal.ApiGateway/src/LINGYUN.MicroService.Internal.ApiGateway/InternalApiGatewayOptions.cs

@ -1,18 +0,0 @@
using LINGYUN.MicroService.Internal.ApiGateway.Models;
namespace LINGYUN.MicroService.Internal.ApiGateway
{
public class InternalApiGatewayOptions
{
/// <summary>
/// 聚合异常时是否忽略
/// </summary>
public bool AggregationIgnoreError { get; set; }
public DownstreamOpenApi[] DownstreamOpenApis { get; set; }
public InternalApiGatewayOptions()
{
AggregationIgnoreError = true;
DownstreamOpenApis = new DownstreamOpenApi[0];
}
}
}

41
gateways/internal/LINGYUN.MicroService.Internal.ApiGateway/src/LINGYUN.MicroService.Internal.ApiGateway/LINGYUN.MicroService.Internal.ApiGateway.csproj

@ -1,41 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<Import Project="..\..\..\..\..\common.props" />
<Import Project="..\..\..\..\..\common.secrets.props" />
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net10.0</TargetFramework>
<RootNamespace>LINGYUN.MicroService.Internal.ApiGateway</RootNamespace>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Ocelot.Provider.Polly" />
<PackageReference Include="AgileConfig.Client" />
<PackageReference Include="Microsoft.AspNetCore.DataProtection.StackExchangeRedis" />
<PackageReference Include="Serilog.AspNetCore" />
<PackageReference Include="Serilog.Enrichers.Environment" />
<PackageReference Include="Serilog.Enrichers.Assembly" />
<PackageReference Include="Serilog.Enrichers.Process" />
<PackageReference Include="Serilog.Enrichers.Thread" />
<PackageReference Include="Serilog.Settings.Configuration" />
<PackageReference Include="Serilog.Sinks.Elasticsearch" />
<PackageReference Include="Serilog.Sinks.File" />
<PackageReference Include="Swashbuckle.AspNetCore" />
<PackageReference Include="Volo.Abp.AspNetCore.Serilog" />
<PackageReference Include="Volo.Abp.AspNetCore.Authentication.JwtBearer" />
<PackageReference Include="Volo.Abp.Autofac" />
<PackageReference Include="Volo.Abp.AspNetCore" />
<PackageReference Include="Volo.Abp.Ddd.Application.Contracts" />
<PackageReference Include="Volo.Abp.Caching.StackExchangeRedis" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\..\..\aspnet-core\framework\auditing\LINGYUN.Abp.AuditLogging.Elasticsearch\LINGYUN.Abp.AuditLogging.Elasticsearch.csproj" />
<!--<ProjectReference Include="..\..\..\..\..\aspnet-core\framework\common\LINGYUN.Abp.AspNetCore.HttpOverrides\LINGYUN.Abp.AspNetCore.HttpOverrides.csproj" />-->
<ProjectReference Include="..\..\..\..\..\aspnet-core\framework\localization\LINGYUN.Abp.Localization.CultureMap\LINGYUN.Abp.Localization.CultureMap.csproj" />
<ProjectReference Include="..\..\..\..\..\aspnet-core\framework\logging\LINGYUN.Abp.Serilog.Enrichers.Application\LINGYUN.Abp.Serilog.Enrichers.Application.csproj" />
<ProjectReference Include="..\..\..\..\..\aspnet-core\framework\logging\LINGYUN.Abp.Serilog.Enrichers.UniqueId\LINGYUN.Abp.Serilog.Enrichers.UniqueId.csproj" />
</ItemGroup>
</Project>

9
gateways/internal/LINGYUN.MicroService.Internal.ApiGateway/src/LINGYUN.MicroService.Internal.ApiGateway/Localization/ApiGatewayResource.cs

@ -1,9 +0,0 @@
using Volo.Abp.Localization;
namespace LINGYUN.MicroService.Internal.ApiGateway.Localization
{
[LocalizationResourceName("InternalApiGateway")]
public class ApiGatewayResource
{
}
}

10
gateways/internal/LINGYUN.MicroService.Internal.ApiGateway/src/LINGYUN.MicroService.Internal.ApiGateway/Localization/Resources/en.json

@ -1,10 +0,0 @@
{
"culture": "en",
"texts": {
"NoLoadBalancer": "No Load balancer",
"RoundRobin": "Round robin",
"LeastConnection": "Least connection",
"CookieStickySessions": "Cookie sticky sessions",
"CustomLoadBalancer": "Custom load balancer - {0}"
}
}

10
gateways/internal/LINGYUN.MicroService.Internal.ApiGateway/src/LINGYUN.MicroService.Internal.ApiGateway/Localization/Resources/zh-Hans.json

@ -1,10 +0,0 @@
{
"culture": "zh-Hans",
"texts": {
"NoLoadBalancer": "无负载均衡",
"RoundRobin": "服务器轮询",
"LeastConnection": "总是空闲服务器",
"CookieStickySessions": "粘性会话类型",
"CustomLoadBalancer": "自定义负载均衡器 - {0}"
}
}

8
gateways/internal/LINGYUN.MicroService.Internal.ApiGateway/src/LINGYUN.MicroService.Internal.ApiGateway/Models/DownstreamOpenApi.cs

@ -1,8 +0,0 @@
namespace LINGYUN.MicroService.Internal.ApiGateway.Models
{
public class DownstreamOpenApi
{
public string Name { get; set; }
public string EndPoint { get; set; }
}
}

132
gateways/internal/LINGYUN.MicroService.Internal.ApiGateway/src/LINGYUN.MicroService.Internal.ApiGateway/Ocelot/Multiplexer/AbpResponseMergeAggregator.cs

@ -1,132 +0,0 @@
using LINGYUN.MicroService.Internal.ApiGateway;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Options;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Ocelot.Middleware;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading.Tasks;
using Volo.Abp.Http;
namespace Ocelot.Multiplexer
{
public class AbpResponseMergeAggregator : IDefinedAggregator
{
public ILogger<AbpResponseMergeAggregator> Logger { protected get; set; }
protected InternalApiGatewayOptions Options { get; }
public AbpResponseMergeAggregator(
IOptions<InternalApiGatewayOptions> options)
{
Options = options.Value;
Logger = NullLogger<AbpResponseMergeAggregator>.Instance;
}
public async Task<DownstreamResponse> Aggregate(List<HttpContext> responses)
{
return await MapAbpApiDefinitionAggregateContentAsync(responses);
}
protected virtual async Task<DownstreamResponse> MapAbpApiDefinitionAggregateContentAsync(List<HttpContext> responses)
{
JObject responseObject = null;
JsonMergeSettings mergeSetting = new JsonMergeSettings
{
MergeArrayHandling = MergeArrayHandling.Union,
PropertyNameComparison = System.StringComparison.CurrentCultureIgnoreCase
};
foreach (var httpResponse in responses)
{
var errorItems = httpResponse.Items.Errors();
var response = httpResponse.Items.DownstreamResponse();
if (errorItems.Any())
{
if (!Options.AggregationIgnoreError)
{
var errorItem = errorItems.First();
string error = errorItems.Select(x => x.Message).JoinAsString(";");
if (httpResponse.Response.Headers.ContainsKey(AbpHttpConsts.AbpErrorFormat))
{
// header 存在abp错误标头, 可以序列化错误信息
error = await response.Content.ReadAsStringAsync();
}
var errorContent = new StringContent(error)
{
Headers = { ContentType = new MediaTypeHeaderValue("application/json") }
};
if (httpResponse.Response.Headers.ContainsKey(AbpHttpConsts.AbpErrorFormat))
{
errorContent.Headers.Add(AbpHttpConsts.AbpErrorFormat, "true");
}
return new DownstreamResponse(
errorContent,
(HttpStatusCode)errorItem.HttpStatusCode,
httpResponse.Response.Headers.Select(x => new Header(x.Key, x.Value)).ToList(),
errorItem.Message);
}
var route = httpResponse.Items.DownstreamRoute();
var service = route.ServiceName ?? route.LoadBalancerKey ?? route.DownstreamPathTemplate.Value;
Logger.LogWarning($"The downstream service {service} returned an error, which is configured to be ignored");
continue;
}
var content = await response.Content.ReadAsStringAsync();
try
{
var contentObject = JsonConvert.DeserializeObject(content);
if (responseObject == null)
{
responseObject = JObject.FromObject(contentObject);
}
else
{
responseObject.Merge(contentObject, mergeSetting);
}
}
catch (System.Exception ex)
{
Logger.LogError(ex, "An error occurred in the aggregate route request result.");
return new DownstreamResponse(
null,
HttpStatusCode.InternalServerError,
new List<KeyValuePair<string, IEnumerable<string>>>(),
content);
}
}
if (responseObject == null)
{
return new DownstreamResponse(
null,
HttpStatusCode.NotFound,
new List<KeyValuePair<string, IEnumerable<string>>>(),
"Not Found");
}
var stringContent = new StringContent(responseObject.ToString())
{
Headers = { ContentType = new MediaTypeHeaderValue("application/json") }
};
//if (responses.Any(response => response.Response.Headers.ContainsKey(AbpHttpConsts.AbpErrorFormat)))
//{
// stringContent.Headers.Add("_AbpErrorFormat", "true");
//}
return new DownstreamResponse(
stringContent,
HttpStatusCode.OK,
new List<KeyValuePair<string, IEnumerable<string>>>(),
"OK");
}
}
}

73
gateways/internal/LINGYUN.MicroService.Internal.ApiGateway/src/LINGYUN.MicroService.Internal.ApiGateway/Program.cs

@ -1,73 +0,0 @@
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Serilog;
using System;
using System.IO;
using System.Threading.Tasks;
using Volo.Abp.IO;
using Volo.Abp.Modularity.PlugIns;
namespace LINGYUN.MicroService.Internal.ApiGateway;
public class Program
{
public async static Task<int> Main(string[] args)
{
try
{
Log.Information("Starting Internal ApiGateway.");
var builder = WebApplication.CreateBuilder(args);
builder.Host.AddAppSettingsSecretsJson()
.UseAutofac()
.ConfigureAppConfiguration((context, config) =>
{
var configuration = config.Build();
var agileConfigEnabled = configuration["AgileConfig:IsEnabled"];
if (agileConfigEnabled.IsNullOrEmpty() || bool.Parse(agileConfigEnabled))
{
config.AddAgileConfig(new AgileConfig.Client.ConfigClient(configuration));
}
config.AddJsonFile("ocelot.json", optional: true, reloadOnChange: true);
if (!context.HostingEnvironment.EnvironmentName.IsNullOrWhiteSpace())
{
config.AddJsonFile($"ocelot.{context.HostingEnvironment.EnvironmentName}.json", optional: true, reloadOnChange: true);
}
})
.UseSerilog((context, provider, config) =>
{
config.ReadFrom.Configuration(context.Configuration);
});
await builder.AddApplicationAsync<InternalApiGatewayModule>(options =>
{
// 搜索 Modules 目录下所有文件作为插件
// 取消显示引用所有其他项目的模块,改为通过插件的形式引用
var pluginFolder = Path.Combine(
Directory.GetCurrentDirectory(), "Modules");
DirectoryHelper.CreateIfNotExists(pluginFolder);
options.PlugInSources.AddFolder(
pluginFolder,
SearchOption.AllDirectories);
});
var app = builder.Build();
await app.InitializeApplicationAsync();
await app.RunAsync();
return 0;
}
catch (Exception ex)
{
Log.Fatal(ex, "Host terminated unexpectedly!");
Console.WriteLine("Host terminated unexpectedly!");
Console.WriteLine(ex.ToString());
return 1;
}
finally
{
Log.CloseAndFlush();
}
}
}

20
gateways/internal/LINGYUN.MicroService.Internal.ApiGateway/src/LINGYUN.MicroService.Internal.ApiGateway/Properties/launchSettings.json

@ -1,20 +0,0 @@
{
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:51466/",
"sslPort": 44332
}
},
"profiles": {
"LINGYUN.MicroService.Internal.ApiGateway": {
"commandName": "Project",
"launchBrowser": false,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
},
"applicationUrl": "https://127.0.0.1:30443;http://127.0.0.1:30000"
}
}
}

21
gateways/internal/LINGYUN.MicroService.Internal.ApiGateway/src/LINGYUN.MicroService.Internal.ApiGateway/Utils/ILoadBalancerFinder.cs

@ -1,21 +0,0 @@
using System.Collections.Generic;
using System.Threading.Tasks;
namespace LINGYUN.MicroService.Internal.ApiGateway.Utils
{
public interface ILoadBalancerFinder
{
Task<List<LoadBalancerDescriptor>> GetLoadBalancersAsync();
}
public class LoadBalancerDescriptor
{
public string Type { get; }
public string DisplayName { get; }
public LoadBalancerDescriptor(string type, string displayName)
{
Type = type;
DisplayName = displayName;
}
}
}

58
gateways/internal/LINGYUN.MicroService.Internal.ApiGateway/src/LINGYUN.MicroService.Internal.ApiGateway/Utils/LoadBalancerFinder.cs

@ -1,58 +0,0 @@
using LINGYUN.MicroService.Internal.ApiGateway.Localization;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Localization;
using Ocelot.LoadBalancer.LoadBalancers;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Volo.Abp.DependencyInjection;
namespace LINGYUN.MicroService.Internal.ApiGateway.Utils
{
public class LoadBalancerFinder : ILoadBalancerFinder, ISingletonDependency
{
private Lazy<List<LoadBalancerDescriptor>> lazyLoadBalancers;
protected List<LoadBalancerDescriptor> LoadBalancers => lazyLoadBalancers.Value;
protected IServiceProvider ServiceProvider { get; }
protected IStringLocalizer Localizer { get; }
public LoadBalancerFinder(
IServiceProvider serviceProvider,
IStringLocalizer<ApiGatewayResource> localizer)
{
Localizer = localizer;
ServiceProvider = serviceProvider;
lazyLoadBalancers = new Lazy<List<LoadBalancerDescriptor>>(() => FindLocalLoadBalancers());
}
public Task<List<LoadBalancerDescriptor>> GetLoadBalancersAsync()
{
return Task.FromResult(LoadBalancers);
}
protected List<LoadBalancerDescriptor> FindLocalLoadBalancers()
{
List<LoadBalancerDescriptor> loadBalancers = new List<LoadBalancerDescriptor>
{
new LoadBalancerDescriptor(typeof(NoLoadBalancer).Name, Localizer["NoLoadBalancer"]),
new LoadBalancerDescriptor(typeof(RoundRobin).Name, Localizer["RoundRobin"]),
new LoadBalancerDescriptor(typeof(LeastConnection).Name, Localizer["LeastConnection"]),
new LoadBalancerDescriptor(typeof(CookieStickySessions).Name, Localizer["CookieStickySessions"])
};
var loadBalancerCreators = ServiceProvider.GetServices<ILoadBalancerCreator>();
loadBalancerCreators = loadBalancerCreators
.Where(lbc => !loadBalancers.Any(l => l.Type.Equals(lbc.Type)))
.ToArray();
foreach(var defintedLoadBalancerCreator in loadBalancerCreators)
{
loadBalancers.Add(new LoadBalancerDescriptor(defintedLoadBalancerCreator.Type, Localizer["CustomLoadBalancer", defintedLoadBalancerCreator.Type]));
}
return loadBalancers;
}
}
}

87
gateways/internal/LINGYUN.MicroService.Internal.ApiGateway/src/LINGYUN.MicroService.Internal.ApiGateway/appsettings.Development.json

@ -1,87 +0,0 @@
{
"AgileConfig": {
"IsEnabled": false,
"env": "DEV",
"appId": "LINGYUN.Internal.ApiGateway",
"secret": "1q2w3E*",
"nodes": "http://127.0.0.1:5000",
"name": "ApiGateway",
"tag": "ApiGateway"
},
"App": {
"CorsOrigins": "http://127.0.0.1:3100",
"ShowPii": true
},
"ApiGateway": {
"AppId": "TEST-APP",
"DownstreamOpenApis": [
{
"Name": "ApiGateway Admin API",
"EndPoint": "/apigateway-admin/v1/swagger.json"
},
{
"Name": "Backend Admin API",
"EndPoint": "/admin/v1/swagger.json"
},
{
"Name": "Platform API",
"EndPoint": "/platform/v1/swagger.json"
},
{
"Name": "Localization API",
"EndPoint": "/localization/v1/swagger.json"
},
{
"Name": "Messages API",
"EndPoint": "/messages/v1/swagger.json"
},
{
"Name": "IdentityServer Admin API",
"EndPoint": "/ids-admin/v1/swagger.json"
},
{
"Name": "Webhooks API",
"EndPoint": "/webhooks/v1/swagger.json"
}
]
},
"Redis": {
"Configuration": "127.0.0.1,defaultDatabase=10",
"InstanceName": "LINGYUN.Abp.Application"
},
"AuthServer": {
"Authority": "http://host.docker.internal:44385/",
"ApiName": "lingyun-abp-application",
"SwaggerClientId": "ApigatewayHostClient",
"SwaggerClientSecret": "1q2w3e*",
"ApiSocpes": "lingyun-abp-application"
},
"Serilog": {
"MinimumLevel": {
"Default": "Debug",
"Override": {
"System": "Warning",
"Microsoft": "Warning",
"DotNetCore": "Debug"
}
},
"WriteTo": [
{
"Name": "Console",
"Args": {
"restrictedToMinimumLevel": "Debug",
"outputTemplate": "{Timestamp:yyyy-MM-dd HH:mm:ss} [{Level:u3}] [{SourceContext}] [{ProcessId}] [{ThreadId}] - {Message:lj}{NewLine}{Exception}"
}
},
{
"Name": "Elasticsearch",
"Args": {
"nodeUris": "http://127.0.0.1:9200",
"indexFormat": "abp.dev.logging-{0:yyyy.MM.dd}",
"autoRegisterTemplate": true,
"autoRegisterTemplateVersion": "ESv7"
}
}
]
}
}

80
gateways/internal/LINGYUN.MicroService.Internal.ApiGateway/src/LINGYUN.MicroService.Internal.ApiGateway/appsettings.json

@ -1,80 +0,0 @@
{
"AuthServer": {
"Authority": "http://127.0.0.1:44385/",
"ApiName": "lingyun-abp-application",
"SwaggerClientId": "ApigatewayHostClient",
"SwaggerClientSecret": "1q2w3e*",
"ApiSocpes": "lingyun-abp-application"
},
"StringEncryption": {
"DefaultPassPhrase": "s46c5q55nxpeS8Ra",
"InitVectorBytes": "s83ng0abvd02js84",
"DefaultSalt": "sf&5)s3#"
},
"Serilog": {
"MinimumLevel": {
"Default": "Debug",
"Override": {
"System": "Warning",
"Microsoft": "Warning"
}
},
"Enrich": [ "FromLogContext", "WithProcessId", "WithThreadId", "WithEnvironmentName", "WithMachineName", "WithApplicationName", "WithUniqueId" ],
"WriteTo": [
{
"Name": "Console",
"Args": {
"initialMinimumLevel": "Information",
"standardErrorFromLevel": "Information",
"restrictedToMinimumLevel": "Information",
"outputTemplate": "{Timestamp:yyyy-MM-dd HH:mm:ss} [{Level:u3}] [{SourceContext}] [{ProcessId}] [{ThreadId}] - {Message:lj}{NewLine}{Exception}"
}
},
{
"Name": "File",
"Args": {
"path": "Logs/Debug-.log",
"restrictedToMinimumLevel": "Debug",
"rollingInterval": "Day",
"outputTemplate": "{Timestamp:yyyy-MM-dd HH:mm:ss} [{Level:u3}] [{SourceContext}] [{ProcessId}] [{ThreadId}] - {Message:lj}{NewLine}{Exception}"
}
},
{
"Name": "File",
"Args": {
"path": "Logs/Info-.log",
"restrictedToMinimumLevel": "Information",
"rollingInterval": "Day",
"outputTemplate": "{Timestamp:yyyy-MM-dd HH:mm:ss} [{Level:u3}] [{SourceContext}] [{ProcessId}] [{ThreadId}] - {Message:lj}{NewLine}{Exception}"
}
},
{
"Name": "File",
"Args": {
"path": "Logs/Warn-.log",
"restrictedToMinimumLevel": "Warning",
"rollingInterval": "Day",
"outputTemplate": "{Timestamp:yyyy-MM-dd HH:mm:ss} [{Level:u3}] [{SourceContext}] [{ProcessId}] [{ThreadId}] - {Message:lj}{NewLine}{Exception}"
}
},
{
"Name": "File",
"Args": {
"path": "Logs/Error-.log",
"restrictedToMinimumLevel": "Error",
"rollingInterval": "Day",
"outputTemplate": "{Timestamp:yyyy-MM-dd HH:mm:ss} [{Level:u3}] [{SourceContext}] [{ProcessId}] [{ThreadId}] - {Message:lj}{NewLine}{Exception}"
}
},
{
"Name": "File",
"Args": {
"path": "Logs/Fatal-.log",
"restrictedToMinimumLevel": "Fatal",
"rollingInterval": "Day",
"outputTemplate": "{Timestamp:yyyy-MM-dd HH:mm:ss} [{Level:u3}] [{SourceContext}] [{ProcessId}] [{ThreadId}] - {Message:lj}{NewLine}{Exception}"
}
}
]
}
}

1
gateways/internal/LINGYUN.MicroService.Internal.ApiGateway/src/LINGYUN.MicroService.Internal.ApiGateway/ocelot.Development.json

@ -1 +0,0 @@
{}

4349
gateways/internal/LINGYUN.MicroService.Internal.ApiGateway/src/LINGYUN.MicroService.Internal.ApiGateway/ocelot.json

File diff suppressed because it is too large

2
gateways/internal/LINGYUN.MicroService.Internal.ApiGateway/src/LINGYUN.MicroService.OpenApi.Gateway/OpenApiGatewayModule.cs

@ -9,7 +9,7 @@ using Microsoft.AspNetCore.Cors;
using Microsoft.AspNetCore.DataProtection;
using Microsoft.AspNetCore.WebSockets;
using Microsoft.Extensions.Caching.StackExchangeRedis;
using Microsoft.OpenApi.Models;
using Microsoft.OpenApi;
using StackExchange.Redis;
using Volo.Abp;
using Volo.Abp.AspNetCore.Mvc;

14
gateways/web/LY.MicroService.ApiGateway/InternalApiGatewayModule.cs

@ -56,13 +56,13 @@ public class InternalApiGatewayModule : AbpModule
Configure<InternalApiGatewayOptions>(options =>
{
options.Aggregator.ConfigurationUrl.ClientName = "_Abp_Application_Configuration";
options.Aggregator.ConfigurationUrl.GetUrls.AddIfNotContains(new RequestUrl("http://10.21.15.28:30010"));
options.Aggregator.ConfigurationUrl.GetUrls.AddIfNotContains(new RequestUrl("http://10.21.15.28:30015"));
options.Aggregator.ConfigurationUrl.GetUrls.AddIfNotContains(new RequestUrl("http://10.21.15.28:30020"));
options.Aggregator.ConfigurationUrl.GetUrls.AddIfNotContains(new RequestUrl("http://10.21.15.28:30025"));
options.Aggregator.ConfigurationUrl.GetUrls.AddIfNotContains(new RequestUrl("http://10.21.15.28:30030"));
options.Aggregator.ConfigurationUrl.GetUrls.AddIfNotContains(new RequestUrl("http://10.21.15.28:30035"));
options.Aggregator.ConfigurationUrl.GetUrls.AddIfNotContains(new RequestUrl("http://10.21.15.28:30040"));
options.Aggregator.ConfigurationUrl.GetUrls.AddIfNotContains(new RequestUrl("http://localhost:30010"));
options.Aggregator.ConfigurationUrl.GetUrls.AddIfNotContains(new RequestUrl("http://localhost:30015"));
options.Aggregator.ConfigurationUrl.GetUrls.AddIfNotContains(new RequestUrl("http://localhost:30020"));
options.Aggregator.ConfigurationUrl.GetUrls.AddIfNotContains(new RequestUrl("http://localhost:30025"));
options.Aggregator.ConfigurationUrl.GetUrls.AddIfNotContains(new RequestUrl("http://localhost:30030"));
options.Aggregator.ConfigurationUrl.GetUrls.AddIfNotContains(new RequestUrl("http://localhost:30035"));
options.Aggregator.ConfigurationUrl.GetUrls.AddIfNotContains(new RequestUrl("http://localhost:30040"));
});
context.Services.AddAbpSwaggerGenWithOAuth(

Loading…
Cancel
Save