Sisk – Lightweight .NET Web Framework
sisk-framework.org
sisk-framework.org
Once you learn that everything that happens in the most complicated corners of web apps is ultimately dancing on top of HttpContext, you can dramatically simplify your life. Even things like web sockets and 3rd party auth are just a handful of lines on top.
These abstractions are not scary to use. The Microsoft documentation is really poor with regard to guiding developers towards these simpler approaches. It's fairly obvious that they want you deep in the Azure complexity matrix. The only exception I have found to this perception is noted below [0].
Mastery here means you can avoid pulling down vendors' interpretations of how your entire application should be written. You can write whatever middleware per their specifications and make it fit your patterns perfectly. I recall what it was like allowing my auth vendor's ham-fisted libraries drive much of how my web app and DI had to be structured.
[0]: https://learn.microsoft.com/en-us/aspnet/core/fundamentals/m...
The modern ASP.NET focus on repositories, middleware, service factories, and moving context around in data transfer objects lends itself to a different architecture and arrangement of code that more closely mirrors the stateless needs of the web, where not only are the front end and back end separate applications, but the client is entirely untrustworthy. That's very different than the expectations when OOP was originally envisioned.
Could you be more specific? As I’m aware, all you need to look for is where DI is being messed with.
I discovered two things :
- 1 ) I was wrong : it’s a very good and really well designed framework. I have a huge respect for the engineers behind it. It’s easy to work on all the layers you need. It’s also easy to ignore every layers you don’t need.
- 2) The documentation is awful. And I don’t mean the inline documentation : every method of the framework is very well documented in code. However the online docs, the one which should guide you and make the pieces click together is a real pain to navigate, is poorly written, and that’s when it exists.
Fortunately I also discovered that there is a very welcoming community in the ecosystem. Mostly composed of people who discovered at their $job that the tech was indeed cool but that Microsoft is shitty when it comes to be friendly.
So for the last 6 months I've been using golang. I really like it. Fast, simple, native.... but it's like using tonka toys compared to .net core. It's slower for webservices and everything is just more work. Yes being a wizard with .net and only decently productive in golang is a difference but that's not it. It's just nowhere near as mature as a language or ecosystem.
The only last, and most critical piece of the puzzle that's missing is laser focus on simplicity of implementation. It is improving, but if you use generic composition and very terse writing style ala Rust traits and impl., pass data by `ref T`, occasionally manually manage the memory, you soon enough have people up in arms about this kind of unorthodox approach in the sphere of line of business applications (even when it tolerates that in other lower-level or just more limited languages, everything has to be abstracted away and very wordy).
I'd like there to be some kind of profile / compiler level support so we can do full manual memory management (stealing from Rust as you say) but also to be easily able to forbid that within our own projects. I don't want my frontend developers doing silly things :-)
C#/.NET supports unsafe mode, which can be configured on a per-csproj basis.
https://learn.microsoft.com/en-us/dotnet/csharp/language-ref...
https://github.com/sisk-http/core/blob/main/src/Http/HttpSer...
HttpListener, while not deprecated, is frozen and not taking new features. For example, it does not support HTTP 2. It might not be as high performance as Kestrel (the default web server in ASP.NET Core).
https://github.com/dotnet/runtime/issues/63941#issuecomment-...
So the authors of the framework might want to consider longer term whether layering onto of Kestrel or some other HTTP server implementation may make more sense.
Or did they port it during NET/NET Core. As Kestrel has been the recommendation from NET Core 1.0 onwards.
https://github.com/dotnet/runtime/blob/ac663e44b907618c631ed...
It’s “ok”. You can’t do things like re use contexts but it’s perfectly fine for throwing small backends together when you don’t want to force users to install asp runtime. I did a minimal web api around it to make life easier going between it and kestrel.
If kestrel was nugettable without the asp runtime it would be a dream but it requires a lot of that aspcore infra so it is what it is.
But I read the docs. Sisk is supposed to be simple. But the code samples are nearly the same as ASP.NET minimal APIs. Can you clarify why Sisk is better than out of the box .NET?
Asp.net is very overbearing (even using minimal APIs) when you want to use other Microsoft utilities like DI, logging or config since it wants to be the main entry of the application.
Never found an easy way to use the host feature with a optional web application where they both shared the DI. Note that this is more a problem with the generic host than asp.net itself.
We're using ASP.NET Core Minimal APIS for both API and UI (if configured to run in that mode )
var builder = WebApplication.CreateBuilder([]);
builder.WebHost.UseUrls($"http://127.0.0.1:0");
builder.Services.AddSingleton(...);
var app = builder.Build();
app.Map...
await app.StartAsync(cancellationToken);
We run multiple of these at a time and have no problems. public static ConfigurationExtensions{
public static AddMyApp(this IServiceCollection services){
services.AddScoped<IFooService,FooService>();
}
}
//In console app:
var consoleBuilder = Host.CreateApplicationBuilder(args);
consoleBuilder.Services.AddMyApp();
...
//pseudocode - in real world you'd put this in another class or method called by the commandline code:
if(webHostCommandLineSwitch){
var webBuilder = WebApplication.CreateBuilder(args);
webBuilder.Services.AddMyApp();
...
}The reason for this is the first-party solution has to be both fast and support a lot of different features, and be compatible with all the ways to process request input/output, parameter bindings, rich telemetry (particularly OTLP) integration and so on and so forth.
Which is why a lightweight UTF-8-based zero-copy pipeline (via span/memory) that tries to reduce call stack depth and context switching, and that moves as much of the feature composition to compilation time as possible could be indispensable.
Such server could be built on top of either raw `Socket` and `SslStream` (of which a toy attempt, really, can be thrown together in under an hour) and its async engine or via a custom one - we have all the tools to build an io-uring network stack with async integration.
.NET's compiler is way better than any other GC-based platform except OpenJDK/Graal but JVM has few features to optimize this further and bridge the gap with C++ and Rust based applications, unlike .NET.
There is a lot of raw runtime performance left on the table and an alternate implementation that gets back to the top of the chart on Techempower would be a welcome change :)
After all, this already has been proven to be possible by Garnet: https://microsoft.github.io/garnet/docs
The higher level ASP.NET Core stack is also quite efficient and optimized.
BUT: as soon as you gove above the basic middleware pipeline its tends to get bloated and slow. ASP.NET COre MVC is particulary bad.
System.Text.Json is also quite nice, and often is allocation free.
We bascially just us the middleware pipeline and nothing else, and can get millions of requests per second on basic hardware.
As you noted, the problems happen later in the handling pipeline. There are also choices that ASP.NET Core has to make as an extremely general-purpose web framework like that.
System.Text.Json is pretty good for a default serializer, but it's far from how fast json serialization can be done in .NET too.
Both of these end up reallocating and transcoding data, and STJ also might take a hit if deserialized classes/structs have converters in them as it can disable a fast-path.
My idea here is that a new implementation can benefit from the hindsight of strength/weaknesses of asp.net core and how its design influences the implementation choices of the user (and whether they end up being optimal or not).
It would also not be constrained by backwards compatibility or certain organizational restrictions - for example you do not need to explicitly use out-of-box QuicConnection and QuicStream that rely on mquic and opt to statically link to parts of the stack implemented in Rust, or bring over more logic to C# instead. There is a certain conventional way you are expected to approach this in dotnet/* repositories, and it might be a bit restrictive in achieving end goals of such a design.
It would be able to approach the problem as a library that expects a more advanced user, closer to how e.g. Axum or back in the day actix-web did (and by advanced I mean effective usage of (ref) structs and generics, not that it would need boilerplate).
p.s.: does this organization with millions of RPS have a name?
What are the advantages compared to e.g ASP.NET Core Minimal Api ?
(or for example FastEndpoints) ?
> Robustness hidden in it's simplicity
No apostrophe in its.
now everything follows the express pattern because it's explicit and simple.