使用客户端TTL;

可用率使用最近10次扫描结果来统计;
This commit is contained in:
陈国伟 2021-06-17 13:18:45 +08:00
parent 4ad3323314
commit 3c2a4a0dfb
10 changed files with 64 additions and 105 deletions

View File

@ -1,4 +1,5 @@
using System.Net; using System;
using System.Net;
namespace FastGithub.Dns namespace FastGithub.Dns
{ {
@ -6,5 +7,7 @@ namespace FastGithub.Dns
sealed class DnsOptions sealed class DnsOptions
{ {
public IPAddress UpStream { get; set; } = IPAddress.Parse("114.114.114.114"); public IPAddress UpStream { get; set; } = IPAddress.Parse("114.114.114.114");
public TimeSpan GithubTTL { get; set; } = TimeSpan.FromMinutes(10d);
} }
} }

View File

@ -18,8 +18,7 @@ namespace FastGithub
public static IServiceCollection AddGithubDns(this IServiceCollection services, IConfiguration configuration) public static IServiceCollection AddGithubDns(this IServiceCollection services, IConfiguration configuration)
{ {
var assembly = typeof(DnsServiceCollectionExtensions).Assembly; var assembly = typeof(DnsServiceCollectionExtensions).Assembly;
return services return services
.AddMemoryCache()
.AddServiceAndOptions(assembly, configuration) .AddServiceAndOptions(assembly, configuration)
.AddHostedService<DnsHostedService>() .AddHostedService<DnsHostedService>()
.AddGithubScanner(configuration); .AddGithubScanner(configuration);

View File

@ -6,8 +6,7 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="DNS" Version="6.1.0" /> <PackageReference Include="DNS" Version="6.1.0" />
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="5.0.0" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View File

@ -2,12 +2,11 @@
using DNS.Protocol; using DNS.Protocol;
using DNS.Protocol.ResourceRecords; using DNS.Protocol.ResourceRecords;
using FastGithub.Scanner; using FastGithub.Scanner;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using System; using System;
using System.Linq; using System.Linq;
using System.Net;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
@ -17,17 +16,16 @@ namespace FastGithub.Dns
sealed class GithubRequestResolver : IRequestResolver sealed class GithubRequestResolver : IRequestResolver
{ {
private readonly IGithubScanResults githubScanResults; private readonly IGithubScanResults githubScanResults;
private readonly IMemoryCache memoryCache; private readonly IOptionsMonitor<DnsOptions> options;
private readonly ILogger<GithubRequestResolver> logger; private readonly ILogger<GithubRequestResolver> logger;
private readonly TimeSpan TTL = TimeSpan.FromMinutes(10d);
public GithubRequestResolver( public GithubRequestResolver(
IGithubScanResults githubScanResults, IGithubScanResults githubScanResults,
IMemoryCache memoryCache, IOptionsMonitor<DnsOptions> options,
ILogger<GithubRequestResolver> logger) ILogger<GithubRequestResolver> logger)
{ {
this.githubScanResults = githubScanResults; this.githubScanResults = githubScanResults;
this.memoryCache = memoryCache; this.options = options;
this.logger = logger; this.logger = logger;
} }
@ -39,11 +37,12 @@ namespace FastGithub.Dns
if (question != null && question.Type == RecordType.A) if (question != null && question.Type == RecordType.A)
{ {
var domain = question.Name.ToString(); var domain = question.Name.ToString();
var address = this.GetGithubAddress(domain, TTL); var address = this.githubScanResults.FindBestAddress(domain);
if (address != null) if (address != null)
{ {
var record = new IPAddressResourceRecord(question.Name, address); var ttl = this.options.CurrentValue.GithubTTL;
var record = new IPAddressResourceRecord(question.Name, address, ttl);
response.AnswerRecords.Add(record); response.AnswerRecords.Add(record);
this.logger.LogInformation(record.ToString()); this.logger.LogInformation(record.ToString());
} }
@ -51,37 +50,5 @@ namespace FastGithub.Dns
return Task.FromResult<IResponse>(response); return Task.FromResult<IResponse>(response);
} }
/// <summary>
/// 模拟TTL
/// 如果ip可用则10分钟内返回缓存的ip防止客户端ip频繁切换
/// </summary>
/// <param name="domain"></param>
/// <param name="ttl"></param>
/// <returns></returns>
private IPAddress? GetGithubAddress(string domain, TimeSpan ttl)
{
if (domain.Contains("github", StringComparison.OrdinalIgnoreCase) == false)
{
return default;
}
var key = $"ttl:{domain}";
if (this.memoryCache.TryGetValue<IPAddress>(key, out var address))
{
if (this.githubScanResults.IsAvailable(domain, address))
{
return address;
}
this.memoryCache.Remove(key);
}
address = this.githubScanResults.FindBestAddress(domain);
if (address != null)
{
this.memoryCache.Set(key, address, ttl);
}
return address;
}
} }
} }

View File

@ -8,7 +8,8 @@ namespace FastGithub.Scanner
private record Github( private record Github(
string Domain, string Domain,
IPAddress Address, IPAddress Address,
double SuccessRate, bool Available,
double AvailableRate,
TimeSpan AvgElapsed); TimeSpan AvgElapsed);
/// <summary> /// <summary>
@ -58,8 +59,9 @@ namespace FastGithub.Scanner
return new Github( return new Github(
this.Domain, this.Domain,
this.Address, this.Address,
this.History.GetSuccessRate(), this.Available,
this.History.GetAvgElapsed() this.History.AvailableRate,
this.History.AvgElapsed
).ToString(); ).ToString();
} }
} }

