Advanced OpenResty Features
Introduction
Welcome to lesson four of "Extending NGINX with Lua and OpenResty"! You've already built a solid foundation: you learned to embed Lua code in NGINX, manipulate HTTP traffic dynamically, and even construct a complete REST API with JSON handling and CRUD operations. Now, we're ready to explore advanced OpenResty features that will take your capabilities to the next level. In this lesson, we'll work with shared memory dictionaries to implement rate limiting and caching. These are the building blocks of production-ready systems that need to manage traffic, optimize performance, and integrate with other APIs.
Understanding Shared Memory in OpenResty
When we built our API in the previous lesson, we used a lua_shared_dict to store data. That worked well, but it’s worth highlighting why we chose it: regular global Lua variables live inside a single NGINX worker process, so other workers won’t see updates. If worker 1 increments a counter, worker 2 won’t see that change. For features like rate limiting—where we need to track requests across all workers—we need a shared, worker-safe storage layer.
Shared memory dictionaries solve this problem. They’re regions of memory that all worker processes can access simultaneously, making them perfect for:
- Tracking request counts across the entire server
- Storing cached data that all workers can read
- Maintaining application-wide state that survives individual requests
OpenResty provides the lua_shared_dict directive to create these shared spaces. The data persists as long as NGINX is running, and all operations on shared dictionaries are atomic, preventing race conditions when multiple workers access the same keys simultaneously.
Setting Up Shared Dictionaries
Before we can use shared memory, we need to allocate it in our NGINX configuration. This happens at the HTTP level, outside any server block:
Here's what these declarations accomplish:
lua_shared_dict rate_limit 10m: Creates a shared dictionary namedrate_limitwith 10 megabytes of memorylua_shared_dict cache 10m: Creates another dictionary namedcache, also with 10 megabytes- These names become accessible in our Lua code via
ngx.shared.rate_limitandngx.shared.cache - The size limit ensures we don't consume unlimited memory; when full, OpenResty will evict the least recently used entries
Each dictionary is essentially a key-value store where keys are strings and values can be strings, numbers, or booleans. We can also set expiration times on entries, making them perfect for temporary data like rate limit counters or cached responses.
