2012-11-12 2 views
1

Какой самый быстрый способ обслуживать статические файлы в Python? Я ищу что-то равное или достаточно близко к статическому файлу Nginx.Быстрый статический файл Python

Я знаю об SimpleHTTPServer, но не уверен, что он может эффективно и надежно обрабатывать несколько файлов.

Кроме того, я не возражаю против того, что он является частью lib/framework какого-то рода, пока его lib/framework является легким.

+1

Это действительно не работа для Python. Что случилось с Nginx? –

+0

@ DanielRoseman: Я могу представить себе, что Ngix менее практичен для упаковки и развертывания с помощью приложения Python. – Hubro

+0

@ DanielRoseman, потому что мне нужно что-то на основе Python и в легком, насколько это возможно, но эффективно. Я пишу небольшой пакет, и я хочу, чтобы в нем был включен статический файл, а не полагался на внешние зависимости, такие как Nginx. – Marconi

ответ

3

Что относительно FAPWS3? Один из пунктов продажи:

статических файлового сервер

FAPWS может быть использован, чтобы служить огромное количество статических файловых запросов. С помощью базы данных async в бэкэнд вы можете использовать FAPWS как свою собственную Amazon S3.

+0

Да, я видел это и мог бы подумать об этом. Просто нужно больше ресурсов, поэтому я спросил. – Marconi

1

Я бы рекомендовал использовать сторонний HTTP-сервер для обслуживания статических файлов.

Серверы, такие как nginx, в большой степени оптимизированы под задачу, распараллеленную и написанную на быстрых языках.

Python привязан к одному процессору и интерпретирован.

+0

> интерпретируется. Dosen't matter much, File serve is IOBound. –

+0

'Python привязан к одному процессору Это не так, он никогда не был привязан к одному процессору. Только GIL блокирует Threading. Стандартная библиотека Python mutliprocessing и многие параллельные обработки сторонних библиотек лучше, чем потоки, и все они работают на нескольких процессорах. –

-1

Если вы посмотрите на Oneliner вы можете сделать следующее:

$> питон -m SimpleHTTPServer

Это не в полной мере ощутить всю необходимую задачу, но стоит отметить, что этот является самый простой способ :-)

+0

Я знаю об SimpleHTTPServer, я упомянул об этом выше. :) – Marconi

+0

Примечание для себя: сначала прочитайте вопрос :-) – kiddouk

0

Оригинал SimpleHTTPServer из стандартной библиотеки python НЕ «дескриптор, обслуживающий несколько файлов эффективно и надежно ". Например, если вы загружаете один файл из него, другой HTTP-доступ к нему должен зависать, поскольку SimpleHTTPServer.py - это простой HTTP-сервер с одним потоком, который может поддерживать только одно соединение одновременно.

К счастью, обратите внимание, что SimpleHTTPServer.py использования BaseHTTPServer.HTTPServer в качестве обработчика, который может быть обернут SocketServer.ForkingMixIn и SocketServer.ThreadingMixIn также из питона стандартной библиотеки для поддержки мульти-процесса и режима многопоточного, что может сильно повысить простой HTTP-сервер "Efficience и надежность ".

Согласно этой идеи, SimpleHTTPServer с поддержкой многопоточной/мульти-процесса модифицированного от исходной задается следующим образом:

$ python2.7 ModifiedSimpleHTTPServer.py 
usage: ModifiedSimpleHTTPServer.py [-h] [--pydoc] [--port PORT] 
            [--type {process,thread}] [--root ROOT] 
            [--run] 

Modified SimpleHTTPServer with MultiThread/MultiProcess and IP bind support. 

Original: https://docs.python.org/2.7/library/simplehttpserver.html 
Modified by: [email protected] 

optional arguments: 
    -h, --help   show this help message and exit 
    --pydoc    show this module's pydoc 

