Mixing Web and WAMP code with Twisted Klein

, Tobias Oberstein

A tutorial that shows how to combine Web and WAMP application components in one system.

Preface

This post (part 2/2) about Twisted Klein assumes basic knowledge of Python, Twisted and Autobahn. Read the previous part 1:

WAMP is an application messaging protocol on top of WebSocket that allows to create applications from loosely coupled components that communicate in real-time. Read more about WAMP:

Introduction

In the first part of this post, we saw how to use Twisted Klein to write Flask-like Web handlers that can run asynchronous code. We implemented a HTTP/POST handler that in turn issued an outgoing request to a Web service before completing.

Using Web services to create apps which are assembled from decoupled, interacting services or components is one way to tame complexity. It allows you to break down your app into manageable parts. Additionally, you can mix and match services implemented in different languages.

However, Web services - being based on HTTP - come with their own limitations. They are strictly request-response.

Now WAMP (The Web Application Messaging Protocol) provides a protocol for communication between application components that isn't limited in this way.

Using WAMP, application components can talk over WAMP with each other - in both directions and in real-time (since WAMP is running over WebSocket).

And WAMP provides us with nice, high-level interactions instead of raw messaging:

With both patterns available in one protocol, we can create applications from loosely coupled application components that can talk to each other freely, without limitations.

For a more detailed introduction to WAMP, please see here.

Here we'll show how to implement a WAMP application component and how to use it from Twisted Klein Web code.

Top

Going WAMP

You can find all the code for this example on GitHub here.

To run the following examples, you will need to install:

pip install autobahn[twisted] klein

The WAMP Part

Alright. Let's create a WAMP application component that can square numbers! Awesome ;)

from autobahn.twisted.wamp import Application

app = Application()


@app.register('com.example.square')
def square(x):
   print("square() called with {}".format(x))
   return x * x


if __name__ == "__main__":
   app.run("ws://localhost:9000", "realm1", standalone = True)
   

As you can see, this looks quite similar to Flask/Klein:

  1. We create a app application object (line 3). As with Flask/Klein, you can name you app object anything you like.
  2. We define a function to square numbers (line 7). We also print something when being called.
  3. We make this function callable from other components by using the @app.register() decorator (line 6). The function will now be remoted under the URI ("name") com.example.square for others to call via WAMP.
  4. We start our app (line 13) for development (running an embedded WAMP router via standalone = True).

Calling in from JavaScript

You can now call square from any WAMP client - no matter what language (see here for currently supported ones).

For example, here is how to call square from JavaScript running in a browsers:

 var connection = new autobahn.Connection({
    url: "ws://localhost:9000", realm: 'realm1'
 });

 connection.onopen = function (session) {

    console.log("connected");

    session.call("com.example.square", [23]).then(
       function (res) {
          console.log("result:", res);
       },
       function (err) {
          console.log("error:", err);
       }
    );
 };

 connection.open();
   

To run this, put the above code into the following frame:

When you open this in your browser (while watching the JavaScript console), you should see the result of 23 squared being logged. Also, the Python part will log that is was indeed being called by someone.

The Web Part

Great. Now the Web part of our app. Remember, we want to call our new shiny square function from within a Web request handler.

First the code, then the explanation:

from twisted.internet.defer import inlineCallbacks, returnValue
from klein import Klein
from autobahn.twisted.wamp import Application

app = Klein()
wampapp = Application()


@app.route('/square/submit', methods = ['POST'])
@inlineCallbacks
def square_submit(request):
   x = int(request.args.get('x', [0])[0])
   res = yield wampapp.session.call('com.example.square', x)
   returnValue("{} squared is {}".format(x, res))


if __name__ == "__main__":
   import sys
   from twisted.python import log
   from twisted.web.server import Site
   from twisted.internet import reactor
   log.startLogging(sys.stdout)

   reactor.listenTCP(8080, Site(app.resource()))
   wampapp.run("ws://localhost:9000", "realm1", standalone = False)
   

What we do is:

  1. We create app objects for both Klein (line 5) and WAMP (line 6).
  2. In our Web request handler (line 11), we extract the form variable from the HTTP/POST (line 12) and then call our remote procedure com.example.square (line 13)
  3. Since we use a co-routine / "synchronous looking" style, we need to use inlineCallbacks, yield and returnValue
  4. This time, the Klein app is started slightly differenty (line 24). The reason is that Klein's app.run() method will never return, since it internally starts the Twisted reactor. But we want to enter wampapp.run() (which also start the Twisted reactor).
  5. And we run our WAMP app connecting to the development router we started above (line 25). This time we use standalone = False, so no developement router will be started in this process.

To test, run the WAMP Part from above, run this Web Part, and then open the following in your browser:

When you submit the form, our Web code (the Klein based request handler for /square/submit) will receive HTML form data via a plain old HTTP/POST.

Then, upon receiving the HTTP/POST, the request handler will perform an asynchronous call to a WAMP procedure before returning.

This is just an example. Request handlers can:

  • call remote procedures, using app functionality provided in other application components (both local and remote)
  • publish events, received by other application components (running locally, remote or in the browser)

Here is another example which is using publish & subscribe.

Summary

WAMP allows to create systems from loosely coupled application components talking to each other - in real-time and in any direction.

Using Twisted Klein we can use WAMP application components directly from within Web request handlers.

The combination allows us to create powerful applications with Web and WAMP parts. Since everything is running asynchronously, we won't run into performance problems due to blocking. It'll happily handle large numbers of concurrent requests and connections.

And most importantly: we can tame complexity by building apps from components - both Web and WAMP.

Top

Start experimenting and prototyping for IoT applications with Crossbar.io and Raspberry Pi!

Loaded with all the software you need to make the board part of WAMP applications!



Learn more

Recent posts

Atom Feed

Search this Site

Stay Informed

Sign up for our newsletter to stay informed of new product releases and features: