Hacker Newsnew | past | comments | ask | show | jobs | submitlogin
Sisk – Lightweight .NET Web Framework (sisk-framework.org)
70 points by mrcsharp on Aug 31, 2024 | hide | past | favorite | 35 comments


As an alternative to using additional framework layers, I'd recommend spending an afternoon familiarizing yourself with AspNetCore middleware - and HttpContext in particular. Getting at the raw primitives can allow for you to throw away a lot of confusing boilerplate.

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...


We have some APIs written with.NET Core and ASP.NET. They work pretty well but the number of dependency injections, overrides and options are starting to kill me. Basically you get the spaghetti code version of OO. Relevant behavior is spread out over many files and I find it really hard to figure out what's going on if I haven't written the code myself.


That's because it's not really OOP. If you're struggling with using the latest .NET conventions, then perhaps you are still thinking in terms of deep inheritance, polymorphism, constructors, and rich object models.

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.


I don’t really care if it’s OOP by some definition or not. My point was that there are a lot of places where one can change the behavior of the app and it’s not easy to figure this out when you look at a codebase you don’t know yet.


> My point was that there are a lot of places where one can change the behavior of the app

Could you be more specific? As I’m aware, all you need to look for is where DI is being messed with.


Yeah. I’ve been trained to aspnetcore in my previous job. Saying I wasn’t hyped is an understatement. Then I had to work with it for more than a year.

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.


I've been using .Net since 2002, as my main framework since 2007 and have always stayed on the edge (as part of alt.net, the part of the community who pushed .net core and basically the entire .net team nowadays).

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.


And it is moving so much faster in runtime/compiler improvements. Implementation wise, it outdoes the Go at things Go is renown for.

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).


Exactly, if I'd have started this project then I would have used .Net. We don't need to support native / cgo. Without that C# is better in every way.

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 :-)


> so we can do full manual memory management

C#/.NET supports unsafe mode, which can be configured on a per-csproj basis.

https://learn.microsoft.com/en-us/dotnet/csharp/language-ref...


Yeah, it's so confusing because it talks about ASP. If Microsoft just named things after what they are, instead of branding them, it would be much better.


I appears that this is using HttpListener to implement the HTTP protocol:

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.


Isn't HttpListener still windows only ? I Remember the times we used it and it relied on http.sys on windows...

Or did they port it during NET/NET Core. As Kestrel has been the recommendation from NET Core 1.0 onwards.


It appears to use Socket (which is POSIX socket's send/recv and epoll/kqueue based) and SslStream. Can't say much about HttpListener itself, but the first two are used by Kestrel (and everything else). There is Http.sys integration for Windows though.

https://github.com/dotnet/runtime/blob/ac663e44b907618c631ed...


Httplistener has two implementations. Managed (cross platform) and http.sys (windows only).

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.


I’d like to support this, truly I do—I’m a .net fan.

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?


.NET deserves a good, separate non-ASP.NET Core+Kestrel web server.

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


Last time i looked Kestrel already uses most of the techniques above ( sans an IOUring backend for Socket ) Almost all allocations are pooled, and zero copy as well. Header parsing is even done with System.Runtime.Intrinsics using SIMD where possible.

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.


This is absolutely true, there is a lot of performance work invested at all layers or features that benefit from improvements in runtime itself.

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?


This project might have helped me when I needed to implement a console app that might or not start a web server.

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.


It is actually possible, to seperate those things, but it's tricky. Our current product can run in several modes, one with a web ui and api and one without. If running without there is no trace of the ASP.NET Core Pipeline ( and Kestrel is also not running )

We're using ASP.NET Core Minimal APIS for both API and UI (if configured to run in that mode )


Yeah we have a similar product, where we spin up multiple web servers. Code looks something like this:

        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.


If I understand the problem, just move all your DI registrations to a shared extension method:

  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();
     ...
  }


500kb httpserver with no ASPNETCORE dependency? Sounds like less bloat which is nice for a lot of things.


Dammit you were first to ask that question :-) I also don't see the difference.


I remember way back (10? years) a library called Suave, a from scratch Web Server that was fun to work with (although never very high performance). I believe the original developer eventually kind of abandoned it because he felt he was treated poorly by the community (not sure, memory of this is a bit hazy). It's a real shame honestly, because it feels like all the novel F# stuff just got abandoned over time. This has a similar feel (not the code style, but just the intent). Dotnet needs a lot more goofy/unique libraries in general. Otherwise Microsoft pushes a monoculture and nothing good ever happens.


Dotnet is crying out for a lightweight front end for desktop and mobile.... But not a new backend??? That's already sooo good. Strange choice to add to the ecosystem.


Avalonia


What are the use cases for going with this framework over something like ASP.NET Core Minimal APIs? https://learn.microsoft.com/en-us/aspnet/core/fundamentals/m...


ASP.NET Core Minimal Api is already lightweight


Reading the docs and samples...

What are the advantages compared to e.g ASP.NET Core Minimal Api ?

(or for example FastEndpoints) ?


FastEndpoints is nice but they don't seem to plan on supporting NativeAOT anytime soon https://github.com/FastEndpoints/FastEndpoints/issues/565#is....


Nit:

> Robustness hidden in it's simplicity

No apostrophe in its.


Anyone know the story behind the name? Couldn't find anything in the docs.


people like to shit on node.js / express.js but it changed the game on what lightweight server-side frameworks could be.

now everything follows the express pattern because it's explicit and simple.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: