• Stars
    star
    631
  • Rank 71,222 (Top 2 %)
  • Language
    JavaScript
  • License
    MIT License
  • Created almost 10 years ago
  • Updated almost 4 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Clustering solution for Meteor with load balancing and service discovery

Cluster Build Status

Clustering solution for Meteor with load balancing and service discovery.

TLDR; With cluster, we can scale Meteor apps by simply installing a Meteor package. No need to use tools like Nginx or HaProxy. It's built for Meteor and you don't need to worry about configuring IP addresses and so on. Just add more instances and let cluster take care of load balancing.

Cluster also has first class support for MicroServices.

Table of Contents

Concept

When we need to scale Meteor, it's about scaling DDP requests since that's where the traffic flows. When we need to do horizontal scaling, we generally put a load balancer like Ngnix or HaProxy in front of Meteor. Then, it'll do the load balancing.

Okay, what happens when we need to add another instance? We'd need to reconfigure the load balancer again. That will reset all the existing DDP connections.

Then what if the load balancer goes down? To fix that, we'd need to add a load balancer working parentally. That makes the setup more complex.


Cluster has an interesting way to solve this problem. It turns your Meteor app into a load balancer and you don't need to use a separate tool for that. When you add a new server (or an instance), you wouldn't even need to reconfigure cluster. Cluster will simply detect new instances and route traffic to them.

Any of the instances within the cluster can act as a load balancer. So, even if one server goes down, you don't need to worry much. Also, it's configured specially for Meteor.

Cluster can do these things because it acts as a service discovery solution. Currently, that is implemented using MongoDB, but we can have more implementations later.

Since cluster is a service discovery solution, it's perfect for MicroServices. Read to the end :)

Getting Started

Simply add cluster into your app.

meteor add meteorhacks:cluster

Then when you are deploying or starting your app, export the following environment variables.

# You can use your existing MONGO_URL for this
export CLUSTER_DISCOVERY_URL=mongodb://host:port/db,
# this is the direct URL to your server (it could be a private URL)
export CLUSTER_ENDPOINT_URL=http://ipaddress
# mark your server as a web service (you can set any name for this)
export CLUSTER_SERVICE=web

Now start as many servers as you like and DDP traffic will be sent to each of the instances randomly. You can also remove instances anytime without affecting the cluster or your app.

Live Demo - How to use cluster to scale your app

API

We have a very simple API and there are two version of the API:

  1. JavaScript API
  2. Environment Variables based API

For a production app, it's recommended to use the Environment Variables based API.

JS API

// Connect to the cluster with a MongoDB URL. Better if it's a replica set
var connectOptions = {
  // Value of 0 to 1, mentioning which portion of requestes to process here or proxy
  // If 1, all the requests allocated to this host will get processed
  // If 0.5 half of the requsted allocated to this host will get processed, others will get proxied
  // If 0, only do proxying 
  selfWeight: 1 // optional
};

Cluster.connect("mongodb://mongo-url", connectOptions)

// Register a service to the cluster
var options = {
  endpoint: "a direct url to the instance",
  balancer: "balancer URL, if this is a balancer" // optional
  uiService: "service to proxy UI" // (optional) read to the end for more info
};

Cluster.register("serviceName", options);

// Expose services to the public
Cluster.allowPublicAccess(["service1", "service2"]);

// Discover a DDP connection
// > This is available on both the client and the server
Cluster.discoverConnection("serviceName");

Environment Variables based API

# Connect to the cluster with a MongoDB URL. Better if it's a replica set
export CLUSTER_DISCOVERY_URL=mongodb://mongo-url

# Register a service to the cluster
export CLUSTER_ENDPOINT_URL="a direct url to the instance"
export CLUSTER_SERVICE="serviceName"

export CLUSTER_BALANCER_URL="balancer URL, if this is a balancer" #optional
export CLUSTER_UI_SERVICE="ui-service-name" #optional - read to the end for more info

# Expose services to the public
export CLUSTER_PUBLIC_SERVICES="service1, service2"

# Check JS API's connectOptions.selfWeight for docs
export CLUSTER_SELF_WEIGHT="0.6"

Multi-Core Support

Cluster has the multi-core support as well. You can run your Meteor app utilizing all the cores in your server as follows:

Make sure you've added the meteorhacks:cluster package.

Then expose following environment variable:

export CLUSTER_WORKERS_COUNT=auto

That’s all you have to do. Now your Meteor app will use all the cores available on your server.

