/stack

local serverless multi-language streaming microservices

Primary LanguageJavaScriptOtherNOASSERTION

Stack

Software Development Kit and Command Line Interface for spawning streaming HTTP microservices in multiple programming languages.

At it's core, stack maps HTTP request/response streams to the STDIN/STDOUT streams of any programming language binary.

Introduction

This project is the component which several production services, including hook.io, use to spawn real-time arbitrary streaming microservices in response to streaming HTTP requests. It's been battle-hardened with over two years of development and 100+ million microservice deployments.

You are encouraged to use this module as-is, or modify it to suite your needs. If you are interested in contributing please let us know by opening a Pull Request.

Features

  • Creates HTTP microservices in multiple Programming Languages
  • Ships with stack binary for starting HTTP microservice servers
  • Maps HTTP request / response to STDIN / STDOUT of spawned child processes
  • Uses a "system process per microservice request" design
  • Isolates state of microservice per system process and request ( stateless service requests )
  • Microservice error handling and custom timeouts
  • Can parse any kind of request data
    • Query
    • JSON
    • Form
    • Multipart
    • Streaming
    • Binary

Installation

npm install -g stackvana

This will install the stack binary globally on your system.

Usage

  Usage: stack [command] [options]

  Commands:

    help  Display help

  Options:

    -t, --timeout <number>  Sets max timeout of service in milliseconds
    -h, --host <value>      Host to listen on
    -p, --port <number>     Port to listen on
    -l, --language <value>  Target programming languages
    -w, --watch <bool>      Reloads source files on every request ( dev only )
    -v, --version           Output the version number

By default, stack will attempt to start a listening HTTP server based on a microservice file path.

As Command Line Interface

stack ./path/to/script.foo

CLI Examples

stack ./examples/services/echo/echo.js
stack -l babel ./examples/services/echo/echo-es6-async.js
stack ./examples/services/echo/echo.sh
stack ./examples/services/echo/echo.lua
stack ./examples/services/echo/echo.php
stack ./examples/services/echo/echo.pl
stack ./examples/services/echo/echo.py
stack -l python3 ./examples/services/echo/echo-py3.py
stack ./examples/services/echo/echo.rb
stack ./examples/services/echo/echo.coffee
stack ./examples/services/echo/echo.ss
stack ./examples/services/echo/echo.st
stack ./examples/services/echo/echo.tcl

Each call to stack will automatically start a listening HTTP server on port 3000, additional instances of stack will auto-increment the port to 3001, 3002, etc.

Service target language is automatically detected based on the file extension of the service. This can be overridden using the --language option.

Note: For certain languages ( such as Babel ), the first microservice request to stack may take additional time as it will perform an initial compile and cache step.

Note: Please see Babel Support for additional Babel configuration

Programmatically Inside Node.js

Node API

spawn(opts, req, res)

   opts.code      - source code of microservice
   opts.language  - target programming language
   opts.log       - optional custom logging handler function ( defaults to `console.log` )
   opts.env       - environment variables which populates `service.env` ( defaults to `process.env` )

   req - http request stream
   res - http response stream

Hook Object API

   Every service is populated with an object named "hook"
   Note: This API may differ slightly per language implementation, see ./examples/services

   service.params - combined scope of all incoming HTTP request variables
   service.env    - environment variables of service ( defaults to `process.env`)
   service.req    - HTTP Request stream
   service.res    - HTTP Response stream

Example services

see: ./examples/services for more details

var stack = require('stackvana');
var http = require('http');

var bashService = 'echo "hello bash"';
var pythonService = 'print "hello python"';
var nodeService = function testService (opts) {
  var res = opts.res;
  res.write('hello node!');
  res.end();
};

var server = http.createServer(function(req, res){
  stack.spawn({
    code: nodeService,
    language: "javascript"
  }, req, res);
  // or you could use bash / any other supported language
  // stack.spawn({code: bashService, language: "bash" }, req, res);
  // stack.spawn({code: pythonService, language: "python" }, req, res);
  
});
server.listen(3000, function () {
  console.log('http server started on port 3000');
});

Multiple Microservices Per Server Instance

In some configurations you may want to safetly run multiple kinds of microservices on one server instance ( a small monolith ). stack is designed exactly for this use case.

Since every incoming service request will spawn a separate process, stack can safely and easily handle spawning multiple types of microservices at once without affecting the state of other services.

If you look at the ./examples/simple-http-server file, you will see that spawn() can be used as a standard Node.js or Express HTTP middleware. For multiple services per server, simply map the spawn() method to any custom routes you may want to define.

Supports Microservices In Many Languages

  • javascript
  • babel ( ES6 / ES7 / etc ... )
  • coffee-script
  • bash
  • lua
  • perl
  • php
  • python
  • python3
  • ruby
  • scheme
  • smalltalk
  • tcl

Additional language support is both planned and welcomed. Please open a Pull Request if you wish to see a specific language added

Babel

In order to run Babel / ES6 / ES7 microservices, you must install the following packages:

npm install babel-core
npm install babel-plugin-syntax-async-functions
npm install babel-plugin-transform-regenerator
npm install babel-polyfill
npm install babel-preset-es2015
npm install babel-preset-stage-3
npm install then-sleep

Security

Running untrusted microservice code in a safe way is a complex problem. The stack module is only intended to isolate a small part of the entire untrusted source code execution chain.

If you intend to use this module to run untrusted source code please consider the following:

What this module does isolate

  • Microservice state
  • Microservice errors
  • Stream / Socket errors
  • HTTP Request state

Every incoming HTTP request will spawn a new system process to run the microservice instance, which then sends data to the HTTP response. Every microservice execution will happen in a fresh systems process.

All errors that can possibly happen during the execution of a microservice should be trapped and isolated to not affect any other microservices.

What this mode does NOT isolate

  • Server Memory
  • Server CPU
  • Server file-system
  • Process CPU
  • Process Memory

stack cannot make any guarantees about the isolation of the server or spawned processes. All microservices will have default access to the server's file-system and child processes.

To ensure isolation of the server file-system, you would want to use the stack binary in a chroot jail, or another similar container solution.

To ensure isolation of the server memory and cpu, you will want to use the stack binary in a virtualized environment capable of monitoring and managing resource usage per process.

Credits

Author:

Special thanks to:

for their feedback in helping review early versions of this project.