using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.IO; using System.Linq; using System.Net; using System.Text.Json; using System.Threading; using System.Threading.Tasks; namespace FastGithub.Scanner { /// /// github扫描结果 /// [Service(ServiceLifetime.Singleton)] sealed class GithubScanResults { private const string dataFile = "FastGithub.dat"; private readonly object syncRoot = new(); private readonly List contexts = new(); private readonly ILogger logger; /// /// github扫描结果 /// /// public GithubScanResults(ILogger logger) { this.logger = logger; var datas = LoadDatas(logger); foreach (var context in datas) { this.Add(context); } } /// /// 从磁盘加载数据 /// /// /// private static GithubContext[] LoadDatas(ILogger logger) { try { if (File.Exists(dataFile) == true) { var json = File.ReadAllBytes(dataFile); var datas = JsonSerializer.Deserialize(json); if (datas != null) { return datas.Select(item => item.ToGithubContext()).ToArray(); } } } catch (Exception ex) { logger.LogWarning($"从{dataFile}加载数据失败:{ex.Message}"); } return Array.Empty(); } /// /// 添加GithubContext /// /// /// public bool Add(GithubContext context) { lock (this.syncRoot) { if (this.contexts.Contains(context)) { return false; } this.contexts.Add(context); return true; } } /// /// 转换为数组 /// /// public GithubContext[] ToArray() { lock (this.syncRoot) { return this.contexts.ToArray(); } } /// /// 查找最优的ip /// /// /// public IPAddress? FindBestAddress(string domain) { lock (this.syncRoot) { return this.contexts .Where(item => item.Domain == domain) .OrderByDescending(item => item.AvailableRate) .ThenByDescending(item => item.Available) .ThenBy(item => item.AvgElapsed) .Select(item => item.Address) .FirstOrDefault(); } } /// /// 保存数据到磁盘 /// /// /// public async Task SaveDatasAsync(CancellationToken cancellationToken = default) { try { using var stream = File.OpenWrite(dataFile); var datas = this.ToArray() .OrderByDescending(item => item.AvailableRate) .Select(item => GithubDomainAddress.From(item)); await JsonSerializer.SerializeAsync(stream, datas, cancellationToken: cancellationToken); } catch (Exception ex) { this.logger.LogWarning($"保存数据到{dataFile}失败:{ex.Message}"); } } /// /// github的域名与ip关系数据 /// private class GithubDomainAddress { [AllowNull] public string Domain { get; set; } [AllowNull] public string Address { get; set; } public GithubContext ToGithubContext() { return new GithubContext(this.Domain, IPAddress.Parse(this.Address)) { Available = true }; } public static GithubDomainAddress From(GithubContext context) { return new GithubDomainAddress { Domain = context.Domain, Address = context.Address.ToString() }; } } } }