You can also specify the number of workers explicitly like this:

export CLUSTER_WORKERS_COUNT=2

For more information follow this article.

If you are using Meteor Up to deploy your app, make sure you've the latest version.

MicroServices

With Microservices, we build apps as a set of tiny services rather than create a monolithic app. These services can be deployed independently.

Cluster is a tool which has built-in support for Microservices. With cluster, you can manage a set of Microservices very easily. Cluster has many features that are perfect for Microservices:

  • Register and Discover Services
  • Discover DDP Connections in both client and server
  • Load Balancing and Failovers

A Simple app based on Microservices

For example, if we want to build our own version of Atmosphere to search Meteor packages and we decided to build it with Microservices. In this case, we'd have two such services:

  • search - handles searching
  • web - has the UI

Each of these services is it's own Meteor app.

Service Registration & Discovery

First we need a Mongo URL for the cluster. That's how cluster communicates with each node. It's better if you can create a separate MongoDB ReplicaSet for that. It doesn't need to have oplog support.

Next, we add the following configuration to the search service inside server/app.js.

Cluster.connect("mongodb://mongo-url");
Cluster.register("search");

// Meteor methods
Meteor.methods({
  "searchPackages": function(searchText) {
    return ["a list of packages"];
  }
});

Then we add the following configuration to server/app.js to the web service.

Cluster.connect("mongodb://mongo-url");
Cluster.register("web");
Cluster.allowPublicAccess("search");

var searchConn = Cluster.discoverConnection("search");
var packagesFromMeteorHacks = searchConn.call("searchPackages", "meteorhacks");
console.log("here is list of", packagesFromMeteorHacks);

In the above example, you can see how we made a connection to the "search" service from the "web" service.

We've also allowed clients to connect directly to the search service from the browser (or from cordova apps). That was done with a single line:

Cluster.allowPublicAccess("search");

This way, you can access the "search" service from the client side of the "web" service as shown below:

var searchConn = Cluster.discoverConnection("search");
searchConn.call("searchPackages", "meteorhacks", function(err, packages) {
  if(err) throw err;
  console.log("here is list of", packages);
});

Learn More about Microservices

If you'd like to learn more, here are a few resources for you.

Multiple Balancers

The setup we discussed earlier in the getting-started section still has one issue. All the DDP connections are routing through a single instance which is normally the server you pointed your domain name to via DNS.

But that's not ideal. If it goes down you lose access to the whole cluster. Additionally, you will likely be facing scaling issues as well since all traffic is routing through that single server.

Cluster has a built in solution for that. It's called Balancers. A Balancer is an instance of your cluster which acts as a load balancer. You can add or remove them as needed.

Making your instance a Balancer is pretty simple. Just export the following environment variable.

export CLUSTER_BALANCER_URL=https://subdomain.domainname.com

This URL is open to public and it should point to this instance. Now configure your instances to run as Balancers and your cluster will start to load balance DDP connections through them.

Demo & Presentation - Learn more about Balancers

UI Service

In cluster, there is a special concept called "UI Service". By default, if you visit a service, it's UI will be served to you. For an example, let's say we've two services:

  • web - the user inteface of our app (on port 7000)
  • search - the service which expose a search API (on port 8000)

So, if you visit the web app on port 7000, you'll get the UI of the web app. If you visit the search app on port 8000, you'll get the UI of the search app.

But it's possible, search apps to give the UI of the "web" app as well. With that we can make sure, all the services in the cluster exposes the same UI. For that, simply expose following environment variable.

export CLUSTER_UI_SERVICE="web"

Practical Setup

Let's see how you could setup Cluster in a practical scenario. BulletProof Meteor is already running Cluster so I will show you an excerpt of its setup. (I've changed some information for the educational purposes)

We have four Servers and three of them are Balancers. This is how they are structured.

{
  "ip-1": {
    "endpointUrl": "http://ip-1",
    "balancerUrl": "https://one.bulletproofmeteor.com"
  },
  "ip-2": {
    "endpointUrl": "http://ip-2",
    "balancerUrl": "https://two.bulletproofmeteor.com"
  },
  "ip-3": {
    "endpointUrl": "http://ip-3",
    "balancerUrl": "https://three.bulletproofmeteor.com"
  },
  "ip-4": {
    "endpointUrl": "http://ip-4"
  }
}

I'm using Meteor Up to deploy and here's a sample configuration file. With Meteor Up, you don't need to expose CLUSTER_ENDPOINT_URL. It'll automatically do that by itself.

Make sure you install the latest version of Meteor Up.

DNS & SSL

We'll use cloudflare for DNS and SSL setup.

We turn off WebSockets since cloudflare does not support SSL with WebSockets yet!

As this setup, ip-1 and ip-2 will take care of load balancing for static content while ip-1, ip-2 and ip-3 is take care of load balancing DDP connections.

All 4 servers process DDP and provide Static Content.

More Repositories

1

npm

Complete NPM integration for Meteor
JavaScript
508
star
2

meteord

MeteorD - Docker Runtime for Meteor Apps for Production Deployments
Shell
439
star
3

meteor-ssr

Server Side Rendering for Meteor
JavaScript
264
star
4

sikka

Sikka - A Firewall for Meteor Apps
JavaScript
258
star
5

kadira

Performance Monitoring for Meteor
JavaScript
217
star
6

meteor-aggregate

Proper MongoDB aggregations support for Meteor
JavaScript
189
star
7

picker

Server Side Router for Meteor
JavaScript
182
star
8

meteor-down

Load testing for Meteor
JavaScript
175
star
9

search-source

Reactive Data Source for Search
JavaScript
146
star
10

flow-components

Build your Meteor app with Components.
JavaScript
132
star
11

unblock

this.unblock inside publications :D
JavaScript
87
star
12

meteor-inject-initial

Allow injection of arbitrary data to initial Meteor HTML page
JavaScript
80
star
13

meteor-async

Set of async utilities to work with NPM modules inside Meteor
JavaScript
63
star
14

goddp

DDP Server implemented with golang
Go
47
star
15

meteorx

Exposing some of the internal Meteor API prototypes
JavaScript
41
star
16

mup-frontend-server

Frontend Server for Meteor Up
Nginx
37
star
17

kube-init

Easiest way to deploy a Kubernetes Cluster to learn Kubernetes
Shell
34
star
18

zones

Zone.js integration for meteor
JavaScript
30
star
19

meteorhacks.github.io

MeteorHacks Website
HTML
29
star
20

hyperkube

Hyperkube
Shell
20
star
21

inject-data

A way to inject data to the client with initial HTML
JavaScript
18
star
22

kadira-profiler

CPU Profiling support for Kadira
JavaScript
14
star
23

bddp

DDP like binary protocol implemented using cap'n proto
Go
12
star
24

repeeet

making repeeet tweeting super simple
JavaScript
11
star
25

code-standards

Code Standards for MeteorHacks projects
9
star
26

js-pipes

MongoDB aggregation pipeline implementation in JavaScript
JavaScript
9
star
27

meteor-customer-io

Customer.io Integration for Meteor
JavaScript
9
star
28

cluster-performance

Performance Test for Cluster
JavaScript
8
star
29

docker-librato

Forward all stats from all running docker containers to Librato
JavaScript
8
star
30

kdb

ACID High Performance Time Series DB for any kind of storage - No it isn't
Go
7
star
31

meteor-todo-app

Simple Todo App with Meteor
JavaScript
5
star
32

gocluster

Meteor Cluster protocol implemented for Go
Go
4
star
33

meteor-collection-utils

Expose some underline collection apis
JavaScript
4
star
34

find-faster-chat-demo

Simple Chat To Demo Meteor Fast Finder Use
JavaScript
3
star
35

ddptest

Test DDP servers
JavaScript
3
star
36

zones-simple-example

Simple Example Meteor App with Zones
JavaScript
2
star
37

mongo-search

Simple API to use MongoDB Full Text search with Meteor
2
star
38

kmdb

metric database powered by kdb
Go
2
star
39

meteor-down-backdoor

Backdoor Meteor package for MeteorDown
JavaScript
2
star
40

kadira-binary-deps

Binary Dependencies for Kadira
JavaScript
2
star
41

simple-rpc-go

A really simple and fast binary RPC middleware
Go
2
star
42

kmdb-node

NodeJS client for kmdb
JavaScript
2
star
43

nsqd-to-librato

Send NSQ stats to librato
JavaScript
2
star
44

lean-components-example

lean-components-example
JavaScript
2
star
45

meteor-zone-example

Example meteor app for meteor-zone package
JavaScript
1
star
46

node-histo-utils

A set of utilities to create, merge and manage histograms
JavaScript
1
star
47

blaze-ext

Create blaze templates with .blaze extension
1
star