辅导案例-COMPSCI 711 1-Assignment 2
COMPSCI 711 1 radu/2020 Assignment 2 Assignment 1 required the design and develop a faithful sequential emulation of the Echo algorithm, extended to determine the size of the network. Assignment 2 requires two non-sequential versions of Assignment 1’s solution: 1. A multi-threaded implementation using channels, as available in System.Threading.Channels. 2. A multi-process implementation based on HTTP services, as available via ASP.NET Core - additional layers such as WebAPI (REST), Carter, gRPC are acceptable (but please ask). As before, there is a central message dispatcher called Arcs, which can also act as virtual node 0. Arcs read the input config from stdin and traces messages on standout, using the same conventions, constraints, specs. You can your own sequential solution for A#1, or the model solution, or a combination of both (your choice). To minimize the changes, use the following blueprint, where thin lines represent procedure calls, thick line channels or HTTP request/responses, and dotted lines an optional initialisation component: A good implementation will localise most of the communication specifics in the proxy classes, and then the main Arcs and Node classes will be virtually the same. In the Channels version, all components are included in the same source file. In the HTTP version, the arcs process is described in one file, and the node process in another. Each node will start its own HTTP service, listening at port 8080+node number. COMPSCI 711 2 radu/2020 Development language and platform C#, for .NET Core 3.1.X (which is free, open source, cross-platform, and LTS). Marks: 7 course marks Part I: 3 marks Part II: 4 marks, plus 1 bonus mark for each of the other two alternatives Submission For part 1 (Channels), submit to automarker: https://www.automarker.cs.auckland.ac.nz/student.php Submit just one single C# source file, named as UPI.cs. For part 2 (HTTP), submit to ADB: https://adb.auckland.ac.nz/ Submit two C# source files, named as UPI-arcs.cs and UPI-node.cs. For both parts, replace UPI by your actual university ID, e.g. jbon007. Ensure that all programs terminate properly, otherwise you may get timeout errors. Readings An Introduction to System.Threading.Channels https://devblogs.microsoft.com/dotnet/an-introduction-to-system-threading-channels/ System.Threading.Channels Namespace https://docs.microsoft.com/en- us/dotnet/api/system.threading.channels?view=netcore-3.1 Create web APIs with ASP.NET Core https://docs.microsoft.com/en-us/aspnet/core/web-api/?view=aspnetcore-3.1 Carter https://github.com/CarterCommunity/Carter gRPC https://docs.microsoft.com/en-us/aspnet/core/grpc/?view=aspnetcore-3.1 COMPSCI 711 3 radu/2020 gRPC Appendix A#2.2 FAQ - Problems and solutions Problems o Multiple servers starting on the same code base -- concurrent updates o Process API ok for console and UI apps, but less for server apps o Process API redirection complicated o Servers have unpredictable startup delays -- ensure they are listening o Servers may accidentally hang -- self stop, outside (Arcs) stop Solutions o Projects structure: Arcs contains a Node subfolder, for the node services o Arcs write out starting batch script -- .bat, .sh o Servers start with delays -- 1 sec? o Arcs calls this batch via Process.Start -- does not directly start individual node servers o Before starting, Arcs pings each url with a new AYT (AreYouThere) message o After ending its message dispatching loop (normally or on exceptions), Arcs send out STOP messages o Each node stops itself after sending out the Return message Demo o At the lecture / tutorial COMPSCI 711 4 radu/2020 Protobuf file node.proto – MANDATORY! syntax = "proto3"; option csharp_namespace = "Echo"; package echosize; // The service definition. service Node { rpc Process (MsgArray) returns (MsgArray); } // The request/response message. message MsgArray { message Msg { int32 time = 1; int32 code = 2; int32 from = 3; int32 to = 4; int32 tok = 5; int32 pay = 6; } repeated Msg Msgs = 1; } COMPSCI 711 5 radu/2020 Project layout Arcs folder Bin folder Node folder – for Node project Bin folder Obj folder Protos folder – with mandatory node.proto files Obj folder Protos folder – with mandatory node.proto files Sample startup .bat script generated by Arcs, on Windows Assuming test case #1, with four nodes, 1, 2, 3, 4: start "node_1" cmd /k .\Node\bin\Debug\netcoreapp3.1\a2-model-node-grpc.exe 1 2 3 4 timeout 1 start "node_2" cmd /k .\Node\bin\Debug\netcoreapp3.1\a2-model-node-grpc.exe 2 1 3 timeout 1 start "node_4" cmd /k .\Node\bin\Debug\netcoreapp3.1\a2-model-node-grpc.exe 4 3 1 timeout 1 start "node_3" cmd /k .\Node\bin\Debug\netcoreapp3.1\a2-model-node-grpc.exe 3 1 2 4 timeout 1 Sorry for the four wrapped lines. COMPSCI 711 6 radu/2020 Testing .bat script for Windows @set PROMPT=$G dotnet build a2-model-arcs-grpc.csproj @pause @set EchoNodePath=dotnet run -p .\Node\a2-model-node-grpc.csproj -- @set EchoNodePath= .\Node\bin\Debug\netcoreapp3.1\a2-model-node-grpc.exe @set EchoNodePath= dotnet .\Node\bin\Debug\netcoreapp3.1\a2-model-node-grpc.dll @echo EchoNodePath=%EchoNodePath% @for /l %%X in (1, 1, 10) do @( rem dotnet run -p a2-model-arcs-grpc.csproj -- %EchoNodePath% < a2-inp%%X.txt > a2-outp%%X-test.txt rem .\bin\Debug\netcoreapp3.1\a2-model-arcs-grpc.exe %EchoNodePath% < a2-inp%%X.txt > a2-outp%%X-test.txt dotnet .\bin\Debug\netcoreapp3.1\a2-model-arcs-grpc.dll %EchoNodePath% < a2-inp%%X.txt > a2-outp%%X-test.txt rem type a2-outp%%X.txt fc a2-outp%%X.txt a2-outp%%X-test.txt pause ) pause Sorry for the four wrapped lines. To minimise delays and reduce the conflicts likelihood, prefer the path that points to the exe or dotnet dll (not dotnet run-ning the project, which implies a build). Please note that Arcs takes one command line argument = the path to run the Node project (no spaces assumed). COMPSCI 711 7 radu/2020 Testing .sh script for *nix and *nix enabled Windows – tested on Windows only! #!/bin/bash set +v function pause () { read -n1 -p "Press any key to continue . . ." echo } set -v dotnet build a2-model-arcs-grpc.csproj set +v pause EchoNodePath="dotnet run -p .\Node\a2-model-node-grpc.csproj --" EchoNodePath= "dotnet .\Node\bin\Debug\netcoreapp3.1\a2-model-node-grpc.dll" echo EchoNodePath=$EchoNodePath #for ((X=1; X<=10; X++)) for X in {1..10} do #dotnet run -p a2-model-arcs-grpc.csproj -- $EchoNodePath < a2-inp$X.txt > a2-outp$X-test.txt dotnet ./bin/Debug/netcoreapp3.1/a2-model-arcs-grpc.dll $EchoNodePath < a2-inp$X.txt > a2-outp$X-test.txt #cat a2-outp$X.txt diff --strip-trailing-cr a2-outp$X.txt a2-outp$X-test.txt echo a2 case $X : $? pause done pause Sorry for the three wrapped lines. COMPSCI 711 8 radu/2020 Singletons in ASP.NET public void ConfigureServices (IServiceCollection services) { … services.AddSingleton (); // !!! } New message tokens: public readonly static int TokAYT = 0; // ayt public readonly static int TokSTOP = 3; // stop Arcs stops all nodes by broadcasting STOP messages In proxy: public async Task STOP () { try { Console.Error.WriteLine ($"... STOP NODE={Self}"); await Process (new[] {new Message (0, 1, 0, Self, Message.TokSTOP, 0)}); } catch (Exception ex) { _ = ex; Console.Error.WriteLine ($"*** STOP NODE={Self} UNREACHABLE"); } } After the dispatching loop (assuming that nn.Value is a proxy): foreach (var nn in nodes) { await nn.Value.STOP (); } COMPSCI 711 9 radu/2020 Node stops itself, if completed or required Call Stop when you want to stop, e.g. just before sending the return, or after receiving a stop message -- but w/o awaiting its completion! public async Task Stop () { await Task.Delay (100); _logger.Log (LogLevel.Information, $"... STOP"); Environment.Exit (0); } Arcs tests that a node service has really started In proxy, call AYT 3 times, at 1 sec intervals: public async Task AYT () { var count = 0; while (true) { try { count += 1; Console.Error.WriteLine ($"... AYT NODE={Self} ({count})"); await Process (new[] {new Message (0, 1, 0, Self, Message.TokAYT, 0)}); break; } catch (Exception ex) { _ = ex; Console.Error.WriteLine ($"*** AYT NODE={Self} ({count})"); await Task.Delay (1000); } if (count == 3) { throw new Exception ($"AYT {Self}"); } } }