run arguments: 

    --port PORT   specify server port (default: 8000) 
    --type {process,thread} 
         specify server type (default: 'thread') 
    --root ROOT   specify root directory (default: cwd '/home/vbem') 
    --run     run http server foreground 

NOTE: stdin for input, stdout for result, stderr for logging 

Например, ModifiedSimpleHTTPServer.py --run --root /var/log --type process будет работать мульти-процесса HTTP статический файловый сервер с «/ var/log» в качестве корневого каталога.

Модифицированные коды:

#! /usr/bin/env python2.7 
# -*- coding: utf-8 -*- 
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # 
r"""Modified SimpleHTTPServer with MultiThread/MultiProcess and IP bind support. 

Original: https://docs.python.org/2.7/library/simplehttpserver.html 
Modified by: [email protected] 
""" 

# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # 
import os, sys, pwd, posixpath, BaseHTTPServer, urllib, cgi, shutil, mimetypes, socket, SocketServer, BaseHTTPServer 
from cStringIO import StringIO 

USERNAME = pwd.getpwuid(os.getuid()).pw_name 
HOSTNAME = socket.gethostname() 
PORT_DFT = 8000 

class SimpleHTTPRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler): 

    server_version = "SimpleHTTP/0.6" 

    def do_GET(self): 
     f = self.send_head() 
     if f: 
      self.copyfile(f, self.wfile) 
      f.close() 

    def do_HEAD(self): 
     f = self.send_head() 
     if f: 
      f.close() 

    def send_head(self): 
     path = self.translate_path(self.path) 
     f = None 
     if os.path.isdir(path): 
      if not self.path.endswith('/'): 
       self.send_response(301) 
       self.send_header("Location", self.path + "/") 
       self.end_headers() 
       return None 
      for index in "index.html", "index.htm": 
       index = os.path.join(path, index) 
       if os.path.exists(index): 
        path = index 
        break 
      else: 
       return self.list_directory(path) 
     ctype = self.guess_type(path) 
     try: 
      f = open(path, 'rb') 
     except IOError: 
      self.send_error(404, "File not found") 
      return None 
     self.send_response(200) 
     self.send_header("Content-type", ctype) 
     fs = os.fstat(f.fileno()) 
     self.send_header("Content-Length", str(fs[6])) 
     self.send_header("Last-Modified", self.date_time_string(fs.st_mtime)) 
     self.end_headers() 
     return f 

    def list_directory(self, path): 
     try: 
      list = ['..'] + os.listdir(path) # 
     except os.error: 
      self.send_error(404, "No permission to list directory") 
      return None 
     list.sort(key=lambda a: a.lower()) 
     f = StringIO() 
     displaypath = cgi.escape(urllib.unquote(self.path)) 
     f.write('<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">') 
     f.write("<html>\n<title>%s %s</title>\n<body>" % (HOSTNAME, displaypath)) 
     f.write("%[email protected]%s:<strong>%s</strong>\n" % (USERNAME, HOSTNAME, path.rstrip('/')+'/')) 
     f.write("<hr>\n<ul>\n") 
     for name in list: 
      fullname = os.path.join(path, name) 
      displayname = linkname = name 
      if os.path.isdir(fullname): 
       displayname = name + "/" 
       linkname = name + "/" 
      if os.path.islink(fullname): 
       displayname = name + "@" 
      f.write('<li><a href="%s">%s</a>\n' 
        % (urllib.quote(linkname), cgi.escape(displayname))) 
     f.write("</ul>\n<hr>\n<pre>%s</pre>\n</body>\n</html>\n" % __doc__) 
     length = f.tell() 
     f.seek(0) 
     self.send_response(200) 
     encoding = sys.getfilesystemencoding() 
     self.send_header("Content-type", "text/html; charset=%s" % encoding) 
     self.send_header("Content-Length", str(length)) 
     self.end_headers() 
     return f 

    def translate_path(self, path): 
     path = path.split('?',1)[0] 
     path = path.split('#',1)[0] 
     path = posixpath.normpath(urllib.unquote(path)) 
     words = path.split('/') 
     words = filter(None, words) 
     path = os.getcwd() 
     for word in words: 
      drive, word = os.path.splitdrive(word) 
      head, word = os.path.split(word) 
      if word in (os.curdir, os.pardir): continue 
      path = os.path.join(path, word) 
     return path 

    def copyfile(self, source, outputfile): 
     shutil.copyfileobj(source, outputfile) 

    def guess_type(self, path): 
     base, ext = posixpath.splitext(path) 
     if ext in self.extensions_map: 
      return self.extensions_map[ext] 
     ext = ext.lower() 
     if ext in self.extensions_map: 
      return self.extensions_map[ext] 
     else: 
      return self.extensions_map[''] 

    if not mimetypes.inited: 
     mimetypes.init() 
    extensions_map = mimetypes.types_map.copy() 
    extensions_map.update({'': 'text/plain'}) 

