Getting started with services

The dploy stack is composed of a many separate services interacting with each other. The majority of these services are built using zeromq as a transport the rest are built using HTTP to communicate. Since HTTP has a plethora of tools for creating related web services dploylib does not need to do much to aid that process. However, ZeroMQ based services can be more complicated to implement. The dploylib simplifies and unifies portions of this process through it’s concept of services.

What are services?

In dploy, services are complete applications composed of multiple dploy servers. Each of these servers reacts to input events on various zeromq sockets.

Simple Echo Service

The simplest way to understand services is to create a very simple service. Let’s start by creating a very simple echo service.

Here is one of the simplest services you could define:

from dploylib import servers
from dploylib.services import Service

class EchoServer(servers.Server):
    @servers.bind_in('request', 'rep')
    def echo_request(self, socket, received):
        socket.send_envelope(received)

service = Service()
service.add_server('echo', EchoServer)

if __name__ == '__main__':
    config_dict = {
        "servers": {
            "echo": {
                "request": {
                    "uri": "tcp://127.0.0.1:5000",
                },
            },
        },
    }
    service.run(config_dict=config_dict)

To run simply do:

$ python myservice.py

If you run the service you will be able to interact with it as follows:

>>> from dploylib.transport import Socket
>>> echo_socket = Socket.new('req', 'tcp://127.0.0.1:14445')
>>> echo_socket.send_text('hello')
>>> print echo_socket.receive_text()
hello

Fantastic! You now have a working echo server.

If you can’t tell, server and service definition was inspired by both flask and django web development frameworks. Let’s have a look at what just happened:

  1. First we import servers. This import will allow us to create our echo server easily.
  2. Next we import the Service class. If you’re familiar with flask, the service class is much like the Flask class in that it can be instantiated at the module level and run as an application later.
  3. For the next few lines we define the EchoServer class.
  1. The first line of the class makes it so we subclass from Server.
  2. The next line decorates the method echo_request. The decorator bind_in() provides instructions to the containing server class, EchoServer. It tells the server class the following:
    • Bind a zeromq REP socket named requests to the server
    • The _in suffix on the decorator means the decorated method is the socket’s input handler
  3. Finally, within the method echo_request, we define the echo server logic. It simply gets the data it receives and sends it back to the user. When using dploylib’s sockets, data is received in a standard envelope. This will be explained later.
  1. The line starting with service = instantiates a Service object into the module’s namespace. On the next line the EchoServer we defined on step 3 is registered to the service as a server named echo via the method add_server.
  2. A fake configuration is defined and set saved in config_dict
  3. Finally, the service is started by the method run(). It takes the fake configuration as the keyword argument, config_dict.

To stop the server, use control-C.

Standard service configuration

One of the things that we don’t want to do is hard code configuration. However, in the previous example we hard coded the configuration into the if __name__ == '__main__' block. Luckily, services are not meant to be used in this way, although the facility is available for easy debugging or testing if necessary. Let’s do a better job by using configuration files that we can change without touching any code.

dploy defines a standard configuration file that can is to be used with all services. The basic structure is able to translate to a multitude of configuration languages, but YAML is chosen by default due to it’s writability, readability and portability to other languages.

Here is a basic configuration:

servers:  # Server configurations
  echo:   # Config for "echo" server
    request:   # Config for "echo" server's "request" socket
      uri: tcp://127.0.0.1:14445  # URI for "request" socket

# General configuration
general:
  someconfig1: somevalue1
  someconfig2: somevalue2

If this file is saved to config.yaml we can simplify the previous service to this:

from dploylib import servers
from dploylib.services import Service

class EchoServer(servers.Server):
    @servers.bind_in('request', 'rep')
    def echo_request(self, socket, received):
        socket.send_envelope(received)

service = Service()
service.add_server('echo', EchoServer)

if __name__ == '__main__':
    service.run(config_file="config.yaml")

Now, all we have to do to change the uri of the request server is change the config.yaml file. At this time the service does not yet have a standard command line interface. This feature is planned for the not-so-distant future.