-
Notifications
You must be signed in to change notification settings - Fork 1.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Downstream requests aggregation #79
Comments
@MykhailoKyrychenko at the moment I don't have any specific plans to do this. I talked to a friend at work and he suggested this would be a good feature! You can inject custom functions as middleware at various stages in the pipeline that allow you to do anything but this is probably not ideal! |
@TomPallister thank you for your answer! It looks like the only way to have some complex logic inside of injected middleware is to use Service Locator pattern. The other option is to use MVC, but it is not that good at all, because it will support routing only via attributes, so there is no chance to have everything in configuration file. I am going to check possibility of using middleware injection one more time. Thank you for your answer once again! |
@MykhailoKyrychenko Yeah service locator sucks :( this annoys me that it is the only way you can inject middleware into Ocelot. Can you explain how you would see this feature working? From my point of view you would need to describe in the ocelot configuration that you want to call multiple endpoints and map the results from each into some kind of object to return? e.g. I have a mobile app client and I want to get a product search results page. In order to get all of this data I need to call the search service to get some information and then we also want something that isn't in the search service say, offers/promotions so we call into the offers service to see if we can show any discounts. This means the mobile client would have to make two calls but we want to only make one. In this case we want to aggregate the calls and return one object to the mobile client. I think something like this would do the job http://blog.tamizhvendan.in/blog/2015/12/29/implementing-api-gateway-in-f-number-using-rx-and-suave/ I think the hardest part is making this configurable. |
@TomPallister yes, I have seen that article and I liked it very much. The problem is that F# in our current solution would be yet one more new thing for half of team, so we have to skip it. At least for now. Regarding of how I see the feature itself: As for me, aggregation of downstream requests can be freely applied only to GET requests, since in PUT and POST you need to provide some additional logic for how to decouple different parts of POST body between different downstreams. And aggregation of DELETE requests does not sound safe for me. As for the implementation, you can map upstream as done now in Ocelot, and then just run in parallel downstream requests. Then they can be combined into one single response object under defined in configuration property names. Configuration file would look like example below.
|
@MykhailoKyrychenko Yep makes sense, I guess we will make this next! |
@TomPallister great news! Do you need any help with that? |
@MykhailoKyrychenko yes, at the moment I'm working on another feature which is quite complex. If you have the time it would be great if you could look at implementing this feature. Dont worry if you dont have time! |
@TomPallister I have been trying to implement this particular feature inside Ocelot for a while now. But I am still not sure about architecture: should it be a separate execution flow starting from point when upstream path was matched or should it be just a unified flow. In case of unified flow all current configurations have to be updated to support multiple downstreams with restriction on config validation level, that only GET really supports multiple downstreams. That might be confusing for the end user. Also, some features like Load balancing and QoS are pretty complex for this use case as well. |
@MykhailoKyrychenko Yeah its pretty complicated. I would add a new piece of middleware somewhere that says...OK this person wants to call two different downstream re routes to compose their return object. That would then call into the ocelot stack for each downstream re route in parallel....wait and then compose the return object. Add the new middleware after DownstreamRouteFinderMiddleware In the middleware have a branch that says if you just have one re route call the next middleware as normal and return. If you have more than one re route call each and then compose the object and return it. I think that might work, though could not say for sure without actually writing the code! |
@MykhailoKyrychenko @TomPallister this feature has any progress ? |
Had draft version implemented as custom fork for internal project. Now, as Ocelot evolved, I'm not even sure if it's possible to simply solve merge conflicts 😞 Even if that won't consume a lot of time, unfortunately, I won't be able to continue on that feature before November. Also, there are a few moments I'm not sure about:
|
@daniellaoding @MykhailoKyrychenko thanks for your interest in the project. I personally have not worked on this but I am coming to the end of a feature so might be able to take a look.
This is a very tricky feature to get right :( |
good idea @TomPallister @MykhailoKyrychenko 。 |
Im going to pick this up again! |
Middleware Analysis UseDownstreamRouteFinderMiddleware - finds the ReRoute specific to the upstream request...maybe doesnt need to change... ** Could you just add a branching middleware here that calls next for each downstream and then collates everything on the way back up??? ** UseHttpHeadersTransformationMiddleware - does find and replace on headers...would need applying to all downstreams.. UseDownstreamRequestInitialiser - creates initial httprequestmessage object - would need applying to all downstreams.. UseRateLimiting - rate limits downstream requests - would need applying to all downstreams.. UseRequestIdMiddleware - sets request id would need applying to all downstreams.. UseAuthenticationMiddleware - would need applying to all downstreams.. UseClaimsBuilderMiddleware - would need applying to all downstreams.. UseAuthorisationMiddleware - would need applying to all downstreams.. UseHttpRequestHeadersBuilderMiddleware - runs claims to headers logic - would need applying to all downstreams.. UseQueryStringBuilderMiddleware - would need applying to all downstreams..though this might not but could be added later UseLoadBalancingMiddleware - would need applying to all downstreams.. UseDownstreamUrlCreatorMiddleware - this middleware probably doesnt need to be on its own, its a bit shit could maybe be in UseHttpRequestBuilderMiddleware or UseDownstreamRequestInitialiser? - would need applying to all downstreams.. UseOutputCacheMiddleware - would need applying to all downstreams.. UseHttpRequestBuilderMiddleware - would need applying to all downstreams.. UseHttpRequesterMiddleware - would need applying to all downstreams.. Configuration Options First just make user set a key on ReRoutes then call them all and aggregate Tradeoffs - makes the user add a key to ReRoutes they want to aggregate "AggregateReRoutes" : [ Or have a new section for aggregates called Downstreams (dont pay too much attention to data in the json below or names etc) Tradeoffs - doesnt make the user add a key to ReRoutes they want to aggregate "ReRoutes": [ Implementation Ideas foreach downstream route call a pipeline of some kind async.. |
For anyone interested I think I’m going to leave the asp.net middleware after working out the downstream route. Then multiplex further requests into an ocelot middleware pipeline which will be the same as the asp.net one apart from it will take a different object. Not httpcontext. For multiplex I will either just use tasks or something like rx.net. I probably will use tasks so I don’t have to take the dependency. Anyway let’s see! |
@TomPallister This is a good way to support multiple protocols for downstream services |
|
previous pipeline apache bench Benchmarking localhost (be patient) Server Software: cloudflare Document Path: /posts Concurrency Level: 100 Connection Times (ms) Percentage of the requests served within a certain time (ms) |
new pipeline apache bench Benchmarking localhost (be patient) Server Software: cloudflare Document Path: /posts Concurrency Level: 100 Connection Times (ms) Percentage of the requests served within a certain time (ms) |
Hi - I am trying to use the "Aggregation feature" of Ocelot but its throwing an error - "ERROR|Ocelot.DownstreamRouteFinder.Middleware.DownstreamRouteFinderMiddleware|DownstreamRouteFinderMiddleware setting pipeline errors. IDownstreamRouteFinder returned Error Code: UnableToFindDownstreamRouteError Message: UnableToFindDownstreamRouteError : OcelotRequestId - not set Here is the configuration.json {
], "GlobalConfiguration": { I am trying to aggregate the output of 2 routes identified by keys "Item1" &"Item2".Am I missing something ??I get the above error when access However when I use "http://localhost:9000/products" or "http://localhost:9000/customers" I get the desired output.Can somebody help me out on the same. Awaiting reply. |
@skg170383 thanks for your interest in the project! Can you try removing UpstreamHost from your aggregate config and see if that makes a difference? |
I tried removing UpstreamHost but got the same error. 2018-03-30 12:46:33.6518||ERROR|Ocelot.DownstreamRouteFinder.Middleware.DownstreamRouteFinderMiddleware|DownstreamRouteFinderMiddleware setting pipeline errors. IDownstreamRouteFinder returned Error Code: UnableToFindDownstreamRouteError Message: UnableToFindDownstreamRouteError : OcelotRequestId - not set Can you kindly help as we are evaluating "Ocelot" for our solution architecture |
@skg170383 I think I can see the problem now. Please try this configuration.json {
"ReRoutes": [
{
"DownstreamPathTemplate": "/api/customers",
"UpstreamPathTemplate": "/customers",
"UpstreamHttpMethod": [
"Get"
],
"DownstreamScheme": "http",
"DownstreamHostAndPorts": [
{
"Host": "localhost",
"Port": 9001
}
],
"Key": "Item2"
},
{
"DownstreamPathTemplate": "/api/customers/{id}",
"DownstreamScheme": "http",
"DownstreamHostAndPorts": [
{
"Host": "localhost",
"Port": 9001
}
],
"UpstreamPathTemplate": "/customers/{id}",
"UpstreamHttpMethod": [
"Get"
],
"QoSOptions": {
"ExceptionsAllowedBeforeBreaking": 3,
"DurationOfBreak": 10,
"TimeoutValue": 5000
},
"Key": "Item3"
},
{
"DownstreamPathTemplate": "/api/products",
"UpstreamPathTemplate": "/products",
"UpstreamHttpMethod": [
"Get"
],
"DownstreamScheme": "http",
"DownstreamHostAndPorts": [
{
"Host": "localhost",
"Port": 9002
}
],
"Key": "Item1"
}
],
"Aggregates": [
{
"ReRouteKeys": [
"Item1",
"Item2"
],
"UpstreamPathTemplate": "/aggregated"
}
],
"GlobalConfiguration": {
"RequestIdKey": "OcRequestId",
"AdministrationPath": "/administration"
}
} I think it's because you are not using "DownstreamHostAndPorts": [
{
"Host": "localhost",
"Port": 9002
}
], This is the way of setting config in Ocelot now! Can I ask where did you find your example? |
Hi Tom- For example - http://localhost:9000/customers But now this individual urls do not respond. Have attached log file for your reference. debug-2018-03-31.log |
@skg170383 mmmm this is very strange that config worked fine for me :( what version of Ocelot are you using? |
I am using 2.0.1.Do you have any suggestion... Kindly help |
@skg170383 ok so that’s probably why the config I suggested didn’t work. Also version 2.0.1 did not support aggregates. Please try the latest version 5.3.0! |
Thanks a lot for all the help !! It works with 5.3.0. |
@skg170383 no problem, glad it worked! :) |
hi, I have a question about the request to aggregates from Angular CLI. |
Hi,
Thank you for this great project. We started to look at .Net based API Getaway frameworks and it looks like yours is what we need.
According to API Getaway pattern purpose, there might be need to aggregate some downstreams into one upstream. For example, for mobile applications you can combine two downstreams like
api.yourcompany.com/posts/{id}
andapi.yourcompany.com/comments/{id}
under one upstreamgetaway.yourcompany.com/mobile/posts/{id}
.What do you think about this feature? Are there any plans to implement it?
The text was updated successfully, but these errors were encountered: