PageRenderTime 45ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 0ms

/src/Microsoft.AspNet.SignalR.Crank/ControllerHub.cs

https://gitlab.com/scgitlab/SignalR
C# | 316 lines | 267 code | 47 blank | 2 comment | 42 complexity | 67a9304eaf7ba7f077174eb70eee8878 MD5 | raw file
  1. // Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.md in the project root for license information.
  2. using System;
  3. using System.Collections.Generic;
  4. using System.Diagnostics;
  5. using System.IO;
  6. using System.Linq;
  7. using System.Threading;
  8. using System.Threading.Tasks;
  9. using Microsoft.AspNet.SignalR.Hubs;
  10. using Microsoft.Owin.Hosting;
  11. using Owin;
  12. namespace Microsoft.AspNet.SignalR.Crank
  13. {
  14. public class ControllerHub : Hub
  15. {
  16. private static CrankArguments Arguments;
  17. private static IDisposable AppHost = null;
  18. private static int ClientsConnected;
  19. private static PerformanceCounters PerformanceCounters;
  20. private static List<ConnectionsSample> Samples = new List<ConnectionsSample>();
  21. private static int NextSample = 0;
  22. private static object FlushLock = new object();
  23. private static ControllerEvents TestPhase = ControllerEvents.None;
  24. private static Stopwatch TestTimer;
  25. private static IHubContext HubContext;
  26. public override Task OnConnected()
  27. {
  28. ClientsConnected++;
  29. return base.OnConnected();
  30. }
  31. public override Task OnDisconnected(bool stopCalled)
  32. {
  33. ClientsConnected--;
  34. return base.OnDisconnected(stopCalled);
  35. }
  36. public void Mark(int sampleId, int[] states)
  37. {
  38. MarkInternal(sampleId, states);
  39. }
  40. internal static void MarkInternal(int sampleId, int[] states)
  41. {
  42. Debug.Assert(sampleId < Samples.Count);
  43. Samples[sampleId].Add(states);
  44. FlushLog();
  45. }
  46. internal static void Start(CrankArguments arguments)
  47. {
  48. ControllerHub.Arguments = arguments;
  49. PerformanceCounters = new PerformanceCounters(new Uri(arguments.Url).Host, arguments.SignalRInstance);
  50. ThreadPool.QueueUserWorkItem(_ => Run());
  51. }
  52. private static void Run()
  53. {
  54. if (Arguments.NumClients > 1)
  55. {
  56. AppHost = WebApp.Start<Startup>(Arguments.ControllerUrl);
  57. if (!WaitForClientsToConnect())
  58. {
  59. SignalPhaseChange(ControllerEvents.Abort);
  60. for (int attempts = 0; (ClientsConnected > 0) && (attempts < CrankArguments.ConnectionPollAttempts); attempts++)
  61. {
  62. Thread.Sleep(CrankArguments.ConnectionPollIntervalMS);
  63. }
  64. AppHost.Dispose();
  65. return;
  66. }
  67. }
  68. RunConnect();
  69. RunSend();
  70. RunDisconnect();
  71. WaitForLastSamples();
  72. if (AppHost != null)
  73. {
  74. AppHost.Dispose();
  75. }
  76. FlushLog(force:true);
  77. }
  78. private static void RunConnect()
  79. {
  80. InitializeLog();
  81. SignalPhaseChange(ControllerEvents.Connect);
  82. StartSampleLoop();
  83. BlockWhilePhase(ControllerEvents.Connect, breakCondition: () =>
  84. {
  85. if (Samples.Count == 0)
  86. {
  87. return false;
  88. }
  89. if (TestTimer.Elapsed >= TimeSpan.FromSeconds(Arguments.ConnectTimeout))
  90. {
  91. return true;
  92. }
  93. var lastSample = Samples.Last();
  94. if (lastSample.ServerAvailableMBytes > 0)
  95. {
  96. if (lastSample.ServerAvailableMBytes <= Arguments.MinServerMBytes)
  97. {
  98. return true;
  99. }
  100. }
  101. var connections = lastSample.Connected + lastSample.Reconnected;
  102. return connections >= Arguments.Connections;
  103. });
  104. SignalPhaseChange(ControllerEvents.Send);
  105. }
  106. private static void RunSend()
  107. {
  108. var timeout = TestTimer.Elapsed.Add(TimeSpan.FromSeconds(Arguments.SendTimeout));
  109. BlockWhilePhase(ControllerEvents.Send, breakCondition: () =>
  110. {
  111. return TestTimer.Elapsed >= timeout;
  112. });
  113. SignalPhaseChange(ControllerEvents.Disconnect);
  114. }
  115. private static void RunDisconnect()
  116. {
  117. BlockWhilePhase(ControllerEvents.Disconnect, breakCondition: () =>
  118. {
  119. if (TestTimer.Elapsed >= TimeSpan.FromSeconds(Arguments.ConnectTimeout))
  120. {
  121. return true;
  122. }
  123. var lastSample = Samples.Last();
  124. var connections = lastSample.Connected + lastSample.Reconnected;
  125. return connections == 0;
  126. });
  127. SignalPhaseChange(ControllerEvents.Complete);
  128. }
  129. private static void StartSampleLoop()
  130. {
  131. ThreadPool.QueueUserWorkItem(_ =>
  132. {
  133. TestTimer = Stopwatch.StartNew();
  134. while ((TestPhase != ControllerEvents.Abort) && (TestPhase != ControllerEvents.Complete))
  135. {
  136. SignalSample(TestTimer.Elapsed);
  137. Thread.Sleep(Arguments.SampleInterval);
  138. }
  139. });
  140. }
  141. private static void InitializeLog()
  142. {
  143. File.WriteAllText(Arguments.LogFile, Environment.CommandLine + Environment.NewLine);
  144. File.WriteAllText(Arguments.LogFile, "TestPhase,Elapsed,Connected,Reconnected,Disconnected,ServerAvailableMBytes,ServerTcpConnectionsEst" + Environment.NewLine);
  145. }
  146. private static void FlushLog(bool force = false)
  147. {
  148. if (Samples.Count == 0)
  149. {
  150. return;
  151. }
  152. var expectedSampleCount = GetExpectedSampleCount();
  153. if ((NextSample < Samples.Count) && (Samples[NextSample].Count >= expectedSampleCount))
  154. {
  155. lock (FlushLock)
  156. {
  157. for (; NextSample < Samples.Count; NextSample++)
  158. {
  159. var sample = Samples[NextSample];
  160. if ((sample.Count < expectedSampleCount) && !force)
  161. {
  162. break;
  163. }
  164. var args = new object[] { sample.TestPhase, sample.TimeStamp, sample.Connected, sample.Reconnected, sample.Disconnected, sample.ServerAvailableMBytes, sample.ServerTcpConnectionsEst };
  165. Console.WriteLine("{1} ({0}): {2} Connected, {3} Reconnected, {4} Disconnected, {5} AvailMBytes, {6} TcpConnEst", args);
  166. File.AppendAllText(Arguments.LogFile, String.Format("{0},{1},{2},{3},{4},{5},{6}", args) + Environment.NewLine);
  167. }
  168. }
  169. }
  170. }
  171. private static bool WaitForClientsToConnect()
  172. {
  173. Console.WriteLine("Waiting on Clients...");
  174. int attempts = 0;
  175. while (ClientsConnected < Arguments.NumClients)
  176. {
  177. Thread.Sleep(CrankArguments.ConnectionPollIntervalMS);
  178. if (++attempts > CrankArguments.ConnectionPollAttempts)
  179. {
  180. Console.WriteLine("Aborting: Clients did not connect in time.");
  181. return false;
  182. }
  183. }
  184. return true;
  185. }
  186. private static int GetExpectedSampleCount()
  187. {
  188. return PerformanceCounters.SignalRCountersAvailable ? 1 : Arguments.NumClients;
  189. }
  190. private static void WaitForLastSamples()
  191. {
  192. var attempts = 0;
  193. var expectedSampleCount = GetExpectedSampleCount();
  194. while (Samples.Last().Count < expectedSampleCount)
  195. {
  196. if (++attempts > CrankArguments.ConnectionPollAttempts)
  197. {
  198. break;
  199. }
  200. Thread.Sleep(CrankArguments.ConnectionPollIntervalMS);
  201. }
  202. }
  203. private static void BlockWhilePhase(ControllerEvents phase, Func<bool> breakCondition = null)
  204. {
  205. while (TestPhase == phase)
  206. {
  207. if ((breakCondition != null) && breakCondition())
  208. {
  209. break;
  210. }
  211. Thread.Sleep(CrankArguments.ConnectionPollIntervalMS);
  212. }
  213. }
  214. private static void SignalPhaseChange(ControllerEvents phase)
  215. {
  216. if (phase != ControllerEvents.Abort)
  217. {
  218. TestPhase = phase;
  219. }
  220. if (AppHost == null)
  221. {
  222. Client.OnPhaseChanged(phase);
  223. }
  224. else
  225. {
  226. BroadcastEvent(phase);
  227. }
  228. }
  229. private static void SignalSample(TimeSpan timestamp)
  230. {
  231. Samples.Add(new ConnectionsSample(Enum.GetName(typeof(ControllerEvents), TestPhase), timestamp, PerformanceCounters.ServerAvailableMBytes, PerformanceCounters.ServerTcpConnectionsEst));
  232. if (PerformanceCounters.SignalRCountersAvailable)
  233. {
  234. ControllerHub.MarkInternal(Samples.Count - 1, new int[] {
  235. PerformanceCounters.SignalRConnectionsCurrent,
  236. PerformanceCounters.SignalRConnectionsReconnected,
  237. PerformanceCounters.SignalRConnectionsDisconnected
  238. });
  239. }
  240. else
  241. {
  242. // Use client connection states
  243. if (AppHost == null)
  244. {
  245. Client.OnSample(Samples.Count - 1);
  246. }
  247. else
  248. {
  249. BroadcastEvent(ControllerEvents.Sample, Samples.Count - 1);
  250. }
  251. }
  252. }
  253. private static void BroadcastEvent(ControllerEvents controllerEvent, int id = 0)
  254. {
  255. if (HubContext == null)
  256. {
  257. HubContext = GlobalHost.ConnectionManager.GetHubContext<ControllerHub>();
  258. }
  259. HubContext.Clients.All.broadcast(controllerEvent, id);
  260. }
  261. public class Startup
  262. {
  263. public void Configuration(IAppBuilder app)
  264. {
  265. app.MapSignalR();
  266. }
  267. }
  268. }
  269. }