It’s fair to ask why we’d use ØMQ for connecting our microservices, as opposed to using sockets directly like we did last chapter. The answer is that the Node.js community believes in the Unix philosophy: “do one thing well.” The committers keep the Node.js core small and tight, leaving everything else to the broader base of developers who publish their modules through npm.
Although the Node.js core has great, low-level support for binding and connecting to sockets, it leaves out higher-level messaging patterns. ØMQ’s purpose is to expose higher-level messaging patterns and take care of many low-level networking concerns for you. Take the following examples:
ØMQ endpoints automatically reconnect if they become unhitched for any reason—like if there’s a hiccup in the network or if a process restarts.
ØMQ delivers only whole messages, so you don’t have to create buffers to deal with chunked data.
ØMQ’s low-overhead protocol takes care of many routing details, like sending responses back to the correct clients.
With ØMQ, like with any good library, your application can focus on what really matters.
You may be asking yourself why we’d start with the ØMQ library rather than diving right into the most widely used protocol, HTTP. My main reason is that ØMQ affords the exploration of several different messaging patterns all in one package. You don’t have to piece together different solutions for your publish/subscribe and your request/reply patterns. ØMQ can do it all—and the exposure you’ll gain here will transfer to HTTP and other protocols.
Another reason is that ØMQ gives you the flexibility to design your architecture your way. Other messaging protocols, such as MQTT and AMQP, require a dedicated message broker to act as a central hub of activity in your system. With ØMQ, you get to decide which parts of your architecture will be more permanent and which will be transitory.
And another thing: HTTP is hard! It’s easy to overlook, but negotiating content between a client and a server over HTTP is a complex dance of headers and responses. The Node.js built-in http module has great low-level support, but you have to know what you’re doing to make proper use of it. We’ll explore HTTP in depth starting in Chapter 6, Commanding Databases.
Lastly, Node.js is about more than just making web servers. Since 2015, releases of Raspbian, the OS for the Raspberry Pi, have included Node.js. Learning ØMQ’s patterns and approach to distributed architecture will help you if you decide to venture into the realm of embedded Node.js systems and the Internet of Things.
With that preamble out of the way, let’s get everything set up so we can build fast, robust microservices in Node.js with ØMQ.
npm is your gateway to a large and growing pool of open source Node.js modules. They do everything from parsing streams to pooling connections to managing sessions.
To install a module from npm and save the dependency, you’ll need a package.json file. Start by creating a directory called microservices and navigate to this directory on the command line.
Then generate an initial package.json file there with npm init:
| | $ npm init -y |
| | Wrote to ./code/microservices/package.json: |
| | |
| | { |
| | "name": "microservices", |
| | "version": "1.0.0", |
| | "description": "", |
| | "main": "index.js", |
| | "scripts": { |
| | "test": "echo \"Error: no test specified\" && exit 1" |
| | }, |
| | "keywords": [], |
| | "author": "", |
| | "license": "ISC" |
| | } |
Take a look at the license attribute at the end. The default license that npm uses is ISC.[22] This is a permissive license written by the Internet Systems Consortium and is similar to the more familiar MIT license.[23]
We’ll discuss what the other package.json fields mean and how to configure them in future chapters. For now let’s move on to installing the zeromq module.
You’ll rarely write a Node.js application that doesn’t use at least one module from npm. We’ll use many external modules in this book, and right now we’re going to use zeromq, the official Node.js binding for ØMQ.
Modules managed by npm can be pure JavaScript or a combination of JavaScript and native addon code.[27]
Addons are dynamically linked shared objects—they provide the glue for working with native libraries written in C or C++.
The zeromq module should take care of building the necessary underlying libraries. Install zeromq like so:
| | $ npm install --save --save-exact zeromq@4.2.1 |
The --save flag tells npm to remember this dependency in your package.json under dependencies. These are runtime dependencies, in contrast to the development dependencies we used in Installing Mocha with npm.
Just like when we installed Mocha, here we’re using the --save-exact flag to explicitly depend on a particular version of the module. In your own projects you can choose to be less strict about which versions of modules you require, but in this book we’re going to use exact versions.
There’s nothing particularly special about version 4.2.1 of the zeromq module—it’s simply the most recent version available at the time of this writing. For your own development purposes you could leave off the version number when you run the npm install command, in which case npm will figure out the most recent version for you.
Let’s take a look at some of the output of npm install. This output has been truncated quite a bit for formatting reasons and to focus attention on the relevant parts.
| | $ npm install --save --save-exact zeromq@4.2.1 |
| | |
| | > zeromq@4.2.1 install ./code/microservices/node_modules/zeromq |
| | > prebuild-install || (npm run build:libzmq && node-gyp rebuild) |
| | |
| | prebuild-install info begin Prebuild-install version 2.1.2 |
| | prebuild-install info looking for local prebuild @ prebuilds/zeromq-v4.2.1-z... |
| | prebuild-install info looking for cached prebuild @ ~/.npm/_prebuilds/https-... |
| | prebuild-install http request GET https://github.com/zeromq/zeromq.js/releas... |
| | prebuild-install http 200 https://github.com/zeromq/zeromq.js/releases/downl... |
| | prebuild-install info downloading to @ ~/.npm/_prebuilds/https-github.com-ze... |
| | prebuild-install info renaming to @ ~/.npm/_prebuilds/https-github.com-zerom... |
| | prebuild-install info unpacking @ ~/.npm/_prebuilds/https-github.com-zeromq-... |
| | prebuild-install info unpack resolved to ./code/microservices/node_modules/z... |
| | prebuild-install info unpack required ./code/microservices/node_modules/zero... |
| | prebuild-install info install Prebuild successfully installed! |
| | microservices@1.0.0 ./code/microservices |
| | └─┬ zeromq@4.2.1 |
| | ├── nan@2.6.2 |
| | └─┬ prebuild-install@2.1.2 |
| | ├── ... |
| | |
| | npm WARN microservices@1.0.0 No description |
| | npm WARN microservices@1.0.0 No repository field |
Notice the call to prebuild-install toward the top. This is a command-line utility for downloading and installing prebuilt binaries for Node.js. In my case, it’s downloading the linux-x64 prebuilt binary, but the ØMQ team publishes binaries for Mac OS X and Windows as well.
If the prebuild-install fails, the fallback mechanism is to attempt to build the libzmq C library and then use node-gyp to rebuild the Node.js bindings for it. node-gyp is a cross-platform tool for compiling native addons. If anything went wrong with that build, the output should tell you, but remember that this is only a backup to the preferred prebuilt version.
When you run the preceding command, npm will download the zeromq module (and its dependencies) to a folder called node_modules under the current directory. To test that the module was installed successfully, run this command:
| | $ node -p -e "require('zeromq').version" |
| | 4.1.6 |
The -e flag tells Node.js to evaluate the provided string, and the -p flag tells it to print that output to the terminal. The zeromq module’s .version property is a string containing the version of the ØMQ base library it found—i.e., the prebuilt binary.
You may notice that the version number logged here is different from the version number of the zeromq module you installed (4.1.6 vs. 4.2.1). This is OK! The reason is that the .version property of the module object (4.1.6) reflects the version of the underlying libzmq binary that powers the Node.js module, not the version of the zeromq npm module (4.2.1). If you get a version number, then you’re all set. If you see an exception being thrown, then you might want to stop here and troubleshoot.
Now that the library and the Node.js module are installed, it’s time to start programming Node.js with ØMQ!