fengling-gateway/src/yarpgateway/Services/RouteCache.cs
Kimi CLI ca27d8659d docs: remove GSD workflow planning documents
- Remove .planning/ directory (GSD workflow artifacts)
- Remove 网关配置的新想法.md (outdated design doc)
- Keep only essential technical documentation
2026-03-08 15:49:12 +08:00

140 lines
4.0 KiB
C#

using System.Collections.Concurrent;
using Fengling.Platform.Domain.AggregatesModel.GatewayAggregate;
using YarpGateway.Data;
using Microsoft.Extensions.Logging;
using Microsoft.EntityFrameworkCore;
namespace YarpGateway.Services;
public class RouteInfo
{
public string Id { get; set; } = string.Empty;
public string ClusterId { get; set; } = string.Empty;
public string PathPattern { get; set; } = string.Empty;
public int Priority { get; set; }
}
public interface IRouteCache
{
Task InitializeAsync();
Task ReloadAsync();
RouteInfo? GetRoute(string serviceName);
RouteInfo? GetRouteByPath(string path);
}
public class RouteCache : IRouteCache
{
private readonly IDbContextFactory<GatewayDbContext> _dbContextFactory;
private readonly ILogger<RouteCache> _logger;
private readonly ConcurrentDictionary<string, RouteInfo> _routes = new();
private readonly ConcurrentDictionary<string, RouteInfo> _pathRoutes = new();
private readonly ReaderWriterLockSlim _lock = new();
public RouteCache(IDbContextFactory<GatewayDbContext> dbContextFactory, ILogger<RouteCache> logger)
{
_dbContextFactory = dbContextFactory;
_logger = logger;
}
public async Task InitializeAsync()
{
_logger.LogInformation("Initializing route cache from database...");
try
{
await LoadFromDatabaseAsync();
_logger.LogInformation("Route cache initialized: {Count} routes", _routes.Count);
}
catch (Exception ex)
{
_logger.LogWarning(ex, "Failed to load routes from database. This is normal if database is not initialized yet.");
}
}
public async Task ReloadAsync()
{
_logger.LogInformation("Reloading route cache...");
await LoadFromDatabaseAsync();
_logger.LogInformation("Route cache reloaded: {Count} routes", _routes.Count);
}
public RouteInfo? GetRoute(string? serviceName)
{
if (string.IsNullOrEmpty(serviceName))
{
_logger.LogDebug("GetRoute called with null or empty serviceName");
return null;
}
_lock.EnterReadLock();
try
{
if (_routes.TryGetValue(serviceName, out var route))
{
_logger.LogDebug("Found route: {Service} -> {Cluster}", serviceName, route.ClusterId);
return route;
}
_logger.LogWarning("No route found for: {Service}", serviceName);
return null;
}
finally
{
_lock.ExitReadLock();
}
}
public RouteInfo? GetRouteByPath(string path)
{
return _pathRoutes.TryGetValue(path, out var route) ? route : null;
}
private async Task LoadFromDatabaseAsync()
{
using var db = _dbContextFactory.CreateDbContext();
List<GwRoute> routes;
try
{
routes = await db.GwRoutes
.Where(r => r.Status == 1 && !r.IsDeleted)
.ToListAsync();
}
catch (Exception ex)
{
_logger.LogWarning(ex, "Database table not found. Returning empty route list.");
routes = new List<GwRoute>();
}
_lock.EnterWriteLock();
try
{
_routes.Clear();
_pathRoutes.Clear();
foreach (var route in routes)
{
var pathPattern = route.Match?.Path ?? string.Empty;
var routeInfo = new RouteInfo
{
Id = route.Id,
ClusterId = route.ClusterId,
PathPattern = pathPattern,
Priority = route.Priority
};
_routes[route.ServiceName] = routeInfo;
_pathRoutes[pathPattern] = routeInfo;
_logger.LogDebug("Loaded route: {Service} -> {Cluster}",
route.ServiceName, route.ClusterId);
}
}
finally
{
_lock.ExitWriteLock();
}
}
}