How to stop WsgiServer in Linux - linux

I am a newbee in cherrypy.
I just tried with a sample program of wsgiserver.The program is as follows
from cherrypy import wsgiserver
def my_crazy_app(environ, start_response):
status = '200 OK'
response_headers = [('Content-type','text/plain')]
start_response(status, response_headers)
return ['Hello world!']
server = wsgiserver.CherryPyWSGIServer(
('127.0.0.1', 8080), my_crazy_app,
server_name='localhost')
server.start()
I got the output Hello world successfully,
but the problem is when i hit Ctrl-c on the terminal to stop the server, its not getting stopped. How to do it?

IIRC, the wsgiserver itself is not associated to any signal and therefore doesn't respect the SIGINT interruption.
Only the higher level CherryPy engine will provide that. If you can't use it, you probably want to
install a handler using the Python signal module.
Well, something along those lines will do the job:
import signal
from cherrypy import wsgiserver
def my_crazy_app(environ, start_response):
status = '200 OK'
response_headers = [('Content-type','text/plain')]
start_response(status, response_headers)
return ['Hello world!']
server = wsgiserver.CherryPyWSGIServer( ('127.0.0.1', 8080), my_crazy_app, server_name='localhost')
def stop_server(*args, **kwargs):
server.stop()
signal.signal(signal.SIGINT, stop_server)
server.start()

Related

Flask server using asynchronous Rpc client only answer 1 request out of two

I'm trying to implement an async RPC client within a Flask server.
The idea is that each request spawn a thread with an uuid, and each request is going to wait until there is a response in the RpcClient queue attribute object with the correct uuid.
The problem is that one request out of two fails. I think that might be a problem with multi-threading, but I don't see where it comes from.
Bug can be seen here.
Using debug print, it seems that the message with the correct uuid is received in the _on_response callback and update the queue attribute in this instance correctly, but the queue attribute within the /rpc_call/<payload> endpoint doesn't synchronize (so queue[uuid] has a value of response in the RpcClient callback but still None in the scope of the endpoint).
My code:
from flask import Flask, jsonif
from gevent.pywsgi import WSGIServer
import sys
import os
import pika
import uuid
import time
import threading
class RpcClient(object):
"""Asynchronous Rpc client."""
internal_lock = threading.Lock()
queue = {}
def __init__(self):
self.connection = pika.BlockingConnection(
pika.ConnectionParameters(host='rabbitmq'))
self.channel = self.connection.channel()
self.channel.basic_qos(prefetch_count=1)
self.channel.exchange_declare(exchange='kaldi_expe', exchange_type='topic')
# Create all the queue and bind them to the corresponding routing key
self.channel.queue_declare('request', durable=True)
result = self.channel.queue_declare('answer', durable=True)
self.channel.queue_bind(exchange='kaldi_expe', queue='request', routing_key='kaldi_expe.web.request')
self.channel.queue_bind(exchange='kaldi_expe', queue='answer', routing_key='kaldi_expe.kaldi.answer')
self.callback_queue = result.method.queue
.
thread = threading.Thread(target=self._process_data_events)
thread.setDaemon(True)
thread.start()
def _process_data_events(self):
self.channel.basic_consume(self.callback_queue, self._on_response, auto_ack=True)
while True:
with self.internal_lock:
self.connection.process_data_events()
time.sleep(0.1)
def _on_response(self, ch, method, props, body):
"""On response we simply store the result in a local dictionary."""
self.queue[props.correlation_id] = body
def send_request(self, payload):
corr_id = str(uuid.uuid4())
self.queue[corr_id] = None
with self.internal_lock:
self.channel.basic_publish(exchange='kaldi_expe',
routing_key="kaldi_expe.web.request",
properties=pika.BasicProperties(
reply_to=self.callback_queue,
correlation_id=corr_id,
),
body=payload)
return corr_id
def flask_app():
app = Flask("kaldi")
#app.route('/', methods=['GET'])
def server_is_up():
return 'server is up', 200
#app.route('/rpc_call/<payload>')
def rpc_call(payload):
"""Simple Flask implementation for making asynchronous Rpc calls. """
corr_id = app.config['RPCclient'].send_request(payload)
while app.config['RPCclient'].queue[corr_id] is None:
#print("queue server: " + str(app.config['RPCclient'].queue))
time.sleep(0.1)
return app.config['RPCclient'].queue[corr_id]
if __name__ == '__main__':
while True:
try:
rpcClient = RpcClient()
app = flask_app()
app.config['RPCclient'] = rpcClient
print("Rabbit MQ is connected, starting server", file=sys.stderr)
app.run(debug=True, threaded=True, host='0.0.0.0')
except pika.exceptions.AMQPConnectionError as e:
print("Waiting for RabbitMq startup" + str(e), file=sys.stderr)
time.sleep(1)
except Exception as e:
worker.log.error(e)
exit(e)
I found where the bug came from:
Thedebug=True of the line app.run(debug=True, threaded=True, host='0.0.0.0') restart the server at the beginning.
The whole script is then restarted from the beginning. Because of it, another rpcClient is initialized and consume from the same queue. Problem is that the previous thread is also running. This cause two rpcClient to consume from the same thread, with one that is virtually useless.

how to put a method in to thread and use it when performing the test

I have this part of code which is doing psubscribe to redis. I want to run this part of code in a thread an working in the background while the other part of code will check some notifications from this below.
def psubscribe(context, param1, param2, param3):
context.test_config = load_config()
RedisConnector(context.test_config["redis_host"],
context.test_config["redis_db_index"])
redis_notification_subscriber_connector = RedisConnector(context.test_config["notification__redis_host"],
int(param3),
int(context.test_config[
"notification_redis_port"]))
context.redis_connectors = redis_notification_connector.psubscribe_to_redis_event(param1,
timeout_seconds=int(
param2)
)
what I have done till now: but its not running :(
context.t = threading.Thread(target=psubscribe, args=['param1', 'param2', 'param3'])
context.t.start()
It is actually working. I think you didn't need actually to pass context variable to your psubscribe function.
Here is an example:
Start http server that listens on port 8000 as a background thread
Send http requests to it and validate response
Feature scenario:
Scenario: Run background process and validate responses
Given Start background process
Then Validate outputs
background_steps.py file:
import threading
import logging
from behave import *
from features.steps.utils import run_server
import requests
#given("Start background process")
def step_impl(context):
context.t = threading.Thread(target=run_server, args=[8000])
context.t.daemon = True
context.t.start()
#then("Validate outputs")
def step_impl(context):
response = requests.get('http://127.0.0.1:8000')
assert response.status_code == 501
utils.py file
from http.server import HTTPServer, BaseHTTPRequestHandler
def run_server(port, server_class=HTTPServer, handler_class=BaseHTTPRequestHandler):
server_address = ('', port)
httpd = server_class(server_address, handler_class)
httpd.serve_forever()

Flask-socketio doesn't recieve message from client

I'm trying to write a basic Socket.io program where the python client (python-socketio[asyncio_client] 4.6.0) emits a single string message to the flask server (with Flask-SocketIO 4.3.1 and eventlet).
The client appears to connect and send the message properly, but there is no output seen at the Flask server.
Server code:
from flask import Flask
from flask_socketio import SocketIO, emit
app = Flask(__name__)
app.config['SECRET_KEY'] = 'secret!'
socketio = SocketIO(app)
#socketio.on('connect')
def test_connect():
print('connected')
#socketio.on('disconnect')
def test_disconnect():
print('Client disconnected')
#socketio.on('message')
def handle_message(msg):
print('Recieved',msg)
#socketio.on('json')
def handle_json(json):
print(str(json))
if __name__ == '__main__':
socketio.run(app,debug=True)
Client code:
import asyncio
import socketio
sio = socketio.AsyncClient()
#sio.event
def connect():
print('connection established')
#sio.event
def disconnect():
print('disconnected from server')
async def main():
await sio.connect('http://localhost:5000')
await sio.emit('message',data='detection')
print('message sent')
await sio.disconnect()
if __name__ == '__main__':
asyncio.run(main())
Server output:
PS C:\Users\daksh\sih\sihPython> python .\test_socketio.py
* Restarting with stat
* Debugger is active!
* Debugger PIN: 101-561-255
(16664) wsgi starting up on http://127.0.0.1:5000
(16664) accepted ('127.0.0.1', 59497)
connected
127.0.0.1 - - [23/Jul/2020 20:38:42] "GET /socket.io/?transport=polling&EIO=3&t=1595516920.71801 HTTP/1.1" 200 367 0.004934
Client disconnected
127.0.0.1 - - [23/Jul/2020 20:38:42] "GET /socket.io/?transport=websocket&EIO=3&sid=88790300120f4b899e019ae7cc16ee87&t=1595516922.7757218 HTTP/1.1" 200 0 0.010027
Client output:
PS C:\Users\daksh\sih\sihPython> python .\socketio-client.py
connection established
message sent
The print statement from handle_message() is missing in the server output.
I've gone through multiple tutorials online, and I've tried it with and without namespaces. Have not been able to figure out what's wrong.
Any help is appreciated.
(I'm using Python 3.8.3 on Windows 10)
UPDATE: It works if I change the client code to use socketio.Client() instead of AsyncClient(), however I need the client to connect using AsyncClient.
The problem is that your async client is obviously asynchronous, you can't just send and exit because you don't give the background tasks that support the Socket.IO protocol time to do their thing.
Here is a more robust version of the client that lets the event go through before exiting:
import asyncio
import socketio
sio = socketio.AsyncClient()
#sio.event
async def connect():
print('connection established')
await sio.emit('message',data='detection', callback=done)
print('message sent')
#sio.event
def disconnect():
print('disconnected from server')
async def done():
await sio.disconnect()
async def main():
await sio.connect('http://localhost:5000')
await sio.wait()
if __name__ == '__main__':
asyncio.run(main())
The trick here is to use a callback on the emit. When the callback is invoked you are sure the message was delivered, so at that point it is safe to disconnect.

Can't stop aiohttp websocket server

I can't cancel my aiohttp websocket server from within the application. I want to stop the server and shutdown when I get a "cancel" string
from the client. Yes, I get it, and I finish my co-routine (websocket_handler), but there are three co-routines from the aiohttp library which still continue working.
Of course, I can invoke asyncio.get_event_loop().stop() at the end of my co-routine, but is there a graceful way for stopping aiohttp server?
From my code one can see that I've tried to use Application().on_shutdown.append(), but it failed.
What is the right way?
#!/usr/bin/env python
# -- coding: utf-8 --
import os
import asyncio
import signal
import weakref
import aiohttp.web
from aiohttp import ClientConnectionError, WSCloseCode
# This restores the default Ctrl+C signal handler, which just kills the process
#https://stackoverflow.com/questions/27480967/why-does-the-asyncios-event-loop-suppress-the-keyboardinterrupt-on-windows
import signal
signal.signal(signal.SIGINT, signal.SIG_DFL)
HOST = os.getenv('HOST', 'localhost')
PORT = int(os.getenv('PORT', 8881))
async def testhandle(request):
#Сопрограмма одрабатывающая http-запрос по адресу "http://127.0.0.1:8881/test"
print("server: into testhandle()")
return aiohttp.web.Response(text='Test handle')
async def websocket_handler(request):
#Сопрограмма одрабатывающая ws-запрос по адресу "http://127.0.0.1:8881"
print('Websocket connection starting')
ws = aiohttp.web.WebSocketResponse()
await ws.prepare(request)
request.app['websockets'].add(ws)
print('Websocket connection ready')
try:
async for msg in ws:
if msg.type == aiohttp.WSMsgType.TEXT:
if msg.data == 'close':
print(msg.data)
break
else:
print(msg.data)
await ws.send_str("You said: {}".format(msg.data))
elif msg.type == aiohttp.WSMsgType.ERROR:
print('ws connection closed with exception %s' %
ws.exception())
except (asyncio.CancelledError, ClientConnectionError):
pass # Тут оказываемся когда, клиент отвалился.
# В будущем можно тут освобождать ресурсы.
finally:
print('Websocket connection closed')
request.app['websockets'].discard(ws)
#pending = asyncio.Task.all_tasks()
#asyncio.get_event_loop().stop()
return ws
async def on_shutdown(app):
for ws in set(app['websockets']):
await ws.close(code=WSCloseCode.GOING_AWAY, message='Server shutdown')
def main():
loop = asyncio.get_event_loop()
app = aiohttp.web.Application()
app['websockets'] = weakref.WeakSet()
app.on_shutdown.append(on_shutdown)
app.add_routes([aiohttp.web.get('/', websocket_handler)]) #, aiohttp.web.get('/test', testhandle)
try:
aiohttp.web.run_app(app, host=HOST, port=PORT, handle_signals=True)
print("after run_app")
except Exception as exc:
print ("in exception")
finally:
loop.close()
if __name__ == '__main__':
main()
I believe the correct answer is simply:
raise aiohttp.web.GracefulExit()
After catching the exception, it calls all the handlers appended to the on_shutdown and on_cleanup signals and dies.
One can see in the source code the aiohttp.web.run_app() waits for two exceptions: GracefulExit and KeyboardInterrupt. While the latter is rather uninteresting, following the trace of GracefulExit can lead you to this place in web_runner.py, that registers SIGINT and SIGTERM signal handlers to a function with raise GracefulExit().
Indeed, I also managed to gracefully shut it down by raising the signal.SIGINT or signal.SIGTERM from itself, e.g.
import signal
signal.raise_signal(signal.SIGINT)
This was tested to work on Fedora Linux 34, Python 3.9.7, aiohttp 3.7.4.
https://docs.aiohttp.org/en/v3.0.1/web_reference.html#aiohttp.web.Application.shutdown
app.shutdown()
app.cleanup()
After shutdown you should also do cleanup()

Memory leak with gunicorn gevent stream responses and zeromq

I'm serving an MJPEG streaming response from gunicorn with gevent workers. Everything works fine but when the connection is terminated by the client it appears that the zeromq subscriber isn't GC'd and just keeps on accepting image data.
Here is the memory usage for gunicorn:
Here is the source code for the wsgi app:
from gevent_zeromq import zmq
context = zmq.Context()
def app(environ, start_response):
if environ['PATH_INFO'] == '/':
subscriber = context.socket(zmq.SUB)
subscriber.connect("ipc:///tmp/camera")
subscriber.setsockopt(zmq.SUBSCRIBE, "")
boundary = "--ipcamera"
status = '200 OK'
headers = [('Content-type', 'multipart/x-mixed-replace;boundary={}'.format(boundary))]
start_response(status, headers)
def get_frames():
while True:
yield subscriber.recv()
return get_frames()
status = '404 NOT FOUND'
headers = []
start_response(status, headers)
return status
I'm running gunicorn with --max-requests 5 to help but obviously isn't a solution. I don't even know where to begin to hunt down the culprit :/
I was able to correct this memory leak by removing gevent_zeromq and just using pyzmq==13.1.0 which includes gevent support.

Resources