全局ping
This commit is contained in:
parent
92ff4a54b4
commit
e212fdc5dc
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -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>
|
||||||
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<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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user