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.

HighAvailabilityStrategy.cs 6.1 kB

11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177
  1. using Shadowsocks.Model;
  2. using System;
  3. using System.Collections.Generic;
  4. using System.Text;
  5. namespace Shadowsocks.Controller.Strategy
  6. {
  7. class HighAvailabilityStrategy : IStrategy
  8. {
  9. protected Server _currentServer;
  10. protected Dictionary<Server, ServerStatus> _serverStatus;
  11. ShadowsocksController _controller;
  12. Random _random;
  13. public class ServerStatus
  14. {
  15. // time interval between SYN and SYN+ACK
  16. public TimeSpan latency;
  17. public DateTime lastTimeDetectLatency;
  18. // last time anything received
  19. public DateTime lastRead;
  20. // last time anything sent
  21. public DateTime lastWrite;
  22. // connection refused or closed before anything received
  23. public DateTime lastFailure;
  24. public Server server;
  25. public double score;
  26. }
  27. public HighAvailabilityStrategy(ShadowsocksController controller)
  28. {
  29. _controller = controller;
  30. _random = new Random();
  31. _serverStatus = new Dictionary<Server, ServerStatus>();
  32. }
  33. public string Name
  34. {
  35. get { return I18N.GetString("High Availability"); }
  36. }
  37. public string ID
  38. {
  39. get { return "com.shadowsocks.strategy.ha"; }
  40. }
  41. public void ReloadServers()
  42. {
  43. // make a copy to avoid locking
  44. var newServerStatus = new Dictionary<Server, ServerStatus>(_serverStatus);
  45. foreach (var server in _controller.GetCurrentConfiguration().configs)
  46. {
  47. if (!newServerStatus.ContainsKey(server))
  48. {
  49. var status = new ServerStatus();
  50. status.server = server;
  51. status.lastFailure = DateTime.MinValue;
  52. status.lastRead = DateTime.Now;
  53. status.lastWrite = DateTime.Now;
  54. status.latency = new TimeSpan(0, 0, 0, 0, 10);
  55. status.lastTimeDetectLatency = DateTime.Now;
  56. newServerStatus[server] = status;
  57. }
  58. }
  59. _serverStatus = newServerStatus;
  60. ChooseNewServer();
  61. }
  62. public Server GetAServer(IStrategyCallerType type, System.Net.IPEndPoint localIPEndPoint)
  63. {
  64. if (type == IStrategyCallerType.TCP)
  65. {
  66. ChooseNewServer();
  67. }
  68. return _currentServer;
  69. }
  70. /**
  71. * once failed, try after 5 min
  72. * and (last write - last read) < 5s
  73. * and (now - last read) < 5s // means not stuck
  74. * and latency < 200ms, try after 30s
  75. */
  76. public void ChooseNewServer()
  77. {
  78. Server oldServer = _currentServer;
  79. List<ServerStatus> servers = new List<ServerStatus>(_serverStatus.Values);
  80. DateTime now = DateTime.Now;
  81. foreach (var status in servers)
  82. {
  83. // all of failure, latency, (lastread - lastwrite) normalized to 1000, then
  84. // 100 * failure - 2 * latency - 0.5 * (lastread - lastwrite)
  85. status.score =
  86. 100 * 1000 * Math.Min(5 * 60, (now - status.lastFailure).TotalSeconds)
  87. -2 * 5 * (Math.Min(2000, status.latency.TotalMilliseconds) / (1 + (now - status.lastTimeDetectLatency).TotalSeconds / 30 / 10) +
  88. -0.5 * 200 * Math.Min(5, (status.lastRead - status.lastWrite).TotalSeconds));
  89. Logging.Debug(String.Format("server: {0} latency:{1} score: {2}", status.server.FriendlyName(), status.latency, status.score));
  90. }
  91. ServerStatus max = null;
  92. foreach (var status in servers)
  93. {
  94. if (max == null)
  95. {
  96. max = status;
  97. }
  98. else
  99. {
  100. if (status.score >= max.score)
  101. {
  102. max = status;
  103. }
  104. }
  105. }
  106. if (max != null)
  107. {
  108. _currentServer = max.server;
  109. if (_currentServer != oldServer)
  110. {
  111. Console.WriteLine("HA switching to server: {0}", _currentServer.FriendlyName());
  112. }
  113. Logging.Debug(String.Format("choosing server: {0}", _currentServer.FriendlyName()));
  114. }
  115. }
  116. public void UpdateLatency(Model.Server server, TimeSpan latency)
  117. {
  118. Logging.Debug(String.Format("latency: {0} {1}", server.FriendlyName(), latency));
  119. ServerStatus status;
  120. if (_serverStatus.TryGetValue(server, out status))
  121. {
  122. status.latency = latency;
  123. status.lastTimeDetectLatency = DateTime.Now;
  124. }
  125. }
  126. public void UpdateLastRead(Model.Server server)
  127. {
  128. Logging.Debug(String.Format("last read: {0}", server.FriendlyName()));
  129. ServerStatus status;
  130. if (_serverStatus.TryGetValue(server, out status))
  131. {
  132. status.lastRead = DateTime.Now;
  133. }
  134. }
  135. public void UpdateLastWrite(Model.Server server)
  136. {
  137. Logging.Debug(String.Format("last write: {0}", server.FriendlyName()));
  138. ServerStatus status;
  139. if (_serverStatus.TryGetValue(server, out status))
  140. {
  141. status.lastWrite = DateTime.Now;
  142. }
  143. }
  144. public void SetFailure(Model.Server server)
  145. {
  146. Logging.Debug(String.Format("failure: {0}", server.FriendlyName()));
  147. ServerStatus status;
  148. if (_serverStatus.TryGetValue(server, out status))
  149. {
  150. status.lastFailure = DateTime.Now;
  151. }
  152. }
  153. }
  154. }