/elasticnode

lightweight horizontal scaleable nodejs faas boilerplate

Primary LanguageJavaScript

horizontal scaleable nodejs boilerplate

$ node server.js
[loadbal|300457|tcp/9999] loadbalancer started
[service|300469|tcp/5000] cluster-node started on port 5000
[service|300471|tcp/5001] cluster-node started on port 5001
[service|300469|tcp/5000] cluster-node connected
[service|300471|tcp/5001] cluster-node connected
[loadbal|300457|tcp/9999] initing cluster-app
[03-16T14:40:27] ├☑ myservice:  connected!          # executed on cluster 
[03-16T14:40:27] ├☑ myservice:  ping!               # executed on cluster 

cluster definition

{
  "master":  "./cluster/loadbalancer.js", 
  "workers": {                                   // cpus
    "serviceA":{ "worker":"./service/index.js", "count":1, "port":5000 },
    "serviceB":{ "worker":"./service/index.js", "count":1, "port":5001 }
  }, 
  "remotes":[
    { "host":"192.23.4.56", "port":5000 },       // same app but runs on other server
    { "host":"192.23.4.56", "port":5001 },       // they become workers of this server
  ], 
  "remotes":[], 
  "accessKey": "test",                           // cluster management over rest/cli
  "cli":false                                    // thanks to npmjs.org/cluster-service
}

cluster functions:

const service = require('./../cluster/service').service('myservice')

service.module.exports = {
  async ping () {
    let app = service.client.methods
    app.log('ping!', 'myservice')
    //log('local ping!')
    return 123
  }
}

service.init = async (app) => {
  await app.log("connected!",'myservice')
  await app.ping()
}

scale horizontally

when using remotes, use env-var upstream=main.myserver.org e.g. on remotes. By doing so, app.ping() will run through loadbalancer main.myserver.org.

centralized calls & data

all workers can execute / save data centrally on cluster/loadbalancer.js:

await app.set("foo.bar",[{x:1}])         // generates db.json
await app.get("foo.bar") )               // [{x:1}]
await app.find("foo.bar",   {x:{$lt:2}}) // [{x:1}]
await app.findOne("foo.bar",{x:{$lt:2}}) // {x:1}

db.json now contains {foo:{bar:[{x:1}]}}, which you can easily backup/edit

decentralized data (proxy-to-jsonfile)

workers can easily use own databases:

const db = require('./server/db')({file:'mydb.json', ratelimit:1500})
db.accounts = {a:[{foo:1},{foo:2}]}
let some = db.find('accounts.a',{foo:{$lt:2}}) )
let one  = db.findOne('accounts.a',{foo:{$lt:2}}) )

Lightweight 55MB all-in-one-V8-binary / docker-image

$ podman build -t elastinode
$ podman images
REPOSITORY                   TAG             IMAGE ID       CREATED              SIZE
elasticnode                  latest          79988bafb26d   32 seconds ago       51.5MB
$ DOCKER_BUILDKIT=1 docker build -t elasticnode --output out
$ ls -la out/server
-rwxr-xr-x 1 44M mrt 16 16:10 out/server

not bad for a distributed scalable app no?

test

$ node test/test.js
[loadbal|301490|tcp/9999] loadbalancer started
[service|301502|tcp/5000] cluster-node started on port 5000
[service|301502|tcp/5000] cluster-node connected
[service|301503|tcp/5001] cluster-node started on port 5001
[loadbal|301490|tcp/9999] initing cluster-app
[service|301503|tcp/5001] cluster-node connected
[ { id: 'projectA',  data: {} },  { id: 'projectB',  data: {} } ]
connected 
[03-16T14:50:15] ├☑ myservice:  connected!
[03-16T14:50:15] ├☑ myservice:  ping!
OK : app.ping => 123
done