Startup dan Pipa (Pipeline)
Pendahuluan
Dokumen ini memberikan panduan detail tentang urutan startup aplikasi, registrasi layanan, konfigurasi pipa middleware, dan mekanisme pemeriksaan kesehatan dependensi. Memahami alur ini sangat penting untuk memperluas aplikasi atau men-debug masalah startup.
Alur Bootstrap Program.cs
File Program.cs mengikuti urutan inisialisasi yang diatur secara ketat. Urutannya sangat penting: validasi konfigurasi harus selesai sebelum registrasi layanan, dan pemeriksaan kesehatan harus lulus sebelum migrasi.
Urutan Inisialisasi
- Konfigurasi Logging: Membuat pembangun (builder) dan menginisialisasi Serilog untuk menangkap semua peristiwa startup berikutnya.
- Validasi Konfigurasi: Memvalidasi semua kunci konfigurasi yang diperlukan. Gagal segera jika ada yang hilang.
- Pemeriksaan Keamanan: Mengeluarkan peringatan untuk konfigurasi yang tidak aman (misalnya, rahasia JWT yang pendek).
- Log Pengaturan: Mengeluarkan pengaturan migrasi otomatis saat ini untuk debugging.
- Daftarkan Layanan: Mengikat opsi, mengonfigurasi database, menghubungkan ke Redis, dan mendaftarkan semua layanan DI.
- Bangun Aplikasi: Mengompilasi koleksi layanan ke dalam instansi WebApplication.
- Pemeriksaan Kesehatan Dependensi: Melakukan ping ke PostgreSQL, Redis, InfluxDB, dan OpenSearch. Gagal segera jika tidak terjangkau.
- Terapkan Migrasi: Secara otomatis menerapkan migrasi EF Core yang tertunda (jika diaktifkan).
- Konfigurasi Pipa: Menyiapkan middleware (CORS, Rate Limiting, Auth) dan memulai server.
Implementasi
// 1. Konfigurasi logging
var builder = WebApplication.CreateBuilder(args);
builder.Host.UseSerilog((context, services, loggerConfiguration) =>
loggerConfiguration
.ReadFrom.Configuration(context.Configuration)
.ReadFrom.Services(services)
.Enrich.FromLogContext());
// 2. Validasi konfigurasi
var validatedConfigGroups = builder.Configuration.ValidateRequiredConfiguration();
Log.Information("Validasi konfigurasi lulus untuk grup: {ConfigGroups}",
string.Join(", ", validatedConfigGroups));
// 3. Pemeriksaan keamanan
var jwtSecretLength = builder.Configuration[BackendConfigurationKeys.JwtSecretKey]?.Length ?? 0;
if (jwtSecretLength < 32)
{
Log.Warning("Panjang JwtSettings:Secret adalah {SecretLength}. Gunakan setidaknya 32 karakter.",
jwtSecretLength);
}
// 4. Log pengaturan
Log.Information("Pengaturan migrasi otomatis: AutoMigrate={AutoMigrate}, MaxRetries={MaxRetries}...",
builder.Configuration.GetValue(BackendConfigurationKeys.AutoMigrateKey, true), ...);
// 5. Daftarkan layanan
await builder.Services.AddBackendServicesAsync(builder.Configuration);
// 6. Bangun aplikasi
var app = builder.Build();
// 7. Pemeriksaan kesehatan dependensi
await app.RunStartupDependencyHealthChecksAsync();
// 8. Terapkan migrasi
await app.ApplyDatabaseMigrationsAsync();
// 9. Konfigurasi pipa
app.UseBackendPipeline();
await app.RunAsync();
Registrasi Layanan — AddBackendServicesAsync
Metode ServiceCollectionExtensions.AddBackendServicesAsync mendaftarkan semua dependensi aplikasi dalam satu panggilan. Metode ini bersifat async karena secara aktif menghubungkan ke Redis.
Urutan Registrasi
- Ikat opsi ber-tipe dari bagian konfigurasi
PostgresqlSettingsOptions,RedisSettingsOptions,JwtSettingsOptionsInfluxDbOptions,OpenSearchOptions,AnsibleSettingsOptionsSensorApiSettingsOptions,DataCollectorOptions,DockerRegistryOptionsSensorSettingsOptions,DatabaseOptionsBackendAppOptions,SensorRuntimeOptions
- Daftarkan Controller dengan opsi serializer JSON
PropertyNameCaseInsensitive = true
- Daftarkan EndpointsApiExplorer
- Daftarkan Swagger dengan definisi keamanan JWT Bearer
- Daftarkan layanan gRPC
- Daftarkan pembatas laju (rate limiters)
auth-login: jendela tetap, 5 permintaan/menitauth-register: jendela tetap, 3 permintaan/menit
- Konfigurasi PostgreSQL DbContext
- Membangun string koneksi dari opsi ber-tipe
services.AddDbContext<ApplicationDbContext>(UseNpgsql)
- Hubungkan ke Redis (aktif, asinkron)
- Membangun string koneksi dari opsi ber-tipe
ConnectionMultiplexer.ConnectAsync(...)- Daftarkan
IConnectionMultiplexersebagai singleton - Daftarkan
IRedisService(implementasiRedisService) sebagai singleton
- Daftarkan layanan singleton
JwtService,InfluxDbService,OpenSearchService,ResilientHttpService
- Daftarkan repositori scoped
EfRepository<T>(basis generik)PermissionRepository,RoleRepository,OrganizationRepositoryUserRepository,UserRoleRepository,OrganizationUserRoleRepositorySensorRepository,SensorHeartbeatRepository,VirtualSensorRepositoryAssetRepository
- Daftarkan layanan scoped
PermissionService,RoleService,LocationService,AnalyticsServiceOrganizationService,UserService,AuthServiceVirtualSensorsAnalyticsService,SensorHeartbeatServiceSensorInterfaceAllocationService,SensorProvisioningScriptServiceSensorActivationService,SensorsService,AssetServiceVirtualSensorsService,OpenSearchAnalyticsService
- Daftarkan pabrik HttpClient
- Konfigurasi autentikasi JWT Bearer
ValidateIssuer,ValidateAudience,ValidateLifetime = trueClockSkew = TimeSpan.ZeroOnTokenValidated: periksa blacklist token Redis
- Konfigurasi kebijakan otorisasi
"manage-users","manage-sensors","manage-assets","manage-roles","edit-organization"
- Daftarkan kebijakan CORS
Kelas Opsi Ber-tipe (Typed Options)
Semua bagian konfigurasi diikat ke kelas opsi ber-tipe kuat yang didefinisikan dalam BackendConfiguration.cs:
| Kelas Opsi | Bagian Konfigurasi | Properti Kunci |
|---|---|---|
PostgresqlSettingsOptions | PostgresqlSettings | Host, Port, Username, Password, Database, MaxPoolSize |
RedisSettingsOptions | RedisSettings | Host, Port, Password, DefaultDatabase |
JwtSettingsOptions | JwtSettings | Secret, Issuer, Audience, ExpiryMinutes |
InfluxDbOptions | InfluxDb | Url, Token, Org, Bucket |
OpenSearchOptions | OpenSearch | Url, Username, Password, IndexName |
AnsibleSettingsOptions | AnsibleSettings | ServiceUrl |
SensorApiSettingsOptions | SensorApiSettings | ApiKey |
DataCollectorOptions | DataCollector | Endpoint, Port |
DockerRegistryOptions | DockerRegistry | Registry, Username, Password |
SensorSettingsOptions | SensorSettings | HeartbeatTimeoutMinutes, ProvisioningSudoPassword |
DatabaseOptions | Database | AutoMigrate, MigrationMaxRetries, MigrationRetryDelaySeconds |
OpenSearchAnalyticsOptions | OpenSearchAnalytics | DefaultTimeoutSeconds, DashboardTimeoutSeconds, AggregationTimeoutSeconds, ListQueryTimeoutSeconds |
AnalyticsCacheWarmingOptions | AnalyticsCacheWarming | Enabled, IntervalMinutes, InitialDelaySeconds |
BackendAppOptions | (komposit) | Url |
SensorRuntimeOptions | (komposit) | Menggabungkan beberapa bagian untuk operasi sensor |
Pipa Middleware — UseBackendPipeline
Metode WebApplicationExtensions.UseBackendPipeline mengonfigurasi pipa pemrosesan permintaan:
public static WebApplication UseBackendPipeline(this WebApplication app)
{
app.UseSerilogRequestLogging(); // 1. Log semua permintaan HTTP
app.UseGlobalExceptionHandling(); // 2. Penangan pengecualian global
app.UseSwagger(); // 3. Sajikan spek OpenAPI
app.UseSwaggerUI(c => // 4. UI Swagger di /swagger
{
c.SwaggerEndpoint("/swagger/v1/swagger.json", "Ravenxcope Backend API v1");
c.DocExpansion(DocExpansion.None);
});
app.UseCors(); // 5. Terapkan kebijakan CORS
app.UseHttpsRedirection(); // 6. Alihkan HTTP → HTTPS
app.UseRateLimiter(); // 7. Terapkan kebijakan pembatasan laju
app.UseAuthentication(); // 8. Validasi token JWT
app.UseAuthorization(); // 9. Penegakan kebijakan
app.MapControllers(); // 10. Petakan controller berbasis atribut
app.MapGrpcService<SensorHealthcheckService>(); // 11. Petakan layanan gRPC
return app;
}
Urutan Pipa: Urutan middleware sangat penting.
UseGlobalExceptionHandlingharus diletakkan di awal untuk menangkap semua pengecualian hilir.UseRateLimiterharus diletakkan sebelumUseAuthenticationagar pembatasan laju diterapkan sebelum validasi token.UseAuthenticationharus diletakkan sebelumUseAuthorization, dan keduanya harus diletakkan sebelumMapControllersagar atribut auth berfungsi.
Validasi Konfigurasi
Metode ConfigurationValidationExtensions.ValidateRequiredConfiguration memvalidasi semua kunci yang diperlukan saat startup:
Kunci yang Divalidasi
| Bagian Konfigurasi | Kunci yang Diperlukan |
|---|---|
PostgresqlSettings | Host, Port, Username, Password, Database |
RedisSettings | Host, Port |
JwtSettings | Secret |
InfluxDb | Url, Token, Org, Bucket |
OpenSearch | Url, Username, Password, IndexName |
AnsibleSettings | ServiceUrl |
SensorApiSettings | ApiKey |
SensorSettings | ProvisioningSudoPassword |
BackendUrl | (kunci tingkat root) |
DataCollector | Endpoint, Port |
Aturan Validasi
- Nilai hilang atau kosong → melempar
InvalidOperationException - Nilai placeholder → pola kurung kurawal ganda
{{...}}dideteksi dan dianggap hilang - String placeholder yang dikenal →
"your-sensor-api-key-here"dianggap hilang - Validasi port → Port PostgreSQL, Redis, dan DataCollector harus berupa integer positif
- Pengaturan migrasi → MigrationMaxRetries dan MigrationRetryDelaySeconds harus berupa integer positif
Pemeriksaan Kesehatan Dependensi
Metode StartupDependencyHealthChecksExtensions.RunStartupDependencyHealthChecksAsync memverifikasi semua dependensi eksternal sebelum melayani lalu lintas.
Detail Pemeriksaan
| Dependensi | Metode Pemeriksaan | Jumlah Retry | Batas Waktu |
|---|---|---|---|
| PostgreSQL | dbContext.Database.CanConnectAsync() | 3 | 10 detik |
| Redis | redis.GetDatabase().PingAsync() | 3 | 10 detik |
| InfluxDB | HTTP GET {url}/health | 3 | 10 detik |
| OpenSearch | HTTP GET {url}/ dengan Basic Auth | 3 | 10 detik |
Perilaku Retry
Pemeriksaan menggunakan HealthCheckHelper.RunHealthCheckWithRetry<T>:
public static async Task<bool> RunHealthCheckWithRetry<T>(
IServiceProvider serviceProvider,
ILogger logger,
Func<T, CancellationToken, Task> checkFunc,
string serviceName,
int maxRetries = 3,
int timeoutSeconds = 10)
- Setiap percobaan memiliki
CancellationTokenSourcedengan batas waktu yang dikonfigurasi - Setelah batas waktu atau pengecualian, sistem menunggu 1 detik sebelum mencoba lagi
- Jika semua percobaan gagal, sistem mencatat kesalahan dan mengembalikan
false - Jika ada satu dependensi yang gagal, startup akan melempar
InvalidOperationException
Penanganan Khusus OpenSearch
Pemeriksaan kesehatan OpenSearch menggunakan DangerousAcceptAnyServerCertificateValidator untuk melewati validasi sertifikat SSL, dan mengautentikasi dengan kredensial Basic Auth dari konfigurasi.
Migrasi Database
Metode DatabaseMigrationExtensions.ApplyDatabaseMigrationsAsync menangani migrasi skema database otomatis:
Alur Migrasi
- Periksa Konfigurasi: Baca
Database:AutoMigratedari opsi (default:true) - Evaluasi: Jika
false, lewati migrasi sepenuhnya. - Loop Eksekusi: Untuk setiap percobaan (hingga
MigrationMaxRetries):- Buat
ApplicationDbContextscoped - Panggil
dbContext.Database.MigrateAsync() - Jika berhasil: Log keberhasilan migrasi dan hentikan loop.
- Jika gagal: Log peringatan, tunggu selama
RetryDelaySeconds, lalu coba lagi.
- Buat
- Evaluasi Akhir: Jika semua percobaan gagal, catat kesalahan kritis dan lempar pengecualian untuk menghentikan startup.
Konfigurasi Migrasi
| Kunci | Tipe | Default | Deskripsi |
|---|---|---|---|
Database:AutoMigrate | bool | true | Aktifkan/nonaktifkan migrasi otomatis |
Database:MigrationMaxRetries | int | 10 | Jumlah maksimal percobaan ulang |
Database:MigrationRetryDelaySeconds | int | 5 | Penundaan antar percobaan ulang |