- Remove .planning/ directory (GSD workflow artifacts) - Remove 网关配置的新想法.md (outdated design doc) - Keep only essential technical documentation
140 lines
4.0 KiB
C#
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();
|
|
}
|
|
}
|
|
}
|