From f29dca6b43a70171c44dc5de139944d49a4c7ebf Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E9=99=88=E5=9B=BD=E4=BC=9F?= <366193849@qq.com>
Date: Tue, 28 Sep 2021 11:48:22 +0800
Subject: [PATCH] =?UTF-8?q?=E5=AE=9E=E7=8E=B0=E5=9F=9F=E5=90=8D=E4=B8=8B?=
=?UTF-8?q?=E7=9A=84ip=E6=B5=8B=E9=80=9F=E5=8A=9F=E8=83=BD?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
FastGithub.DomainResolve/DnsClient.cs | 4 -
FastGithub.DomainResolve/DomainResolver.cs | 29 +++-
.../DomainSpeedTestHostedService.cs | 61 +++++++++
.../DomainSpeedTestService.cs | 126 ++++++++++++++++++
FastGithub.DomainResolve/IPAddressItem.cs | 32 ++++-
.../IPAddressItemHashSet.cs | 44 ++++--
.../ServiceCollectionExtensions.cs | 5 +-
7 files changed, 272 insertions(+), 29 deletions(-)
create mode 100644 FastGithub.DomainResolve/DomainSpeedTestHostedService.cs
create mode 100644 FastGithub.DomainResolve/DomainSpeedTestService.cs
diff --git a/FastGithub.DomainResolve/DnsClient.cs b/FastGithub.DomainResolve/DnsClient.cs
index 4d1f03f..8a29636 100644
--- a/FastGithub.DomainResolve/DnsClient.cs
+++ b/FastGithub.DomainResolve/DnsClient.cs
@@ -81,7 +81,6 @@ namespace FastGithub.DomainResolve
if (cryptDns != null)
{
yield return cryptDns;
- yield return cryptDns;
}
foreach (var fallbackDns in this.fastGithubConfig.FallbackDns)
@@ -109,9 +108,6 @@ namespace FastGithub.DomainResolve
{
value = await this.LookupCoreAsync(dns, domain, cancellationToken);
this.dnsCache.Set(key, value, this.dnsExpiration);
-
- var items = string.Join(", ", value.Select(item => item.ToString()));
- this.logger.LogInformation($"dns://{dns}:{domain}->[{items}]");
}
return value;
}
diff --git a/FastGithub.DomainResolve/DomainResolver.cs b/FastGithub.DomainResolve/DomainResolver.cs
index e44ef41..236e1a4 100644
--- a/FastGithub.DomainResolve/DomainResolver.cs
+++ b/FastGithub.DomainResolve/DomainResolver.cs
@@ -1,6 +1,7 @@
using FastGithub.Configuration;
using System.Collections.Generic;
using System.Net;
+using System.Runtime.CompilerServices;
using System.Threading;
using System.Threading.Tasks;
@@ -12,14 +13,19 @@ namespace FastGithub.DomainResolve
sealed class DomainResolver : IDomainResolver
{
private readonly DnsClient dnsClient;
+ private readonly DomainSpeedTestService speedTestService;
///
/// 域名解析器
- ///
+ ///
///
- public DomainResolver(DnsClient dnsClient)
+ ///
+ public DomainResolver(
+ DnsClient dnsClient,
+ DomainSpeedTestService speedTestService)
{
this.dnsClient = dnsClient;
+ this.speedTestService = speedTestService;
}
///
@@ -43,9 +49,24 @@ namespace FastGithub.DomainResolve
/// 域名
///
///
- public IAsyncEnumerable ResolveAllAsync(string domain, CancellationToken cancellationToken)
+ public async IAsyncEnumerable ResolveAllAsync(string domain, [EnumeratorCancellation] CancellationToken cancellationToken)
{
- return this.dnsClient.ResolveAsync(domain, cancellationToken);
+ var addresses = this.speedTestService.GetIPAddresses(domain);
+ if (addresses.Length > 0)
+ {
+ foreach (var address in addresses)
+ {
+ yield return address;
+ }
+ }
+ else
+ {
+ this.speedTestService.Add(domain);
+ await foreach (var address in this.dnsClient.ResolveAsync(domain, cancellationToken))
+ {
+ yield return address;
+ }
+ }
}
}
}
diff --git a/FastGithub.DomainResolve/DomainSpeedTestHostedService.cs b/FastGithub.DomainResolve/DomainSpeedTestHostedService.cs
new file mode 100644
index 0000000..6699073
--- /dev/null
+++ b/FastGithub.DomainResolve/DomainSpeedTestHostedService.cs
@@ -0,0 +1,61 @@
+using Microsoft.Extensions.Hosting;
+using System;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace FastGithub.DomainResolve
+{
+ ///
+ /// 域名的IP测速后台服务
+ ///
+ sealed class DomainSpeedTestHostedService : BackgroundService
+ {
+ private readonly DomainSpeedTestService speedTestService;
+ private readonly TimeSpan testDueTime = TimeSpan.FromMinutes(1d);
+
+ ///
+ /// 域名的IP测速后台服务
+ ///
+ ///
+ public DomainSpeedTestHostedService(DomainSpeedTestService speedTestService)
+ {
+ this.speedTestService = speedTestService;
+ }
+
+ ///
+ /// 启动时
+ ///
+ ///
+ ///
+ public override async Task StartAsync(CancellationToken cancellationToken)
+ {
+ await this.speedTestService.LoadDataAsync(cancellationToken);
+ await base.StartAsync(cancellationToken);
+ }
+
+ ///
+ /// 停止时
+ ///
+ ///
+ ///
+ public override async Task StopAsync(CancellationToken cancellationToken)
+ {
+ await this.speedTestService.SaveDataAsync();
+ await base.StopAsync(cancellationToken);
+ }
+
+ ///
+ /// 后台测速
+ ///
+ ///
+ ///
+ protected override async Task ExecuteAsync(CancellationToken stoppingToken)
+ {
+ while (stoppingToken.IsCancellationRequested == false)
+ {
+ await this.speedTestService.TestSpeedAsync(stoppingToken);
+ await Task.Delay(this.testDueTime, stoppingToken);
+ }
+ }
+ }
+}
diff --git a/FastGithub.DomainResolve/DomainSpeedTestService.cs b/FastGithub.DomainResolve/DomainSpeedTestService.cs
new file mode 100644
index 0000000..890305c
--- /dev/null
+++ b/FastGithub.DomainResolve/DomainSpeedTestService.cs
@@ -0,0 +1,126 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Net;
+using System.Text.Json;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace FastGithub.DomainResolve
+{
+ ///
+ /// 域名的IP测速服务
+ ///
+ sealed class DomainSpeedTestService
+ {
+ private const string DATA_FILE = "domains.json";
+ private readonly DnsClient dnsClient;
+
+ private readonly object syncRoot = new();
+ private readonly Dictionary domainIPAddressHashSet = new();
+
+ ///
+ /// 域名的IP测速服务
+ ///
+ ///
+ public DomainSpeedTestService(DnsClient dnsClient)
+ {
+ this.dnsClient = dnsClient;
+ }
+
+ ///
+ /// 添加要测速的域名
+ ///
+ ///
+ ///
+ public bool Add(string domain)
+ {
+ lock (this.syncRoot)
+ {
+ return this.domainIPAddressHashSet.TryAdd(domain, new IPAddressItemHashSet());
+ }
+ }
+
+ ///
+ /// 获取测试后排序的IP
+ ///
+ ///
+ ///
+ public IPAddress[] GetIPAddresses(string domain)
+ {
+ lock (this.syncRoot)
+ {
+ if (this.domainIPAddressHashSet.TryGetValue(domain, out var hashSet) && hashSet.Count > 0)
+ {
+ return hashSet.ToArray().OrderBy(item => item.PingElapsed).Select(item => item.Address).ToArray();
+ }
+ return Array.Empty();
+ }
+ }
+
+ ///
+ /// 加载数据
+ ///
+ ///
+ ///
+ public async Task LoadDataAsync(CancellationToken cancellationToken)
+ {
+ if (File.Exists(DATA_FILE) == false)
+ {
+ return;
+ }
+
+ var fileStream = File.OpenRead(DATA_FILE);
+ var domains = await JsonSerializer.DeserializeAsync(fileStream, cancellationToken: cancellationToken);
+ if (domains == null)
+ {
+ return;
+ }
+
+ lock (this.syncRoot)
+ {
+ foreach (var domain in domains)
+ {
+ this.domainIPAddressHashSet.TryAdd(domain, new IPAddressItemHashSet());
+ }
+ }
+ }
+
+ ///
+ /// 保存数据
+ ///
+ ///
+ public async Task SaveDataAsync()
+ {
+ var domains = this.domainIPAddressHashSet.Keys.ToArray();
+ using var fileStream = File.OpenWrite(DATA_FILE);
+ await JsonSerializer.SerializeAsync(fileStream, domains);
+ }
+
+ ///
+ /// 进行一轮IP测速
+ ///
+ ///
+ ///
+ public async Task TestSpeedAsync(CancellationToken cancellationToken)
+ {
+ KeyValuePair[] keyValues;
+ lock (this.syncRoot)
+ {
+ keyValues = this.domainIPAddressHashSet.ToArray();
+ }
+
+ foreach (var keyValue in keyValues)
+ {
+ var domain = keyValue.Key;
+ var hashSet = keyValue.Value;
+ await foreach (var address in this.dnsClient.ResolveAsync(domain, cancellationToken))
+ {
+ hashSet.Add(new IPAddressItem(address));
+ }
+ await hashSet.PingAllAsync();
+ }
+ }
+ }
+}
diff --git a/FastGithub.DomainResolve/IPAddressItem.cs b/FastGithub.DomainResolve/IPAddressItem.cs
index 189bf1b..0e058ea 100644
--- a/FastGithub.DomainResolve/IPAddressItem.cs
+++ b/FastGithub.DomainResolve/IPAddressItem.cs
@@ -1,34 +1,54 @@
using System;
+using System.Diagnostics;
using System.Net;
using System.Net.NetworkInformation;
using System.Threading.Tasks;
namespace FastGithub.DomainResolve
{
+ ///
+ /// IP地址项
+ ///
+ [DebuggerDisplay("Address = {Address}, PingElapsed = {PingElapsed}")]
sealed class IPAddressItem : IEquatable
{
+ private readonly Ping ping = new();
+
+ ///
+ /// 地址
+ ///
public IPAddress Address { get; }
- public TimeSpan Elapsed { get; private set; } = TimeSpan.MaxValue;
+ ///
+ /// Ping耗时
+ ///
+ public TimeSpan PingElapsed { get; private set; } = TimeSpan.MaxValue;
+ ///
+ /// IP地址项
+ ///
+ ///
public IPAddressItem(IPAddress address)
{
this.Address = address;
}
- public async Task TestSpeedAsync()
+ ///
+ /// 发起ping请求
+ ///
+ ///
+ public async Task PingAsync()
{
try
{
- using var ping = new Ping();
- var reply = await ping.SendPingAsync(this.Address);
- this.Elapsed = reply.Status == IPStatus.Success
+ var reply = await this.ping.SendPingAsync(this.Address);
+ this.PingElapsed = reply.Status == IPStatus.Success
? TimeSpan.FromMilliseconds(reply.RoundtripTime)
: TimeSpan.MaxValue;
}
catch (Exception)
{
- this.Elapsed = TimeSpan.MaxValue;
+ this.PingElapsed = TimeSpan.MaxValue;
}
}
diff --git a/FastGithub.DomainResolve/IPAddressItemHashSet.cs b/FastGithub.DomainResolve/IPAddressItemHashSet.cs
index c98b2da..d8de684 100644
--- a/FastGithub.DomainResolve/IPAddressItemHashSet.cs
+++ b/FastGithub.DomainResolve/IPAddressItemHashSet.cs
@@ -4,14 +4,24 @@ using System.Threading.Tasks;
namespace FastGithub.DomainResolve
{
+ ///
+ /// IPAddressItem集合
+ ///
sealed class IPAddressItemHashSet
{
private readonly object syncRoot = new();
-
private readonly HashSet hashSet = new();
+ ///
+ /// 获取元素数量
+ ///
public int Count => this.hashSet.Count;
+ ///
+ /// 添加元素
+ ///
+ ///
+ ///
public bool Add(IPAddressItem item)
{
lock (this.syncRoot)
@@ -20,17 +30,10 @@ namespace FastGithub.DomainResolve
}
}
- public void AddRange(IEnumerable items)
- {
- lock (this.syncRoot)
- {
- foreach (var item in items)
- {
- this.hashSet.Add(item);
- }
- }
- }
-
+ ///
+ /// 转换为数组
+ ///
+ ///
public IPAddressItem[] ToArray()
{
lock (this.syncRoot)
@@ -39,9 +42,22 @@ namespace FastGithub.DomainResolve
}
}
- public Task TestSpeedAsync()
+ ///
+ /// Ping所有IP
+ ///
+ ///
+ public Task PingAllAsync()
{
- var tasks = this.ToArray().Select(item => item.TestSpeedAsync());
+ var items = this.ToArray();
+ 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);
}
}
diff --git a/FastGithub.DomainResolve/ServiceCollectionExtensions.cs b/FastGithub.DomainResolve/ServiceCollectionExtensions.cs
index c35acc7..cf646a5 100644
--- a/FastGithub.DomainResolve/ServiceCollectionExtensions.cs
+++ b/FastGithub.DomainResolve/ServiceCollectionExtensions.cs
@@ -18,8 +18,11 @@ namespace FastGithub
{
services.TryAddSingleton();
services.TryAddSingleton();
+ services.TryAddSingleton();
services.TryAddSingleton();
- return services.AddHostedService();
+ services.AddHostedService();
+ services.AddHostedService();
+ return services;
}
}
}