開源FastGithub

jiulang發表於2021-06-16

0 前言

github網站訪問慢或訪問不了,相信很多人都會遇到過,解決方式大概有兩種:一種是使用代理訪問;另一種是使用ipaddress.com等域名解析網站查詢域名的ip,然後在host檔案增加ip與域名的對映。

1 代理訪問

代理訪問是在一臺能上github的伺服器開通代理服務,然後你所在機器在訪問github時,流量由代理伺服器轉發,穩定的代理服務,一般都是收費用。

2 域名解析網站

例如使用ipaddress.com查詢域名的ip,但你的網路可能還是無法正常的訪問這個ip,或者無法連線此ip的443埠。所以如果你在網上搜尋"github慢",得到別人貼出的"最新github ip"資料,貼上到你的host檔案,你可能還是無法訪問github。

3 FastGithub

github定製版的dns服務,解析訪問github最快的ip

3.1 加速原理

  • 使用github公開的ip範圍,掃描所有可用的ip;
  • 間隔指定時間(5min)檢測與記錄掃描到的ip的訪問耗時;
  • 攔截dns,訪問github時,返回最快的ip;

3.2 獲取github的ip

訪問https://api.github.com/meta,我們就可以拿到github公開其使用的ip,為了能夠在所有環境獲取到這個meta資料,我們需要把這個meta轉存到gitee或本機,因為這份資料更新不頻繁。FastGithub依賴這份資料,目前從gitee獲取到快取副本。

3.3 443埠掃描

github使用了https,所以對應的tcp埠是443,嘗試連線到github各ip下的443埠,如果在指定時間內(預設1s)能連線成功,證明這個ip是有用的,反之就要丟棄這個ip了。在.net下,我們可以使用Socket來進行tcp連線:

[Service(ServiceLifetime.Singleton)]
sealed class PortScanMiddleware : IMiddleware<GithubContext>
{
    private const int PORT = 443;
    private readonly IOptionsMonitor<GithubOptions> options;
    private readonly ILogger<PortScanMiddleware> logger;

    public PortScanMiddleware(
        IOptionsMonitor<GithubOptions> options,
        ILogger<PortScanMiddleware> logger)
    {
        this.options = options;
        this.logger = logger;
    }

    public async Task InvokeAsync(GithubContext context, Func<Task> next)
    {
        try
        {
            using var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            using var cancellationTokenSource = new CancellationTokenSource(this.options.CurrentValue.PortScanTimeout);
            await socket.ConnectAsync(context.Address, PORT, cancellationTokenSource.Token);

            await next();
        }
        catch (Exception)
        {
            this.logger.LogTrace($"{context.Domain} {context.Address}的{PORT}埠未開放");
        }
    }
}

3.4 Https檢測

443埠開放,不意味這個ip就能正常的使用github,有可能在建立ssl時會失敗,或者在https請求時,服務返回的內容並不是github官網的內容,所以我們需要進一步的偵測是否有http響應,響應內容是不是github的內容。據我觀察,正常的github響應Server頭,都有GitHub.com值。

[Service(ServiceLifetime.Singleton)]
sealed class HttpsScanMiddleware : IMiddleware<GithubContext>
{
    private readonly IOptionsMonitor<GithubOptions> options;
    private readonly ILogger<HttpsScanMiddleware> logger;

    public HttpsScanMiddleware(
        IOptionsMonitor<GithubOptions> options,
        ILogger<HttpsScanMiddleware> logger)
    {
        this.options = options;
        this.logger = logger;
    }

    public async Task InvokeAsync(GithubContext context, Func<Task> next)
    {
        try
        {
            var request = new HttpRequestMessage
            {
                Method = HttpMethod.Get,
                RequestUri = new Uri($"https://{context.Address}"),
            };
            request.Headers.Host = context.Domain;

            using var httpClient = new HttpClient(new HttpClientHandler
            {
                Proxy = null,
                UseProxy = false,
            });

            var startTime = DateTime.Now;
            using var cancellationTokenSource = new CancellationTokenSource(this.options.CurrentValue.HttpsScanTimeout);
            var response = await httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, cancellationTokenSource.Token);
            var server = response.EnsureSuccessStatusCode().Headers.Server;
            if (server.Any(s => string.Equals("GitHub.com", s.Product?.Name, StringComparison.OrdinalIgnoreCase)))
            {
                context.HttpElapsed = DateTime.Now.Subtract(startTime);
                await next();
            }
        }
        catch (TaskCanceledException)
        {
            this.logger.LogTrace($"{context.Domain} {context.Address}連線超時");
        }
        catch (Exception ex)
        {
            this.logger.LogTrace($"{context.Domain} {context.Address} {ex.Message}");
        }
    }
}

3.5 Dns服務

我們可以建設一個本機或區域網的dns服務,訪問github時,就返回檢測到的最快的一條ip,訪問其它域名時,轉發給8.8.8.8這樣穩定的上游dns服務來解析。這樣既不影響其它域名的解析速度與穩定性,同時又能正常的使用github所有服務。

4 原始碼和軟體

4.1 原始碼

github

4.2 軟體下載

gitee

相關文章