I am working on a simple Flask-SocketIO app that streams data data from a server to the users browser in real time. I am stuck though when trying to use emit() in a loop. The behavior I am getting is that emit() does not actually send any data to the client until the entire loop is finished executing.
Here is some example code that shows the same behavior:
@socketio.on('rcv_args_and_run')
def rcv_args_and_run(args):
numy = 1
while numy < 10:
socketio.emit('results','Message from server #' + str(numy))
print('Message from server #' + str(numy))
time.sleep(5)
numy += 1
disconnect()
I am developing this locally using the flask/flask-socketio dev server and when I do not have the eventlet package installed it performs as expected, I believe because it then uses long polling. When I install eventlet though I start to get this behavior.
I have looked around at similar issues but can't exactly tell if the behavior I am getting would be solved by the steps you recommended they take.
Try switching time.sleep(5) to socketio.sleep(5). The sleep function from the Python library is not compatible with eventlet, you have to either monkey patch it, or use eventlet's own sleep function. The socketio.sleep() function routes the sleep call to the proper place, depending on what asynchronous framework you are using.
Ok that seems to work! So, I don't know a lot about Eventlet, is it considered an asynchronous framework? Or is there some other package/library I should be using that you are alluding to?
Yes, eventlet is an async framework, and so is gevent. Many functions from the Python standard library are incompatible with these frameworks, so they provide their own replacements for them. Both give you the option to "monkey patch" the Python library, so that when you call time.sleep() the call goes to eventlet.sleep() or gevent.sleep(), but if you don't want to do that, you can call the proper function directly, or use the socketio wrapper, as I suggested.
Functions that are related to I/O and threading are in the category of functions that need evenlet/gevent replacements, so if you plan to do anything around those sets of functions keep in mind you need to use the functions that come with the framework and not Python's.
Thanks so much!
So, by using the the flask socketio wait function I was able to eliminate the temporary blocking of messages that were being sent to the client in the example code I posted prior.
However, now I am trying to integrate that technique into my real code and running into some issues. What is a good technique to find out what piece of my code is causing the blocking?
I am also a bit confused as to why the blocking is occurring in the first place because of my unfamiliarity with eventlet. Correct me if I am wrong, but I assume what happens under the hood is that when my code gets to your emit() function/method it uses an eventlet function that adds that message to be sent to some kind of message queue. I am thinking that for some reason, something in my code is causing eventlet to not get any cpu time until my loop is completely done. At which point evenlet gets cpu time and then sends all the queued messages.
I am also going to try monkey patching my code so that all the incompatible io calls are rerouted to compatible eventlet calls automatically.
I am thinking that for some reason, something in my code is causing eventlet to not get any cpu time until my loop is completely done
Yes, this is the most likely problem. If you call any of the incompatible functions, they are going to work just fine, but evenlet is blocked. The easiest solution is to monkey patch the library to see if that helps. That takes care of making network I/O and threading calls compatible. If you are not doing anything outside of these, then you should be fine.
Hum...it seems like the monkey patching might have worked! I used this document and it does seem to explain things very well: http://eventlet.net/doc/patching.html.
I guess the idea solution would have been to wrote my code from the perspective of using Eventlet from the beginning and to only use eventlet/flask socket io approved io methods.
I need to learn more about Evenlet and asynchronous programming with Python in the future!
I am still confused as to good debugging techniques to identify what line of code is actually causing the blocking in the future.
Regardless, thanks so much for you quick responses! Do you have a donation link or something to help support Flask-socketio?
I am still confused as to good debugging techniques to identify what line of code is actually causing the blocking in the future
Yeah, I sort of avoided your question. I'm not sure there is a way to figure this out, because as I said above, nothing really breaks, just eventlet is suspended for the duration of the offending call.
Usually monkey patching takes care of the problem. One aspect not covered by monkey patching is file I/O. With files there is no easy way to make I/O non-blocking, because OSes do not have great support for async file I/O. In particular Linux is pretty bad at this, and most implementations of async file I/O are done with background threads in the application space. So if you plan on doing a lot of file I/O, keep in mind that those operations will be blocking unless you implement your own solution to make them non-blocking.
Yeah, I sort of avoided your question. I'm not sure there is a way to figure this out, because as I said above, nothing really breaks, just eventlet is suspended for the duration of the offending call.
Ok good, I was worried there was some good debugging method I wasn't thinking of! :)
Have a great day!
Most helpful comment
Try switching
time.sleep(5)tosocketio.sleep(5). The sleep function from the Python library is not compatible with eventlet, you have to either monkey patch it, or use eventlet's own sleep function. Thesocketio.sleep()function routes the sleep call to the proper place, depending on what asynchronous framework you are using.