Overview of Snorky¶
The following is an example server with Snorky exposing a simple Pub Sub service. The goal of this section is to explain each of the parts involved on it, both from Snorky and from Tornado.
import os from tornado.ioloop import IOLoop from tornado.web import Application from snorky import ServiceRegistry from snorky.request_handlers.websocket import SnorkyWebSocketHandler from snorky.services.pubsub import PubSubService if __name__ == "__main__": service_registry = ServiceRegistry() service_registry.register_service(PubSubService("messaging")) application = Application([ SnorkyWebSocketHandler.get_route(service_registry, "/ws"), ]) application.listen(8002, address="") try: print("Snorky running...") IOLoop.instance().start() except KeyboardInterrupt: pass
The I/O loop¶
tornado.ioloop.IOLoop is the Tornado event loop. Being an asynchronous server, there are no separate threads for each connection. Instead, all sockets are managed by this class.
IOLoop performs and endless loop in which tells the operating system to notify it of any event occurred in the managed sockets and in turn dispatches the event to the class that owns the socket. This loop starts when
IOLoop.instance().start() is called.
IOLoop is a singleton class. In normal usage you even don’t need to keep references to it, the framework manages this automatically. Most of the times you only need call
IOLoop.instance().start() and go on.
Services are classes which process messages. Messages are JSON entities, that is: they can be any data type representable with JSON, like strings, arrays (also called lists) or objects (also called dictionaries). Services often are stateful and track client connections.
Seen in the heading example,
snorky.services.pubsub.PubSubService is a service which accepts several methods:
publish. Clients can connect to the server and ask
PubSubService to join them to a certain channel (which is also JSON entity, typically a string). An user can send a
publish command to a channel which would trigger notifications in all those clients which joined that channel.
PubSubService is a simple service which can be useful in a number of situations, but often you will also write your own services, either from scratch or subclassing other services. Writing services explains this in detail.
ServiceRegistry class tracks a fixed number of services, each identified with a name, which should be a string.
ServiceRegistry is also responsible for delivery of messages to its services. There can be several instances tracking different services at the same time.
Wiring a backend explains how this can be used to offer both a public set of services on an interface, and a private one in other, often firewalled. This can be used for example to only allow a trusted machine to send certain types of events to the clients subscribed to the public services.
Request handlers are Tornado classes which are intended to respond to HTTP and WebSocket requests. They inherit from
tornado.web.RequestHandler and they are explained in detail in the Tornado documentation.
Snorky request handlers are associated with a service registry. Their job is to receive messages from the outside and forward them to the service registry, providing also the service with means to send messages in the other way.
Several request handlers can be attached to the same service registry or to independent registries.
Currently there are three request handlers bundled with Snorky:
snorky.request_handlers.websocket.SnorkyWebSocketHandler: Handles WebSocket connections.
snorky.request_handlers.sockjs.SnorkySockJSHandler: Handles SockJS connections, which are an abstraction layer of WebSocket providing fallbacks for old browsers which do not support it natively.
snorky.request_handlers.http.BackendHTTPHandler: This is a more limited request handler. It works over plain HTTP and each connection can only exchange one message from each party, one for the request, and one for the response. It’s usually used in order to expose a backend interface.
In Tornado, a
tornado.web.Application is a collection of request handlers that make up a web application.
This class manages a set of routes, each one consisting of an URL pattern, a request handler class and optionally a set of parameters which are fed to the
SnorkyWebSocketHandler.get_route() returns such a route for a WebSocket request handler.
tornado.web.Application.listen() sets up an HTTP server listening on the specified port and address. If no address is specified, it will listen in all interfaces, both in IPv4 and IPv6, if supported.
The following UML diagram resumes the collaborations explained above.
The next chapters will cover further details on the inner working of each of the components and how they can be extended.