全局ping

This commit is contained in:
陈国伟 2021-09-29 12:45:17 +08:00
parent 92ff4a54b4
commit e212fdc5dc
4 changed files with 221 additions and 6 deletions

View File

@ -9,6 +9,7 @@ using Microsoft.Extensions.Options;
using System; using System;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq; using System.Linq;
using System.Net; using System.Net;
using System.Net.NetworkInformation; using System.Net.NetworkInformation;
@ -30,6 +31,8 @@ namespace FastGithub.DomainResolve
private readonly FastGithubConfig fastGithubConfig; private readonly FastGithubConfig fastGithubConfig;
private readonly ILogger<DnsClient> logger; private readonly ILogger<DnsClient> logger;
private readonly ConcurrentDictionary<string, IPAddressCollection> domainIPAddressCollection = new();
private readonly ConcurrentDictionary<string, SemaphoreSlim> semaphoreSlims = new(); private readonly ConcurrentDictionary<string, SemaphoreSlim> semaphoreSlims = new();
private readonly IMemoryCache dnsCache = new MemoryCache(Options.Create(new MemoryCacheOptions())); private readonly IMemoryCache dnsCache = new MemoryCache(Options.Create(new MemoryCacheOptions()));
private readonly TimeSpan defaultEmptyTtl = TimeSpan.FromSeconds(30d); private readonly TimeSpan defaultEmptyTtl = TimeSpan.FromSeconds(30d);
@ -60,6 +63,69 @@ namespace FastGithub.DomainResolve
/// <param name="cancellationToken"></param> /// <param name="cancellationToken"></param>
/// <returns></returns> /// <returns></returns>
public async IAsyncEnumerable<IPAddress> ResolveAsync(string domain, [EnumeratorCancellation] CancellationToken cancellationToken) 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>(); var hashSet = new HashSet<IPAddress>();
foreach (var dns in this.GetDnsServers()) foreach (var dns in this.GetDnsServers())
@ -75,7 +141,6 @@ namespace FastGithub.DomainResolve
} }
} }
/// <summary> /// <summary>
/// 获取dns服务 /// 获取dns服务
/// </summary> /// </summary>
@ -184,7 +249,6 @@ namespace FastGithub.DomainResolve
timeToLive = this.defaultEmptyTtl; timeToLive = this.defaultEmptyTtl;
} }
this.logger.LogWarning($"{domain} [{timeToLive}]");
return new LookupResult(addresses, timeToLive); return new LookupResult(addresses, timeToLive);
} }

View File

@ -1,4 +1,5 @@
using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Hosting;
using System;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
@ -7,17 +8,23 @@ namespace FastGithub.DomainResolve
/// <summary> /// <summary>
/// 域名解析后台服务 /// 域名解析后台服务
/// </summary> /// </summary>
sealed class DnscryptProxyHostedService : BackgroundService sealed class DomainResolveHostedService : BackgroundService
{ {
private readonly DnscryptProxy dnscryptProxy; private readonly DnscryptProxy dnscryptProxy;
private readonly DnsClient dnsClient;
private readonly TimeSpan speedTestTimeSpan = TimeSpan.FromMinutes(2d);
/// <summary> /// <summary>
/// 域名解析后台服务 /// 域名解析后台服务
/// </summary> /// </summary>
/// <param name="dnscryptProxy"></param> /// <param name="dnscryptProxy"></param>
public DnscryptProxyHostedService(DnscryptProxy dnscryptProxy) /// <param name="dnsClient"></param>
public DomainResolveHostedService(
DnscryptProxy dnscryptProxy,
DnsClient dnsClient)
{ {
this.dnscryptProxy = dnscryptProxy; this.dnscryptProxy = dnscryptProxy;
this.dnsClient = dnsClient;
} }
/// <summary> /// <summary>
@ -28,6 +35,11 @@ namespace FastGithub.DomainResolve
protected override async Task ExecuteAsync(CancellationToken stoppingToken) protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{ {
await this.dnscryptProxy.StartAsync(stoppingToken); await this.dnscryptProxy.StartAsync(stoppingToken);
while (stoppingToken.IsCancellationRequested == false)
{
await this.dnsClient.PingAllDomainsAsync(stoppingToken);
await Task.Delay(this.speedTestTimeSpan, stoppingToken);
}
} }
/// <summary> /// <summary>

View 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();
}
}
}
}

View File

@ -19,7 +19,7 @@ namespace FastGithub
services.TryAddSingleton<DnsClient>(); services.TryAddSingleton<DnsClient>();
services.TryAddSingleton<DnscryptProxy>(); services.TryAddSingleton<DnscryptProxy>();
services.TryAddSingleton<IDomainResolver, DomainResolver>(); services.TryAddSingleton<IDomainResolver, DomainResolver>();
services.AddHostedService<DnscryptProxyHostedService>(); services.AddHostedService<DomainResolveHostedService>();
return services; return services;
} }
} }