floret

1.1.4 • Public • Published

Event Flow floret - Noun: one of the small flowers making up a composite flower head.

Floret Microservices

Overview

A microservice framework for node

Floret.js is a library for building microservices in nodejs environments.

Floret builds your microservice based upon the floret.json configuration file you provide. Configure service details (host, port, uri, etc), channels and subscriptions for event-driven, service-to-service communication, apis, gateway information and more.

Rapid service development

With a few lines of code, your floret microservice will:

  • self-register with an api gateway
  • create healthcheck endpoints
  • generate an Open API spec
  • create an api for managing pub/sub operations
  • discover channels and their subscribers from the gateway
  • discover subscriptions to other floret services

Architectural Requirements

Refer here to understand floret's microservice architecture.

Installation

Prerequisites

Floret requires node v8.x or higher.

Floret requires an api gateway for communication features. Kong API Gateway is currently supported.
See here for container-based deployment instructions.

$ npm i -s floret
  npm i -s floret-gateway-kong

Get Started with Example Guides and Projects

Guides

stand-alone service

hello-galaxy-microservices

Projects

floret-chat

Appendix

Architecture

Core libraries

Floret is built upon KOA. KOA provides the http middleware framework for writing web applications and apis.
Floret uses reactive programming concepts to handle observable incoming events. See rxjs for more details.

Floret Environment

Floret services are event driven, publishing and subscribing to event data via a central API Gateway. The illustration
below is an example of a floret. Each floret service provide specific functionality.

Ecosystem

Installing Kong API Gateway

Floret uses Kong API Gateway CE version > 0.14.x

The recommended way to run your gateway is with docker containers.
https://hub.docker.com/_/kong/

Example Run commands for Kong + Cassandra

postgres

docker run -d --name kong-database-14 \
                -p 5432:5432 \
                -e "POSTGRES_USER=kong" \
                -e "POSTGRES_DB=kong" \
                postgres:9.5

db staging

docker run --rm \
    --link kong-database-14:kong-database \
    -e "KONG_DATABASE=postgres" \
    -e "KONG_PG_HOST=kong-database" \
    kong:0.14.0-alpine kong migrations up

kong

docker run -d --name kong-14 \
    --link kong-database-14:kong-database \
    -e "KONG_DATABASE=postgres" \
    -e "KONG_PG_HOST=kong-database-14" \
    -e "POSTGRES_USER=kong" \
    -e "POSTGRES_DB=kong" \
    -e "KONG_PROXY_ACCESS_LOG=/dev/stdout" \
    -e "KONG_ADMIN_ACCESS_LOG=/dev/stdout" \
    -e "KONG_PROXY_ERROR_LOG=/dev/stderr" \
    -e "KONG_ADMIN_ERROR_LOG=/dev/stderr" \
    -e "KONG_ADMIN_LISTEN=0.0.0.0:8001" \
    -e "KONG_ADMIN_LISTEN_SSL=0.0.0.0:8444" \
    -p 8000:8000 \
    -p 8443:8443 \
    -p 8001:8001 \
    -p 8444:8444 \
    kong:0.14.0-alpine

Verifying installation

The default gateway api port is 8000. The default gateway admin port is 8001.

Verify installation with this url

curl -i -H "Accept: application/json" -H "Content-Type: application/json" -X GET http://localhost:8001/status
HTTP/1.1 200 OK
Date: Tue, 05 Jun 2018 16:30:53 GMT
Content-Type: application/json; charset=utf-8
Transfer-Encoding: chunked
Connection: keep-alive
Access-Control-Allow-Origin: *
Server: kong/0.12.2

