-
Notifications
You must be signed in to change notification settings - Fork 14
New issue
Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? # to your account
Request Dispatch Pipeline #86
Comments
I like this even though it's quite complicated! We'll definitely need some easy to use extensions for registering servants for services and possibly a Do we really need Also if we allow registering servants with the following Map method:
Isn't this potentially expensive if the user registers a bunch of services with this method? If I understand it correctly, each Map dispatcher will be chained. It might be clearer to provide the following instead:
Or |
I like this especially the splitting of the servant map and the server, for servers hosting many servants |
I read more about it and switched the ASM to a UseASM extension method, which is more appropriate than MapXxx. There are basically 3 types of methods:
Now, ASP.NET IApplicationBuilder also provides a very powerful mechanism called "routing", that relies on two Use methods: UseRouting and UseEndpoints. See https://docs.microsoft.com/en-us/aspnet/core/fundamentals/routing?view=aspnetcore-5.0. Note that endpoint here does not mean transport endpoint: it's more like a "terminal" in a dispatch pipeline. And the endpoints are actually defined within the UseEndpoints(...) call.
In grpc dotnet, it's in UseEndpoints that services are registered, see: https://docs.microsoft.com/en-us/aspnet/core/grpc/aspnetcore?view=aspnetcore-5.0&tabs=visual-studio I believe we could do the same, with UseRouting() and UseEndpoints(), and a Current.Endpoint set during UseRouting(). All the endpoint-definition methods (https://docs.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.builder.endpointroutebuilderextensions?view=aspnetcore-5.0) start with Map. In addition to UseASM (for DispatchBuilder), we could have a MapASM (for our DispatchEndpointRouteBuilder):
And just like UseASM, it could be called several times with different little ASMs, e.g.
|
After thinking more about this topic, I think we should only implement a basic pipeline and leave routing/endpoints for a later version. routing/endpoints are really about communications between interceptors, particularly with built-in interceptors like authentication (and many others), and obviously so far we don't have any interceptors. Side note: So to restate the proposal:
I'll create a separate proposal for the proxy factories. |
I would drop "ASM". What's an active servant? Are there inactive servants if there are active ones :)? It's a bit strange to mix We could consider the following names otherwise:
Why is there two |
It's just like ASP.NET middlewares, see: https://docs.microsoft.com/en-us/aspnet/core/fundamentals/middleware/?view=aspnetcore-5.0 (toward the end). Basically, plain I think the bright-line here is if an argument calls next (for the next interceptor) => plain Use. Otherwise => UseXxx.
I picked ASM as an abstract name :). It's really an identity to servant dictionary, and
Are you in favor of dropping the distinction servant/service, and using service for both? In practice, I suspect this ASM support (regardless of the name) is not going to be used all that much. And we should provide a way to register a single identity/servant pair, e.g.:
and perhaps even:
There are all helpers built using Using Map/MapXxx is not appropriate here. Map creates a new branch in the pipeline and is therefore pretty heavy. Look at its configuration parameter. This also highlights a missing item with this proposal: how/when is the DispatchBuilder called? In ASP.NET, all this setup occurs in Startup.Configure, and the builder ( |
Now that you're mentioning it, dropping "servant" might indeed be worth considering :) A service implementation can be registered with an identity, a default identity or no identity (in which case it will be called for any identity). I don't think it's too difficult to explain without "servant". If we add I'm not a specialist of ASP.NET and it's quite possible there are things I didn't understand with the proposal, but I'm wondering if we really need a builder for building the pipeline. What is the advantage here? It seems to me that the builder is useful when using dependency injection to not have to instantiate directly the concrete classes. Unless we decide to do dependency injection, couldn't we simply provide factory methods to create the dispatchers? |
I think would be nice to have Using I might be wrong, but I think the builder is unrelated to DI, it just holds the pipeline state while it is being built, calling Build just materialize the pipeline as a delegate and returns it. |
Right the builder design pattern isn't specific to DI but is often used with DI. I'm fine for using a builder if it really makes things simpler. |
Indeed, that's not good because a user could naively add a bunch of services with
I am wondering if
This way, it can be efficient even if you add a bunch of services: we're just building a dictionary at configuration-time. One difficulty is this dictionary would be static, and some applications want to create more services at runtime. So we still need an I also suspect creating services with a default identity post-startup is not needed. |
Ok, let's drop servant! |
If you have a single catch-all service, you should use |
How we plug [I]DispatchBuilder into Server is still TBD. I think we definitely want to build the dispatch pipeline during startup in a thread-unsafe way, and then somehow provide a "frozen" dispatch pipeline to the server. While the pipeline itself is frozen, some elements (interceptors) can use a |
Seems good to me, here you can add services with the default identity or custom identities and still end up on the same map
I was thinking |
I think it would make sense to integrate ASM and ForCategory services in UseServices, with more MapXxx overloads. See also: https://docs.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.routing.iendpointroutebuilder?view=aspnetcore-5.0 For example:
And the logic for this interceptor could be:
TBD: what about duplicate registrations? It's also possible the first registration (not the last) wins with ASP.NET. |
The ASP.NET UseRouting => IceRPC: implicit service lookup at the beginning of the pipeline (if the pipeline has a UseServices), and set Current.Service if found ASP.NET UseEndpoints => IceRPC UseServices ASP.NET endpoint => IceRPC service ASP.NET endpoint properties => IceRPC service properties (doesn't exist currently) ASP.NET HttpContext Features (including Endpoint) => IceRPC Current.Service |
Not clear what you mean, do you mean that only the first call to UseServices has any effect? |
I think UseEndpoints() builds a filter where order matters. So if a Map matches first, further Map that match as well are ignored. I could be wrong of course. |
I suspect you're correct here. That's typically how it works with many web frameworks, order added is important and first match wins. |
One issue with my proposal is ASP.NET endpoint does not map well to IceRPC service, because UseEndpoints actually builds the endpoints, including the endpoints metadata. With the UseServices suggested above, it's the user who actually builds the services. Keeping them separate sounds better. So the API could be:
with |
For |
I guess we can use type-id as the default, and have metadata to control it, or what do you have in mind? |
Something like the following: #65 (comment) and yes, either it's required or we provide a default derived from the type-id. |
A popular alternative to the ASP.NET pipeline (and terminology) is https://github.com/go-chi/chi and maybe using the Chi terminology for IceRPC would be better?
I find go-chi more elegant and a better match for IceRPC. The IceRPC verbs would be Use, Mount, Route, Handle (but not Get, Put, Delete, Post, Method) If we follow the go-chi example, we would need to convert Dispatcher into an interface, with a Router than extends Dispatcher. |
Restated go-chi -like proposal:
The dictionary is naturally not copied and the application can update it after server creation.
|
Fixed by #178 and subsequent PRs. |
This is a proposal to convert
ObjectAdapter
/Server
into a request dispatch pipeline, similar to ASP.NET's pipeline.A request dispatch pipeline consists of a chain of
Dispatcher
(RequestDelegate
in ASP.NET). A dispatch interceptor is aFunc<Dispatcher, Dispatcher>
that inserts a new dispatcher in this chain. See also #85.The API is very simple and consists of a (thread-unsafe) DispatchBuilder, a Server class plus various extension classes. The extension classes use only public APIs exposed by DispatchBuilder / Server.
See https://docs.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.builder.iapplicationbuilder?view=aspnetcore-5.0
The user constructs the request dispatch pipeline by adding dispatch interceptors, usually through easy-to-use DispatchBuilder extensions similar to ASP.NET's extensions:
https://docs.microsoft.com/en-us/aspnet/core/fundamentals/middleware/?view=aspnetcore-5.0
In particular, the ASM is replaced by a Use extension:
This way, the user can insert her own ASM dictionary (preferably a concurrent dictionary) anywhere in the pipeline. And if there is no hit in the ASM, the pipeline continues. Naturally the leaf dispatcher throws ONE.
The old default servant is replaced by a terminal Run:
Note that unlike default servants today, this default servant cannot be added or removed once the server is built. It's not a big drawback, and if the user needs runtime addition/removal, she can always add her own interceptor.
A default servant for a given category could be replaced by a
Map
:Note that you can very easily convert a servant into a Dispatcher in C#, for example this works fine:
For ease of use, we would provide additional extensions for servants, e.g.
With this proposal, adding a servant / default servant to the "server" no longer returns a proxy. Proxies would be manufactured separately using an extension class for Server:
Not included in this proposal (to keep it focused):
The text was updated successfully, but these errors were encountered: