#298 initial hacking around better aggregation (#310)

* #298 initial hacking around better aggregation

* #298 bit more hacking around

* #298 abstraction over httpresponsemessage

* #298 tidying up

* #298 docs

* #298 missed this
This commit is contained in:
Tom Pallister
2018-04-12 17:35:04 +01:00
committed by GitHub
parent 982eebfc74
commit a15f75dda8
63 changed files with 1203 additions and 410 deletions

View File

@ -5,12 +5,114 @@ Ocelot allow's you to specify Aggregate ReRoutes that compose multiple normal Re
a client that is making multiple requests to a server where it could just be one. This feature allows you to start implementing back end for a front end type
architecture with Ocelot.
This feature was requested as part of `Issue 79 <https://github.com/TomPallister/Ocelot/pull/79>`_ .
This feature was requested as part of `Issue 79 <https://github.com/TomPallister/Ocelot/pull/79>`_ and further improvements were made as part of `Issue 298 <https://github.com/TomPallister/Ocelot/issue/298>`_.
In order to set this up you must do something like the following in your configuration.json. Here we have specified two normal ReRoutes and each one has a Key property.
We then specify an Aggregate that composes the two ReRoutes using their keys in the ReRouteKeys list and says then we have the UpstreamPathTemplate which works like a normal ReRoute.
Obviously you cannot have duplicate UpstreamPathTemplates between ReRoutes and Aggregates. You can use all of Ocelot's normal ReRoute options apart from RequestIdKey (explained in gotchas below).
Advanced register your own Aggregators
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Ocelot started with just the basic request aggregation and since then we have added a more advanced method that let's the user take in the responses from the
downstream services and then aggregate them into a response object.
The configuration.json setup is pretty much the same as the basic aggregation approach apart from you need to add an Aggregator property like below.
.. code-block:: json
{
"ReRoutes": [
{
"DownstreamPathTemplate": "/",
"UpstreamPathTemplate": "/laura",
"UpstreamHttpMethod": [
"Get"
],
"DownstreamScheme": "http",
"DownstreamHostAndPorts": [
{
"Host": "localhost",
"Port": 51881
}
],
"Key": "Laura"
},
{
"DownstreamPathTemplate": "/",
"UpstreamPathTemplate": "/tom",
"UpstreamHttpMethod": [
"Get"
],
"DownstreamScheme": "http",
"DownstreamHostAndPorts": [
{
"Host": "localhost",
"Port": 51882
}
],
"Key": "Tom"
}
],
"Aggregates": [
{
"ReRouteKeys": [
"Tom",
"Laura"
],
"UpstreamPathTemplate": "/",
"Aggregator": "FakeDefinedAggregator"
}
]
}
Here we have added an aggregator called FakeDefinedAggregator. Ocelot is going to look for this aggregator when it tries to aggregate this ReRoute.
In order to make the aggregator available we must add the FakeDefinedAggregator to the OcelotBuilder like below.
.. code-block:: csharp
services
.AddOcelot()
.AddSingletonDefinedAggregator<FakeDefinedAggregator>();
Now when Ocelot tries to aggregate the ReRoute above it will find the FakeDefinedAggregator in the container and use it to aggregate the ReRoute.
Because the FakeDefinedAggregator is registered in the container you can add any dependencies it needs into the container like below.
.. code-block:: csharp
services.AddSingleton<FooDependency>();
services
.AddOcelot()
.AddSingletonDefinedAggregator<FooAggregator>();
In this example FooAggregator takes a dependency on FooDependency and it will be resolved by the container.
In addition to this Ocelot lets you add transient aggregators like below.
.. code-block:: csharp
services
.AddOcelot()
.AddTransientDefinedAggregator<FakeDefinedAggregator>();
In order to make an Aggregator you must implement this interface.
.. code-block:: csharp
public interface IDefinedAggregator
{
Task<DownstreamResponse> Aggregate(List<DownstreamResponse> responses);
}
With this feature you can pretty much do whatever you want because DownstreamResponse contains Content, Headers and Status Code. We can add extra things if needed
just raise an issue on GitHub. Please note if the HttpClient throws an exception when making a request to a ReRoute in the aggregate then you will not get a DownstreamResponse for
it but you would for any that succeed. If it does throw an exception this will be logged.
Basic expecting JSON from Downstream Services
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
.. code-block:: json
{
@ -65,9 +167,6 @@ If the ReRoute /tom returned a body of {"Age": 19} and /laura returned {"Age": 2
{"Tom":{"Age": 19},"Laura":{"Age": 25}}
Gotcha's / Further info
^^^^^^^^^^^^^^^^^^^^^^^
At the moment the aggregation is very simple. Ocelot just gets the response from your downstream service and sticks it into a json dictionary
as above. With the ReRoute key being the key of the dictionary and the value the response body from your downstream service. You can see that the object is just
JSON without any pretty spaces etc.
@ -76,20 +175,13 @@ All headers will be lost from the downstream services response.
Ocelot will always return content type application/json with an aggregate request.
If you downstream services return a 404 the aggregate will just return nothing for that downstream service.
It will not change the aggregate response into a 404 even if all the downstreams return a 404.
Gotcha's / Further info
-----------------------
You cannot use ReRoutes with specific RequestIdKeys as this would be crazy complicated to track.
Aggregation only supports the GET HTTP Verb.
If you downstream services return a 404 the aggregate will just return nothing for that downstream service.
It will not change the aggregate response into a 404 even if all the downstreams return a 404.
Future
^^^^^^
There are loads of cool ways to enchance this such as..
What happens when downstream goes slow..should we timeout?
Can we do something like GraphQL where the user chooses what fields are returned?
Can we handle 404 better etc?
Can we make this not just support a JSON dictionary response?