全局ping
This commit is contained in:
parent
92ff4a54b4
commit
e212fdc5dc
@ -9,6 +9,7 @@ using Microsoft.Extensions.Options;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.NetworkInformation;
|
||||
@ -30,6 +31,8 @@ namespace FastGithub.DomainResolve
|
||||
private readonly FastGithubConfig fastGithubConfig;
|
||||
private readonly ILogger<DnsClient> logger;
|
||||
|
||||
private readonly ConcurrentDictionary<string, IPAddressCollection> domainIPAddressCollection = new();
|
||||
|
||||
private readonly ConcurrentDictionary<string, SemaphoreSlim> semaphoreSlims = new();
|
||||
private readonly IMemoryCache dnsCache = new MemoryCache(Options.Create(new MemoryCacheOptions()));
|
||||
private readonly TimeSpan defaultEmptyTtl = TimeSpan.FromSeconds(30d);
|
||||
@ -60,6 +63,69 @@ namespace FastGithub.DomainResolve
|
||||
/// <param name="cancellationToken"></param>
|
||||
/// <returns></returns>
|
||||
public async IAsyncEnumerable<IPAddress> ResolveAsync(string domain, [EnumeratorCancellation] CancellationToken cancellationToken)
|
||||
{
|
||||
if (this.TryGetPingedIPAddresses(domain, out var addresses))
|
||||
{
|
||||
foreach (var address in addresses)
|
||||
{
|
||||
yield return address;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
this.domainIPAddressCollection.TryAdd(domain, new IPAddressCollection());
|
||||
await foreach (var adddress in this.ResolveCoreAsync(domain, cancellationToken))
|
||||
{
|
||||
yield return adddress;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 对所有域名所有IP进行ping测试
|
||||
/// </summary>
|
||||
/// <param name="cancellationToken"></param>
|
||||
/// <returns></returns>
|
||||
public async Task PingAllDomainsAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
foreach (var keyValue in this.domainIPAddressCollection)
|
||||
{
|
||||
var domain = keyValue.Key;
|
||||
var collection = keyValue.Value;
|
||||
|
||||
await foreach (var address in this.ResolveCoreAsync(domain, cancellationToken))
|
||||
{
|
||||
collection.Add(address);
|
||||
}
|
||||
await collection.PingAllAsync();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 尝试获取域名下已经过ping排序的IP地址
|
||||
/// </summary>
|
||||
/// <param name="domain"></param>
|
||||
/// <param name="addresses"></param>
|
||||
/// <returns></returns>
|
||||
private bool TryGetPingedIPAddresses(string domain, [MaybeNullWhen(false)] out IPAddress[] addresses)
|
||||
{
|
||||
if (this.domainIPAddressCollection.TryGetValue(domain, out var collection) && collection.Count > 0)
|
||||
{
|
||||
addresses = collection.ToArray();
|
||||
return true;
|
||||
}
|
||||
|
||||
addresses = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 解析域名
|
||||
/// </summary>
|
||||
/// <param name="domain">域名</param>
|
||||
/// <param name="cancellationToken"></param>
|
||||
/// <returns></returns>
|
||||
private async IAsyncEnumerable<IPAddress> ResolveCoreAsync(string domain, [EnumeratorCancellation] CancellationToken cancellationToken)
|
||||
{
|
||||
var hashSet = new HashSet<IPAddress>();
|
||||
foreach (var dns in this.GetDnsServers())
|
||||
@ -75,7 +141,6 @@ namespace FastGithub.DomainResolve
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 获取dns服务
|
||||
/// </summary>
|
||||
@ -184,7 +249,6 @@ namespace FastGithub.DomainResolve
|
||||
timeToLive = this.defaultEmptyTtl;
|
||||
}
|
||||
|
||||
this.logger.LogWarning($"{domain} [{timeToLive}]");
|
||||
return new LookupResult(addresses, timeToLive);
|
||||
}
|
||||
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
@ -7,17 +8,23 @@ namespace FastGithub.DomainResolve
|
||||
/// <summary>
|
||||
/// 域名解析后台服务
|
||||
/// </summary>
|
||||
sealed class DnscryptProxyHostedService : BackgroundService
|
||||
sealed class DomainResolveHostedService : BackgroundService
|
||||
{
|
||||
private readonly DnscryptProxy dnscryptProxy;
|
||||
private readonly DnsClient dnsClient;
|
||||
private readonly TimeSpan speedTestTimeSpan = TimeSpan.FromMinutes(2d);
|
||||
|
||||
/// <summary>
|
||||
/// 域名解析后台服务
|
||||
/// </summary>
|
||||
/// <param name="dnscryptProxy"></param>
|
||||
public DnscryptProxyHostedService(DnscryptProxy dnscryptProxy)
|
||||
/// <param name="dnscryptProxy"></param>
|
||||
/// <param name="dnsClient"></param>
|
||||
public DomainResolveHostedService(
|
||||
DnscryptProxy dnscryptProxy,
|
||||
DnsClient dnsClient)
|
||||
{
|
||||
this.dnscryptProxy = dnscryptProxy;
|
||||
this.dnsClient = dnsClient;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -28,6 +35,11 @@ namespace FastGithub.DomainResolve
|
||||
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
|
||||
{
|
||||
await this.dnscryptProxy.StartAsync(stoppingToken);
|
||||
while (stoppingToken.IsCancellationRequested == false)
|
||||
{
|
||||
await this.dnsClient.PingAllDomainsAsync(stoppingToken);
|
||||
await Task.Delay(this.speedTestTimeSpan, stoppingToken);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
139
FastGithub.DomainResolve/IPAddressCollection.cs
Normal file
139
FastGithub.DomainResolve/IPAddressCollection.cs
Normal file
@ -0,0 +1,139 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.NetworkInformation;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace FastGithub.DomainResolve
|
||||
{
|
||||
/// <summary>
|
||||
/// IPAddress集合
|
||||
/// </summary>
|
||||
[DebuggerDisplay("Count = {Count}")]
|
||||
sealed class IPAddressCollection
|
||||
{
|
||||
private readonly object syncRoot = new();
|
||||
private readonly HashSet<IPAddressItem> hashSet = new();
|
||||
|
||||
/// <summary>
|
||||
/// 获取元素数量
|
||||
/// </summary>
|
||||
public int Count => this.hashSet.Count;
|
||||
|
||||
/// <summary>
|
||||
/// 添加元素
|
||||
/// </summary>
|
||||
/// <param name="address"></param>
|
||||
/// <returns></returns>
|
||||
public bool Add(IPAddress address)
|
||||
{
|
||||
lock (this.syncRoot)
|
||||
{
|
||||
return this.hashSet.Add(new IPAddressItem(address));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 转后为数组
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public IPAddress[] ToArray()
|
||||
{
|
||||
return this.ToItemArray().OrderBy(item => item.PingElapsed).Select(item => item.Address).ToArray();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ping所有IP
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public Task PingAllAsync()
|
||||
{
|
||||
var items = this.ToItemArray();
|
||||
if (items.Length == 0)
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
if (items.Length == 1)
|
||||
{
|
||||
return items[0].PingAsync();
|
||||
}
|
||||
var tasks = items.Select(item => item.PingAsync());
|
||||
return Task.WhenAll(tasks);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 转换为数组
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
private IPAddressItem[] ToItemArray()
|
||||
{
|
||||
lock (this.syncRoot)
|
||||
{
|
||||
return this.hashSet.ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// IP地址项
|
||||
/// </summary>
|
||||
[DebuggerDisplay("Address = {Address}, PingElapsed = {PingElapsed}")]
|
||||
private class IPAddressItem : IEquatable<IPAddressItem>
|
||||
{
|
||||
/// <summary>
|
||||
/// 地址
|
||||
/// </summary>
|
||||
public IPAddress Address { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Ping耗时
|
||||
/// </summary>
|
||||
public TimeSpan PingElapsed { get; private set; } = TimeSpan.MaxValue;
|
||||
|
||||
/// <summary>
|
||||
/// IP地址项
|
||||
/// </summary>
|
||||
/// <param name="address"></param>
|
||||
public IPAddressItem(IPAddress address)
|
||||
{
|
||||
this.Address = address;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 发起ping请求
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public async Task PingAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
using var ping = new Ping();
|
||||
var reply = await ping.SendPingAsync(this.Address);
|
||||
this.PingElapsed = reply.Status == IPStatus.Success
|
||||
? TimeSpan.FromMilliseconds(reply.RoundtripTime)
|
||||
: TimeSpan.MaxValue;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
this.PingElapsed = TimeSpan.MaxValue;
|
||||
}
|
||||
}
|
||||
|
||||
public bool Equals(IPAddressItem? other)
|
||||
{
|
||||
return other != null && other.Address.Equals(this.Address);
|
||||
}
|
||||
|
||||
public override bool Equals(object? obj)
|
||||
{
|
||||
return obj is IPAddressItem other && this.Equals(other);
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return this.Address.GetHashCode();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -19,7 +19,7 @@ namespace FastGithub
|
||||
services.TryAddSingleton<DnsClient>();
|
||||
services.TryAddSingleton<DnscryptProxy>();
|
||||
services.TryAddSingleton<IDomainResolver, DomainResolver>();
|
||||
services.AddHostedService<DnscryptProxyHostedService>();
|
||||
services.AddHostedService<DomainResolveHostedService>();
|
||||
return services;
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user