I have a server written in Python 2.7/Tornado and I am trying to deploy it on AWS.
I came across AWS Elastic Beanstalk which looked like a very convenient method to deploy my code.
I went through this tutorial and was able to deploy the Flask sample app.
However, I can't figure out how to deploy a test tornado app like below.
import tornado.web
import tornado.ioloop
class MainHandler(tornado.web.RequestHandler):
def get(self):
self.write("Hello, world")
if __name__ == "__main__":
app = tornado.web.Application([
(r"/.*", MainHandler),
])
app.listen(5000)
tornado.ioloop.IOLoop.current().start()
All my requests result in an Error 500 when I try to deploy the above application and I have no idea how to troubleshoot this problem as I have no idea how the Flask sample is working but the Tornado code is not.
The requirements.txt file has an entry for tornado==4.4.2 in it.
I tried adding a few log statements to write to an external file but the file is not being created, which probably means the application does not even start.
It would be great if someone can provide some steps on deploying a Tornado app on AWS-EB or how I should start troubleshooting this.
Please let me know if I need to provide any more details.
Thanks!
Update
After noticing the errors in httpd error_log file, AWS Documentation and Berislav Lopac's answer, I found the correct way to implement the Tornado server.
Here is a simple server
import tornado.web
import tornado.wsgi
import tornado.ioloop
class MainHandler(tornado.web.RequestHandler):
def get(self):
self.write("Hello, world")
webApp = tornado.web.Application([
(r"/", MainHandler),
])
# Wrapping the Tornado Application into a WSGI interface
# As per AWS EB requirements, the WSGI interface must be named
# 'application' only
application = tornado.wsgi.WSGIAdapter(webApp)
if __name__ == '__main__':
# If testing the server locally, start on the specific port
webApp.listen(8080)
tornado.ioloop.IOLoop.current().start()
Additional Links:
Tornado WSGI Documentation
you can deploy tornado application with WSGI mod
import tornado.web
import tornado.wsgi
class MainHandler(tornado.web.RequestHandler):
def get(self):
self.write("Hello, world")
tornado_app = tornado.web.Application([
(r"/", MainHandler),
])
application = tornado.wsgi.WSGIAdapter(tornado_app)
http://www.tornadoweb.org/en/stable/guide/running.html
I believe your issue is related to the fact that Elastic Beanstalk uses WSGI for serving Python Web apps, while Tornado's server is not WSGI-compliant. You might want to try wrapping your app in the WSGI adapter before serving it via WSGI.
This should work fine unless you rely on Tornado's asynchronous capabilities, as WSGI is strictly synchronous.
Related
When I try to use the functionality that uses websockets in my application, I get this error in the console:
File "/Users/user/venv/lib/python3.7/site-packages/simple_websocket/ws.py", line 138, in __init__
raise RuntimeError('Cannot obtain socket from WSGI environment.')
RuntimeError: Cannot obtain socket from WSGI environment.
I also get this error in the browser console:
WebSocket connection to 'ws://localhost:5000/socket.io/?EIO=4&transport=websocket&sid=40NYzDgGYStMR0CEAAAJ' failed:
I tried using gevent, gevent-websocket, and eventlet, but this created other issues, and I'm not sure that I need to use gevent or eventlet for this use case.
Here's the rest of the relevant code:
__ init __.py
from flask_socketio import SocketIO
...
socketio = SocketIO()
def create_app(config_class=Config):
app = Flask(__name__)
app.config.from_object(Config)
socketio.init_app(app, cors_allowed_origins='*')
...
return app
app.py
from app import create_app, socketio
app = create_app()
if __name__ == '__main__':
socketio.run(app)
routes.py
This only accepts POST requests because I send data to this route from a Celery task
from app import socketio
...
#main_bp.route('/send_message', methods=['POST'])
def send_message():
...
socketio.emit('test_message', {'msg':'Test'}, namespace='/test')
return 'Results Sent'
index.html
var socket = io.connect('http://localhost:5000/test');
socket.on('connect', function(){
console.log('Connected to socket')
});
socket.on('test_message', function(message){
console.log('Received test message');
}
)
Note that in the browser console I'll see "Connected to socket", but not "Received test message"
You are using the simple-websocket package. This package has a list of supported web servers. The error indicates that you are using a web server that is not in the supported list.
Supported web servers are:
The Flask dev server (for development purposes only, of course)
Gunicorn
Eventlet
Gevent
From this list, it seems your only choice for a production web server is Gunicorn, since you say that eventlet/gevent won't work for your needs.
The use of Gunicorn is covered in the documentation. In that section, simple-websocket is the third option mentioned. Here is the example start up command:
gunicorn -w 1 --threads 100 module:app
Maybe you can turn on the logger and the engine.io logger. that qould give you an idea what is the issue.
don't use eventlet and gevent together. use anyone and uninstall the other.
I was trying to deploy my Flask app on CherryPy server. I liked its simplistic and minimalistic nature.
So I PIP'ed CherryPy like below
pip install CherryPy-15.0.0-py2.py3-none-any.whl
and wrote script like below -very common suggested by many sources
from cherrypy import wsgiserver
from hello import app
d = wsgiserver.WSGIPathInfoDispatcher({'/': app})
server = wsgiserver.CherryPyWSGIServer(('0.0.0.0', 80), d)
if __name__ == '__main__':
try:
server.start()
except KeyboardInterrupt:
server.stop()
To my surprise, I had imports errors. After a few googling around, I learned that I had to change my import lines to cheroot to make it work.
from cheroot.wsgi import Server
from cheroot.wsgi import PathInfoDispatcher
Now, my code is working fine.
However, I am a bit confused if this is the right way of using CherryPy WSGI server or if I pip'ed a wrong version of CherryPy. I am confused because Cheroot seems to be more than year old (dates all the way back to 2014), yet all the information I found around Flask on CherryPy WSGI server is using from cherrypy import wsgiserver, not from cheroot.wsgi import Server, even the latest postings.
This makes me unsure if I am doing the right thing or not.
Can someone please shed light on this confusion?
Cheroot (src) is a low-level HTTP and WSGI server, which used to be a part of CherryPy (src) once, but has been factored out into a separate repo a while back. So former cherrypy.wsgiserver has moved to cheroot.wsgi module.
It's completely replaceable and designed to allow developers to depend on Cheroot directly if they only use WSGI server, not requiring other parts of CherryPy.
So here's how you can use it in a version-agnostic way:
try:
from cheroot.wsgi import Server as WSGIServer, PathInfoDispatcher
except ImportError:
from cherrypy.wsgiserver import CherryPyWSGIServer as WSGIServer, WSGIPathInfoDispatcher as PathInfoDispatcher
from hello import app
d = PathInfoDispatcher({'/': app})
server = WSGIServer(('0.0.0.0', 80), d)
if __name__ == '__main__':
try:
server.start()
except KeyboardInterrupt:
server.stop()
I want to be able to run twisted servers on multiple different directories (exp: /example1, /example2...etc), So I thought I'd use flask. Here is what I have so far:
from flask import Flask
from twisted.web.server import Site
from twisted.web.wsgi import WSGIResource
from twisted.internet import reactor
from twisted.web.proxy import ReverseProxyResource
from twisted.web.resource import Resource
app = Flask(__name__)
#app.route('/example1')
def index():
return 'My Twisted Flask'
flask_site = WSGIResource(reactor, reactor.getThreadPool(), app)
root = Resource()
root.putChild('my_flask', flask_site)
site_example = ReverseProxyResource('www.example.com', 80, ''.encode('utf-8'))
root.putChild('example1', site_example)
reactor.listenTCP(80, Site(root))
reactor.run()
The only problem is that it doesn't work, I'm not sure what I'm doing wrong. I appreciate any help, thanks!
My personal opinion: running Flask in Twisted's reactor isn't a good idea because Twisted's reactor is blocked when a request is processed by Flask.
I think you might be interested in Klein, which provided API similar to Flask, but works on Twisted out of the box: http://klein.readthedocs.io/en/latest/
Another option: I'd take a look into nginx as a reverse proxy for Flask applications instead of Twisted. nginx runs in a separate process and isn't blocked while a request is processed by Flask.
https://www.nginx.com/resources/admin-guide/reverse-proxy/
You can use twisted web, as documented on the Flask deploy documentation. Here's how I managed to run a server on my machine:
pip3 install twisted[tls]
export PYTHONPATH=${PYTHONPATH}:${PWD} # exports python path
twistd web -n --port tcp:5000 --wsgi path-to-your-app-root --logfile log.txt
Though I've had some issues with the server after it's up and running for my particular scenario, this might work for you
I have a python web app developed using the bottle framework. My bottle app is web API that provide methods that return JSon data, so no static content is needed. I am trying to deploy it to production using a CherryPy server which is supposed to be robust for production applications.
My web_api.py file (my bottle app) looks something like this:
from bottle import Bottle, request
app = Bottle()
#app.get('/stuff')
def do_stuff():
'''
Method that does stuff.
'''
stuff = {'data': 'some data'}
# Return the environment info as Json data
return stuff
I have a server.py file to launch the Bottle app over the CherryPy server that looks like this:
from my_package.web_api import app
from cherrypy.wsgiserver import CherryPyWSGIServer
server = CherryPyWSGIServer(
('0.0.0.0', 80),
app,
server_name='My_App',
numthreads=30)
server.start()
so when I run my server using this command:
python server.py
My server is successfully started and start listening in port 80 as expected. However once I start my web server I cannot stop it any more. I have tried Ctrl + C which works with the development server but has no effect here. Am I starting the server the right way? How do I stop it once it is running? Is this the correct way to launch a Bottle app over CherryPy?
BTW, I am running python 2.7 in Windows 8.
Your code is correct, you just need to add a try/catch statement:
from my_package.web_api import app
from cherrypy.wsgiserver import CherryPyWSGIServer
server = CherryPyWSGIServer(
('0.0.0.0', 80),
app,
server_name='My_App',
numthreads=30)
try:
server.start()
except KeyboardInterrupt:
server.stop()
You might wanna also consider to do some logging with wsgi-request-logger or something similar.
This are three alternative ways on hosting a WSGI application within cherrypy:
import cherrypy as cp
from cherrypy.wsgiserver import CherryPyWSGIServer
from cherrypy.process.servers import ServerAdapter
from bottle import Bottle
app = Bottle()
#app.get('/stuff')
def do_stuff():
'''
Method that does stuff.
'''
stuff = {'data': 'some dataX'}
return stuff
def run_decoupled(app, host='0.0.0.0', port=8080, **config):
server = CherryPyWSGIServer((host, port), app, **config)
try:
server.start()
except KeyboardInterrupt:
server.stop()
def run_in_cp_tree(app, host='0.0.0.0', port=8080, **config):
cp.tree.graft(app, '/')
cp.config.update(config)
cp.config.update({
'server.socket_port': port,
'server.socket_host': host
})
cp.engine.signals.subscribe() # optional
cp.engine.start()
cp.engine.block()
def run_with_adapter(app, host='0.0.0.0', port=8080, config=None, **kwargs):
cp.server.unsubscribe()
bind_addr = (host, port)
cp.server = ServerAdapter(cp.engine,
CherryPyWSGIServer(bind_addr, app, **kwargs),
bind_addr).subscribe()
if config:
cp.config.update(config)
cp.engine.signals.subscribe() # optional
cp.engine.start()
cp.engine.block()
The run_in_cp_tree and run_with_adapter functions are using the cherrypy engine, which enables the use of plugins to have off-the-shelf auto-reload, pidfile, daemonization, signal management and some more goodies, along with the possibility to create one of your own.
Notice that you can also use the WSGIPathInfoDispatcher to attach multiple wsgi applications on the CherryPyWSGIServer.
Trying to connect any WSGI server to my BottlePy app here in 2019 turned out to be rather tricky(to a noobie like me).
I tried connecting several ones, spent most off my time with CherryPy, which has changed his syntax.
The simpliest to me turned out to be waitress https://waitress.readthedocs.io/en/latest/usage.html
After i figured out how to use it on waitress i got it in cherrypy also. So:
CherryPy http://docs.cherrypy.org/en/latest/advanced.html?highlight=WSGi#host-a-foreign-wsgi-application-in-cherrypy
1)add after imports
import cherrypy as cp
app = bottle.Bottle()
2) change in routes "#bottle" to "#app"
3)add this as main function
cp.tree.graft(app, '/')
cp.server.start()
Waitress
1)add after imports
import waitress
app = bottle.Bottle()
2)add this as main function
waitress.serve(app, listen='*:44100')
3) change in routes "#bottle" to "#app"
My end goal is to implement a WebSocket server using python.
I'm accomplishing this by importing tornado in my python scripts. I've also installed mod_wsgi in apache, and their script outputs Hello World!, so WSGI seems to be working fine. Tornado is also working fine as far as I can tell.
The issue comes when I use tornado's wsgi "Hello, world" script:
import tornado.web
import tornado.wsgi
import wsgiref.simple_server
class MainHandler(tornado.web.RequestHandler):
def get(self):
self.write("Hello, world")
if __name__ == "__main__":
application = tornado.wsgi.WSGIApplication([
(r"/", MainHandler),
])
server = wsgiref.simple_server.make_server('', 8888, application)
server.serve_forever()
First, I get a 500 error and the log tells me WSGI can't find 'application'.
So I remove if __name__ == "__main__", and the page loads infinitely.
I assume this is because of server.serve_forever() so I removed it in an attempt to see Hello, world
But now I just get 404: Not Found. It's not my apache 404 page, and I know that the server can find my main .wsgi file...
You can't use websockets with Tornado's WSGIApplication. To use Tornado's websocket support you have to use Tornado's HTTPServer, not apache.
The WSGIApplication handlers are relative to the webserver root. If your application url is /myapp, your 'application' must look like this:
application = tornado.wsgi.WSGIApplication([
(r"/myapp", MainHandler),
(r"/myapp/login/etc", LoginEtcHandler),
])
Oh, and it seems like the documentation is shit (as usual) and __name__ will look something like this when running under apache: _mod_wsgi_8a447ce1677c71c08069303864e1283e.
So! a correct "Hello World" python script will look like this:
/var/www/wsgi-scripts/myapp.wsgi:
import tornado.web
import tornado.wsgi
import wsgiref.simple_server
class MainHandler(tornado.web.RequestHandler):
def get(self):
self.write('Hello World')
application = tornado.wsgi.WSGIApplication([
(r"/myapp", MainHandler),
])
And in the apache config (not .htaccess):
WSGIScriptAlias /myapp /var/www/wsgi-scripts/myapp.wsgi
To use tornado in apache,add a mod-wsgi plugin to apache.
apt-get install libapache2-mod-wsgi
Write a tornado wsgi server with
.wsgi
NOTE:Dont use__name__
Configure the apache.conf to run your server.To configure use this mod-wsgi guide
If you still want to combine them both, you can use Apache as a proxy that will just be the 1st point in front of the user - but actually reroute the traffic to your local Tornado server ( In / Out )
In my case for example, my Apache listen in port 443 ( some default config )
Then I run my tornado in port 8080, and given a path - will redirect
#File: conf.d/myapp.conf
<VirtualHost *:80>
ErrorLog "logs/myapp_error_log"
ProxyPreserveHost On
ProxyRequests off
ProxyPreserveHost On
<Proxy *>
Require all granted
</Proxy>
RewriteEngine on
RewriteCond %{REQUEST_METHOD} ^TRACE
RewriteRule .* - [F]
ProxyPassMatch "/myapp/(.*)" "http://localhost:8080/myapp/$1"
ProxyPassReverse "/myapp/" "http://localhost:8080/myapp/"
</VirtualHost>
If you're using RedHat "family" OS also turn on the ability to forward network connections:
setsebool -P httpd_can_network_connect 1