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:
- First we import
servers
. This import will allow us to create our echo server easily. - 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. - For the next few lines we define the
EchoServer
class.
- The first line of the class makes it so we subclass from
Server
.- The next line decorates the method
echo_request
. The decoratorbind_in()
provides instructions to the containing server class,EchoServer
. It tells the server class the following:
- Bind a zeromq
REP
socket namedrequests
to the server- The
_in
suffix on the decorator means the decorated method is the socket’s input handler- 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.
- 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 namedecho
via the methodadd_server
. - A fake configuration is defined and set saved in
config_dict
- 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.