diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 0000000..825734e --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,10 @@ +{ + "name": "ThreadStarvation Codespace", + "image": "mcr.microsoft.com/devcontainers/dotnet:9.0", + "features": { + "ghcr.io/devcontainers/features/dotnet:1": { + "version": "9.0" + } + }, + "postCreateCommand": "dotnet --version" +} diff --git a/README.md b/README.md index 9858a53..ec90184 100644 --- a/README.md +++ b/README.md @@ -4,11 +4,20 @@ This repository comes from a good presentation of Damian Edwards and David Fowle Simple story: -- One project is a little Hello world web application with 4 methods - - Good old full synchronous execution, works but not very efficient - - Modern app with legacy services: async-over-sync, not best but works - - Old app code using modern apis: sync-over-async, shows dead IIS (☠) because of thread starvation - - Full async, shows unlimited web scale +## Threading + +A little Hello world backend/web/app/server/ with 4 endpoints + +- Good old full synchronous execution down the line, works but not very efficient **sync-over-sync** +- Modern app with legacy services: Not best but works at least not worse than before **async-over-sync** +- Old code using modern apis runs into dead locks in backend ☠ (IIS , kestrel, ngix) because of thread starvation :-( **sync-over-async (☠)** +- Full async, unlimited web scale **async-over-async** (:-)) + +## Requestor + +Makes as may requests against an enpoint as you like! +Just press key arrow up/down for parallel request threads. +You can even start multiple requestor-clients in different terminals against different endpoints. [Source code] https://github.com/davidfowl/NdcLondon2018 @@ -16,9 +25,9 @@ https://github.com/davidfowl/NdcLondon2018 [Youtube] https://www.youtube.com/watch?v=RYI0DHoIVaA -## Howto use it +## Howto to run it -- Open the repository folder in VS Code +- Open the repository folder in VS Code (or GitHub Codespace) - Open two terminal windows (CTRL+`) - Navigate in the left to ~/Requestor - Navigate in the right to ~/Threading @@ -28,7 +37,7 @@ On the left side you can `dotnet run ` where method name is one of |dotnet run \ | comment| |---------|---| -|hello | good old sync | +|hello | good old sync-over-sync | |hello-async-over-sync | modern controller over old services | |hello-sync-over-async | ☠ can kill the server, must close terminal | |hello-async | scales!| diff --git a/Requestor/Program.cs b/Requestor/Program.cs index ad25887..cb2354f 100644 --- a/Requestor/Program.cs +++ b/Requestor/Program.cs @@ -5,6 +5,7 @@ using System.Net.Http; using System.Threading; + namespace Requestor { class Program @@ -13,6 +14,19 @@ class Program static string path = "hello"; static List requestThreads = new List(); + // Maps long response strings to short codes for console output + static string ShortenResponse(string input) + { + return input switch + { + "sync-over-sync" => "sos", + "async-over-sync" => "aos", + "sync-over-async (☠)" => "soa ☠", + "async-over-async" => "aoa*", + _ => input + }; + } + static void Main(string[] args) { if (args.Length > 0 && !string.IsNullOrEmpty(args[0])) path = args[0]; @@ -20,7 +34,7 @@ static void Main(string[] args) Console.WriteLine("--------------------------------"); Console.WriteLine($"Hammering endpoint: /{path}"); Console.WriteLine("Press ESC to stop, up- down- keys to change request count"); - http.Timeout = TimeSpan.FromMilliseconds(2700); + http.Timeout = TimeSpan.FromMilliseconds(3000); while (!(Console.KeyAvailable && Console.ReadKey(true).Key == ConsoleKey.Escape)) { @@ -60,7 +74,8 @@ static void RequestAndLogLoopAsync() var sw = Stopwatch.StartNew(); var res = http.GetAsync($"http://localhost:5000/{path}").Result; var cnt = res.Content.ReadAsStringAsync().Result; - Console.Write($"{sw.ElapsedMilliseconds}, "); + + Console.Write($"{sw.ElapsedMilliseconds}-{ShortenResponse(cnt)}, "); } } catch (ThreadInterruptedException) diff --git a/Requestor/Requestor.csproj b/Requestor/Requestor.csproj index 120e38c..92e46dd 100644 --- a/Requestor/Requestor.csproj +++ b/Requestor/Requestor.csproj @@ -2,7 +2,7 @@ Exe - net7.0 + net9.0 diff --git a/Threading/Controllers/HomeController.cs b/Threading/Controllers/HomeController.cs index 7588941..8a32f34 100644 --- a/Threading/Controllers/HomeController.cs +++ b/Threading/Controllers/HomeController.cs @@ -10,28 +10,28 @@ public class HomeController : Controller public string Hello() { Thread.Sleep(2000); // DB query, REST call, - return "Hello World"; + return "sync-over-sync"; } [HttpGet("/hello-async-over-sync")] // New project using old services public async Task HelloAsyncOverSync() { await Task.Run(() => Thread.Sleep(2000)); - return "Hello World"; + return "async-over-sync"; } [HttpGet("/hello-sync-over-async")] // Old code using new APIs public string HelloSyncOverAsync() { Task.Delay(2000).Wait(); - return "Hello World"; + return "sync-over-async (☠)"; } [HttpGet("/hello-async")] // All new public async Task HelloAsync() { await Task.Delay(2000); - return "Hello World"; + return "async-over-async"; } } } diff --git a/Threading/Startup.cs b/Threading/Startup.cs index c1be74e..816a2ab 100644 --- a/Threading/Startup.cs +++ b/Threading/Startup.cs @@ -2,6 +2,7 @@ using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; namespace Threading { @@ -12,7 +13,7 @@ public void ConfigureServices(IServiceCollection services) services.AddMvc(opt => opt.EnableEndpointRouting = false); } - public void Configure(IApplicationBuilder app, IHostingEnvironment env) + public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { if (env.IsDevelopment()) { diff --git a/Threading/Threading.csproj b/Threading/Threading.csproj index 1541571..8e2a1f9 100644 --- a/Threading/Threading.csproj +++ b/Threading/Threading.csproj @@ -1,15 +1,13 @@ - net7.0 + net9.0 - - - + diff --git a/packages-microsoft-prod.deb b/packages-microsoft-prod.deb new file mode 100644 index 0000000..2ba9abc Binary files /dev/null and b/packages-microsoft-prod.deb differ