I am a Sr. Software Developer at Oracle Cloud. The opinions expressed here are my own and not necessarily those of my employer.
In previous articles in this blog we expored various options for using Redis for caching. This time we will compare Redis to Nginx as a caching technology. Code is avaiable at https://github.com/dmitrypol/cache_nginx_redis
- Local environment
- Performance test
We can bring up our environment with
docker-compose. It conists of two APIs (api1 and api2), Redis and Nginx. Nginx will be used to route requests to and between APIs and to cache their responses.
Our APIs will be simple Python
Flask apps with
http://localhost:5001/ for api1 and
http://localhost:5002/ for api2 will be cached in Redis for 60 seconds.
Data in Redis will be stored as strings. On the first request Flask will check if data exists in Redis. It will then generate the data, store it in Redis and return response to the browser. Subsequent requests will get data from Redis until 60 seconds later Redis deletes the key. Then data will need to be generated again in Python.
The benefit of this approach is that we have a great deal of control of which data we cache and for how long. Redis will respond quickly from RAM and cache can be shared between servers running the same code.
The downside of this approach is that each of our APIs needs to integrate separately with Redis. The request will have to pass through Nginx to our Python code and then to Redis. As we will see in the perf test results even with caching this can slow things down.
We will be using Nginx
proxy_cache modules. It will also cache data for 60 seconds but Nginx will use filesystem (/tmp/cache/ path) to store cache results.
To avoid confusion and not mix caching technologies we can disable Redis caching by commenting out
@CACHE.cached() in Python. If we browse to
http://localhost/api1/ for api1 and
http://localhost/api2/ for api2 these requests will pass through Nginx proxy.
If we look in
tmp/cache/api1/.../.../... we will see files with contents like this:
Nginx as cache proxy between APIs
Now we want to integrate api1 with api2. We will create new route in api1 code that requests data from api2. It can be accessed either by hitting api1 directly at
http://localhost:5001/getapi2 or via
http://localhost/api1/getapi2. Server side our request can be routed directly to
http://api2:5002 or via Nginx proxy with
Nginx allows us to create a shared proxy cache that can be used by many different services regardless of their software stack. Our API could be completely down and Nginx will still respond with cached content.
The downside is that we loose flexibility in what and for how long we cache data. Real APIs are much more complex and could use combination of cached and real-time data sources. We can only apply caching logic on the URL pattern.
With Redis we can use replication to create additional caches. While we could store cache files in a shared folder Nginx stores the cache keys in memory so this makes it difficult to scale out our caching solution. We could put individual cache proxy in front of each API instance but that can lead to different content cached in different proxies.
With Redis we could delete individual keys to purge specific cache. Free Nginx does not give us such granularity but the premium Nginx Plus has support.
We will use WRK https://github.com/wg/wrk. First we need to clone the repo and compile the source code. Note of caution, your mileage may vary depending on numerous factors, this perf test is meant to be a very high level estimate.
We will make requests directly against Flask API running on port 5001.
Now we will make requests against the Nginx proxy.
We can see that Nginx cache proxy is MUCH faster. Nginx code is very optimized and we are saving significant time on not having requests go to our Python API.