{"database":{"reachable":true},"server":{"connections_writing":1,"total_requests":1312,"connections_handled":1291,"connections_accepted":1291,"connections_reading":0,"connections_active":1,"connections_waiting":0}

Basic Concepts

Custom APIs

Add custom API endpoints to the built-in floret http server.

app/routes/routes.js

    module.exports = (app) => {
        app.router.get('/hello-world', async(ctx, next) => {
            ctx.body = "Hello World";
        });
        
        app.router.post('/hello-world', async(ctx, next) => {
            console.log('Incoming message to /hello-world');
            console.log(ctx.body);
        });
    }
    
    // index.js
    ...
    const floret = new Floret(floretConfig);
    ...
    // include any apis (routes) your web api will serve
    const routes = require('./app/routes/routes')(floret);
    
    floret.listen(() => {
        // make your api public by registering it at the gateway
        floret.registerAPI(`hello-world`, '/hello-world', 'GET,POST').then( (res) => {
            console.log('api hello-world created at the api gateway');
        })
    });
    

Pub/Sub

Floret has a built-in pub/sub model for event-driven communication with other floret services.
Publishers emit messages on subscribable channels. A subscription to a channel in effect means that the published messages will be POSTed to the subscriber-provided api endpoint.

Subscriptions can be created dynamically after both the Publisher and Subscriber services are active.

Channels

Messages are published to channels, and channels can be subscribed to. Channels can be
defined statically or dynamically.

Static Channel
    const soapboxConfig = {
        "name": "soapbox",
        "description": "a channel for rants",
        "hostURL": floretConfig.serviceHost,
        "hostPort": floretConfig.servicePort,
        "uri": "/soapbox"
    };
    
    ...
    let soapboxChannel = new floret.Channel(soapboxConfig);
    floret.channels = [soapboxChannel];
    floret.listen(()=>{});
    ...
Dynamic Channel
    // create new channels under the /rooms/ uri
    floret.router.post('/rooms/:channel', async (ctx, next) => {
        let channelName = ctx.params.channel;
        let channel = app.channels[channelName];

        if (!channel){
            let newChannel = new floret.Channel({
                "name": room,
                "description": "general topics",
                "endpoint": `${floret.url}/rooms/${channel}`,
                "serviceName": floret.name,
                "hostURL": floret.host,
                "hostPort": floret.url,
                "uri": "/rooms/" + channelName
            });
            
            await app.addChannel(newChannel);
        }
    });

Subscriptions

Similar to a webhook, a subscription is the configured landing spot for incoming service messages.

Create a new subscription with multiple handlers
    ...
    const floret = new Floret(floretConfig);
    
    // creates a new handler function    
    let fooHandler = (ctx) => {
        let pkg = new floret.Package(ctx.body);
        console.log('received message: ' + JSON.stringify(pkg.payload))
    });
    
    let barHandler = (ctx) => {
        let pkg = new floret.Package(ctx.body);
        console.log('received message: ' + JSON.stringify(pkg.payload))
    });
    
    // create a subscription object    
    let bazSubscription = new floret.Subscription('fooSubscription', floret.service, floret.router, floret.gateway);
    
    // attach 1 or more handler functions to the subscription event 
    bazSubscription.observable.subscribe(fooHandler);
    bazSubscription.observable.subscribe(barHandler);
    
    // add to array of subscriptions, which will be invoked during floret initialization
    floret.addSubscription(bazSubscription);
    ...
Subscribe to a Floret Service by name and channel
    ...
    floret.listen().then(() =>{
        // subscribe to a service once floret is up.  specify a subscription
        let subscriberId = floret.subscribe('baz', 'bazChannel', bazSubscription);
    })
Subscribe a Floret Service to another via the REST API
    POST http://api-gateway:8000/baz/subscribe/
    Content-Type application/json
    body:
    {
        "name": "example-service",
        "url": "http://192.168.1.158:8084/example-service/subscription/bazSubscription",
        "channel": "bazChannel"
    }
Unsubscribe from a Floret Service
    ...
    floret.unsubscribe('baz', 'bazChannel', subscriberId);
Unsubscribe a Floret Service from another via the REST API
    POST http://api-gateway:8000/baz/unsubscribe/
    Content-Type application/json
    body:
    {
        "channelName": "baz",
        "url": "http://192.168.1.158:8084/example-service/subscription/bazSubscription",
        "channel": "bazChannel"
    }

Channel and Subscriber discovery process

When a floret service stands up, an immediate survey of the api gateway occurs. It immediately discovers
all of its channels registered at the gateway, and builds any it has not already created. When a new channel
is created, subscribers of each channel are discovered. They are attached and become observers of the channel.

Self-discovery of channels and subscribers allows horizontal scaling of floret services that stand up and
become aware of the services current state.

Documentation

Floret creates an Open API spec for your api at bootstrap. It exposes a /api-spec.json resource, and this url can be used by consuming applications. One such application may be Swagger UI, which is deployed as a floret core service called api-doc. Upon standing up, a notification of new documentation is sent to api-doc, which in turn hosts the spec.

Documenting Custom APIs

If you added custom apis with your service, you'll want to document them in the Open API spec. To do so, just add annotations above your api like this:

    /**
     * @swagger
     * /test/it/out:
     *   get:
     *     description: test doc
     *     responses:
     *       200:
     *         description: bar bang
     *     tags: [pulse]
     */
    floret.router.get('/test/it/out', (ctx) => {
        console.log('test/it/out')
    })

Once your documentation is registered with floret api-doc service, it will appear in swagger ui.

Swagger UI

Authors

clint brown - clint.brown.atx@gmail.com | github - cbrown23

Dependencies (14)

Dev Dependencies (10)

Package Sidebar

Install

npm i floret

Weekly Downloads

5

Version

1.1.4

License

Apache-2.0

Unpacked Size

1.41 MB

Total Files

32

Last publish

Collaborators

  • cred23