Building a Fast and Reliable Reverse Proxy With YARP
.NET based reverse proxy library for microservice architectures
When it comes to microservice architectures, multiple endpoints often have to be combined to reduce complexity for consuming clients. This is one of the main reasons for using reverse proxies and API gateways respectively. With the first preview of YARP a new technological possibility emerged. This article explains the key concepts and shows how to use the toolkit in your application.
What is YARP?
First of all, let’s explain the acronym. YARP stands for Yet Another Reverse Proxy. It’s the new implementation from Microsoft targeting the consolidation of multiple internal activities concerning reverse proxies. In the future, it might possible that YARP could replace the implementation currently used in Azure Front Door.
YARP is a reverse proxy toolkit for building fast proxy servers in .NET using the infrastructure from ASP.NET and .NET. The key differentiator for YARP is that it’s been designed to be easily customized and tweaked to match the specific needs of each deployment scenario. (https://github.com/microsoft/reverse-proxy)
Of course, there are already a lot of implementations of API gateways and reverse proxies out there. Basically, think of NGINX or Ocelot. But there is something special about YARP. It fully integrates into the ASP.NET environment and can easily be customized and tweaked to match the specific requirements. As of this writing it’s still in preview but already supports a bunch of possibilities, for example:
- Dynamic config-based route definitions
- Pipeline model for extensibility
- Load-balancing with support for different and custom algorithms
- Session Affinity to ensure requests from a given client are always sent to the same destination server
- Transforms to modify the request sent to or the response received from the destination server
- Authorization and CORS specified per route
You might ask if YARP is more like an API gateway instead of a reverse proxy. But is there even a difference between API gateways and reverse proxies?
API gateways are a specific kind of reverse proxies.
Basic functionality is always a kind of reverse proxy. By adding support on more pieces like authentication, transformation, or dynamic configuration it’s more like an API gateway. That’s why I understand YARP more as a kind of API gateway than a reverse proxy. But as you see it is a smooth transition.
A first implementation with YARP
When you are familiar with developing solutions based on ASP.NET Core, it’s straight forward to get started. YARP is available for .NET Core 3.1 and .NET 5, but we will focus on .NET 5 since it is the latest version.
To get started, you have to create a new project using the command line or Visual Studio project wizard.
dotnet new web -n ReverseProxy
Afterward, you have to install the latest NuGet package either by using the package manager console or by modifying the *.csproj-file.
As already mentioned before, YARP is an ASP.NET component. Hence, it fits perfectly into the dependency injection and middleware approaches of the framework. To activate YARP during execution we have to adjust our
Startup.cs as usual in ASP.NET.
ConfigureServices the method
AddReverseProxy is called to inject all necessary dependencies. Via
LoadFromConfig the endpoints for the proxy are loaded based on the configuration file. There are even more options to adjust the configuration. We will have a look at it later in this article.
Now it’s time to specify the endpoints of our reverse proxy. In our
Startup.cs we have specified to use the application settings as source. Hence, we have to adjust the
appsettings.json file and append a valid configuration as shown in the following example.
Basically, we have to differ two main config sections:
Describes a set of routes that matches incoming requests based on the match criteria and proxies matching requests to the respective cluster
Describes a set of clusters. A cluster is a group of equivalent endpoints and associated policies.
In our example, we defined two destinations within our cluster. So YARP has to decide which endpoint to use for a specific request. That is where load-balancing takes place. When no load-balancing method is specified YARP uses PowerOfTwoChoices. This algorithm chooses two random destinations and selects the one with the least assigned requests. To adjust the load-balancing method, e.g. use RoundRobin, we have to extend our configuration like shown in the following snippet.
Code-based proxy configuration
Till now we have used the application configuration to set-up YARP. Furthermore, the proxy configuration can be loaded programmatically by implementing
IProxyConfigProvider. This could be very helpful in scenarios in which you need a dynamic proxy configuration based on your application needs.
The following snippet shows a custom provider that loads the same configuration we have used via
appsettings.json file. Since the proxy configuration is validated during startup, we have to provide at least empty lists for routes and clusters. In our case, we define the routes and clusters from previous the example.
To use the
CustomProxyConfigProvider it should be registered as a singleton in the dependency injection container during startup. How this can be achieved is shown in the next code snippet.
From this point on YARP uses the code-based configuration of
CustomProxyConfigProvider. To perform a configuration update during runtime you have to inject the instance and call the
Update method. YARP then refreshes its internal state immediately without any downtime.
Request and response transformation
When implementing a reverse proxy it’s common to modify various parts of incoming requests or outgoing responses before forwarding them to the cluster or the client respectively.
YARP supports a bunch of transformations like various header or path transformations. The configuration is supported via
appsettings.json as well as via custom code based scenarios. The following snippet shows path and header transformations configured via
The reverse proxy listens on
https://localhost:5000/api/service1/ for incoming requests. In case of a matching request, one of the backend servers is called without
/api/service1/ from the initial route. Backend request for this example looks like
https://localhost:<<5001-or-5002>>/<<value-from-catch-all>>. Before the response is sent to the client an additional header will be added.
As mentioned at the beginning of the section transformations are supported via code-based configuration as well. The next snippet shows the constructor of the previously shown
AddTransformResponseHeader we can easily add the same transformations with this approach.
All available transformation can be found here: https://microsoft.github.io/reverse-proxy/articles/transforms.html
In this article, I walked you through the basics of YARP. Based on that, you can now build an own reverse proxy that fits your requirements. Since the toolkit is based on ASP.NET Core stack it can be executed on any environment you have used for your .NET Core projects till now. From my point of view, YARP is the next generation reverse proxy library when you prefer .NET as a programming language.
On GitHub, a full working sample application is available. It covers application settings based configuration as well as code-based configuration as described in this article. (Example how to work with YARP using appsettings.json and code based configuration.)
Thank you for taking the time to read my article. 😄
If you enjoyed it and want to see more coding-related content then follow me on my social profiles. Don't hesitate to like, comment or share this post. I appreciate any kind of feedback on this article.
Did you find this article valuable?
Support Fabian Zankl by becoming a sponsor. Any amount is appreciated!