Skip to content

Instantly share code, notes, and snippets.

@mattbennett
Last active September 3, 2019 11:44
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save mattbennett/1fdc9df9ccde3cd4798af5e47a714fce to your computer and use it in GitHub Desktop.
Save mattbennett/1fdc9df9ccde3cd4798af5e47a714fce to your computer and use it in GitHub Desktop.
Nameko + gRPC Article

Nameko + gRPC

I recently spent some time on a library that allows Nameko microservices to speak gRPC. It's available on PyPI and Github.

With this library it's possible for Nameko microservices to implement gRPC APIs as a server or call them as a client.

Here's an example implementing the Greeter service from the Python gRPC QuickStart:

# import definitions generated from protobufs
import helloworld_pb2_grpc
from helloworld_pb2 import HelloReply

# set up the Grpc entrypoint to implement the Greeter stub
from nameko_grpc.entrypoint import Grpc
grpc = Grpc.implementing(helloworld_pb2_grpc.GreeterStub)

class Service:
    name = "Greeter"

    @grpc
    def SayHello(self, request, context):
        return HelloReply(message='Hello, %s!' % request.name)

This doesn't look very different from an implementation using the standard Python client. But as we'll see, using Nameko gives you a lot of power.

What is Nameko?

Nameko is a toolkit for writing microservices in Python, but it's best thought of in three distinct ways:

1. As a framework

Nameko encourages a particular way of writing services which encourages separation of concerns and testability. It aims to let service developers concentrate on application logic rather than the nuts-and-bolts of external connectivity.

Nameko is extensible. It roughly uses the "ports and adapters" pattern, where each port or adapter is considered an "extension", and you can mix-and-match extensions to suit your needs.

2. As a library

Nameko comes with some "built-in" extensions out of the box. These are:

  • An AMQP RPC implementation
  • An AMQP pub-sub style event system
  • Flask-like HTTP handlers
  • A few other bits and pieces

This means you can get something useful up and running after just running pip install nameko, but they just scratch the surface of what you can do with Nameko.

3. As an ecosystem

The "extension" concept means it's easy to plug new capabilities into your Nameko service. There are lots and lots of Nameko extensions that have been open-sourced, of which nameko-grpc is just one.

You can leverage the power of the ecosystem to give your services new capabilities. For developers or companies that want to give back, contributing an extension to the ecosystem is a great and lightweight way to be involved.

Why gRPC?

Over the past few years, gRPC has become the defacto standard for RPC and is increasingly widely supported compared to similar projects like Apache Thrift.

Like Thrift, gRPC uses an Interface Definition Language (IDL) to define the API surface, and client libraries can be generated from the IDL in many different languages. This is a huge boon for interoperability.

gRPC uses Protocol Buffers as its IDL. Protobufs are an already well-established mechanism for describing and serializing structured data.

gRPC uses fast and now ubiquitous HTTP/2 as its transport. Whereas older RPC implementations had to create their own binary protocol and transport, gRPC is a fairly simple wrapper around HTTP/2 and Protobufs. This makes it relatively easy for tools and libraries to add support for gRPC, hence increasing usage.

gRPC versus AMQP RPC

When Nameko was first published, AMQP was a common choice for RPC implementations. Using an AMQP broker brought some advantages over peer-to-peer HTTP/1 connections, such as removing the need for service discovery and balancing, and ensuring message delivery.

Now, this choice seems outdated. It is better to solve the problems of service discovery and load-balancing with dedicated tools. Recently, service meshes like Istio have appeared that solve the service discovery problem and add many additional features: secure connections, rate-limiting, blue-green deployments, bolt-on observability, and more. Service meshes rely on direct peer-to-peer connections, rather than going through a broker, and Istio supports gRPC as a first-class citizen.

Combining gRPC and Nameko

It's easy to mistake Nameko as just an RPC implementation that might compete with gRPC. Indeed, that is the origin of the project -- back in distant 2014, long before gRPC existed.

Examples abound of using the "built-in" AMQP RPC extensions to write Nameko services, because it's included out of the box and makes for a simple demonstration.

But as we've seen, Nameko is much more than an RPC implementation -- it's a framework with a really nice developer experience, and it gives you access to an ecosystem of extensions that let you tap in to the power of open-source.

In contrast, the gRPC developer experience, at least in Python, is less than awesome. You get a scaffolding and a service runner, and it's easy to get something simple off the ground, but after that you're on your own.

The RPC implementation is very nicely taken care of and abstracted away, but there's no obvious way to do the same for anything else your service depends on. Developers are forced to roll their own.

There are so many things that a microservice may need to do beyond handle and respond to RPC requests. For example:

  • Persist state to a relational database
  • Generate trace information for observability
  • Report errors to an exception handling service
  • Dispatch events to for decoupled communication between services
  • ... and the list goes on (forever)

The Nameko framework and ecosystem can help you avoid reinventing these. We can extend the example from the beginning of this blog to add some of these features:

# import some extensions
from nameko.events import EventDispatcher
from nameko_sentry import SentryReporter
from nameko_sqlalchemy import Database

# import models for our app
from .models import Greeting, DeclarativeBase

# import definitions generated from protobufs
import helloworld_pb2_grpc
from helloworld_pb2 import HelloReply

# set up the Grpc entrypoint to implement the Greeter stub
from nameko_grpc.entrypoint import Grpc
grpc = Grpc.implementing(helloworld_pb2_grpc.GreeterStub)


class Service:
    name = "Greeter"

    sentry = SentryReporter()
    db = Database(DeclarativeBase)
    dispatch_event = EventDispatcher()

    @grpc
    def SayHello(self, request, context):

        # persist the greeting in RDBMS
        greeting = Greeting(name=request.name)
        self.db.session.add(greeting)
        self.db.session.commit()

		 # dispatch a greeting event for subscribing services
		 self.dispatch_event("greeting", request.name)

        # ... more application logic

        return HelloReply(message='Hello, %s!' % request.name)

This example extends the QuickStart Greeter service to persist the greetings to a relational database using nameko-sqlalchemy, and dispatch a Nameko event containing the greeting for any listening service to consume. It also reports any exceptions to Sentry using the nameko-sentry extension.

If you provide the model definitions (omitted for brevity) and a config file, this is a complete and valid service. You can find the whole thing in this gist.

Things to note are:

  1. How easy it is to add something like Sentry integration. There is no visible "plumbing" to distract from the service code, and this extension is already available in the ecosystem.

  2. The SayHello method is stateless. The side-effects - persistence to the database, and dispatching the event - are both abstracted away behind DependencyProviders (a special type of Nameko extension). This is an important separation of concerns, and Nameko provides tooling that makes testing them separately easy too.

  3. The service still looks very similar to the reference gRPC implementation in Python. This is intentional and should make transitioning existing code into Nameko easy.

Summary

Hopefully this article has made the case that Nameko and gRPC are a powerful combination.

gRPC is a fantastic RPC implementation that is being deployed more and more often. Nameko is a framework for writing microservices in Python that has a great developer experience, and you can now use it to write services that do gRPC and much more besides.

Postscript

My colleague Jakub has written a great article on using this library, including an example project that brings together Nameko, gRPC and GraphQL. Check it out!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment