tornado: http_server.start multiple instance fails in python3.5

the very basic multi instances code fails on python 3.5 env. It works with python2.7 and 3.6.4.

Python 3.5.2

tornado                5.0.1
Flask                  0.12.2
Flask-Cors             3.0.3
gevent                 1.2.2
greenlet               0.4.13
gunicorn               19.7.1
lxml                   4.2.0
from flask import Flask, request, jsonify
from flask_cors import CORS, cross_origin
from tornado.wsgi import WSGIContainer
from tornado.httpserver import HTTPServer
from tornado.ioloop import IOLoop

app = Flask(__name__)
PROD = True
CORS(app)


@app.route('/api/check/alive', methods=['GET'])
def handle_api_alive():
    resp = jsonify(dict(status="OK"))

    return resp


if __name__ == '__main__':
    # start HTTP server
    http_server = HTTPServer(WSGIContainer(app))
    http_server.bind(9999)
    http_server.start(num_processes=2)
    IOLoop.instance().start()

About this issue

  • Original URL
  • State: closed
  • Created 6 years ago
  • Comments: 15 (5 by maintainers)

Commits related to this issue

Most upvoted comments

This is a result of the move to asyncio. asyncio is not as multiprocess-friendly as the old Tornado implementation was. I highly recommend using a process manager like supervisord and a separate load balancer instead of Tornado’s multi-process mode.

If you want to continue using Tornado’s multi-process mode, you must be very careful not to do anything that might initialize the IOLoop before the fork. In particular, this may mean using the “advanced” multi-process instructions so that the only thing that happens before the fork is tornado.netutil.bind_sockets.

It may not be possible to use the non-advanced versions of multi-process mode any more in Python 3. If so, maybe these modes should be removed in Tornado 6.0.

@bdarnell Thanks for helping. As you said, yes I created an unused IOLoop instance using unused = IOLoop.current(), and an Application with debug=True.

In the source code about Application.__init__, debug=True will set autoreload=True if you don’t set autoreload=False in the application kwargs. And the autoreload=True call tornado.autoreload.start(), this method initializes the IOLoop as you said “too soon”.

So I remove the unused IOLoop instance, and create an Application with debug=True and autoreload=False, it works fine. I’ve used nginx and docker for load balancing even though it’s a bit more complicated than this multi-process mode.

I see what happened, at least in @chrjxj 's case. In Tornado 5.0, simply importing tornado.wsgi is enough to initialize the IOLoop and break bind/start.

@MioYvo, I’m not sure if you’re seeing the same thing. If you’re seeing this error without importing tornado.wsgi, you’re probably doing something else that initializes the IOLoop too soon. The most common things that do this are creating an Application with debug=True and creating an AsyncHTTPClient. This is not allowed in older versions of Tornado either, although the move to asyncio in Tornado 5.0 has made it harder to give good error messages.

@ajayuranakar on Linux, listen works, i tried on my centos7 server and debian docker container on macOS ; on macOS(10.13.5), listen don’t work.

Thank you for your help.