You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

StatisticsStrategy.cs 6.1 kB

9 years ago
9 years ago
9 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Net;
  5. using System.Net.NetworkInformation;
  6. using System.Threading;
  7. using Newtonsoft.Json;
  8. using Shadowsocks.Model;
  9. namespace Shadowsocks.Controller.Strategy
  10. {
  11. class StatisticsStrategy : IStrategy
  12. {
  13. private readonly ShadowsocksController _controller;
  14. private Server _currentServer;
  15. private readonly Timer _timer;
  16. private Dictionary<string, List<AvailabilityStatistics.RawStatisticsData>> _filteredStatistics;
  17. private int ChoiceKeptMilliseconds
  18. => (int) TimeSpan.FromMinutes(_controller.StatisticsConfiguration.ChoiceKeptMinutes).TotalMilliseconds;
  19. public StatisticsStrategy(ShadowsocksController controller)
  20. {
  21. _controller = controller;
  22. var servers = controller.GetCurrentConfiguration().configs;
  23. var randomIndex = new Random().Next() % servers.Count();
  24. _currentServer = servers[randomIndex]; //choose a server randomly at first
  25. _timer = new Timer(ReloadStatisticsAndChooseAServer);
  26. }
  27. private void ReloadStatisticsAndChooseAServer(object obj)
  28. {
  29. Logging.Debug("Reloading statistics and choose a new server....");
  30. var servers = _controller.GetCurrentConfiguration().configs;
  31. LoadStatistics();
  32. ChooseNewServer(servers);
  33. }
  34. private void LoadStatistics()
  35. {
  36. _filteredStatistics = _controller.availabilityStatistics.RawStatistics ?? _filteredStatistics ?? new Dictionary<string, List<AvailabilityStatistics.RawStatisticsData>>();
  37. }
  38. //return the score by data
  39. //server with highest score will be choosen
  40. private float GetScore(string serverName)
  41. {
  42. var config = _controller.StatisticsConfiguration;
  43. List<AvailabilityStatistics.RawStatisticsData> dataList;
  44. if (_filteredStatistics == null || !_filteredStatistics.TryGetValue(serverName, out dataList)) return 0;
  45. var successTimes = (float) dataList.Count(data => data.ICMPStatus.Equals(IPStatus.Success.ToString()));
  46. var timedOutTimes = (float) dataList.Count(data => data.ICMPStatus.Equals(IPStatus.TimedOut.ToString()));
  47. var statisticsData = new AvailabilityStatistics.StatisticsData
  48. {
  49. PackageLoss = timedOutTimes/(successTimes + timedOutTimes)*100,
  50. AverageResponse = Convert.ToInt32(dataList.Average(data => data.RoundtripTime)),
  51. MinResponse = dataList.Min(data => data.RoundtripTime),
  52. MaxResponse = dataList.Max(data => data.RoundtripTime)
  53. };
  54. float factor;
  55. float score = 0;
  56. if (!config.Calculations.TryGetValue("PackageLoss", out factor)) factor = 0;
  57. score += statisticsData.PackageLoss*factor;
  58. if (!config.Calculations.TryGetValue("AverageResponse", out factor)) factor = 0;
  59. score += statisticsData.AverageResponse*factor;
  60. if (!config.Calculations.TryGetValue("MinResponse", out factor)) factor = 0;
  61. score += statisticsData.MinResponse*factor;
  62. if (!config.Calculations.TryGetValue("MaxResponse", out factor)) factor = 0;
  63. score += statisticsData.MaxResponse*factor;
  64. Logging.Debug($"{serverName} {JsonConvert.SerializeObject(statisticsData)}");
  65. return score;
  66. }
  67. private void ChooseNewServer(List<Server> servers)
  68. {
  69. if (_filteredStatistics == null || servers.Count == 0)
  70. {
  71. return;
  72. }
  73. try
  74. {
  75. var bestResult = (from server in servers
  76. let name = server.FriendlyName()
  77. where _filteredStatistics.ContainsKey(name)
  78. select new
  79. {
  80. server,
  81. score = GetScore(name)
  82. }
  83. ).Aggregate((result1, result2) => result1.score > result2.score ? result1 : result2);
  84. LogWhenEnabled($"Switch to server: {bestResult.server.FriendlyName()} by statistics: score {bestResult.score}");
  85. _currentServer = bestResult.server;
  86. }
  87. catch (Exception e)
  88. {
  89. Logging.LogUsefulException(e);
  90. }
  91. }
  92. private void LogWhenEnabled(string log)
  93. {
  94. if (_controller.GetCurrentStrategy()?.ID == ID) //output when enabled
  95. {
  96. Console.WriteLine(log);
  97. }
  98. }
  99. public string ID => "com.shadowsocks.strategy.scbs";
  100. public string Name => I18N.GetString("Choose By Total Package Loss");
  101. public Server GetAServer(IStrategyCallerType type, IPEndPoint localIPEndPoint)
  102. {
  103. var oldServer = _currentServer;
  104. if (oldServer == null)
  105. {
  106. ChooseNewServer(_controller.GetCurrentConfiguration().configs);
  107. }
  108. if (oldServer != _currentServer)
  109. {
  110. }
  111. return _currentServer; //current server cached for CachedInterval
  112. }
  113. public void ReloadServers()
  114. {
  115. ChooseNewServer(_controller.GetCurrentConfiguration().configs);
  116. _timer?.Change(0, ChoiceKeptMilliseconds);
  117. }
  118. public void SetFailure(Server server)
  119. {
  120. Logging.Debug($"failure: {server.FriendlyName()}");
  121. }
  122. public void UpdateLastRead(Server server)
  123. {
  124. //TODO: combine this part of data with ICMP statics
  125. }
  126. public void UpdateLastWrite(Server server)
  127. {
  128. //TODO: combine this part of data with ICMP statics
  129. }
  130. public void UpdateLatency(Server server, TimeSpan latency)
  131. {
  132. //TODO: combine this part of data with ICMP statics
  133. }
  134. }
  135. }