class ProcessedHTTPServer(SocketServer.ForkingMixIn, BaseHTTPServer.HTTPServer): 
    r"""Handle requests in multi process.""" 

class ThreadedHTTPServer(SocketServer.ThreadingMixIn, BaseHTTPServer.HTTPServer): 
    r"""Handle requests in a separate thread.""" 

SERVER_DICT = { 
    'thread' : ThreadedHTTPServer, 
    'process' : ProcessedHTTPServer, 
} 
SERVER_DFT = 'thread' 

def run(sCwd=None, sServer=SERVER_DFT, nPort=PORT_DFT, *lArgs, **dArgs): 
    r""" 
    """ 
    sys.stderr.write('start with %r\n' % sys._getframe().f_locals) 
    if sCwd is not None: 
     os.chdir(sCwd) 
    cServer = SERVER_DICT[sServer] 
    oHttpd = cServer(("", nPort), SimpleHTTPRequestHandler) 
    sys.stderr.write('http://%s:%s/\n' % (HOSTNAME, nPort)) 
    oHttpd.serve_forever() 

# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # 
# main 

def _main(): 
    r"""Main. 
    """ 
    import argparse 

    oParser = argparse.ArgumentParser(
     description = __doc__, 
     formatter_class = argparse.RawTextHelpFormatter, 
     epilog = 'NOTE: stdin for input, stdout for result, stderr for logging', 
    ) 
    oParser.add_argument('--pydoc', action='store_true', 
     help = "show this module's pydoc", 
    ) 

    oGroupR = oParser.add_argument_group(title='run arguments', description='') 
    oGroupR.add_argument('--port', action='store', type=int, default=PORT_DFT, 
     help = 'specify server port (default: %(default)r)', 
    ) 
    oGroupR.add_argument('--type', action='store', default=SERVER_DFT, choices=SERVER_DICT.keys(), 
     help = 'specify server type (default: %(default)r)', 
    ) 
    oGroupR.add_argument('--root', action='store', default=os.getcwd(), 
     help = 'specify root directory (default: cwd %(default)r)', 
    ) 
    oGroupR.add_argument('--run', action='store_true', 
     help = '\n'.join((
      'run http server foreground', 
    ))) 

    oArgs = oParser.parse_args() 

    if oArgs.pydoc: 
     help(os.path.splitext(os.path.basename(__file__))[0]) 
    elif oArgs.run: 
     return run(sCwd=oArgs.root, sServer=oArgs.type, nPort=oArgs.port) 
    else: 
     oParser.print_help() 
     return 1 

    return 0 

if __name__ == "__main__": 
    exit(_main()) 

Между тем, единственный питон файл только 200 строк может удовлетворить ваши "в Python" и "легкие" требования.

И последнее, но не менее важное: ModifiedSimpleHTTPServer.py может быть «убийственным приложением» вручную для временного использования, однако Nginx рекомендуется для долгосрочного использования.

Смежные вопросы