From 4faa241b87712222e13047dd2d46430f23144d3b Mon Sep 17 00:00:00 2001 From: xljiulang <366193849@qq.com> Date: Tue, 17 Aug 2021 23:23:18 +0800 Subject: [PATCH] =?UTF-8?q?=E6=8C=91=E9=80=89=E5=8F=AF=E7=94=A8=E4=B8=94?= =?UTF-8?q?=E6=9C=80=E5=BF=AB=E7=9A=84ip?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- FastGithub.Dns/RequestResolver.cs | 9 +- FastGithub.DomainResolve/DomainResolver.cs | 112 +++++++++++++++----- FastGithub.DomainResolve/IDomainResolver.cs | 4 +- FastGithub.Http/HttpClientHandler.cs | 3 +- FastGithub.ReverseProxy/GithubSshHandler.cs | 8 +- 5 files changed, 92 insertions(+), 44 deletions(-) diff --git a/FastGithub.Dns/RequestResolver.cs b/FastGithub.Dns/RequestResolver.cs index 73dc259..e1ee910 100644 --- a/FastGithub.Dns/RequestResolver.cs +++ b/FastGithub.Dns/RequestResolver.cs @@ -18,19 +18,14 @@ namespace FastGithub.Dns { private readonly TimeSpan ttl = TimeSpan.FromMinutes(1d); private readonly FastGithubConfig fastGithubConfig; - private readonly ILogger logger; /// /// dns解析者 /// /// - /// - public RequestResolver( - FastGithubConfig fastGithubConfig, - ILogger logger) + public RequestResolver(FastGithubConfig fastGithubConfig) { this.fastGithubConfig = fastGithubConfig; - this.logger = logger; } /// @@ -60,8 +55,6 @@ namespace FastGithub.Dns var localAddress = remoteEndPointRequest.GetLocalIPAddress() ?? IPAddress.Loopback; var record = new IPAddressResourceRecord(domain, localAddress, this.ttl); response.AnswerRecords.Add(record); - - this.logger.LogInformation($"[{domain}->{localAddress}]"); return response; } diff --git a/FastGithub.DomainResolve/DomainResolver.cs b/FastGithub.DomainResolve/DomainResolver.cs index 46c5784..6782279 100644 --- a/FastGithub.DomainResolve/DomainResolver.cs +++ b/FastGithub.DomainResolve/DomainResolver.cs @@ -4,8 +4,11 @@ using FastGithub.Configuration; using Microsoft.Extensions.Caching.Memory; using Microsoft.Extensions.Logging; using System; +using System.Collections.Concurrent; +using System.Collections.Generic; using System.Linq; using System.Net; +using System.Net.Sockets; using System.Threading; using System.Threading.Tasks; @@ -19,7 +22,11 @@ namespace FastGithub.DomainResolve private readonly IMemoryCache memoryCache; private readonly FastGithubConfig fastGithubConfig; private readonly ILogger 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 semaphoreSlims = new(); /// /// 域名解析器 @@ -38,42 +45,49 @@ namespace FastGithub.DomainResolve } /// - /// 解析指定的域名 + /// 解析域名 /// - /// + /// + /// /// - /// - public Task ResolveAsync(string domain, CancellationToken cancellationToken) + public async Task ResolveAsync(DnsEndPoint endPoint, CancellationToken cancellationToken = default) { - // 缓存以避免做不必要的并发查询 - var key = $"{nameof(DomainResolver)}:{domain}"; - return this.memoryCache.GetOrCreateAsync(key, e => + var semaphore = this.semaphoreSlims.GetOrAdd(endPoint, _ => new SemaphoreSlim(1, 1)); + try { - e.SetAbsoluteExpiration(this.cacheTimeSpan); - return this.LookupAsync(domain, cancellationToken); - }); + await semaphore.WaitAsync(cancellationToken); + if (this.memoryCache.TryGetValue(endPoint, out var address) == false) + { + address = await this.LookupAsync(endPoint, cancellationToken); + this.memoryCache.Set(endPoint, address, this.resolveCacheTimeSpan); + } + return address; + } + finally + { + semaphore.Release(); + } } /// /// 查找ip /// - /// + /// /// /// - /// - private async Task LookupAsync(string domain, CancellationToken cancellationToken) + private async Task LookupAsync(DnsEndPoint endPoint, CancellationToken cancellationToken) { var pureDns = this.fastGithubConfig.PureDns; var fastDns = this.fastGithubConfig.FastDns; try { - return await LookupCoreAsync(pureDns, domain, cancellationToken); + return await LookupCoreAsync(pureDns, endPoint, cancellationToken); } catch (Exception) { - this.logger.LogWarning($"由于{pureDns}解析{domain}失败,本次使用{fastDns}"); - return await LookupCoreAsync(fastDns, domain, cancellationToken); + this.logger.LogWarning($"由于{pureDns}解析{endPoint.Host}失败,本次使用{fastDns}"); + return await LookupCoreAsync(fastDns, endPoint, cancellationToken); } } @@ -81,24 +95,66 @@ namespace FastGithub.DomainResolve /// 查找ip /// /// - /// + /// /// /// - private async Task LookupCoreAsync(IPEndPoint dns, string domain, CancellationToken cancellationToken) + private async Task LookupCoreAsync(IPEndPoint dns, DnsEndPoint endPoint, CancellationToken cancellationToken) { 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); - var addresses = await dnsClient.Lookup(domain, RecordType.A, linkedTokenSource.Token); - var address = addresses?.FirstOrDefault(); - if (address == null) - { - throw new FastGithubException($"dns{dns}解析不到{domain}的ip"); - } + var addresses = await dnsClient.Lookup(endPoint.Host, RecordType.A, linkedTokenSource.Token); + var fastAddress = await this.GetFastIPAddressAsync(addresses, endPoint.Port, cancellationToken); - this.logger.LogInformation($"[{domain}->{address}]"); - return address; + if (fastAddress != null) + { + this.logger.LogInformation($"[{endPoint.Host}->{fastAddress}]"); + return fastAddress; + } + throw new FastGithubException($"dns{dns}解析不到{endPoint.Host}可用的ip"); + } + + /// + /// 获取最快的ip + /// + /// + /// + /// + /// + private async Task GetFastIPAddressAsync(IEnumerable addresses, int port, CancellationToken cancellationToken) + { + var tasks = addresses.Select(address => this.IsAvailableAsync(address, port, cancellationToken)); + var fastTask = await Task.WhenAny(tasks); + return await fastTask; + } + + /// + /// 验证远程节点是否可连接 + /// + /// + /// + /// + /// + private async Task 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; + } + catch (OperationCanceledException) + { + return default; + } + catch (Exception) + { + await Task.Delay(this.connectTimeout, cancellationToken); + return default; + } } } } diff --git a/FastGithub.DomainResolve/IDomainResolver.cs b/FastGithub.DomainResolve/IDomainResolver.cs index 5d7b609..2d2674f 100644 --- a/FastGithub.DomainResolve/IDomainResolver.cs +++ b/FastGithub.DomainResolve/IDomainResolver.cs @@ -12,9 +12,9 @@ namespace FastGithub.DomainResolve /// /// 解析域名 /// - /// + /// /// /// - Task ResolveAsync(string domain, CancellationToken cancellationToken); + Task ResolveAsync(DnsEndPoint endPoint, CancellationToken cancellationToken = default); } } \ No newline at end of file diff --git a/FastGithub.Http/HttpClientHandler.cs b/FastGithub.Http/HttpClientHandler.cs index 4dedf28..2f61176 100644 --- a/FastGithub.Http/HttpClientHandler.cs +++ b/FastGithub.Http/HttpClientHandler.cs @@ -67,7 +67,8 @@ namespace FastGithub.Http { 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(); request.Headers.Host = context.Domain; diff --git a/FastGithub.ReverseProxy/GithubSshHandler.cs b/FastGithub.ReverseProxy/GithubSshHandler.cs index 127e2a3..3d83969 100644 --- a/FastGithub.ReverseProxy/GithubSshHandler.cs +++ b/FastGithub.ReverseProxy/GithubSshHandler.cs @@ -3,7 +3,6 @@ using Microsoft.AspNetCore.Connections; using System.IO.Pipelines; using System.Net; using System.Net.Sockets; -using System.Threading; using System.Threading.Tasks; namespace FastGithub.ReverseProxy @@ -13,9 +12,8 @@ namespace FastGithub.ReverseProxy /// 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 DnsEndPoint githubSshEndPoint = new("ssh.github.com", 443); /// /// github的ssh处理者 @@ -33,9 +31,9 @@ namespace FastGithub.ReverseProxy /// 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); - 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 task1 = targetStream.CopyToAsync(connection.Transport.Output);