TwistedWeb on multicore/multiprocessor - twisted

What techniques are people using to utilize multiple processors/cores when running a TwistedWeb server? Is there a recommended way of doing it?
My twisted.web based web service is running on Amazon EC2 instances, which often have multiple CPU cores (8, 16), and the type of work that the service is doing benefits from extra processing power, so i would very much like to use that.
I understand that it is possible to use haproxy, squid or a web server, configured as a reverse proxy, in front of multiple instances of Twisted. In fact, we are currently using such a setup, with nginx serving as a reverse proxy to several upstream twisted.web services running on the same host, but each on different port.
This works fine, but what i'm really interested in, is a solution where there is no "front-facing" server, but all twistd processes somehow bind to the same socket and accept requests. Is such thing even possible... or am i being crazy? The operating system is Linux (CentOS).
Thanks.
Anton.

There are a number of ways to support multiprocess operation for a Twisted application. One important question to answer at the start, though, is what you expect your concurrency model to be, and how your application deals with shared state.
In a single process Twisted application, concurrency is all cooperative (with help from Twisted's asynchronous I/O APIs) and shared state can be kept anywhere a Python object would go. Your application code runs knowing that, until it gives up control, nothing else will run. Additionally, any part of your application that wants to access some piece of shared state can probably do so quite easily, since that state is probably kept in a boring old Python object that is easy to access.
When you have multiple processes, even if they're all running Twisted-based applications, then you have two forms of concurrency. One is the same as for the previous case - within a particular process, the concurrency is cooperative. However, you have a new kind, where multiple processes are running. Your platform's process scheduler might switch execution between these processes at any time, and you have very little control over this (as well as very little visibility into when it happens). It might even schedule two of your processes to run simultaneously on different cores (this is probably even what you're hoping for). This means that you lose some guarantees about consistency, since one process doesn't know when a second process might come along and try to operate on some shared state. This leads in to the other important area of consideration, how you will actually share state between the processes.
Unlike the single process model, you no longer have any convenient, easily accessed places to store your state where all your code can reach it. If you put it in one process, all the code in that process can access it easily as a normal Python object, but any code running in any of your other processes no longer has easy access to it. You might need to find an RPC system to let your processes communicate with each other. Or, you might architect your process divide so that each process only receives requests which require state stored in that process. An example of this might be a web site with sessions, where all state about a user is stored in their session, and their sessions are identified by cookies. A front-end process could receive web requests, inspect the cookie, look up which back-end process is responsible for that session, and then forward the request on to that back-end process. This scheme means that back-ends typically don't need to communicate (as long as your web application is sufficiently simple - ie, as long as users don't interact with each other, or operate on shared data).
Note that in that example, a pre-forking model is not appropriate. The front-end process must exclusively own the listening port so that it can inspect all incoming requests before they are handled by a back-end process.
Of course, there are many types of application, with many other models for managing state. Selecting the right model for multi-processing requires first understanding what kind of concurrency makes sense for your application, and how you can manage your application's state.
That being said, with very new versions of Twisted (unreleased as of this point), it's quite easy to share a listening TCP port amongst multiple processes. Here is a code snippet which demonstrates one way you might use some new APIs to accomplish this:
from os import environ
from sys import argv, executable
from socket import AF_INET
from twisted.internet import reactor
from twisted.web.server import Site
from twisted.web.static import File
def main(fd=None):
root = File("/var/www")
factory = Site(root)
if fd is None:
# Create a new listening port and several other processes to help out.
port = reactor.listenTCP(8080, factory)
for i in range(3):
reactor.spawnProcess(
None, executable, [executable, __file__, str(port.fileno())],
childFDs={0: 0, 1: 1, 2: 2, port.fileno(): port.fileno()},
env=environ)
else:
# Another process created the port, just start listening on it.
port = reactor.adoptStreamPort(fd, AF_INET, factory)
reactor.run()
if __name__ == '__main__':
if len(argv) == 1:
main()
else:
main(int(argv[1]))
With older versions, you can sometimes get away with using fork to share the port. However, this is rather error prone, fails on some platforms, and isn't a supported way to use Twisted:
from os import fork
from twisted.internet import reactor
from twisted.web.server import Site
from twisted.web.static import File
def main():
root = File("/var/www")
factory = Site(root)
# Create a new listening port
port = reactor.listenTCP(8080, factory)
# Create a few more processes to also service that port
for i in range(3):
if fork() == 0:
# Proceed immediately onward in the children.
# The parent will continue the for loop.
break
reactor.run()
if __name__ == '__main__':
main()
This works because of the normal behavior of fork, where the newly created process (the child) inherits all of the memory and file descriptors from the original process (the parent). Since processes are otherwise isolated, the two processes don't interfere with each other, at least as far as the Python code they are executing goes. Since the file descriptors are inherited, either the parent or any of the children can accept connections on the port.
Since forwarding HTTP requests is such an easy task, I doubt you'll notice much of a performance improvement using either of these techniques. The former is a bit nicer than proxying, because it simplifies your deployment and works for non-HTTP applications more easily. The latter is probably more of a liability than it's worth accepting.

The recommended way IMO is to use haproxy (or another load balancer) like you already are, the bottleneck shouldn't be the load balancer if configured correctly. Besides, you'll want to have some fallover method which haproxy provides in case one of your processes goes down.
It isn't possible to bind multiple processes to the same TCP socket, but it is possible with UDP.

If you wish to serve your web content over HTTPS as well, this is what you will need to do on top of #Jean-Paul's snippet.
from twisted.internet.ssl import PrivateCertificate
from twisted.protocols.tls import TLSMemoryBIOFactory
'''
Original snippet goes here
..........
...............
'''
privateCert = PrivateCertificate.loadPEM(open('./server.cer').read() + open('./server.key').read())
tlsFactory = TLSMemoryBIOFactory(privateCert.options(), False, factory)
reactor.adoptStreamPort(fd, AF_INET, tlsFactory)
By using fd, you will serve either HTTP or HTTPS but not both.
If you wish to have both, listenSSL on the parent process and include the ssl fd you get from the ssl port as the second argument when spawning the child process.
Complete snipper is here:
from os import environ
from sys import argv, executable
from socket import AF_INET
from twisted.internet import reactor
from twisted.web.server import Site
from twisted.web.static import File
from twisted.internet import reactor, ssl
from twisted.internet.ssl import PrivateCertificate
from twisted.protocols.tls import TLSMemoryBIOFactory
def main(fd=None, fd_ssl=None):
root = File("/var/www")
factory = Site(root)
spawned = []
if fd is None:
# Create a new listening port and several other processes to help out.
port = reactor.listenTCP(8080, factory)
port_ssl = reactor.listenSSL(8443, factory, ssl.DefaultOpenSSLContextFactory('./server.key', './server.cer'))
for i in range(3):
child = reactor.spawnProcess(
None, executable, [executable, __file__, str(port.fileno()), str(port_ssl.fileno())],
childFDs={0: 0, 1: 1, 2: 2, port.fileno(): port.fileno(), port_ssl.fileno(): port_ssl.fileno()},
env=environ)
spawned.append(child)
else:
# Another process created the port, just start listening on it.
port = reactor.adoptStreamPort(fd, AF_INET, factory)
cer = open('./server.cer')
key = open('./server.key')
pem_data = cer.read() + key.read()
cer.close()
pem.close()
privateCert = PrivateCertificate.loadPEM(pem_data )
tlsFactory = TLSMemoryBIOFactory(privateCert.options(), False, factory)
reactor.adoptStreamPort(fd_ssl, AF_INET, tlsFactory)
reactor.run()
for p in spawned:
p.signalProcess('INT')
if __name__ == '__main__':
if len(argv) == 1:
main()
else:
main(int(argv[1:]))

Related

Flask appbuilder large application

I have built a "Large" application using Flask-AppBuilder and have 2 questions I have not seen the answer to.
Is there any way to "split" a large application into multiple
components (similar to what Blueprints do).
My business logic has mostly ended up in the View's but... some of
it does not feel right there. Few things I have added to the
models, again does not feel right. This is logic that tends to
create a long running processes so I have been testing out Celery.
Any examples of either of these would be lovely.
it does not really matter what framework you use, but as soon as the application grows you may want to isolate critical logic. Both for the reasons you described above, but also to be future-proof (you may want to move to a new frontend in the future without rewrite the heavy lifting).
I ususally set up a redis worker for this, and use e.g. flask only to trigger the queue with function calls. That also makes the application more scalable (concurrent users, more data) as you can simply start more workers listening to your queue if needed.
In essence:
from redis import Redis
from rq import Queue
from rq.job import Job
conn = Redis()
q = Queue(connection=conn)
Then as example in the flask routes (for appBuilder, use the views, or create your own lib) call:
result = q.enqueue('utils.your_function_name',args=(id,))
Have a look at RQ here for more examples, also how to monitor the status of your jobs etc.
https://python-rq.org/

Using asyncio to talk to daemon

I have a python module that uses Telnetlib to open a connection to a daemon, and is able to send commands and get parse responses. This is used for testing purposes. However, sometimes the daemon will send asynchronous messages too. My current solution is to have a "wait_for_message" method that will start a thread to listen on the telnet socket. Meanwhile, in the main thread, I send a certain command that I know will trigger the daemon to send the particular async message. Then I simply do thread.join() and wait for it to finish.
import telnetlib
class Client():
def __init__(self, host, port):
self.connection = telnetlib.Telnet(host, port)
def get_info(self):
self.connection.write('getstate\r')
idx, _, _ = self.connection.expect(['good','bad','ugly'], timeout=1)
return idx
def wait_for_unexpected_message(self):
idx, _, _ = self.connection.expect(['error'])
Then when I write tests, I can use the module as part of the automation system.
client = Client()
client.connect('192.168.0.45', 6000)
if client.get_info() != 0:
# do stuff
else:
# do other stuff
It works really well until I want to handle async messages coming in. I've been reading about Python's new asyncio library, but I haven't quite figured out how to do what I need it to do, or even if my use case would be benefited by asyncio.
So my question: Is there a better way to handle this better with asyncio? I like using telnetlib because of the expect() features it has. Using a simple TCP socket does not do that for me.

PsychoPy : Displaying in main process and Time through a subprocess

I'm using a psychopy code that was done by a previous Phd student of the lab. This code aims to display stimuli (random dot kinematogram) and use a subprocess for precise timing.
The subprocess is created with the following line :
process = subprocess.Popen(['python', 'LPTmat.py'], shell=False, stdin=subprocess.PIPE, stdout=subprocess.PIPE)
Then when the first frame is displayed, the code writes a number to tell the subprocess to begin timing:
if first_frame == True:
process.stdin.write('%i\n'%1) #start timer and check of the PP
first_frame = False
The Subprocess then start the timer and record the button pressed (parallel port) :
while True:
input = sys.stdin.readline()
if input == '1\n':
timer.reset()
parallel_port_state = ctypes.windll.inpout32.Inp32(lptno)
while parallel_port_state == etat_repos:
parallel_port_state = ctypes.windll.inpout32.Inp32(lptno)
lecture_time = timer.getTime()
if parallel_port_state in port_gauche:
send_trigger (1)
elif parallel_port_state in port_droit:
send_trigger (2)
np.savetxt('mat.txt',mat)#a button has been pressed
The main process than detect the txt file and stop the stimulus presentation :
mtext= os.path.exists('mat.txt')
if mtext== True:#if the mat.txt file exists, a button has been pressed
myWin.flip()#black screen
process.stdin.write('%i\n'%3)
check = process.stdout.readline()#read which button has been pressed
check = int(check)
And go checking back the response time recorder by the subprocess and remove the txt file created :
process.stdin.write('%i\n'%2)
RT = process.stdout.readline()
RT = float (RT)
rep.rt = RT
os.remove('mat.txt')
The problem is that the .txt file created is not really a clean way to do the job so I was wondering if they were another way to use this subprocess and to tell the main process that a response was made ?
Cheers,
Gabriel
Asynchronous device polling was one of the main reasons that lead to the development of ioHub, which has been merged into PsychoPy last year or so. Essentially, ioHub creates a new Python process that only interfaces with your external devices, while the main PsychoPy process can continue to present stimuli, uninterrupted by device polling. At any desired time, for example after time-critical phases of stimulus generation and presentation have passed, the PsychoPy process can request the collected data from the ioHub process.
(Please also note that an ordinary Python thread is never executed really simultaneously in CPython due to the GIL; that is why ioHub creates a whole new process, not just another thread.)
ioHub already supports many different types of hardware and interfaces (serial port, eye trackers, different types of response button boxes etc.) Unfortunately, to my knowledge no parallel port support has been integrated to date.
But do not despair! I see there is already support for LabJack devices, which have replaced the steadily disappearing parallel port in many psychophysics and electrophysiology labs. These devices are relatively cheap (about USD 300) and can be connected to modern computers via a USB port. ioHub has a ready-to-use interface for the LabJacks, which is also demonstrated in this demo.
Another alternative, of course, would be to develop your own parallel port interface for ioHub. But given the vanishing popularity, availability, and therefore applicability of this interface, I am wondering whether this is really worth the effort.

What is the most efficient and concurrent way to implement a public API for an existing command-line program?

An existing command-line program generates some output when called with some parameters, and then exits.
I would like to modify this program to run on an event loop, and listen through a public API (could be in same machine). There seem to be multiple ways of implementing this:
make the API external to the program and do system calls
turn the program into a library, and include the necessary functionality into the API itself
local sockets (like a TCP line server, for eg.)
HTTP server (producing JSON, XML, etc)
Considering efficiency under load, concurrency and scalability, what would be the best approach?
Api-design also depends on the clients, maybe you can give some more information:
kind of client (real user, technical client polling things, etc.)
trust-level of clients (open to anyone, closed set of clients to be authenticated)
Regarding scalability requirements:
how many users
expected load (peak + average)
Another idea could be a simple ssh-server so anyone can execute the script from outside:
$ ssh user#yourhost.com yourCLIProgram.sh -param1 value1 -param2 value2
Then you don't need a overhead of event-listening loop.
What does the program do in more detail?

Simple way to rig an "Activity monitor" for a Twisted socket Factory

I'd like to have a real-time 'system status'/'activity monitor' console for my Twisted application.
The app is basically a protocol.ServerFactory which accepts connections performs different jobs.
Kind of like the twisted.manhole, I'm looking for the simplest way to create a admin application where I can check the current stats of my app.
The admin can be a simple ascii-based shell or html/json setup.
I'm aware that I could build this with a bunch of counters, a separate protocol for authenticating and monitoring these, but I'm thinking Twisted might already have such thing since it at least knows the number of connections, protocol types, etc etc.
Tips?
There's the unmaintained, slowly rotting twisted.internet.gladereactor. If you're using twistd, then you can use this trivally:
twistd --reactor debug-gui ...
If you're running the reactor directly yourself, then it's only slightly more effort:
from twisted.manhole import gladereactor
gladereactor.install()
from twisted.internet import reactor
...
The Inspect feature appears to have been broken for some time, but it will still show you a list of established connections and what state they are in, and it will also apparently give you a traffic log for each connection. Fixing Inspect may also be a fairly straightforward effort, in case you're looking for a little project. :)