Enable reuse_port option to HTTPServer.bind.

This makes solutions that restart the process (e.g., envconsul) work
properly.  Without port reuse, you will get a very unpleasant "port
already in use" when the service restarts.

This requires Tornado >= 4.4
This commit is contained in:
Dave Shawley 2018-01-28 14:32:41 -05:00
parent a6fdbba463
commit fb909088d9
3 changed files with 30 additions and 4 deletions

View file

@ -3,6 +3,10 @@
Release History
===============
`Next Release`_
---------------
- Enable port reuse for Tornado versions newer than 4.3.
`1.4.2`_ (25 Jan 2018)
---------------------
- Allow max_body_size and max_buffer_size to be specified on the http server.

View file

@ -12,6 +12,7 @@ import signal
import sys
from tornado import httpserver, ioloop
import tornado
import sprockets.http.app
@ -90,7 +91,12 @@ class Runner(object):
self.server.listen(port_number)
else:
self.logger.info('starting processes on port %d', port_number)
self.server.bind(port_number)
if tornado.version_info >= (4, 4):
self.server.bind(port_number, reuse_port=True)
else:
self.logger.warning('port reuse disabled, please upgrade to'
'at least Tornado 4.4')
self.server.bind(port_number)
self.server.start(number_of_procs)
def stop_server(self):

View file

@ -14,7 +14,8 @@ except ImportError:
import mock
open_name = '__builtin__.open'
from tornado import concurrent, httputil, ioloop, testing, web
from tornado import concurrent, httpserver, httputil, ioloop, testing, web
import tornado
import sprockets.http.mixins
import sprockets.http.runner
@ -383,7 +384,7 @@ class RunnerTests(MockHelper, unittest.TestCase):
ioloop_module = self.start_mock('sprockets.http.runner.ioloop')
ioloop_module.IOLoop.instance.return_value = self.io_loop
self.http_server = mock.Mock()
self.http_server = mock.Mock(spec=httpserver.HTTPServer)
self.httpserver_module = \
self.start_mock('sprockets.http.runner.httpserver')
self.httpserver_module.HTTPServer.return_value = self.http_server
@ -402,9 +403,24 @@ class RunnerTests(MockHelper, unittest.TestCase):
def test_that_production_run_starts_in_multiprocess_mode(self):
runner = sprockets.http.runner.Runner(self.application)
runner.run(8000)
self.http_server.bind.assert_called_once_with(8000)
self.assertTrue(self.http_server.bind.called)
args, kwargs = self.http_server.bind.call_args_list[0]
self.assertEqual(args, (8000, ))
self.http_server.start.assert_called_once_with(0)
@unittest.skipUnless(tornado.version_info >= (4, 4),
'port reuse requries newer tornado')
def test_that_production_enables_reuse_port(self):
runner = sprockets.http.runner.Runner(self.application)
runner.run(8000)
self.assertTrue(self.http_server.bind.called)
args, kwargs = self.http_server.bind.call_args_list[0]
self.assertEqual(args, (8000, ))
self.assertEqual(kwargs['reuse_port'], True)
def test_that_debug_run_starts_in_singleprocess_mode(self):
self.application.settings['debug'] = True
runner = sprockets.http.runner.Runner(self.application)