diff --git a/FastGithub.DomainResolve/DomainResolver.cs b/FastGithub.DomainResolve/DomainResolver.cs index 5081d5b..b91c742 100644 --- a/FastGithub.DomainResolve/DomainResolver.cs +++ b/FastGithub.DomainResolve/DomainResolver.cs @@ -18,7 +18,7 @@ namespace FastGithub.DomainResolve { private readonly DnsClient dnsClient; private readonly DomainPersistence persistence; - private readonly IPAddressStatusService statusService; + private readonly IPAddressService addressService; private readonly ILogger logger; private readonly ConcurrentDictionary dnsEndPointAddress = new(); @@ -27,17 +27,17 @@ namespace FastGithub.DomainResolve /// /// /// - /// + /// /// public DomainResolver( DnsClient dnsClient, DomainPersistence persistence, - IPAddressStatusService statusService, + IPAddressService addressService, ILogger logger) { this.dnsClient = dnsClient; this.persistence = persistence; - this.statusService = statusService; + this.addressService = addressService; this.logger = logger; foreach (var endPoint in persistence.ReadDnsEndPoints()) @@ -103,7 +103,7 @@ namespace FastGithub.DomainResolve var dnsEndPoint = keyValue.Key; var oldAddresses = keyValue.Value; - var newAddresses = await this.statusService.GetAvailableAddressesAsync(dnsEndPoint, cancellationToken); + var newAddresses = await this.addressService.GetAddressesAsync(dnsEndPoint, oldAddresses, cancellationToken); if (oldAddresses.SequenceEqual(newAddresses) == false) { this.dnsEndPointAddress[dnsEndPoint] = newAddresses; diff --git a/FastGithub.DomainResolve/IPAddressStatusService.cs b/FastGithub.DomainResolve/IPAddressService.cs similarity index 50% rename from FastGithub.DomainResolve/IPAddressStatusService.cs rename to FastGithub.DomainResolve/IPAddressService.cs index 9df0037..28f3671 100644 --- a/FastGithub.DomainResolve/IPAddressStatusService.cs +++ b/FastGithub.DomainResolve/IPAddressService.cs @@ -17,12 +17,17 @@ namespace FastGithub.DomainResolve /// 状态缓存5分钟 /// 连接超时5秒 /// - sealed class IPAddressStatusService + sealed class IPAddressService { + private record DomainAddress(string Domain, IPAddress Address); + private readonly TimeSpan domainExpiration = TimeSpan.FromMinutes(5d); + private readonly IMemoryCache domainAddressCache = new MemoryCache(Options.Create(new MemoryCacheOptions())); + + private record AddressElapsed(IPAddress Address, TimeSpan Elapsed); private readonly TimeSpan brokeExpiration = TimeSpan.FromMinutes(1d); private readonly TimeSpan normalExpiration = TimeSpan.FromMinutes(5d); private readonly TimeSpan connectTimeout = TimeSpan.FromSeconds(5d); - private readonly IMemoryCache statusCache = new MemoryCache(Options.Create(new MemoryCacheOptions())); + private readonly IMemoryCache addressElapsedCache = new MemoryCache(Options.Create(new MemoryCacheOptions())); private readonly DnsClient dnsClient; @@ -30,7 +35,7 @@ namespace FastGithub.DomainResolve /// IP状态服务 /// /// - public IPAddressStatusService(DnsClient dnsClient) + public IPAddressService(DnsClient dnsClient) { this.dnsClient = dnsClient; } @@ -39,24 +44,40 @@ namespace FastGithub.DomainResolve /// 并行获取可连接的IP /// /// + /// /// /// - public async Task GetAvailableAddressesAsync(DnsEndPoint dnsEndPoint, CancellationToken cancellationToken) + public async Task GetAddressesAsync(DnsEndPoint dnsEndPoint, IEnumerable oldAddresses, CancellationToken cancellationToken) { - var addresses = new List(); - await foreach (var address in this.dnsClient.ResolveAsync(dnsEndPoint, fastSort: false, cancellationToken)) + var ipEndPoints = new HashSet(); + + // 历史未过期的IP节点 + foreach (var address in oldAddresses) { - addresses.Add(address); + var domainAddress = new DomainAddress(dnsEndPoint.Host, address); + if (this.domainAddressCache.TryGetValue(domainAddress, out _)) + { + ipEndPoints.Add(new IPEndPoint(address, dnsEndPoint.Port)); + } } - if (addresses.Count == 0) + // 新解析出的IP节点 + await foreach (var address in this.dnsClient.ResolveAsync(dnsEndPoint, fastSort: false, cancellationToken)) + { + ipEndPoints.Add(new IPEndPoint(address, dnsEndPoint.Port)); + var domainAddress = new DomainAddress(dnsEndPoint.Host, address); + this.domainAddressCache.Set(domainAddress, default(object), this.domainExpiration); + } + + if (ipEndPoints.Count == 0) { return Array.Empty(); } - var statusTasks = addresses.Select(address => this.GetStatusAsync(address, dnsEndPoint.Port, cancellationToken)); - var statusArray = await Task.WhenAll(statusTasks); - return statusArray + var addressElapsedTasks = ipEndPoints.Select(item => this.GetAddressElapsedAsync(item, cancellationToken)); + var addressElapseds = await Task.WhenAll(addressElapsedTasks); + + return addressElapseds .Where(item => item.Elapsed < TimeSpan.MaxValue) .OrderBy(item => item.Elapsed) .Select(item => item.Address) @@ -65,18 +86,16 @@ namespace FastGithub.DomainResolve /// - /// 获取IP状态 - /// - /// - /// + /// 获取IP节点的时延 + /// + /// /// /// - private async Task GetStatusAsync(IPAddress address, int port, CancellationToken cancellationToken) + private async Task GetAddressElapsedAsync(IPEndPoint endPoint, CancellationToken cancellationToken) { - var endPoint = new IPEndPoint(address, port); - if (this.statusCache.TryGetValue(endPoint, out var status)) + if (this.addressElapsedCache.TryGetValue(endPoint, out var addressElapsed)) { - return status; + return addressElapsed; } var stopWatch = Stopwatch.StartNew(); @@ -87,16 +106,16 @@ namespace FastGithub.DomainResolve using var socket = new Socket(endPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp); await socket.ConnectAsync(endPoint, linkedTokenSource.Token); - status = new IPAddressStatus(endPoint.Address, stopWatch.Elapsed); - return this.statusCache.Set(endPoint, status, this.normalExpiration); + addressElapsed = new AddressElapsed(endPoint.Address, stopWatch.Elapsed); + return this.addressElapsedCache.Set(endPoint, addressElapsed, this.normalExpiration); } catch (Exception) { cancellationToken.ThrowIfCancellationRequested(); - status = new IPAddressStatus(endPoint.Address, TimeSpan.MaxValue); + addressElapsed = new AddressElapsed(endPoint.Address, TimeSpan.MaxValue); var expiration = NetworkInterface.GetIsNetworkAvailable() ? this.normalExpiration : this.brokeExpiration; - return this.statusCache.Set(endPoint, status, expiration); + return this.addressElapsedCache.Set(endPoint, addressElapsed, expiration); } finally { diff --git a/FastGithub.DomainResolve/IPAddressStatus.cs b/FastGithub.DomainResolve/IPAddressStatus.cs deleted file mode 100644 index 8019f8b..0000000 --- a/FastGithub.DomainResolve/IPAddressStatus.cs +++ /dev/null @@ -1,34 +0,0 @@ -using System; -using System.Net; - -namespace FastGithub.DomainResolve -{ - /// - /// 表示IP的状态 - /// - struct IPAddressStatus - { - /// - /// 获取IP地址 - /// - public IPAddress Address { get; } - - /// - /// 获取延时 - /// 当连接失败时值为MaxValue - /// - public TimeSpan Elapsed { get; } - - - /// - /// IP的状态 - /// - /// - /// - public IPAddressStatus(IPAddress address, TimeSpan elapsed) - { - this.Address = address; - this.Elapsed = elapsed; - } - } -} diff --git a/FastGithub.DomainResolve/ServiceCollectionExtensions.cs b/FastGithub.DomainResolve/ServiceCollectionExtensions.cs index 69c5075..5dc476f 100644 --- a/FastGithub.DomainResolve/ServiceCollectionExtensions.cs +++ b/FastGithub.DomainResolve/ServiceCollectionExtensions.cs @@ -19,7 +19,7 @@ namespace FastGithub services.TryAddSingleton(); services.TryAddSingleton(); services.TryAddSingleton(); - services.TryAddSingleton(); + services.TryAddSingleton(); services.TryAddSingleton(); services.AddHostedService(); return services;