天天看點

gunicorn分析 基于 tag0.2

gunicorn 工作原理

1.工作模型

    gunicorn采用的是master-worker模型,一個master程序,多個worker程序。master程序負責管理worker程序

def spawn_workers(self):
        workers = set(w.id for w in self.WORKERS.values())
        for i in range(self.num_workers):
            if i in workers:
                continue

            worker = Worker(i, self.pid, self.LISTENER, self.modname,
                        self.timeout)
            pid = os.fork()
            if pid != 0:
                self.WORKERS[pid] = worker
                continue
            
            worker_pid = os.getpid()
            try:
                self.log.info("Worker %s booting" % worker_pid)
                worker.run()
                sys.exit(0)
            except SystemExit:
                raise
            except:
                self.log.exception("Exception in worker process.")
                sys.exit(-1)
            finally:
                worker.tmp.close()
                self.log.info("Worker %s exiting." % worker_pid)
           

下面我來個簡化版的: main.py

import os, socket, sys
from worker import Worker

print os.getpid(), 'process start'
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.setblocking(0)
sock.bind(("127.0.0.1", 5000))
sock.listen(2048)

for i in range(3):

    w = Worker(i, os.getpid(), sock)
    pid = os.fork()
    if pid != 0:
        continue

    try:
        w.run()
        sys.exit(0)
    except SystemExit:
        raise
    except:
        sys.exit(-1)

# for read console
import time
time.sleep(200)
print 'end!'
           

worker.py

# -*- coding: utf-8 -

import errno
import os
import select
import socket

CHUNK_SIZE = (16 * 1024)


def read_partial(sock, length):
    while True:
        try:
            ret = select.select([sock.fileno()], [], [], 0)
            if ret[0]: break
        except select.error, e:
            if e[0] == errno.EINTR:
                continue
            raise
    data = sock.recv(length)
    return data


class Worker(object):
    def __init__(self, workerid, ppid, socket):
        self.id = workerid
        self.ppid = ppid
        self.socket = socket
        self.timeout = 10
        self.alive = True

    def run(self):
        while self.alive:
            nr = 0
            while self.alive:
                try:
                    client, addr = self.socket.accept()
                    buf = ''
                    while True:
                        data = read_partial(client, CHUNK_SIZE)
                        if not data: break
                        buf += data
                    print buf
                    nr += 1
                except socket.error, e:
                    if e[0] in (errno.EAGAIN, errno.ECONNABORTED):
                        print 'socekt.error'
                        break  # Uh oh!

                    raise
                if nr == 0: break

            while self.alive:
                try:
                    ret = select.select([self.socket], [], [],
                                        self.timeout)
                    if ret[0]:
                        break
                except select.error, e:
                    if e[0] == errno.EINTR:
                        print 'select error', os.getpid()
                        break
                    raise
           

在 Linux 進行非阻塞的 socket 接收 資料 時經常出現Resource temporarily unavailable,errno代碼為11(EAGAIN)

這表明你在非阻塞模式下調用了阻塞操作,在該操作沒有完成就傳回這個錯誤, 這個錯誤不會破壞socket的同步,可以不用管它,循環接着recv就可以。