文章目录
- 项目地址
- 一、OpenTelemetry
- 1.1 配置OpenTelemetry
- 1. 服务添加
- 2. 添加服务标识
- 3. 添加请求的标识
- 4. 添加中间价
- 二、Rabbit MQ
- 2.1 配置Rabbit MQ
- 1. docker-compose
- 2. 添加Rabbit MQ的Connect String
- 2.2 替换成Rabbit MQ
- 1. 安装所需要的包
- 2. 使用
- 三、API Gateways
- 3.1 创建Gateway
- 1. 配置docker-compose
- 2. 添加各种服务
- 3. 添加jwt配置
- 4. 添加日志追踪
- 5. 配置appsettings
- 6. Yarp反向代理设置
- 四、微服务拆分
- 4.1 创建新的Ticketing api
- 1. 创建.net webapi 项目
- 2. 安装包和引用
- 3. 将公共api复制到该模块
- 4. appsettings
项目地址
- 教程作者:
- 教程地址:
- 代码仓库地址:
- 所用到的框架和插件:
dbt
airflow
一、OpenTelemetry
1.1 配置OpenTelemetry
1. 服务添加
namespace Evently.Common.Infrastructure;
配置
services
.AddOpenTelemetry()
.ConfigureResource(resource => resource.AddService(serviceName))
.WithTracing(tracing =>
{
tracing
.AddAspNetCoreInstrumentation()
.AddHttpClientInstrumentation()
.AddEntityFrameworkCoreInstrumentation()
.AddRedisInstrumentation()
.AddNpgsql()
.AddSource(MassTransit.Logging.DiagnosticHeaders.DefaultListenerName);
tracing.AddOtlpExporter();
});
2. 添加服务标识
- 创建服务标识
namespace Evently.Api.OpenTelemetry;
public static class DiagnosticsConfig
{
public const string ServiceName = "Evently.Api";
}
- program里注册
3. 添加请求的标识
- 在RequestLoggingPipelineBehavior添加的请求和服务的标识
4. 添加中间价
- LogContextTraceLoggingMiddleware
namespace Evently.Api.Middleware;
internal sealed class LogContextTraceLoggingMiddleware(RequestDelegate next)
{
public Task Invoke(HttpContext context)
{
string traceId = Activity.Current?.TraceId.ToString();
using (LogContext.PushProperty("TraceId", traceId))
{
return next.Invoke(context);
}
}
}
- MiddlewareExtensions 用于将自定义日志追踪中间件 LogContextTraceLoggingMiddleware 添加到 ASP.NET Core 的中间件管道中。
namespace Evently.Api.Middleware;
internal static class MiddlewareExtensions
{
internal static IApplicationBuilder UseLogContextTraceLogging(this IApplicationBuilder app)
{
app.UseMiddleware<LogContextTraceLoggingMiddleware>();
return app;
}
}
- 中间件添加
二、Rabbit MQ
2.1 配置Rabbit MQ
1. docker-compose
docker-compose.yml
evently.queue:
image: rabbitmq:management-alpine
container_name: Evently.Queue
hostname: evently-queue
volumes:
- ./.containers/queue/data/:/var/lib/rabbitmq
- ./.containers/queue/log/:/var/log/rabbitmq
environment:
RABBITMQ_DEFAULT_USER: guest
RABBITMQ_DEFAULT_PASS: guest
ports:
- 5672:5672
- 15672:15672
2. 添加Rabbit MQ的Connect String
"ConnectionStrings": {
"Database": "Host=evently.database;Port=5432;Database=evently;Username=postgres;Password=postgres;Include Error Detail=true",
"Cache": "evently.redis:6379",
"Queue": "amqp://evently-queue:5672"
},
2.2 替换成Rabbit MQ
1. 安装所需要的包
- 替换之前内存为Rabbit MQ
- 安装所需要的包
<PackageReference Include="AspNetCore.HealthChecks.Rabbitmq" Version="8.0.1" />
<PackageReference Include="MassTransit.RabbitMQ" Version="8.2.1" />
- 创建MQ配置文件
namespace Evently.Common.Infrastructure.EventBus;
public sealed record RabbitMqSettings(string Host, string Username = "guest", string Password = "guest");
2. 使用
- 修改MassTransit,将内存改为MQ
- 注册Ticketing的消费者
- 注册Event的消费者
- Program里注册
三、API Gateways
3.1 创建Gateway
1. 配置docker-compose
2. 添加各种服务
- 在Gateway的
program.cs
里添加服务
WebApplicationBuilder builder = WebApplication.CreateBuilder(args);
builder.Host.UseSerilog((context, loggerConfig) => loggerConfig.ReadFrom.Configuration(context.Configuration));
builder.Services.AddReverseProxy()
.LoadFromConfig(builder.Configuration.GetSection("ReverseProxy"));
builder.Services
.AddOpenTelemetry()
.ConfigureResource(resource => resource.AddService(DiagnosticsConfig.ServiceName))
.WithTracing(tracing =>
{
tracing
.AddAspNetCoreInstrumentation()
.AddHttpClientInstrumentation()
.AddSource("Yarp.ReverseProxy");
tracing.AddOtlpExporter();
});
builder.Services.AddAuthorization();
builder.Services.AddAuthentication().AddJwtBearer();
builder.Services.ConfigureOptions<JwtBearerConfigureOptions>();
WebApplication app = builder.Build();
app.UseLogContextTraceLogging();
app.UseSerilogRequestLogging();
app.UseAuthentication();
app.UseAuthorization();
app.MapReverseProxy();
app.Run();
3. 添加jwt配置
namespace Evently.Gateway.Authentication;
internal sealed class JwtBearerConfigureOptions(IConfiguration configuration)
: IConfigureNamedOptions<JwtBearerOptions>
{
private const string ConfigurationSectionName = "Authentication";
public void Configure(JwtBearerOptions options)
{
configuration.GetSection(ConfigurationSectionName).Bind(options);
}
public void Configure(string? name, JwtBearerOptions options)
{
Configure(options);
}
}
4. 添加日志追踪
- 添加日志追踪
using System.Diagnostics;
using Serilog.Context;
namespace Evently.Gateway.Middleware;
internal sealed class LogContextTraceLoggingMiddleware(RequestDelegate next)
{
public Task Invoke(HttpContext context)
{
string traceId = Activity.Current?.TraceId.ToString();
using (LogContext.PushProperty("TraceId", traceId))
{
return next.Invoke(context);
}
}
}
- 注册
namespace Evently.Gateway.Middleware;
internal static class MiddlewareExtensions
{
internal static IApplicationBuilder UseLogContextTraceLogging(this IApplicationBuilder app)
{
app.UseMiddleware<LogContextTraceLoggingMiddleware>();
return app;
}
}
5. 配置appsettings
- 基础设置
{
"Authentication": {
"Audience": "account",
"TokenValidationParameters": {
"ValidIssuers": [ "http://evently.identity:8080/realms/evently", "http://localhost:18080/realms/evently" ]
},
"MetadataAddress": "http://evently.identity:8080/realms/evently/.well-known/openid-configuration",
"RequireHttpsMetadata": false
},
"Serilog": {
"Using": [
"Serilog.Sinks.Console",
"Serilog.Sinks.Seq"
],
"MinimumLevel": {
"Default": "Information",
"Override": {
"Microsoft": "Information"
}
},
"WriteTo": [
{ "Name": "Console" },
{
"Name": "Seq",
"Args": { "serverUrl": "http://evently.seq:5341" }
}
],
"Enrich": [ "FromLogContext", "WithMachineName", "WithThreadId" ],
"Properties": {
"Application": "Evently.Gateway"
}
},
"OTEL_EXPORTER_OTLP_ENDPOINT": "http://evently.jaeger:4317",
}
6. Yarp反向代理设置
四、微服务拆分
我现在有个单体架构的票务系统,但是当业务高峰期的时候,突然订票剧增,我想单独把票务拆出来做微服务可以吗,其次由于我想控制成本,平时只想一个实例运行这个微服务,当业务高峰期,需要自动扩充到4个
4.1 创建新的Ticketing api
1. 创建.net webapi 项目
2. 安装包和引用
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<UserSecretsId>ab603965-9d79-46f7-b0fd-56e950ec7073</UserSecretsId>
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
<DockerfileContext>..\..\..</DockerfileContext>
<DockerComposeProjectPath>..\..\..\docker-compose.dcproj</DockerComposeProjectPath>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="AspNetCore.HealthChecks.UI.Client" Version="8.0.1" />
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="8.0.4" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="8.0.4">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.20.1" />
<PackageReference Include="Serilog.AspNetCore" Version="8.0.1" />
<PackageReference Include="Serilog.Sinks.Seq" Version="7.0.0" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\Modules\Ticketing\Evently.Modules.Ticketing.Infrastructure\Evently.Modules.Ticketing.Infrastructure.csproj" />
</ItemGroup>
</Project>
3. 将公共api复制到该模块
4. appsettings
appsettings.json
{
"ConnectionStrings": {
"Database": "",
"Cache": "",
"Queue": ""
},
"AllowedHosts": "*",
"Authentication": {
"Audience": "",
"TokenValidationParameters": {
"ValidIssuers": []
},
"MetadataAddress": "",
"RequireHttpsMetadata": false
},
"KeyCloak": {
"HealthUrl": ""
},
"Serilog": {
"Using": [
"Serilog.Sinks.Console"
],
"MinimumLevel": {
"Default": "Information",
"Override": {
"Microsoft": "Information"
}
},
"WriteTo": [
{ "Name": "Console" }
],
"Enrich": [ "FromLogContext", "WithMachineName", "WithThreadId" ],
"Properties": {
"Application": "Evently.Ticketing.Api"
}
},
"OTEL_EXPORTER_OTLP_ENDPOINT": ""
}
appsettings.Development.json
{
"ConnectionStrings": {
"Database": "Host=evently.database;Port=5432;Database=evently;Username=postgres;Password=postgres;Include Error Detail=true",
"Cache": "evently.redis:6379",
"Queue": "amqp://evently-queue:5672"
},
"Authentication": {
"Audience": "account",
"TokenValidationParameters": {
"ValidIssuers": [ "http://evently.identity:8080/realms/evently", "http://localhost:18080/realms/evently" ]
},
"MetadataAddress": "http://evently.identity:8080/realms/evently/.well-known/openid-configuration",
"RequireHttpsMetadata": false
},
"KeyCloak": {
"HealthUrl": "http://evently.identity:8080/health/"
},
"Serilog": {
"Using": [
"Serilog.Sinks.Console",
"Serilog.Sinks.Seq"
],
"MinimumLevel": {
"Default": "Information",
"Override": {
"Microsoft": "Information",
"Evently.Modules.Ticketing.Infrastructure.Outbox": "Warning",
"Evently.Modules.Ticketing.Infrastructure.Inbox": "Warning"
}
},
"WriteTo": [
{ "Name": "Console" },
{
"Name": "Seq",
"Args": { "serverUrl": "http://evently.seq:5341" }
}
],
"Enrich": [ "FromLogContext", "WithMachineName", "WithThreadId" ],
"Properties": {
"Application": "Evently.Ticketing.Api"
}
},
"OTEL_EXPORTER_OTLP_ENDPOINT": "http://evently.jaeger:4317"
}