Program.cs 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403
  1. using System;
  2. using System.Collections.Generic;
  3. using System.ComponentModel;
  4. using System.Configuration;
  5. using System.Data;
  6. using System.Data.Common;
  7. using System.Linq;
  8. using System.Net;
  9. using System.Threading;
  10. using System.Web.Script.Serialization;
  11. using MongoDB.Driver.Builders;
  12. using Benchmarks.AspNet.Models;
  13. namespace HttpListener
  14. {
  15. class Program
  16. {
  17. private static void RequestCallback(Object state)
  18. {
  19. HttpListenerContext context = (HttpListenerContext)state;
  20. HttpListenerRequest request = context.Request;
  21. HttpListenerResponse response = context.Response;
  22. try
  23. {
  24. string responseString = null;
  25. switch (request.Url.LocalPath)
  26. {
  27. case "/plaintext":
  28. responseString = Plaintext(response);
  29. break;
  30. case "/json":
  31. responseString = Json(response);
  32. break;
  33. case "/db":
  34. responseString = Db(request, response);
  35. break;
  36. case "/fortunes":
  37. responseString = Fortunes(request, response);
  38. break;
  39. case "/updates":
  40. responseString = Updates(request, response);
  41. break;
  42. case "/mongodbdb":
  43. responseString = MongoDBDb(request, response);
  44. break;
  45. case "/mongodbfortunes":
  46. responseString = MongoDBFortunes(request, response);
  47. break;
  48. case "/mongodbupdates":
  49. responseString = MongoDBUpdates(request, response);
  50. break;
  51. default:
  52. responseString = NotFound(response);
  53. break;
  54. }
  55. WriteResponse(response, responseString);
  56. }
  57. finally
  58. {
  59. response.Close();
  60. }
  61. }
  62. private static void WriteResponse(HttpListenerResponse response, String responseString)
  63. {
  64. response.ContentType = response.ContentType + "; charset=utf-8";
  65. byte[] buffer = System.Text.Encoding.UTF8.GetBytes(responseString);
  66. response.ContentLength64 = buffer.Length;
  67. try
  68. {
  69. response.OutputStream.Write(buffer, 0, buffer.Length);
  70. }
  71. catch (Win32Exception)
  72. {
  73. // Ignore I/O errors
  74. }
  75. }
  76. private static void Threads()
  77. {
  78. // To improve CPU utilization, increase the number of threads that the .NET thread pool expands by when
  79. // a burst of requests come in. We could do this by editing machine.config/system.web/processModel/minWorkerThreads,
  80. // but that seems too global a change, so we do it in code for just our AppPool. More info:
  81. //
  82. // http://support.microsoft.com/kb/821268
  83. // http://blogs.msdn.com/b/tmarq/archive/2007/07/21/asp-net-thread-usage-on-iis-7-0-and-6-0.aspx
  84. // http://blogs.msdn.com/b/perfworld/archive/2010/01/13/how-can-i-improve-the-performance-of-asp-net-by-adjusting-the-clr-thread-throttling-properties.aspx
  85. int newMinWorkerThreads = Convert.ToInt32(ConfigurationManager.AppSettings["minWorkerThreadsPerLogicalProcessor"]);
  86. if (newMinWorkerThreads > 0)
  87. {
  88. int minWorkerThreads, minCompletionPortThreads;
  89. ThreadPool.GetMinThreads(out minWorkerThreads, out minCompletionPortThreads);
  90. ThreadPool.SetMinThreads(Environment.ProcessorCount * newMinWorkerThreads, minCompletionPortThreads);
  91. }
  92. }
  93. static void Main(string[] args)
  94. {
  95. Threads();
  96. System.Net.HttpListener listener = new System.Net.HttpListener();
  97. // This doesn't seem to ignore all write exceptions, so in WriteResponse(), we still have a catch block.
  98. listener.IgnoreWriteExceptions = true;
  99. listener.Prefixes.Add("http://*:8080/");
  100. try
  101. {
  102. listener.Start();
  103. for (;;)
  104. {
  105. HttpListenerContext context = null;
  106. try
  107. {
  108. context = listener.GetContext();
  109. ThreadPool.QueueUserWorkItem(new WaitCallback(RequestCallback), context);
  110. context = null; // ownership has been transferred to RequestCallback
  111. }
  112. catch (HttpListenerException ex)
  113. {
  114. Console.WriteLine(ex.Message);
  115. }
  116. finally
  117. {
  118. if (context != null)
  119. context.Response.Close();
  120. }
  121. }
  122. }
  123. catch (HttpListenerException ex)
  124. {
  125. Console.WriteLine(ex.Message);
  126. }
  127. finally
  128. {
  129. // Stop listening for requests.
  130. listener.Close();
  131. Console.WriteLine("Done Listening.");
  132. }
  133. }
  134. public static DbConnection CreateConnection(HttpListenerRequest request)
  135. {
  136. string providerName = request.QueryString["provider"];
  137. if (providerName == null)
  138. {
  139. throw new ApplicationException("Missing provider querystring argument");
  140. }
  141. ConnectionStringSettings connectionSettings = ConfigurationManager.ConnectionStrings[providerName];
  142. DbProviderFactory factory = DbProviderFactories.GetFactory(connectionSettings.ProviderName);
  143. DbConnection connection = factory.CreateConnection();
  144. connection.ConnectionString = connectionSettings.ConnectionString;
  145. return connection;
  146. }
  147. public static int GetQueries(HttpListenerRequest request)
  148. {
  149. int queries = 1;
  150. string queriesString = request.QueryString["queries"];
  151. if (queriesString != null)
  152. {
  153. // If this fails to parse, queries will be set to zero.
  154. int.TryParse(queriesString, out queries);
  155. queries = Math.Max(1, Math.Min(500, queries));
  156. }
  157. return queries;
  158. }
  159. private static string NotFound(HttpListenerResponse response)
  160. {
  161. response.StatusCode = (int)HttpStatusCode.NotFound;
  162. response.ContentType = "text/plain";
  163. return "not found";
  164. }
  165. private static string Plaintext(HttpListenerResponse response)
  166. {
  167. response.ContentType = "text/plain";
  168. return "Hello, World!";
  169. }
  170. private static string Json(HttpListenerResponse response)
  171. {
  172. response.ContentType = "application/json";
  173. return new JavaScriptSerializer().Serialize(new { message = "Hello, World!" });
  174. }
  175. private static string Db(HttpListenerRequest request, HttpListenerResponse response)
  176. {
  177. Random random = new Random();
  178. int queries = GetQueries(request);
  179. List<World> worlds = new List<World>(queries);
  180. using (DbConnection connection = CreateConnection(request))
  181. {
  182. connection.Open();
  183. using (DbCommand command = connection.CreateCommand())
  184. {
  185. command.CommandText = "SELECT * FROM World WHERE id = @ID";
  186. DbParameter parameter = command.CreateParameter();
  187. parameter.ParameterName = "@ID";
  188. command.Parameters.Add(parameter);
  189. for (int i = 0; i < worlds.Capacity; i++)
  190. {
  191. int randomID = random.Next(0, 10000) + 1;
  192. parameter.Value = randomID;
  193. // Don't use CommandBehavior.SingleRow because that will make the MySql provider
  194. // send two extra commands to limit the result to one row.
  195. using (DbDataReader reader = command.ExecuteReader())
  196. {
  197. if (reader.Read())
  198. {
  199. worlds.Add(new World
  200. {
  201. id = reader.GetInt32(0),
  202. randomNumber = reader.GetInt32(1)
  203. });
  204. }
  205. }
  206. }
  207. }
  208. }
  209. response.ContentType = "application/json";
  210. return new JavaScriptSerializer().Serialize(
  211. worlds.Count > 1 ? (Object)worlds : (Object)worlds[0]);
  212. }
  213. private static string Fortunes(HttpListenerRequest request, HttpListenerResponse response)
  214. {
  215. List<Fortune> fortunes = new List<Fortune>();
  216. using (DbConnection connection = CreateConnection(request))
  217. {
  218. connection.Open();
  219. using (DbCommand command = connection.CreateCommand())
  220. {
  221. command.CommandText = "SELECT * FROM Fortune";
  222. using (DbDataReader reader = command.ExecuteReader(CommandBehavior.SequentialAccess))
  223. {
  224. while (reader.Read())
  225. {
  226. fortunes.Add(new Fortune
  227. {
  228. ID = reader.GetInt32(0),
  229. Message = reader.GetString(1)
  230. });
  231. }
  232. }
  233. }
  234. }
  235. fortunes.Add(new Fortune { ID = 0, Message = "Additional fortune added at request time." });
  236. fortunes.Sort();
  237. response.ContentType = "text/html";
  238. var template = new Fortunes { Model = fortunes };
  239. return template.TransformText();
  240. }
  241. private static string Updates(HttpListenerRequest request, HttpListenerResponse response)
  242. {
  243. Random random = new Random();
  244. List<World> worlds = new List<World>(GetQueries(request));
  245. using (DbConnection connection = CreateConnection(request))
  246. {
  247. connection.Open();
  248. using (DbCommand selectCommand = connection.CreateCommand(),
  249. updateCommand = connection.CreateCommand())
  250. {
  251. selectCommand.CommandText = "SELECT * FROM World WHERE id = @ID";
  252. updateCommand.CommandText = "UPDATE World SET randomNumber = @Number WHERE id = @ID";
  253. for (int i = 0; i < worlds.Capacity; i++)
  254. {
  255. int randomID = random.Next(0, 10000) + 1;
  256. int randomNumber = random.Next(0, 10000) + 1;
  257. DbParameter idParameter = selectCommand.CreateParameter();
  258. idParameter.ParameterName = "@ID";
  259. idParameter.Value = randomID;
  260. selectCommand.Parameters.Clear();
  261. selectCommand.Parameters.Add(idParameter);
  262. World world = null;
  263. // Don't use CommandBehavior.SingleRow because that will make the MySql provider
  264. // send two extra commands to limit the result to one row.
  265. using (DbDataReader reader = selectCommand.ExecuteReader())
  266. {
  267. if (reader.Read())
  268. {
  269. world = new World
  270. {
  271. id = reader.GetInt32(0),
  272. randomNumber = reader.GetInt32(1)
  273. };
  274. }
  275. }
  276. DbParameter idUpdateParameter = updateCommand.CreateParameter();
  277. idUpdateParameter.ParameterName = "@ID";
  278. idUpdateParameter.Value = randomID;
  279. DbParameter numberParameter = updateCommand.CreateParameter();
  280. numberParameter.ParameterName = "@Number";
  281. numberParameter.Value = randomNumber;
  282. updateCommand.Parameters.Clear();
  283. updateCommand.Parameters.Add(idUpdateParameter);
  284. updateCommand.Parameters.Add(numberParameter);
  285. updateCommand.ExecuteNonQuery();
  286. world.randomNumber = randomNumber;
  287. worlds.Add(world);
  288. }
  289. }
  290. }
  291. response.ContentType = "application/json";
  292. return new JavaScriptSerializer().Serialize(
  293. worlds.Count > 1 ? (Object)worlds : (Object)worlds[0]);
  294. }
  295. private static string MongoDBDb(HttpListenerRequest request, HttpListenerResponse response)
  296. {
  297. Random random = new Random();
  298. int queries = GetQueries(request);
  299. List<World> worlds = new List<World>(queries);
  300. Benchmarks.AspNet.Models.MongoDB db = new Benchmarks.AspNet.Models.MongoDB("MongoDB");
  301. for (int i = 0; i < worlds.Capacity; i++)
  302. {
  303. int randomID = random.Next(0, 10000) + 1;
  304. worlds.Add(db.Worlds.FindOne(Query<World>.EQ(w => w.id, randomID)));
  305. }
  306. response.ContentType = "application/json";
  307. return new JavaScriptSerializer().Serialize(
  308. worlds.Count > 1 ? (Object)worlds : (Object)worlds[0]);
  309. }
  310. private static string MongoDBFortunes(HttpListenerRequest request, HttpListenerResponse response)
  311. {
  312. Benchmarks.AspNet.Models.MongoDB db = new Benchmarks.AspNet.Models.MongoDB("MongoDB");
  313. List<Fortune> fortunes = db.Fortunes.FindAll().ToList();
  314. fortunes.Add(new Fortune { ID = 0, Message = "Additional fortune added at request time." });
  315. fortunes.Sort();
  316. response.ContentType = "text/html";
  317. var template = new Fortunes { Model = fortunes };
  318. return template.TransformText();
  319. }
  320. private static string MongoDBUpdates(HttpListenerRequest request, HttpListenerResponse response)
  321. {
  322. Random random = new Random();
  323. Benchmarks.AspNet.Models.MongoDB db = new Benchmarks.AspNet.Models.MongoDB("MongoDB");
  324. int queries = GetQueries(request);
  325. List<World> worlds = new List<World>(queries);
  326. for (int i = 0; i < worlds.Capacity; i++)
  327. {
  328. int randomID = random.Next(0, 10000) + 1;
  329. int randomNumber = random.Next(0, 10000) + 1;
  330. World world = db.Worlds.FindOne(Query<World>.EQ(w => w.id, randomID));
  331. world.randomNumber = randomNumber;
  332. worlds.Add(world);
  333. db.Worlds.Save(world);
  334. }
  335. response.ContentType = "application/json";
  336. return new JavaScriptSerializer().Serialize(
  337. worlds.Count > 1 ? (Object)worlds : (Object)worlds[0]);
  338. }
  339. }
  340. }