View File

@ -54,9 +54,9 @@ namespace FastGithub.Scanner
lock (this.syncRoot) lock (this.syncRoot)
{ {
return this.contextList return this.contextList
.Where(item => item.Available && item.Domain == domain) .Where(item => item.Domain == domain && item.History.AvailableRate > 0d)
.OrderByDescending(item => item.History.GetSuccessRate()) .OrderByDescending(item => item.History.AvailableRate)
.ThenBy(item => item.History.GetAvgElapsed()) .ThenBy(item => item.History.AvgElapsed)
.Select(item => item.Address) .Select(item => item.Address)
.FirstOrDefault(); .FirstOrDefault();
} }

View File

@ -1,74 +1,66 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
namespace FastGithub.Scanner namespace FastGithub.Scanner
{ {
sealed class GithubContextHistory sealed class GithubContextHistory
{ {
private record ScanLog(DateTime ScanTime, TimeSpan Elapsed); private const int MAX_LOG_COUNT = 10;
private record ScanLog(bool Available, TimeSpan Elapsed);
private readonly Queue<ScanLog> successLogs = new(); private readonly Queue<ScanLog> scanLogs = new();
private readonly Queue<ScanLog> failureLogs = new(); /// <summary>
/// 获取可用率
private static readonly TimeSpan keepLogsTimeSpan = TimeSpan.FromHours(2d); /// </summary>
/// <returns></returns>
public double AvailableRate
public void AddSuccess(TimeSpan elapsed)
{ {
ClearStaleData(this.successLogs, keepLogsTimeSpan); get
this.successLogs.Enqueue(new ScanLog(DateTime.Now, elapsed));
}
public void AddFailure()
{
ClearStaleData(this.failureLogs, keepLogsTimeSpan);
this.failureLogs.Enqueue(new ScanLog(DateTime.Now, TimeSpan.Zero));
}
static void ClearStaleData(Queue<ScanLog> logs, TimeSpan timeSpan)
{
var time = DateTime.Now.Subtract(timeSpan);
while (logs.TryPeek(out var log))
{ {
if (log.ScanTime < time) if (this.scanLogs.Count == 0)
{ {
logs.TryDequeue(out _); return 0d;
} }
break;
var availableCount = this.scanLogs.Count(item => item.Available);
return (double)availableCount / this.scanLogs.Count;
} }
} }
/// <summary>
/// 获取成功率
/// </summary>
/// <returns></returns>
public double GetSuccessRate()
{
var successCount = this.successLogs.Count;
var totalScanCount = successCount + this.failureLogs.Count;
return totalScanCount == 0 ? 0d : (double)successCount / totalScanCount;
}
/// <summary> /// <summary>
/// 获取平均耗时 /// 获取平均耗时
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
public TimeSpan GetAvgElapsed() public TimeSpan AvgElapsed
{ {
var totalScanCount = this.successLogs.Count + this.failureLogs.Count; get
if (totalScanCount == 0)
{ {
return TimeSpan.MaxValue; var availableCount = 0;
} var availableElapsed = TimeSpan.Zero;
var totalSuccessElapsed = TimeSpan.Zero; foreach (var item in this.scanLogs)
foreach (var item in this.successLogs) {
if (item.Available == true)
{
availableCount += 1;
availableElapsed = availableElapsed.Add(item.Elapsed);
}
}
return availableCount == 0 ? TimeSpan.MaxValue : availableElapsed / availableCount;
}
}
public void Add(bool available, TimeSpan elapsed)
{
this.scanLogs.Enqueue(new ScanLog(available, elapsed));
while (this.scanLogs.Count > MAX_LOG_COUNT)
{ {
totalSuccessElapsed = totalSuccessElapsed.Add(item.Elapsed); this.scanLogs.Dequeue();
} }
return totalSuccessElapsed / totalScanCount;
} }
} }
} }

View File

@ -28,16 +28,12 @@ namespace FastGithub.Scanner.Middlewares
finally finally
{ {
stopwatch.Stop(); stopwatch.Stop();
context.History.Add(context.Available, stopwatch.Elapsed);
if (context.Available) if (context.History.AvailableRate > 0d)
{ {
context.History.AddSuccess(stopwatch.Elapsed);
this.logger.LogInformation(context.ToString()); this.logger.LogInformation(context.ToString());
} }
else
{
context.History.AddFailure();
}
} }
} }
} }

View File

@ -1,6 +1,7 @@
{ {
"Dns": { "Dns": {
"UpStream": "114.114.114.114" "UpStream": "114.114.114.114",
"GithubTTL": "00.10.00"
}, },
"Github": { "Github": {
"ScanAllInterval": "02:00:00", // "ScanAllInterval": "02:00:00", //

View File

@ -4,7 +4,7 @@ github定制版的dns服务解析访问github最快的ip
### 加速原理 ### 加速原理
* 使用github公开的ip范围扫描所有可用的ip * 使用github公开的ip范围扫描所有可用的ip
* 轮询检测并统计可用ip的访问成功率与访问耗时 * 轮询检测并统计可用ip的访问成功率与访问耗时
* 拦截dns访问github时服务端模拟TTL返回最优ip * 拦截dns访问github时返回最优ip
### 使用说明 ### 使用说明
在局域网服务器(没有就使用本机)运行本程序将网络连接的dns设置为程序运行的机器的ip。 在局域网服务器(没有就使用本机)运行本程序将网络连接的dns设置为程序运行的机器的ip。