Skip to content

Echo application example

Anton Tyurin edited this page Oct 21, 2015 · 13 revisions

To demonstrate how to write cocaine applications using python-framework let's write simple echo-application that would send back any message that is sent to it and write some text to the log.

Writing cocaine application

Create python file 'echo.py' and place it to any path you want (e.g. /home/YOUR_NAME/cocaine-examples/).

We want to use cocaine logger and worker classes, so necessary import statements and shebang:

#!/usr/bin/env python

from cocaine.worker import Worker
from cocaine.logger import Logger

Let's make global logger object:

log = Logger()

Our main worker function is actually coroutine. In two words it will read message sent to it asynchronously, write it to the log and send it back. To receive message you have to call request.read() method, which is asynchronous itself and returns future-object with bind method which is used to bind callback and errorback. Seems not so simple, really? Instead of this you can just type message = yield request.read() and these steps will be done for you automatically like magic. From coroutine it will be sent read() request and program control returns with message variable filled with real message.

To send response you have to invoke write method from response object (it is defined in your function signature) - message in our case. At last it's recommended to close response stream.

At this point our function looks like this:

def echo(request, response):
    message = yield request.read()
    log.debug('Message received: "{0}". Sending it back ...'.format(message))
    response.write(message)
    response.close()

Our function is ready, but who and how can invoke it? Answers are: anyone who needs it and through event system. To register function in event processing system you have to define event and bind function object as event handler. As an example define 'doIt' event through cocaine.worker.Worker class:

W = Worker()
W.run({'doIt': echo})

Entire 'echo.py' file's content is:

#!/usr/bin/env python

from cocaine.worker import Worker
from cocaine.logging import Logger

log = Logger()

def echo(request, response):
    message = yield request.read()
    log.debug('Message received: "{0}". Sending it back ...'.format(message))
    response.write(message)
    response.close()

W = Worker()
W.run({'doIt': echo})

Deploying

After completing write code there is only few points left. First, we need to upload our application to the cocaine runtime. There are some properties needed for it which all located in special file called Manifest. Let's create it (manifest.json):

{
    "slave" : "echo.py"
}

There is only one property - slave - location of our application python file. To start an application you must provide some profile for it (profile.json):

{
    "pool-limit" : 4
}

At last only few steps left:

  • Make out application executable: chmod u+x echo.py
  • Start cocaine-runtime daemon if it is not running (providing some config file to it of course). If you want to see how logging option works, start cocaine-runtime as no-daemon.
  • Upload out application through cocaine-tools: cocaine-tool app upload --name Echo --manifest=manifest.json
  • Upload profile: cocaine-tool profile upload --name EchoProfile --profile=profile.json
  • Start out application: cocaine-tool app start --name Echo --profile=EchoProfile

Using

Phew, it is done. Now our echo application is part of cocaine cloud! Let's use it in real application:

#!/usr/bin/env python

from cocaine.services import Service

from tornado.gen import coroutine
from tornado.ioloop import IOLoop

service = Service('Echo')

@coroutine
def main():
    # open new stream
    channel = yield service.enqueue('doIt')
    # send a chunk of data to the worker handler 
    yield channel.tx.write('SomeMessage')
    # close the stream. To notify the worker, that we've finished our data stream.
    yield channel.tx.close()
    # read a reply
    chunk = yield channel.rx.get()
    print('Response received - {0}'.format(chunk))

if __name__ == '__main__':
    IOLoop.current().run_sync(main, timeout=5)

We create service object through Service class by passing our echo-service name ('Echo') in __init__ method. Services don't connect immediately, the conenction will be established, when we call it for the first time. If something goes wrong (e.g. service not found, cocaine-runtime not running etc), there will be exception raised. Also we use synchronous API by invoking enqueue method from service object. It returns future. We can yield it and get the work done by Torando. The result of the future is a channel object, which represents session (a pair of streams) between you and the application. You can send commands via tx and read replies via rx. Every method returns a future, so you have to yield it.

If all is done in right way there must be 'SomeMessage' message in stdout. Also cocaine-runtime log will contain something like (depending of your time and log settings):

[Mon Jun 10 17:46:25 2013] [DEBUG] app/Echo: Message received: 'SomeMessage'. Sending it back ...

That's all! Congratulations with your cocaine python framework experience.

Clone this wiki locally