Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

NGINX JavaScript module with a session storage (like Redis)

I know there is a possibility to process each request via a JS script right inside the NGINX server.

I know there is the Lua Nginx module and the Lua Redis driver, and it's possible to write a script in Lua and use Redis right from the NGINX server.

However, I want to use standard functionality of NGINX and I prefer to code in JS. I wonder is it possible to use some session storage with the NJS? And how to do it? Particularly, I would like to use Redis as a session storage.

like image 809
RussCoder Avatar asked Aug 12 '19 16:08

RussCoder


People also ask

How to use redis to store session?

To add support of Redis you have to use Redis client and connect-redis. Create express-session and pass it to connect-redis object as parameter. This will initialize it. Then in session middleware, pass the Redis store information such as host, port, and other required parameters.

Does nginx support JavaScript?

NGINX JavaScript is a unique JavaScript implementation for NGINX and NGINX Plus, designed specifically for server‑side use cases and per‑request processing. It extends NGINX configuration syntax with JavaScript code in order to implement sophisticated configuration solutions.

What is nginx NJS?

njs is a subset of the JavaScript language that allows extending nginx functionality. njs is created in compliance with ECMAScript 5.1 (strict mode) with some ECMAScript 6 and later extensions. The compliance is still evolving. Download and install. Changes.

Is redis a reverse proxy?

nginx-redis-proxy is a reverse proxy based on nginx and redis to cache objects (web pages and more).

How to build session storage with nginx and Redis?

If one avoids to compile and install third-party modules for Nginx himself, I suppose the best way to build session storage with njs and Redis is to utilize the builtin ngx_http_upstream_module module and set up something like that

How to use NJS in Nginx?

To use njs in nginx: 1 install njs scripting language 2 create an njs script file, for example, http.js . ... 3 in the nginx.conf file, enable ngx_http_js_module module and specify the js_import directive with the http.js script file: load_module modules/ngx_http_js_module.so; events {} http { js_import http.js; server { listen 8000; location ...

Should you write a module in Nginx?

Whether you are using core modules, like the http and stream (TCP/UDP) modules, or third‑party modules like GeoIP or RTMP, the module framework is the same. With the addition of dynamic module support, modules are an even better way to add functionality to NGINX. But just because you can write a module doesn’t necessarily mean you should.

What is the default value of Redis in ngx_http_Redis?

For ngx_http_redis >= 0.3.4 is not obligatory, default value is 0 if not defined. The value of the redis key. In 0.3.5 support of keep-alive connection backported from original ngx_http_memcached module of NGINX 1.1.4.


1 Answers

If one avoids to compile and install third-party modules for Nginx himself, I suppose the best way to build session storage with njs and Redis is to utilize the builtin ngx_http_upstream_module module and set up something like that

http {
    [...]

    upstream redis {
        server unix:/var/run/redis/nginx.sock;
    }

    [...]

    js_path conf.d/js/;

    js_import redismiddleware.js;

    [...]

    server {
        [...]

        location /redisadapter {
            internal;

            try_files @dummy @redis;
        }

        location /request-we-are-tracking-no-1/ {
            js_content redismiddleware.processRequestConditional;
        }

        [...]

        location /request-we-are-tracking-no-2/ {
            js_content redismiddleware.processRequestUnconditional;
        }

        [...]
    }

and the corresponding script

var queryString = require('querystring')

function processRequestConditional (request) {
    request.subrequest('/redisadapter', {
        method : 'POST',
        body : queryString.stringify({
          /* data to transfer to Redis */
        })
    }).then(function (response) {
        var reply = {}

        /**
         * parsing and checking feedback from Redis, 
         * saving some data from feedback to reply 
         * object, doing any things as planned, 
         * running any additional routines et 
         * cetera 
         */
        
       if (/* Redis reply is OK */) {
           return reply;
       } else {
           throw new Error('Because we don`t like you!')
       }
    }).then(function (data) {
        /**
         * Making one more subrequest to obtain the content
         * the client is waiting for
         */

         request.subrequest('/secret-url-with-content').then(
            (response) => request.return(response.httpStatus, response.body)
         )
    }).catch((error) {
        /** 
         * Returning to client with response "There will be
         * be no response"
         */
        request.return(403, error.message)
    })
}

function processRequestUnconditional (request) {
    request.subrequest('/redisadapter', {
        method : 'POST',
        body : queryString.stringify({
          /* data to transfer to Redis */
        })
    }).then(function (response) {
        /**
         * parsing and checking feedback from Redis,
         * doing some things, running some or other
         * additional routines depending on reply
         */
    })

    request.subrequest('/secret-url-with-content').then(
        (response) => request.return(response.httpStatus, response.body)
    )
}

export default { processRequestConditional, processRequestUnconditional }

Short summary

  1. Redis is listening and replying on socket /var/run/redis/nginx.sock
  2. Virtual internal location /redisadapter receives the requests from njs script, transfers them to Redis and returns the replies back to njs method, which started the requests sequense
  3. To establish data exchange with Redis on some location, we take control over Nginx standard flow and maintain these locations with custom njs methods
  4. Thus, the code in methods in addition to Redis related information exchange routines should itself implement the complete subroutine of retrieving the requested content and delivering this content back to client, since we took this job from Nginx
  5. This is achieved by sets of local subrequests from server to server, which are transparent for clients. njs method completes the set and itself delivers the requested content back to the client
  6. The above example contains two methods, processRequestConditional and processRequestUnconditional, with the goal to show the most used alternative logic ways to maintain such kind of tasks
  7. The first one, processRequestConditional, demonstrates subrequests chain - i.e., the content, requested by client, won't be obtained, until njs method is busy with its primary task transferring the next piece of data into Redis session storage. Moreover, if the script is not satisfied with Redis feedback, the request of content for client is skipped at all, and the client faces refusal message instead
  8. The second method, processRequestUnconditional, transfers the data to Redis storage the same way as the first one above, but this time the fate of the client's request does not depend on results of Redis feedback, thus the secondary request for content is issued at the same time with primary, and flows in parallel while script continue the information exchange round with session storage

Of course, my brief explanation leaves a lot of details behind the scenes, but I hope the basic concept is now clear

Feel free to ask additional questions

like image 196
Juliy V. Chirkov Avatar answered Oct 21 '22 21:10

Juliy V. Chirkov