使用客户端TTL;
可用率使用最近10次扫描结果来统计;
This commit is contained in:
parent
4ad3323314
commit
3c2a4a0dfb
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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);
|
||||||
|
|||||||
@ -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>
|
||||||
|
|||||||
@ -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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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", // 完整扫描时间间隔
|
||||||
|
|||||||
@ -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。
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user