|
@@ -0,0 +1,387 @@
|
|
|
+using System;
|
|
|
+using System.Collections.Concurrent;
|
|
|
+using System.Collections.Generic;
|
|
|
+using System.IO;
|
|
|
+using System.Linq;
|
|
|
+using System.Net;
|
|
|
+using System.Text;
|
|
|
+using System.Threading;
|
|
|
+using System.Threading.Tasks;
|
|
|
+using JsonRpcLite.Network;
|
|
|
+using JsonRpcLite.Services;
|
|
|
+using JsonRpcLite.Utilities;
|
|
|
+using Newtonsoft.Json;
|
|
|
+using WingInterfaceLibrary.DTO.ServerInfo;
|
|
|
+using WingInterfaceLibrary.DTO.User;
|
|
|
+using WingInterfaceLibrary.Enum;
|
|
|
+using WingInterfaceLibrary.Interface;
|
|
|
+using WingInterfaceLibrary.Request.Authentication;
|
|
|
+using WingInterfaceLibrary.Result.Device;
|
|
|
+using WingServerCommon.Interfaces.Cache;
|
|
|
+using WingServerCommon.Log;
|
|
|
+using WingServerCommon.Service;
|
|
|
+
|
|
|
+namespace WingCloudServer.Plugin
|
|
|
+{
|
|
|
+ /// <summary>
|
|
|
+ /// 最快服务器列表Plugin接口
|
|
|
+ /// </summary>
|
|
|
+ public interface IServerListPlugin : IJsonRpcHttpServerEnginePlugin
|
|
|
+ {
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 最快服务器列表Plugin
|
|
|
+ /// </summary>
|
|
|
+ public class ServerListPlugin : JsonRpcService, IServerListPlugin
|
|
|
+ {
|
|
|
+ //地球半径,单位米
|
|
|
+ private const double EARTH_RADIUS = 6378137;
|
|
|
+ protected IAuthenticationService _authenticationService;
|
|
|
+ private string _apiName = string.Empty;
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 初始化
|
|
|
+ /// </summary>
|
|
|
+ public override void Load(JsonRpcClientPool jsonRpcClientPool)
|
|
|
+ {
|
|
|
+ base.Load(jsonRpcClientPool);
|
|
|
+ _authenticationService = GetProxy<IAuthenticationService>();
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 前处理
|
|
|
+ /// </summary>
|
|
|
+ /// <param name="context">上下文</param>
|
|
|
+ /// <param name="requestData">请求参数</param>
|
|
|
+ /// <returns></returns>
|
|
|
+ public PluginProcessResult PreProcess(IJsonRpcHttpContext context, byte[] requestData)
|
|
|
+ {
|
|
|
+ var dataLength = (int)context.GetRequestContentLength();
|
|
|
+ var requests = JsonRpcCodec.DecodeRequestsAsync(requestData, new System.Threading.CancellationToken(), dataLength).Result;
|
|
|
+ _apiName = $"{context.GetRequestPath().Trim('/')}/{requests[0].Method.Trim('/')}";
|
|
|
+ return new PluginProcessResult(requestData, false);
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 后处理
|
|
|
+ /// </summary>
|
|
|
+ /// <param name="context">上下文</param>
|
|
|
+ /// <param name="responseData">响应参数</param>
|
|
|
+ /// <returns></returns>
|
|
|
+ public PluginProcessResult PostProcess(IJsonRpcHttpContext context, byte[] responseData)
|
|
|
+ {
|
|
|
+ try
|
|
|
+ {
|
|
|
+ var ipAddress = context.RemoteEndPoint?.Address.ToString();
|
|
|
+ var ipLongAddress = IpToLong(ipAddress);
|
|
|
+ var serverInformationList = CacheMaintenance.Instance.Get<IDistributedServerInfosManager>()
|
|
|
+ .Where(x => !string.IsNullOrEmpty(x.Name) && !string.IsNullOrEmpty(x.ServerUrl))?.ToList();
|
|
|
+ var fasterServerInfo = new ServerInfoDTO();
|
|
|
+ var parallelOption = new ParallelOptions() { MaxDegreeOfParallelism = 4 };
|
|
|
+ //本地IP段检索
|
|
|
+ Parallel.ForEach(serverInformationList, parallelOption, (item, parallelLoopState) => {
|
|
|
+ if (item.AssignClientIPList != null && item.AssignClientIPList.Any()) {
|
|
|
+ var ipInfo = item.AssignClientIPList.Find(c => c.LongStartIP >= ipLongAddress && c.LongEndIP <= ipLongAddress);
|
|
|
+ if (ipInfo != null && !string.IsNullOrEmpty(ipInfo.StartIp)) {
|
|
|
+ //本地库找到此IP
|
|
|
+ fasterServerInfo = new ServerInfoDTO() {
|
|
|
+ Name = item.Name,
|
|
|
+ Host = item.ServerUrl
|
|
|
+ };
|
|
|
+ parallelLoopState.Break();
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ });
|
|
|
+ List<ServerInfoDTO> list = new List<ServerInfoDTO>();
|
|
|
+ if (fasterServerInfo != null && !string.IsNullOrEmpty(fasterServerInfo.Name)) {
|
|
|
+ //找到,直接返回
|
|
|
+ list.Add(fasterServerInfo);
|
|
|
+ //添加其他服务器
|
|
|
+ var otherServerList = serverInformationList.FindAll(c => c.Name != fasterServerInfo.Name).Select(c => new ServerInfoDTO() {
|
|
|
+ Name = c.Name,
|
|
|
+ Host = c.ServerUrl,
|
|
|
+ }).ToList();
|
|
|
+ list.AddRange(otherServerList);
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ //没找到,定位,计算,更新,返回
|
|
|
+ var locationInfo = GetIpLocationInfo(ipAddress);
|
|
|
+ if (locationInfo.ad_info != null && !string.IsNullOrEmpty(locationInfo.ad_info.nation)) {
|
|
|
+ if (locationInfo.ad_info.nation == "中国") {
|
|
|
+ //最快指向北京,
|
|
|
+ var fastServerInfo = serverInformationList.FindAll(c => c.Name.Contains("北京") || c.Name.Contains("BJ"))?.Select(c => new ServerInfoDTO() {
|
|
|
+ Name = c.Name,
|
|
|
+ Host = c.ServerUrl,
|
|
|
+ })?.FirstOrDefault() ?? new ServerInfoDTO();
|
|
|
+ list.Add(fastServerInfo);
|
|
|
+ //添加其他服务器
|
|
|
+ var otherServerList = serverInformationList.FindAll(c => c.Name != fastServerInfo.Name).Select(c => new ServerInfoDTO() {
|
|
|
+ Name = c.Name,
|
|
|
+ Host = c.ServerUrl,
|
|
|
+ }).ToList();
|
|
|
+ list.AddRange(otherServerList);
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ var extendList = new List<ServerInfoExtend>();
|
|
|
+ foreach(var item in serverInformationList) {
|
|
|
+ //计算最短距离
|
|
|
+ // double beelineDistanceTemp = GetDistance(locationInfo.location.lat, locationInfo.location.lng, item.lat, item.lng);
|
|
|
+ // var extendInfo = new ServerInfoExtend() {
|
|
|
+ // Name = item.Name,
|
|
|
+ // Host = item.ServerUrl,
|
|
|
+ // BeelineDistance = beelineDistanceTemp
|
|
|
+ // };
|
|
|
+ // extendList.Add(extendInfo);
|
|
|
+ }
|
|
|
+ list = extendList.OrderBy(c => c.BeelineDistance).Select(e => new ServerInfoDTO() {
|
|
|
+ Name = e.Name,
|
|
|
+ Host = e.Host,
|
|
|
+ }).ToList();
|
|
|
+ }
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ //啥也没找到,默认优先香港服务器[主服务器]
|
|
|
+ list = serverInformationList.OrderBy(c => c.IsMaster).Select(e => new ServerInfoDTO() {
|
|
|
+ Name = e.Name,
|
|
|
+ Host = e.ServerUrl,
|
|
|
+ }).ToList();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ //todo 处理输出数据
|
|
|
+ }
|
|
|
+ catch (Exception ex)
|
|
|
+ {
|
|
|
+ Logger.WriteLineInfo($"IPAddressPlugin failed, api name:{_apiName}, ex:{ex}");
|
|
|
+ }
|
|
|
+ return new PluginProcessResult(responseData, false);
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// ip地址转long
|
|
|
+ /// </summary>
|
|
|
+ /// <param name="ipAddress"></param>
|
|
|
+ /// <returns></returns>
|
|
|
+ private long IpToLong(string ipAddress)
|
|
|
+ {
|
|
|
+ byte[] byts = IPAddress.Parse(ipAddress).GetAddressBytes();
|
|
|
+ Array.Reverse(byts); // 需要倒置一次字节序
|
|
|
+ long ipLong = BitConverter.ToUInt32(byts, 0);
|
|
|
+ return ipLong;
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 计算两点位置的距离,返回两点的距离,单位 米
|
|
|
+ /// 该公式为GOOGLE提供,误差小于0.2米
|
|
|
+ /// </summary>
|
|
|
+ /// <param name="lat1">第一点纬度</param>
|
|
|
+ /// <param name="lng1">第一点经度</param>
|
|
|
+ /// <param name="lat2">第二点纬度</param>
|
|
|
+ /// <param name="lng2">第二点经度</param>
|
|
|
+ /// <returns></returns>
|
|
|
+ public double GetDistance(double lat1, double lng1, double lat2, double lng2)
|
|
|
+ {
|
|
|
+ double radLat1 = Rad(lat1);
|
|
|
+ double radLng1 = Rad(lng1);
|
|
|
+ double radLat2 = Rad(lat2);
|
|
|
+ double radLng2 = Rad(lng2);
|
|
|
+ double a = radLat1 - radLat2;
|
|
|
+ double b = radLng1 - radLng2;
|
|
|
+ double result = 2 * Math.Asin(Math.Sqrt(Math.Pow(Math.Sin(a / 2), 2) + Math.Cos(radLat1) * Math.Cos(radLat2) * Math.Pow(Math.Sin(b / 2), 2))) * EARTH_RADIUS;
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 经纬度转化成弧度
|
|
|
+ /// </summary>
|
|
|
+ /// <param name="d"></param>
|
|
|
+ /// <returns></returns>
|
|
|
+ private double Rad(double d)
|
|
|
+ {
|
|
|
+ return (double)d * Math.PI / 180d;
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 获取ip的定位信息
|
|
|
+ /// </summary>
|
|
|
+ /// <param name="d"></param>
|
|
|
+ /// <returns></returns>
|
|
|
+ private IPServiceResult GetIpLocationInfo(string ip) {
|
|
|
+ IPServiceResult info = new IPServiceResult();
|
|
|
+ string key = "4Z3BZ-L5RK6-MJ5SC-EXNFA-3V7PH-RSB3U";//此处要改成配置
|
|
|
+ var url = $"https://apis.map.qq.com/ws/location/v1/ip?ip={ip}&key={key}";
|
|
|
+ try
|
|
|
+ {
|
|
|
+ var json = HttpGet(url);
|
|
|
+ if (!string.IsNullOrEmpty(json)) {
|
|
|
+ var serviceGuideInfo = JsonConvert.DeserializeObject<IPServiceGuide>(json);
|
|
|
+ if (serviceGuideInfo != null && serviceGuideInfo.status == 0 && serviceGuideInfo.result != null)
|
|
|
+ {
|
|
|
+ info = serviceGuideInfo.result;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ catch(Exception ex) {
|
|
|
+ //异常处理
|
|
|
+ }
|
|
|
+ return info;
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 处理http GET请求,返回数据
|
|
|
+ /// </summary>
|
|
|
+ /// <param name="url">请求的url地址</param>
|
|
|
+ /// <returns>http GET成功后返回的数据,失败抛WebException异常</returns>
|
|
|
+ public static string HttpGet(string url)
|
|
|
+ {
|
|
|
+ System.GC.Collect();
|
|
|
+ string result = "";
|
|
|
+
|
|
|
+ HttpWebRequest request = null;
|
|
|
+ HttpWebResponse response = null;
|
|
|
+
|
|
|
+ //请求url以获取数据
|
|
|
+ try
|
|
|
+ {
|
|
|
+ //设置最大连接数
|
|
|
+ ServicePointManager.DefaultConnectionLimit = 40;
|
|
|
+
|
|
|
+ /***************************************************************
|
|
|
+ * 下面设置HttpWebRequest的相关属性
|
|
|
+ * ************************************************************/
|
|
|
+ request = (HttpWebRequest)WebRequest.Create(url);
|
|
|
+
|
|
|
+ request.Method = "GET";
|
|
|
+
|
|
|
+ //获取服务器返回
|
|
|
+ response = (HttpWebResponse)request.GetResponse();
|
|
|
+
|
|
|
+ //获取HTTP返回数据
|
|
|
+ StreamReader sr = new StreamReader(response.GetResponseStream(), Encoding.UTF8);
|
|
|
+ result = sr.ReadToEnd().Trim();
|
|
|
+ sr.Close();
|
|
|
+ }
|
|
|
+ catch (Exception e)
|
|
|
+ {
|
|
|
+ throw e;
|
|
|
+ }
|
|
|
+ finally
|
|
|
+ {
|
|
|
+ //关闭连接和流
|
|
|
+ if (response != null)
|
|
|
+ {
|
|
|
+ response.Close();
|
|
|
+ }
|
|
|
+ if (request != null)
|
|
|
+ {
|
|
|
+ request.Abort();
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 腾讯云定位响应实体
|
|
|
+ /// </summary>
|
|
|
+ class IPServiceGuide
|
|
|
+ {
|
|
|
+ /// <summary>
|
|
|
+ /// 状态码;正常为0
|
|
|
+ /// </summary>
|
|
|
+ public int status { get; set;}
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 响应信息
|
|
|
+ /// </summary>
|
|
|
+ public string message { get; set;}
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 响应结果
|
|
|
+ /// </summary>
|
|
|
+ public IPServiceResult result { get; set;}
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 腾讯云定位响应结果
|
|
|
+ /// </summary>
|
|
|
+ class IPServiceResult
|
|
|
+ {
|
|
|
+ /// <summary>
|
|
|
+ /// ip地址
|
|
|
+ /// </summary>
|
|
|
+ public string ip { get; set;}
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 经纬度信息
|
|
|
+ /// </summary>
|
|
|
+ public IPServiceLocationResult location { get; set;}
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 地区信息
|
|
|
+ /// </summary>
|
|
|
+ public IPServiceNationResult ad_info { get; set;}
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 腾讯云定位经纬度信息
|
|
|
+ /// </summary>
|
|
|
+ class IPServiceLocationResult
|
|
|
+ {
|
|
|
+ /// <summary>
|
|
|
+ /// 纬度
|
|
|
+ /// </summary>
|
|
|
+ public string lat { get; set;}
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 经度
|
|
|
+ /// </summary>
|
|
|
+ public string lng { get; set;}
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 腾讯云定位地区信息
|
|
|
+ /// </summary>
|
|
|
+ class IPServiceNationResult
|
|
|
+ {
|
|
|
+ /// <summary>
|
|
|
+ /// 国家
|
|
|
+ /// </summary>
|
|
|
+ public string nation { get; set;}
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 省份
|
|
|
+ /// </summary>
|
|
|
+ public string province { get; set;}
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 城市
|
|
|
+ /// </summary>
|
|
|
+ public string city { get; set;}
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ ///
|
|
|
+ /// </summary>
|
|
|
+ public string district { get; set;}
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 编码
|
|
|
+ /// </summary>
|
|
|
+ public string adcode { get; set;}
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 最快服务器扩展
|
|
|
+ /// </summary>
|
|
|
+ class ServerInfoExtend : ServerInfoDTO
|
|
|
+ {
|
|
|
+ /// <summary>
|
|
|
+ /// 最短距离
|
|
|
+ /// </summary>
|
|
|
+ public double BeelineDistance { get; set;}
|
|
|
+ }
|
|
|
+}
|