By Itamar Turner-Trauring: See Microservices.com Practitioner Summit for upcoming event information.

 

You may have heard about microservices: the trendy new architecture that lets you ship features quickly. All you have to do is spend three years rebuilding your organizational structure and rewriting your existing code. Sounds promising, right?

It doesn’t have to be done that way, though. In many ways microservices are more of a development methodology than an architecture: you implement a small feature in a separate server.

-That means:

  • Your code is easier to understand and test, since it’s a small server doing one single thing.
  • You can release faster, since you’re not tied to your existing large codebase and its release cycle.
  • You can use a different technology stack if you want.

Once you’ve built that feature you need just enough new infrastructure to get it running, and then you can move on to the next feature. Over time you will end up with many services, and that will require more infrastructure. But you can add that incrementally as you learn more about your needs.

Let’s build a microservice

A classic use case for having a separate server is using a different technology stack. Let’s say your existing server is built in Python using Flask, and you want to use Node to write a particular endpoint of your website in JavaScript.

Step 1: implement the microservice

Implementing a simple Node server that does a single task is easy. Here’s a “hello world” example that just returns some JSON:

var express = require('express');
var app = express();
app.get('/', function (req, res) {
     res.json({"hello": "world"});
})
app.listen(8080);

Your actual service will be more complicated, of course. Given it’s only implementing one small feature it should still be pretty small.

Step 2: expose it to the world

Your existing Flask application is the public face of your website, and probably does some useful and necessary tasks like authenticating users. Instead of figuring out some complicated way of exposing your new service publicly and passing around authentication tokens, let’s start with the easy solution.

Your Flask application will act as a proxy, sending requests to the Node service. That means users will be authenticated by the time they hit Node, and you don’t have to worry about exposing Node directly to the outside world.

Here’s what the code would look like in your Flask application:

import requests  # HTTP client library
from flask import Flask, jsonify
app = Flask(__name__)
# ... existing code here...
@app.route("/hello")
def hello():
    # By this point we're already authenticated.
    result = requests.get("http://nodeserver:8080/").json()
    # Pass the JSON on to the browser. Alternatively could
    # render it to HTML if that's how your application works.
    return jsonify(result)
if __name__ == '__main__':
    app.run()

Step 3: protect against slowness

If your new Node server starts being slow this will also impact your Flask application: the thread pool or process pool will be used up waiting for Node. To solve that we can add a timeout to the HTTP request:

node_response = requests.get("http://nodeserver:8080/", timeout=1.0)
result = node_response.json()

And that’s it: you’ve got the code for your microservice, and you’ve got code to access it from your main application.

Planning for the future

Now that you have your first microservice implemented, it’s worth thinking about what additional functionality you’ll need when you’re running this microservice in production.

Problem #1: load balancing and discovery

Eventually you’ll want to have multiple copies of the Node service running, and multiple microservices. How will services find each other? Do you need to start worrying about setting up a load balancer for each new microservice?

Problem #2: logs are no longer centralized

Until now you’ve had a single application, a single source of logs. You only needed to look in one place when you debugged a problem.

What happens now that you have multiple processes? You want the ability to see a trace of the logs, from both Flask and Node, for a specific user request that spans both processes. And the more services you the worse this problem will get.

Problem #3: malfunctioning services

You added timeouts to make your main application more robust against slowness. But this only helps somewhat. What happens if your broken out services start breaking in other ways, or some are running slow enough to cause timeouts to be hit consistently?

You want some way to automatically blacklist microservices that are causing problems, so that they don’t impact your main application.

Problem #4: distributing information

In your centralized Flask application you know who the user is, and other relevant information needed to handle requests. How do you get this information to other services?

You can pass it through manually but that means you can’t just pass the relevant JSON back and forth, you have to design an extendable way for passing attributes in general.

Introducing the Microservices Development Kit (MDK)

To solve all of these problems you can use the Microservices Development Kit (MDK), which is available in Javascript, Ruby, Python and Java.

  • The MDK provides service discovery, by default using a SaaS, and client-side load balancing. Just register in the server and clients will be able to find the server automatically.
  • The MDK allows you to trace logs across multiple processes, and view them in a centralized location, Datawire’s Mission Control.
  • The MDK includes a circuit breaker that will automatically blacklist malfunctioning nodes. Malfunctioning doesn’t just mean having network issues, either: exceptions inside your code will also cause relevant nodes to be automatically blacklisted.
  • The MDK supports encoding a session, including information like deadlines and user-specified properties, and then decoding it on the other side.

You can use the MDK with your existing frameworks and clients, but to make it easier to use we provide wrappers for commonly used libraries. In the following example we’ll use the Flask and Express.js integrations.

Example: using the MDK

Here’s what your application would need to do to use the MDK:

import logging
import requests
from flask import g, Flask, jsonify
from mdk.flask import mdk_setup
app = Flask(__name__)
# ... existing code here...
@app.route("/hello")
def hello():
    ssn = g.mdk_session
    # Set a 1-second deadline (which will be preserved in the distributed session):
    ssn.setDeadline(1.0)
    # Log something using the MDK:
    ssn.info("main", "This will get sent to Datawire Mission Control.")
    # Store the user on the session:
    ssn.setProperty("myapp:user_id", g.user.id)
    # Send a request to the microservice, using MDK discovery to find
    # its location:
    url = ssn.resolve("node-hello", "1.0")
    result = requests.get(url, timeout=ssn.getRemainingSeconds(),
                          headers={"X-MDK-CONTEXT": ssn.externalize()})
    return jsonify(result.json())
if __name__ == '__main__':
    # Hook up the MDK to the Flask application:
    mdk_setup(app)
    app.run()

The MDK actually includes a wrapper for the requests library which would reduce boilerplate even more, but I didn’t use it to make it clear you can still use the same client libraries you’ve always used. Similarly you can route your native logging, e.g. Python logging, to the MDK.

Here’s what the Node server will look like:

var express = require('express');
var mdk_express = require('datawire_mdk_express');
// Register with MDK's service discovery:
mdk_express.mdk.register("node-hello", "1.0", "http://myaddress:8080/")
var app = express();
app.use(mdk_express.mdkSessionStart);
app.get('/', function (req, res) {
    // Log to same MDK session as server:
    req.mdk_session.info("node", "Got request from server");
    // Extract username from the MDK session:
    var user_id = req.mdk_session.getProperty("myapp:user_id");
    res.json({"hello": "world"});
})
app.use(mdk_express.mdkErrorHandler);
app.listen(8080);

And that’s it! You’ve got service discovery, distributed logging (visible via Datawire Mission Control), circuit breakers, distributed sessions… all with minimal effort.

Microservices: start today!

As you can see, writing your first microservice is easy. You don’t need a multi-year architecture plan, you don’t need a huge amount of infrastructure; you just need enough to deploy and run your services. And using the MDK you can easily make your microservices more robust and easier to debug.

So don’t wait; write your first microservice today!

Code Your First Microservice in an Hour

About The Author
-

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>