挑选可用且最快的ip
This commit is contained in:
parent
15b31e6fa5
commit
4faa241b87
@ -18,19 +18,14 @@ namespace FastGithub.Dns
|
|||||||
{
|
{
|
||||||
private readonly TimeSpan ttl = TimeSpan.FromMinutes(1d);
|
private readonly TimeSpan ttl = TimeSpan.FromMinutes(1d);
|
||||||
private readonly FastGithubConfig fastGithubConfig;
|
private readonly FastGithubConfig fastGithubConfig;
|
||||||
private readonly ILogger<RequestResolver> logger;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// dns解析者
|
/// dns解析者
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="fastGithubConfig"></param>
|
/// <param name="fastGithubConfig"></param>
|
||||||
/// <param name="logger"></param>
|
public RequestResolver(FastGithubConfig fastGithubConfig)
|
||||||
public RequestResolver(
|
|
||||||
FastGithubConfig fastGithubConfig,
|
|
||||||
ILogger<RequestResolver> logger)
|
|
||||||
{
|
{
|
||||||
this.fastGithubConfig = fastGithubConfig;
|
this.fastGithubConfig = fastGithubConfig;
|
||||||
this.logger = logger;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -60,8 +55,6 @@ namespace FastGithub.Dns
|
|||||||
var localAddress = remoteEndPointRequest.GetLocalIPAddress() ?? IPAddress.Loopback;
|
var localAddress = remoteEndPointRequest.GetLocalIPAddress() ?? IPAddress.Loopback;
|
||||||
var record = new IPAddressResourceRecord(domain, localAddress, this.ttl);
|
var record = new IPAddressResourceRecord(domain, localAddress, this.ttl);
|
||||||
response.AnswerRecords.Add(record);
|
response.AnswerRecords.Add(record);
|
||||||
|
|
||||||
this.logger.LogInformation($"[{domain}->{localAddress}]");
|
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -4,8 +4,11 @@ using FastGithub.Configuration;
|
|||||||
using Microsoft.Extensions.Caching.Memory;
|
using Microsoft.Extensions.Caching.Memory;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
|
using System.Net.Sockets;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
@ -19,7 +22,11 @@ namespace FastGithub.DomainResolve
|
|||||||
private readonly IMemoryCache memoryCache;
|
private readonly IMemoryCache memoryCache;
|
||||||
private readonly FastGithubConfig fastGithubConfig;
|
private readonly FastGithubConfig fastGithubConfig;
|
||||||
private readonly ILogger<DomainResolver> logger;
|
private readonly ILogger<DomainResolver> logger;
|
||||||
private readonly TimeSpan cacheTimeSpan = TimeSpan.FromSeconds(10d);
|
|
||||||
|
private readonly TimeSpan lookupTimeout = TimeSpan.FromSeconds(2d);
|
||||||
|
private readonly TimeSpan connectTimeout = TimeSpan.FromSeconds(2d);
|
||||||
|
private readonly TimeSpan resolveCacheTimeSpan = TimeSpan.FromMinutes(2d);
|
||||||
|
private readonly ConcurrentDictionary<DnsEndPoint, SemaphoreSlim> semaphoreSlims = new();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 域名解析器
|
/// 域名解析器
|
||||||
@ -38,42 +45,49 @@ namespace FastGithub.DomainResolve
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 解析指定的域名
|
/// 解析域名
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="domain"></param>
|
/// <param name="endPoint"></param>
|
||||||
|
/// <param name="cancellationToken"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
/// <exception cref="FastGithubException"></exception>
|
public async Task<IPAddress> ResolveAsync(DnsEndPoint endPoint, CancellationToken cancellationToken = default)
|
||||||
public Task<IPAddress> ResolveAsync(string domain, CancellationToken cancellationToken)
|
|
||||||
{
|
{
|
||||||
// 缓存以避免做不必要的并发查询
|
var semaphore = this.semaphoreSlims.GetOrAdd(endPoint, _ => new SemaphoreSlim(1, 1));
|
||||||
var key = $"{nameof(DomainResolver)}:{domain}";
|
try
|
||||||
return this.memoryCache.GetOrCreateAsync(key, e =>
|
|
||||||
{
|
{
|
||||||
e.SetAbsoluteExpiration(this.cacheTimeSpan);
|
await semaphore.WaitAsync(cancellationToken);
|
||||||
return this.LookupAsync(domain, cancellationToken);
|
if (this.memoryCache.TryGetValue<IPAddress>(endPoint, out var address) == false)
|
||||||
});
|
{
|
||||||
|
address = await this.LookupAsync(endPoint, cancellationToken);
|
||||||
|
this.memoryCache.Set(endPoint, address, this.resolveCacheTimeSpan);
|
||||||
|
}
|
||||||
|
return address;
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
semaphore.Release();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 查找ip
|
/// 查找ip
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="domain"></param>
|
/// <param name="endPoint"></param>
|
||||||
/// <param name="cancellationToken"></param>
|
/// <param name="cancellationToken"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
/// <exception cref="FastGithubException">
|
private async Task<IPAddress> LookupAsync(DnsEndPoint endPoint, CancellationToken cancellationToken)
|
||||||
private async Task<IPAddress> LookupAsync(string domain, CancellationToken cancellationToken)
|
|
||||||
{
|
{
|
||||||
var pureDns = this.fastGithubConfig.PureDns;
|
var pureDns = this.fastGithubConfig.PureDns;
|
||||||
var fastDns = this.fastGithubConfig.FastDns;
|
var fastDns = this.fastGithubConfig.FastDns;
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
return await LookupCoreAsync(pureDns, domain, cancellationToken);
|
return await LookupCoreAsync(pureDns, endPoint, cancellationToken);
|
||||||
}
|
}
|
||||||
catch (Exception)
|
catch (Exception)
|
||||||
{
|
{
|
||||||
this.logger.LogWarning($"由于{pureDns}解析{domain}失败,本次使用{fastDns}");
|
this.logger.LogWarning($"由于{pureDns}解析{endPoint.Host}失败,本次使用{fastDns}");
|
||||||
return await LookupCoreAsync(fastDns, domain, cancellationToken);
|
return await LookupCoreAsync(fastDns, endPoint, cancellationToken);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -81,24 +95,66 @@ namespace FastGithub.DomainResolve
|
|||||||
/// 查找ip
|
/// 查找ip
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="dns"></param>
|
/// <param name="dns"></param>
|
||||||
/// <param name="domain"></param>
|
/// <param name="endPoint"></param>
|
||||||
/// <param name="cancellationToken"></param>
|
/// <param name="cancellationToken"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
private async Task<IPAddress> LookupCoreAsync(IPEndPoint dns, string domain, CancellationToken cancellationToken)
|
private async Task<IPAddress> LookupCoreAsync(IPEndPoint dns, DnsEndPoint endPoint, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var dnsClient = new DnsClient(dns);
|
var dnsClient = new DnsClient(dns);
|
||||||
using var timeoutTokenSource = new CancellationTokenSource(TimeSpan.FromSeconds(2d));
|
using var timeoutTokenSource = new CancellationTokenSource(this.lookupTimeout);
|
||||||
using var linkedTokenSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, timeoutTokenSource.Token);
|
using var linkedTokenSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, timeoutTokenSource.Token);
|
||||||
|
|
||||||
var addresses = await dnsClient.Lookup(domain, RecordType.A, linkedTokenSource.Token);
|
var addresses = await dnsClient.Lookup(endPoint.Host, RecordType.A, linkedTokenSource.Token);
|
||||||
var address = addresses?.FirstOrDefault();
|
var fastAddress = await this.GetFastIPAddressAsync(addresses, endPoint.Port, cancellationToken);
|
||||||
if (address == null)
|
|
||||||
|
if (fastAddress != null)
|
||||||
{
|
{
|
||||||
throw new FastGithubException($"dns{dns}解析不到{domain}的ip");
|
this.logger.LogInformation($"[{endPoint.Host}->{fastAddress}]");
|
||||||
|
return fastAddress;
|
||||||
|
}
|
||||||
|
throw new FastGithubException($"dns{dns}解析不到{endPoint.Host}可用的ip");
|
||||||
}
|
}
|
||||||
|
|
||||||
this.logger.LogInformation($"[{domain}->{address}]");
|
/// <summary>
|
||||||
|
/// 获取最快的ip
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="addresses"></param>
|
||||||
|
/// <param name="port"></param>
|
||||||
|
/// <param name="cancellationToken"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
private async Task<IPAddress?> GetFastIPAddressAsync(IEnumerable<IPAddress> addresses, int port, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
var tasks = addresses.Select(address => this.IsAvailableAsync(address, port, cancellationToken));
|
||||||
|
var fastTask = await Task.WhenAny(tasks);
|
||||||
|
return await fastTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 验证远程节点是否可连接
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="address"></param>
|
||||||
|
/// <param name="port"></param>
|
||||||
|
/// <param name="cancellationToken"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
private async Task<IPAddress?> IsAvailableAsync(IPAddress address, int port, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using var socket = new Socket(SocketType.Stream, ProtocolType.Tcp);
|
||||||
|
using var timeoutTokenSource = new CancellationTokenSource(this.connectTimeout);
|
||||||
|
using var linkedTokenSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, timeoutTokenSource.Token);
|
||||||
|
await socket.ConnectAsync(address, port, linkedTokenSource.Token);
|
||||||
return address;
|
return address;
|
||||||
}
|
}
|
||||||
|
catch (OperationCanceledException)
|
||||||
|
{
|
||||||
|
return default;
|
||||||
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{
|
||||||
|
await Task.Delay(this.connectTimeout, cancellationToken);
|
||||||
|
return default;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -12,9 +12,9 @@ namespace FastGithub.DomainResolve
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 解析域名
|
/// 解析域名
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="domain"></param>
|
/// <param name="endPoint"></param>
|
||||||
/// <param name="cancellationToken"></param>
|
/// <param name="cancellationToken"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
Task<IPAddress> ResolveAsync(string domain, CancellationToken cancellationToken);
|
Task<IPAddress> ResolveAsync(DnsEndPoint endPoint, CancellationToken cancellationToken = default);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -67,7 +67,8 @@ namespace FastGithub.Http
|
|||||||
{
|
{
|
||||||
if (IPAddress.TryParse(this.domainConfig.IPAddress, out var address) == false)
|
if (IPAddress.TryParse(this.domainConfig.IPAddress, out var address) == false)
|
||||||
{
|
{
|
||||||
address = await this.domainResolver.ResolveAsync(context.Domain, cancellationToken);
|
var endPoint = new DnsEndPoint(uri.Host, uri.Port);
|
||||||
|
address = await this.domainResolver.ResolveAsync(endPoint, cancellationToken);
|
||||||
}
|
}
|
||||||
uriBuilder.Host = address.ToString();
|
uriBuilder.Host = address.ToString();
|
||||||
request.Headers.Host = context.Domain;
|
request.Headers.Host = context.Domain;
|
||||||
|
|||||||
@ -3,7 +3,6 @@ using Microsoft.AspNetCore.Connections;
|
|||||||
using System.IO.Pipelines;
|
using System.IO.Pipelines;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Net.Sockets;
|
using System.Net.Sockets;
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace FastGithub.ReverseProxy
|
namespace FastGithub.ReverseProxy
|
||||||
@ -13,9 +12,8 @@ namespace FastGithub.ReverseProxy
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
sealed class GithubSshHandler : ConnectionHandler
|
sealed class GithubSshHandler : ConnectionHandler
|
||||||
{
|
{
|
||||||
private const int SSH_OVER_HTTPS_PORT = 443;
|
|
||||||
private const string SSH_GITHUB_COM = "ssh.github.com";
|
|
||||||
private readonly IDomainResolver domainResolver;
|
private readonly IDomainResolver domainResolver;
|
||||||
|
private readonly DnsEndPoint githubSshEndPoint = new("ssh.github.com", 443);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// github的ssh处理者
|
/// github的ssh处理者
|
||||||
@ -33,9 +31,9 @@ namespace FastGithub.ReverseProxy
|
|||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public override async Task OnConnectedAsync(ConnectionContext connection)
|
public override async Task OnConnectedAsync(ConnectionContext connection)
|
||||||
{
|
{
|
||||||
var address = await this.domainResolver.ResolveAsync(SSH_GITHUB_COM, CancellationToken.None);
|
var address = await this.domainResolver.ResolveAsync(this.githubSshEndPoint);
|
||||||
using var socket = new Socket(address.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
|
using var socket = new Socket(address.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
|
||||||
await socket.ConnectAsync(new IPEndPoint(address, SSH_OVER_HTTPS_PORT));
|
await socket.ConnectAsync(address, this.githubSshEndPoint.Port);
|
||||||
var targetStream = new NetworkStream(socket, ownsSocket: false);
|
var targetStream = new NetworkStream(socket, ownsSocket: false);
|
||||||
|
|
||||||
var task1 = targetStream.CopyToAsync(connection.Transport.Output);
|
var task1 = targetStream.CopyToAsync(connection.Transport.Output);
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user