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