Integration with a web framework.

251 views
Skip to first unread message

Thomas French

unread,
Nov 30, 2016, 12:25:46 PM11/30/16
to thespian.py
Hi Kevin.

Thanks again for your great work with thespian!

We're trying to expose some Actor state via a REST API, using, for example, Flask. Is this possible without having to reimplement a web server as an Actor, as you started in your httpserver example? 

Thanks.

Regards,

Thomas

Kevin Quick

unread,
Nov 30, 2016, 1:42:00 PM11/30/16
to thespian.py
Thanks for the kudos: it's great to hear that people are finding Thespian useful.

As you have probably discovered, the Python WSGI API is not oriented towards direct integration with the Actor model: WSGI internalizes all the socket and thread management and just invokes your supplied code as a synchronous call in an existing thread context.  The https://github.com/godaddy/Thespian/blob/master/examples/httpserver/http_server2.py that you referenced is an alternative approach that doesn't use WSGI; if your REST api does not need much of anything else that Flask provides then that example may be a reasonable approach for you to use.  

The alternative approach (which we have used with Django) is to have your Flask WSGI method talk to the Actor via the ActorSystem API:

    @ns.route('/status')
    class Status(Resource):
        def get(self):
            asys = ActorSystem(...)
            status_info = asys.ask(asys.createActor(fooActor, globalName('FooSingleton')),
                                   "get status", 
                                   timedelta(seconds=2))
            return {'status': status_info}, 200

    class fooActor(Actor):
        def receiveMessage(self, message, sender):
            if message == "get status":
                self.send(sender, { 'running': True })

There are a couple of things to note when using this approach (tl;dr the above works find if you use a multiprocTCPBase or multiprocUDPBase):  

* First, each WSGI invocation could occur on a brand-new thread, which means that it is difficult to rely on preserving variable values from one invocation to the next (i.e. the Status class instance could be a new instance for each WSGI callback); we observed this when using the gunicorn WSGI service.   

* The fooActor exists in the ActorSystem context; if you are using the multiprocTCPBase or multiprocUDPBase, this means that the ActorSystem and its actors (like fooActor) will persist even if the WSGI threads are recycled.  If you use the simpleSystemBase or the multiprocQueueBase, the actors may be killed (depending on the WSGI service implementation).  For the rest of the discussion, I will assume you are using the multiprocTCPBase or multiprocUDPBase; if you are not, we can explore those issue separately.

* The "asys=ActorSystem(..)" call above will automatically connect to and re-utilize the existing ActorSystem, which is what you want.  You will however need to explicitly shutdown the ActorSystem and remaining Actors when your overall application shuts down.

* The "globalName" parameter is used on the createActor call to re-obtain the address of a singleton top-level Actor (again, this is probably what you want to happen).  Without this globalName parameter, you would get a brand new Actor each time.  Trying to create the Actor once and store the address on the Status class instance does not work because of the WSGI behavior described above.  This may mean that you want to have a single entry-point Actor that can be referenced by the globalName that knows how to forward the external request to the various Actors you have running as appropriate.

* The timeout value on the ask() call is just to ensure that the ask doesn't hang forever in case something is wrong with your Actors or the target Actor is busy handling other requests.  Feel free to set the timeout value to whatever would be appropriate for your REST endpoint responsiveness.

As a final note, I had considered extending the https://github.com/godaddy/Thespian/blob/master/examples/httpserver/http_server2.py example to be a more fully-useful model for Actor-based HTTP/REST servers, but that's obviously a different approach than the customary WSGI implementation and the family of Python web frameworks (like Flask and Django) that are built on that model.  I would be curious to know if you think that a cleaner and more well-developed framework based on the http_server2 approach would be useful to people, or if the integration with existing WSGI servers that I described above is preferred.

Thanks,
  Kevin
Reply all
Reply to author
Forward
0 new messages