mirror of
https://github.com/frappe/gunicorn.git
synced 2026-01-14 11:09:11 +08:00
initial commit. working connection handler.
This commit is contained in:
commit
52b950945f
13
.gitignore
vendored
Executable file
13
.gitignore
vendored
Executable file
@ -0,0 +1,13 @@
|
|||||||
|
*.gem
|
||||||
|
*.swp
|
||||||
|
*.pyc
|
||||||
|
Couchapp.egg-info
|
||||||
|
build
|
||||||
|
dist
|
||||||
|
setuptools-*
|
||||||
|
.svn/*
|
||||||
|
.DS_Store
|
||||||
|
*.so
|
||||||
|
distribute-0.6.8-py2.6.egg
|
||||||
|
distribute-0.6.8.tar.gz
|
||||||
|
|
||||||
4
README.md
Normal file
4
README.md
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
gunicorn - Green Unicorn
|
||||||
|
========================
|
||||||
|
|
||||||
|
The beginnigs of a port of [Unicorn](http://unicorn.bogomips.org/) in Python.
|
||||||
0
gunicorn/__init__.py
Normal file
0
gunicorn/__init__.py
Normal file
192
gunicorn/httpserver.py
Normal file
192
gunicorn/httpserver.py
Normal file
@ -0,0 +1,192 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
#
|
||||||
|
# Copyright 2008,2009 Benoit Chesneau <benoitc@e-engura.org>
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at#
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
|
||||||
|
import errno
|
||||||
|
import fcntl
|
||||||
|
import logging
|
||||||
|
import os
|
||||||
|
import select
|
||||||
|
import signal
|
||||||
|
import socket
|
||||||
|
import tempfile
|
||||||
|
import time
|
||||||
|
|
||||||
|
from gunicorn import socketserver
|
||||||
|
from gunicorn.util import NullHandler
|
||||||
|
|
||||||
|
class Worker(object):
|
||||||
|
|
||||||
|
def __init__(self, nr, tmp):
|
||||||
|
self.nr = nr
|
||||||
|
self.tmp = tmp
|
||||||
|
|
||||||
|
def __eq__(self, v):
|
||||||
|
return self.nr == v
|
||||||
|
|
||||||
|
class HTTPServer(object):
|
||||||
|
|
||||||
|
LISTENERS = []
|
||||||
|
|
||||||
|
PIPE = []
|
||||||
|
|
||||||
|
WORKERS = {}
|
||||||
|
|
||||||
|
def __init__(self, app, worker_processes, timeout=60, init_listeners=[],
|
||||||
|
pidfile=None, logging_handler=None, **opts):
|
||||||
|
|
||||||
|
self.opts = opts
|
||||||
|
self.app = app
|
||||||
|
self.timeout = timeout
|
||||||
|
self.pidfile = pidfile
|
||||||
|
self.worker_processes = worker_processes
|
||||||
|
if logging_handler is None:
|
||||||
|
logging_handler = NullHandler()
|
||||||
|
self.logger = logging.getLogger("gunicorn")
|
||||||
|
self.logger.setLevel(logging.INFO)
|
||||||
|
self.logger.addHandler(logging_handler)
|
||||||
|
|
||||||
|
# start to listen
|
||||||
|
self.init_listeners = init_listeners
|
||||||
|
if not self.init_listeners:
|
||||||
|
self.init_listeners = [(('localhost', 8000), {})]
|
||||||
|
|
||||||
|
for address, opts in self.init_listeners:
|
||||||
|
self.listen(address, opts)
|
||||||
|
|
||||||
|
self.master_pid = os.getpid()
|
||||||
|
self.maintain_worker_count()
|
||||||
|
|
||||||
|
|
||||||
|
def listen(self, addr, opts):
|
||||||
|
tries = self.opts.get('tries', 5)
|
||||||
|
delay = self.opts.get('delay', 0.5)
|
||||||
|
|
||||||
|
for i in range(5):
|
||||||
|
try:
|
||||||
|
sock = socketserver.TCPServer(addr, **opts)
|
||||||
|
self.LISTENERS.append(sock)
|
||||||
|
except socket.error, e:
|
||||||
|
if e[0] == errno.EADDRINUSE:
|
||||||
|
self.logger.error("adding listener failed address: %s" % addr)
|
||||||
|
if i < tries:
|
||||||
|
self.logger.error("retrying in %s seconds." % str(delay))
|
||||||
|
time.sleep(delay)
|
||||||
|
break
|
||||||
|
|
||||||
|
def join(self):
|
||||||
|
# this pipe will be used to wake up the master when signal occurs
|
||||||
|
self.init_pipe()
|
||||||
|
try:
|
||||||
|
os.waitpid(-1, 0)
|
||||||
|
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
kill_workers(signal.SIGQUIT)
|
||||||
|
sys.exit()
|
||||||
|
|
||||||
|
def init_worker_process(self, worker):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def process_client(self, conn, addr):
|
||||||
|
""" do nothing just echo message"""
|
||||||
|
flo = conn.makefile()
|
||||||
|
flo.flush()
|
||||||
|
message = flo.readline()
|
||||||
|
flo.write(message)
|
||||||
|
conn.close()
|
||||||
|
|
||||||
|
|
||||||
|
def worker_loop(self, worker):
|
||||||
|
pid = os.fork()
|
||||||
|
|
||||||
|
if pid == 0:
|
||||||
|
worker_pid = os.getpid()
|
||||||
|
yield worker_pid
|
||||||
|
self.init_worker_process(worker)
|
||||||
|
alive = worker.tmp.fileno()
|
||||||
|
m = 0
|
||||||
|
ready = self.LISTENERS
|
||||||
|
|
||||||
|
while alive:
|
||||||
|
m = 0 if m == 1 else 1
|
||||||
|
os.fchmod(alive, m)
|
||||||
|
try:
|
||||||
|
for sock in ready:
|
||||||
|
try:
|
||||||
|
self.process_client(*sock.accept_nonblock())
|
||||||
|
except errno.EAGAIN, errno.ECONNABORTED:
|
||||||
|
pass
|
||||||
|
|
||||||
|
m = 0 if m == 1 else 1
|
||||||
|
os.fchmod(alive, m)
|
||||||
|
|
||||||
|
m = 0 if m == 1 else 1
|
||||||
|
os.fchmod(alive, m)
|
||||||
|
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
fd_sets = select.select([self.LISTENERS], [], self.PIPE, self.timeout)
|
||||||
|
if fd_sets:
|
||||||
|
ready = [fd_sets[0]]
|
||||||
|
break
|
||||||
|
except errno.EINTR:
|
||||||
|
ready = self.LISTENERS
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
sys.exit()
|
||||||
|
except Exception, e:
|
||||||
|
self.logger.error("Unhandled exception in worker %s [%s]" % (worker_pid, e))
|
||||||
|
|
||||||
|
def kill_workers(self, sig):
|
||||||
|
"""kill all workers with signal sig """
|
||||||
|
for pid in self.WORKERS.keys():
|
||||||
|
self.kill_worker(pid, sig)
|
||||||
|
|
||||||
|
def kill_worker(self, pid, sig):
|
||||||
|
""" kill one worker with signal """
|
||||||
|
worker = self.WORKERS[pid]
|
||||||
|
try:
|
||||||
|
os.kill(pid, sig)
|
||||||
|
finally:
|
||||||
|
worker.fd.close()
|
||||||
|
del self.WORKERS[pid]
|
||||||
|
|
||||||
|
|
||||||
|
def spawn_missing_workers(self):
|
||||||
|
for i in range(self.worker_processes):
|
||||||
|
if i in self.WORKERS.values():
|
||||||
|
continue
|
||||||
|
|
||||||
|
worker = Worker(i, os.tmpfile())
|
||||||
|
for worker_pid in self.worker_loop(worker):
|
||||||
|
self.WORKERS[worker_pid] = worker
|
||||||
|
|
||||||
|
def maintain_worker_count(self):
|
||||||
|
if (len(self.WORKERS.keys()) - self.worker_processes) < 0:
|
||||||
|
self.spawn_missing_workers()
|
||||||
|
|
||||||
|
for pid, w in self.WORKERS.items():
|
||||||
|
if w.nr >= self.worker_processes:
|
||||||
|
self.kill_worker(pid, signal.SIGQUIT)
|
||||||
|
|
||||||
|
|
||||||
|
def init_pipe(self):
|
||||||
|
if self.PIPE:
|
||||||
|
[io.close() for io in self.PIPE]
|
||||||
|
self.PIPE = os.pipe()
|
||||||
|
[fcntl.fcntl(io, fcntl.F_SETFD, fcntl.FD_CLOEXEC) for io in self.PIPE]
|
||||||
55
gunicorn/socketserver.py
Normal file
55
gunicorn/socketserver.py
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
#
|
||||||
|
# Copyright 2008,2009 Benoit Chesneau <benoitc@e-engura.org>
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at#
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
|
||||||
|
import socket
|
||||||
|
|
||||||
|
class TCPServer(socket.socket):
|
||||||
|
"""class for server-side TCP sockets.
|
||||||
|
This is wrapper around socket.socket class"""
|
||||||
|
|
||||||
|
def __init__(self, address, **opts):
|
||||||
|
self.address = address
|
||||||
|
self.backlog = opts.get('timeout', 1024)
|
||||||
|
self.timeout = opts.get('timeout', 300)
|
||||||
|
self.reuseaddr = opts.get('reuseaddr', True)
|
||||||
|
self.nodelay = opts.get('nodelay', True)
|
||||||
|
self.recbuf = opts.get('recbuf', 8192)
|
||||||
|
|
||||||
|
socket.socket.__init__(self, socket.AF_INET, socket.SOCK_STREAM)
|
||||||
|
|
||||||
|
if self.reuseaddr:
|
||||||
|
self.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
||||||
|
|
||||||
|
if self.nodelay:
|
||||||
|
self.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
|
||||||
|
|
||||||
|
if self.recbuf:
|
||||||
|
self.setsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF,
|
||||||
|
self.recbuf)
|
||||||
|
|
||||||
|
self.settimeout(self.timeout)
|
||||||
|
self.bind(address)
|
||||||
|
self.listen()
|
||||||
|
|
||||||
|
def listen(self):
|
||||||
|
super(TCPServer, self).listen(self.backlog)
|
||||||
|
|
||||||
|
def accept(self):
|
||||||
|
return super(TCPServer, self).accept()
|
||||||
|
|
||||||
|
def accept_nonblock(self):
|
||||||
|
self.setblocking(0)
|
||||||
|
return self.accept()
|
||||||
22
gunicorn/util.py
Normal file
22
gunicorn/util.py
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
#
|
||||||
|
# Copyright 2008,2009 Benoit Chesneau <benoitc@e-engura.org>
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at#
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
|
||||||
|
import logging
|
||||||
|
|
||||||
|
class NullHandler(logging.Handler):
|
||||||
|
""" null log handler """
|
||||||
|
def emit(self, record):
|
||||||
|
pass
|
||||||
Loading…
x
Reference in New Issue
Block a user