How can I use azure.servicebus with flask?
I Tried to use asyncio to run process_queue function, but it locked REST requests.
Now, I'm trying to use multiprocessing, but never executes the print("while True").
I'm looking for any good practice to use flask and azure message-queues (or message queues in general way).
My code is:
from multiprocessing import Process
from flask import Flask
from src.flask_settings import DevConfig
from src.rest import health
from src.rest import helloworld
import time
def create_app(config_object=DevConfig):
app = Flask(__name__)
app.config.from_object(config_object)
app.register_blueprint(health.blueprint)
app.register_blueprint(helloworld.blueprint)
return app
print("1")
from azure.servicebus import QueueClient, Message
# Create the QueueClient
queue_client = QueueClient.from_connection_string("Endpoint=sb://**********.servicebus.windows.net/;SharedAccessKeyName=RootManageSharedAccessKey;SharedAccessKey=*************", "queue1")
# Receive the message from the queue
def process_queue(sleep_time):
while True:
time.sleep(sleep_time)
print("while True")
with queue_client.get_receiver() as queue_receiver:
messages = queue_receiver.fetch_next(timeout=3)
for message in messages:
print(message)
message.complete()
p = Process(target=process_queue, args=(1, ))
p.start()
p.join()
print("2")
Thanks
Related
I am new to tornado and have an API that makes a blocking database call. Because of this blocking call, tornado isn't able to serve all the requests if multiple requests come at the same time.
I looked about it and found out that it could be solved using two approaches: Making the code asynchronous and/or using Process Pool Executors. My assumption here is that having multiple process pool executors is like having multiple processes on tornado to serve multiple requests. Every single example I looked at about implementing Process Pool Executor also makes the code asynchronous.
I cannot make the code asynchronous for the time being because it would require more code changes and so I was looking at simple fix using Process Pool Executors.
What I have currently
import tornado.ioloop
import tornado.web
def blocking_call():
import time
time.sleep(60)
return "Done"
class MainHandler(tornado.web.RequestHandler):
def get(self):
val = blocking_call()
self.write(val)
if __name__ == "__main__":
app = tornado.web.Application([(r"/", MainHandler)])
app.listen(8888)
tornado.ioloop.IOLoop.current().start()
What I tried
import tornado.ioloop
import tornado.web
from concurrent.futures import ProcessPoolExecutor
def blocking_call():
import time
time.sleep(60)
return "Done"
class MainHandler(tornado.web.RequestHandler):
def initialize(self, executor):
self.executor = executor
def get(self):
val = self.executor.submit(blocking_call)
self.write(val)
if __name__ == "__main__":
executor = ProcessPoolExecutor(5)
app = tornado.web.Application(
[(r"/", MainHandler, dict(executor=executor))])
app.listen(8888)
tornado.ioloop.IOLoop.current().start()
My problem with this approach is that now I am getting a future object instead of actual response. How do I make the Get request wait for self.executor to complete before sending back the response?
The executor.submit() returns a concurrent.futures.Future which is not awaitable.
I suggest you use Tornado's run_in_executor method to execute the blocking task.
async def get(self):
loop = tornado.ioloop.IOLoop.current()
val = await loop.run_in_executor(self.executor, blocking_call)
self.write(val)
I have a flask app with waitress that gets some data in post-request, then it runs some long computations in long_function and returns result. These computations are parallel and I'm using pebble because I need a timeout option. Also I want the user to be able to send a request to restart the server (i.e. he want to change the number of threads for waitress)
I've found this solution https://gist.github.com/naushadzaman/b65534d912f1551c7d8366b326b7a151
It mostly works, but it doesn't interact well with my pebble pool. I'm having trouble reloading the server while it is in the pool. If I use long_function_without_pool, that doesn't use any multiprocessing, I can reload server even if it is currently does some job (results are lost, of course, but this is what I want). But with long_function I have to wait for the pool to be closed and only then can I restart the server. If I try to send restart request while the pool is still open, I get an error:
OSError: [Errno 98] Address already in use
So I suppose that p.terminate() doesn't work if there is a Pool running.
How can I fix this code, or maybe should I use a different solution?
Brief instructions on how to replicate this error:
start the app
send POST-request with empty body to http://localhost:5221/
before you get a response (you'll have 5 seconds) send GET-request without variables to http://localhost:5221/restart/
enjoy. Server is stuck now and is not responding to anything
import subprocess
from flask import Flask
from flask_restful import Api, Resource
from flask_cors import CORS
from webargs.flaskparser import parser, abort
import json
import time
import sys
from waitress import serve
from multiprocessing import Process, Queue
from concurrent.futures import TimeoutError
from pebble import ProcessPool, ProcessExpired
import functools
some_queue = None
APP = Flask(__name__)
API = Api(APP)
CORS(APP)
#APP.route('/restart/', methods=['GET'], endpoint='start_flaskapp')
def restart():
try:
some_queue.put("something")
print("Restarted successfully")
return("Quit")
except:
print("Failed in restart")
return "Failed"
def start_flaskapp(queue):
global some_queue
some_queue = queue
API.add_resource(FractionsResource, "/")
serve(APP, host='0.0.0.0', port=5221, threads=2)
def long_function():
with ProcessPool(5) as pool:
data = [0, 1, 2, 3, 4]
future = pool.map(functools.partial(add_const, const=1), data, timeout=5)
iterator = future.result()
result=[]
while True:
try:
result.append(next(iterator))
except StopIteration:
break
except TimeoutError as error:
print("function took longer than %d seconds" % error.args[1])
return(result)
def long_function_without_pool():
data = [0, 1, 2, 3, 4]
result = list(map(functools.partial(add_const, const=1), data))
return(result)
def add_const(number, const=0):
time.sleep(5)
return number+const
class FractionsResource(Resource):
#APP.route('/', methods=['POST'])
def post():
response = long_function()
return(json.dumps(response))
if __name__ == "__main__":
q = Queue()
p = Process(target=start_flaskapp, args=(q,))
p.start()
while True: #wathing queue, if there is no call than sleep, otherwise break
if q.empty():
time.sleep(1)
else:
break
p.terminate() #terminate flaskapp and then restart the app on subprocess
args = [sys.executable] + [sys.argv[0]]
subprocess.call(args)
I have a RPC Server using zerorpc in Python, written this way
import zerorpc
from service import Service
print('RPC server - loading')
def main():
print('RPC server - main')
s = zerorpc.Server(Service())
s.bind("tcp://*:4242")
s.run()
if __name__ == "__main__" : main()
It works fine when I create a client
import zerorpc, sys
client_rpc = zerorpc.Client()
client_rpc.connect("tcp://127.0.0.1:4242")
name = sys.argv[1] if len(sys.argv) > 1 else "dude"
print(client_rpc.videos('138cd9e5-3c4c-488a-9b6f-49907b55a040.webm'))
and runs it. The print() outputs what this 'videos' function returns.
But when I try to use it this same code inside route from a Flask app, I receive the following error:
File "src/gevent/__greenlet_primitives.pxd", line 35, in
gevent.__greenlet_primitives._greenlet_switch
gevent.exceptions.LoopExit: This operation would block forever Hub:
The flask method/excerpt
import zerorpc, sys
client_rpc = zerorpc.Client()
client_rpc.connect("tcp://127.0.0.1:4242")
#app.route('/videos', methods=['POST'])
def videos():
global client_rpc
client_rpc.videos('138cd9e5-3c4c-488a-9b6f-49907b55a040.webm')
I can't find out what might be happening. I'm quite new to Python and I understand that this may have some relation with Flask and how it handles the thread, but I can't figure out how to solve it.
zerorpc depends on gevent, which provides async IO with cooperative coroutines. This means your flask application must use gevent for all IO operations.
In your specific case, you are likely starting your application with a standard blocking IO WSGI server.
Here is a snippet using the WSGI server from gevent:
import zerorpc
from gevent.pywsgi import WSGIServer
app = Flask(__name__)
client_rpc = zerorpc.Client()
client_rpc.connect("tcp://127.0.0.1:4242")
#app.route('/videos', methods=['POST'])
def videos():
global client_rpc
client_rpc.videos('138cd9e5-3c4c-488a-9b6f-49907b55a040.webm')
# ...
if __name__ == "__main__":
http = WSGIServer(('', 5000), app)
http.serve_forever()
Excerpt from https://sdiehl.github.io/gevent-tutorial/#chat-server
This question already has an answer here:
Flask and Tornado Applciation does not handle multiple concurrent requests
(1 answer)
Closed 5 years ago.
I am trying to run flask application on tornado server to check the asynchronous request handling. I have two files 'flask_req.py' and 'tornado_ex.py'. My both files looks like below:
flask_req.py
from flask import Flask
from flask import request
app = Flask(__name__)
#app.route('/hello',methods=['GET'])
def hello():
print "hello 1"
time.sleep(20)
x= 2*2
print(x)
return "hello"
#app.route('/bye',methods=['GET'])
def bye():
print "bye 1"
time.sleep(5)
y = 4*4
print(y)
return "bye"
tornado_ex.py
from __future__ import print_function
from tornado.wsgi import WSGIContainer
from tornado.web import Application, FallbackHandler
from tornado.websocket import WebSocketHandler
from tornado.ioloop import IOLoop
from tornado import gen
from tornado.httpclient import AsyncHTTPClient
import time
from flask_req import app
class WebSocket(WebSocketHandler):
def open(self):
print("Socket opened.")
def on_message(self, message):
self.write_message("Received: " + message)
print("Received message: " + message)
def on_close(self):
print("Socket closed.")
#gen.coroutine
def fetch_and_handle():
"""Fetches the urls and handles/processes the response"""
urls = [
'http://127.0.0.1:8080/hello',
'http://127.0.0.1:8080/bye'
]
http_client = AsyncHTTPClient()
waiter = gen.WaitIterator(*[http_client.fetch(url) for url in urls])
while not waiter.done():
try:
response = yield waiter.next()
except Exception as e:
print(e)
continue
print(response.body)
if __name__ == "__main__":
container = WSGIContainer(app)
server = Application([
(r'/websocket/', WebSocket),
(r'.*', FallbackHandler, dict(fallback=container))
])
server.listen(8080)
fetch_and_handle()
IOLoop.instance().start()
I want to check the asynchronous behavior of handling the request using tornado server. Right now when I am running it, when both the URL are passed, it is waiting for 20 sec+5sec=25 sec. I want to run it something like that if one request is taking time then it should process the other request so that from above code the total waiting time it should take is only 20 sec, not 25 sec. How I can achieve this behavior here. Right now when I am running the above code as I am getting the response as:
$ python tornado_ex.py
hello 1
4
bye 1
16
hello
bye
after printing 'hello1' it's waiting for 25 sec and then doing further processing and after printing 'bye1' it is again waiting for 5sec. What I want is after printing 'hello1', if it is taking so much time then it should process '/bye'.
Using the WSGI container means only one request is handled at a time and a subsequent request is not handled until the first is complete.
Using Tornado to run WSGI applications is generally not a good idea when you need concurrency.
Either use multiple processes or convert you project to use ASYNC TornadoWeb framework instead of WSGI.
I want to implement Server Side Events in my application. I'm using Flask with a gevent backend. I mostly follow the official snippet, with two important distinctions:
The function that fires events is spawned as a separate process (an instance of multiprocessing.Process), instead of gevent.spawn. It sends "inner" event
Event firing is done via logging, with QueueHandler from logutils
Here's a minimal example:
from flask import Flask, Response
import time
import logging
from gevent.wsgi import WSGIServer
from gevent.queue import Queue
import multiprocessing as mp
from logutils.queue import QueueHandler
app = Flask(__name__)
logger = logging.getLogger('events')
logger.setLevel(logging.INFO)
#app.route("/publish")
def publish():
logger = logging.getLogger('events')
def notify():
time.sleep(1)
logger.info('inner')
logger.info('outer')
p = mp.Process(target=notify)
p.start()
return "OK"
#app.route("/subscribe")
def subscribe():
def gen():
logger = logging.getLogger("events")
q = Queue()
handler = QueueHandler(q)
logger.addHandler(handler)
while True:
result = q.get().message
yield result
return Response(gen(), mimetype="text/event-stream")
if __name__ == "__main__":
root = logging.getLogger()
root.addHandler(logging.StreamHandler())
root.setLevel(logging.INFO)
app.debug = True
server = WSGIServer(("", 5000), app)
server.serve_forever()
The problem is, StreamHandler catches both "inner" and "outer" events and logs them to stdout. QueueHandler, on the other hand, only pushes the "outer" event to the queue, so it fails to catch the "inner" event, which originates in other process.
Now, if I use multiprocessing.Queue instead of gevent.queue, the "/subscribe" route blocks. If I use standard werkzeug server (with threading) in conjunction with multiprocessing.Queue, the code above works as expected.
What's going on and how to tackle this?