文档章节

django源码分析--01服务启动(wsgi)

AllenOR灵感
 AllenOR灵感
发布于 2017/09/10 01:18
字数 3738
阅读 2
收藏 0
点赞 0
评论 0

环境说明

  • [x] Python 3.5
  • [x] Django 1.10.4

创建一个django项目

C:\Users\zhengtong>C:\Python35\Scripts\django-admin.exe startproject HelloWorld

项目文件结构

├── HelloWorld
│   ├── __init__.py
│   ├── settings.py
│   ├── urls.py
│   └── wsgi.py
└── manage.py

初始化项目

C:\Users\zhengtong>cd HelloWorld
C:\Users\zhengtong\HelloWorld>C:\Python35\python.exe manage.py migrate # 创建数据库表(默认是采用的sqlite3).
C:\Users\zhengtong\HelloWorld>C:\Python35\python.exe manage.py createsuperuser # 创建管理员用户, 该用户用来django提供的admin登录管理界面.

启动项目

C:\Users\zhengtong\HelloWorld>C:\Python35\python.exe manage.py runserver

启动项目服务,默认监听的端口是localhost:8000,也就是说打开浏览器,输入 http://localhost:8000 就可以访问到HelloWorld这个项目网站了。
还有就是在浏览器中输入 http://localhost:8000/admin 可以访问到django提供的admin登录管理界面。

 

Django Management

Django框架的整个数据流向、服务启动、端口监听等基础核心功能都是按照WSGI标准进行开发的。WSGI是Web Server Gateway Interface的缩写,它的主要作用是接收客户端(浏览器/软件/APP/调试程序)发送的请求和转发请求给上层服务(例如Django)。它采用了select网络模型进行数据的接收和分发,可以利用操作系统的非堵塞和线程池等特性,因此它是非常高效的(Django采用wsgi是纯python代码实现,开源项目有一个uWSGI是C语言代码实现,因此现在更多的django项目实际上是运行在uWSGI上的,uWSGI不再讨论范围之内)。

接下来是记录我利用pycharm提供的断点调试功能以及自己打日志整个过程的梳理和总结。

源码的分析过程取决与分析人员想要关注的原理点,不同的需求就会有不同的侧重点,我当前的需求点是,想要知道django是如何通过网络层接收数据(wsgi)并将请求转发给django的urls层,因此我会朝着这个主题方向列出每个流转环节中的核心代码并做相应的注解。以后如果有机会的话我将会从另外一个侧重点(程序设计)去分析源码。

每个过程我都会先将代码列出来然后在后面做注解,因此告诫自己以后重读自己的笔记时不需要努力的去看代码,而是直接看注解,然后找到对应的代码进行理解。

代码块我会用...来忽略掉一些跟主题无关的代码。

 

manage.py
#!/usr/bin/env python
import os
import sys

if __name__ == "__main__":
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "TestDrivenDjango.settings")
    try:
        from django.core.management import execute_from_command_line
    except ImportError:
        ...

    execute_from_command_line(sys.argv)

from django.core.management import execute_from_command_line当这行代码开始执行时,首先会去运行django.core.management.__init__.py这一整个文件,接着找到execute_from_command_line函数并将其导入到当前程序的命名空间中。

由于整个django.core.management.__init__.py文件都是class类对象和function函数对象,很多时候很自然的就认为这个文件并没有执行任何命令,只是加载了这些个对象,然后在这些个对象中寻找是否包含有execute_from_command_line。最终忽视了其他很重要的代码块==from和import==。

 

django/core/management/__init__.py
from __future__ import unicode_literals

import os
import pkgutil
import sys
from collections import OrderedDict, defaultdict
from importlib import import_module

import django                               # 这里
from django.apps import apps                # 这里
from django.conf import settings
from django.core.exceptions import ImproperlyConfigured
from django.core.management.base import (
    BaseCommand, CommandError, CommandParser, handle_default_options,
)
from django.core.management.color import color_style
from django.utils import autoreload, lru_cache, six
from django.utils._os import npath, upath
from django.utils.encoding import force_text

@lru_cache.lru_cache(maxsize=None)
def get_commands():
    commands = {name: 'django.core' for name in find_commands(upath(__path__[0]))}
    if not settings.configured:
        return commands
    for app_config in reversed(list(apps.get_app_configs())):
        path = os.path.join(app_config.path, 'management')
        commands.update({name: app_config.name for name in find_commands(path)})
    return commands

class ManagementUtility(object):

    def __init__(self, argv=None):
        ...

    def fetch_command(self, subcommand):
        commands = get_commands()
        try:
            app_name = commands[subcommand]
        except KeyError:
            ...
        if isinstance(app_name, BaseCommand):
            klass = app_name
        else:
            klass = load_command_class(app_name, subcommand)
        return klass        

    def execute(self):
        ...
        if subcommand == 'help':
            if '--commands' in args:
                sys.stdout.write(self.main_help_text(commands_only=True) + '\n')
            elif len(options.args) < 1:
                sys.stdout.write(self.main_help_text() + '\n')
            else:
                self.fetch_command(options.args[0]).print_help(self.prog_name, options.args[0])
        elif subcommand == 'version' or self.argv[1:] == ['--version']:
            sys.stdout.write(django.get_version() + '\n')
        elif self.argv[1:] in (['--help'], ['-h']):
            sys.stdout.write(self.main_help_text() + '\n')
        else:
            self.fetch_command(subcommand).run_from_argv(self.argv)

def execute_from_command_line(argv=None):
    """
    A simple method that runs a ManagementUtility.
    """
    utility = ManagementUtility(argv)
    utility.execute()

import django 这行代码运行了django.__init__.py文件。

from django.apps import apps这行代码运行了django.apps.__init__.py文件,然而整个django的开端就是从这里开始的,它落实了非常多的事情(例如:初始化日志模块、加载INSTALL_APP、检查各APP是否正常、检查缓存模块是否正常等),当一切无误时才会往下走,否则将会报错退出程序。

execute_from_command_line这个方法是一个工厂函数,它负责指挥ManagementUtility类利用execute方法来解析参数和启动wsgi服务。

ManagementUtility.execute方法中的一大堆if条件就是判断参数是否合法,重点还是在self.fetch_command(subcommand).run_from_argv(self.argv),这条命令应该拆成两部分去看。

  • self.fetch_command
    是利用django内置的命令管理工具去匹配到具体的模块,例如self.fetch_command(subcommand)其实就相当于是self.fetch_command('runserver'),它最终找到了==django.contrib.staticfiles.management.commands.runserver.Command==这个命令工具。
    django中的命令工具代码组织采用的是策略模式+接口模式,也就是说django.core.management.commands这个目录下面存在各种命令工具,每个工具下面都有一个Command接口,当匹配到'runserver'时调用'runserver'命令工具的Command接口,当匹配到'migrate'时调用'migrate'命令工具的Command接口。
  • run_from_argv(self.argv)
    run_from_argv的作用是初始化中间件、启动服务,也就是拉起wgsi(但实际上并不是由它来直接完成,而是由后续很多其他代码来完成),直观上看它应该是runserver.Command对象的一个方法,但实际上要稍微更复杂一些,因为没有列出关联代码,所以在下一个代码块中进行说明。

小结
这部分代码实际上是一个匹配命令工具的一个过程,通过提供的参数'runserver'到命令工具集中去找到runserver模块。

 

django/contrib/staticfiles/management/commands/runserver.py
from django.contrib.staticfiles.handlers import StaticFilesHandler
from django.core.management.commands.runserver import \
    Command as RunserverCommand


class Command(RunserverCommand):

    def add_arguments(self, parser):
        super(Command, self).add_arguments(parser)
        parser.add_argument(
            '--nostatic', action="store_false", dest='use_static_handler', default=True,
            help='Tells Django to NOT automatically serve static files at STATIC_URL.',
        )
        parser.add_argument(
            '--insecure', action="store_true", dest='insecure_serving', default=False,
            help='Allows serving static files even if DEBUG is False.',
        )

    def get_handler(self, *args, **options):
        handler = super(Command, self).get_handler(*args, **options)
        use_static_handler = options['use_static_handler']
        insecure_serving = options['insecure_serving']
        if use_static_handler and (settings.DEBUG or insecure_serving):
            return StaticFilesHandler(handler)
        return handler

虽然它一共只有两个方法,但是它继承了django.core.management.commands.runserver.Command,因此实际上它有非常多的‘方法’可以被调用。由于请求入口在这里,所以后续有调用==get_handler==的地方需要优先看这里,因为这非常容易混淆,我就混淆了好多次。

小结
当前类对象中不存在run_from_argv方法,因此我们要往下看它的继承对象django.core.management.commands.runserver.Command

 

django/core/management/commands/runserver.py
from django.core.servers.basehttp import get_internal_wsgi_application, run

class Command(BaseCommand):

    def execute(self, *args, **options):
        if options['no_color']:
            os.environ[str("DJANGO_COLORS")] = str("nocolor")
        super(Command, self).execute(*args, **options)    

    def get_handler(self, *args, **options):
        return get_internal_wsgi_application()

    def handle(self, *args, **options):
        ...
        self.run(**options)

    def run(self, **options):
        use_reloader = options['use_reloader']

        if use_reloader:
            autoreload.main(self.inner_run, None, options)
        else:
            self.inner_run(None, **options)        

    def inner_run(self, *args, **options):
        ...
        try:
            handler = self.get_handler(*args, **options)
            run(self.addr, int(self.port), handler,
                ipv6=self.use_ipv6, threading=threading)
        except socket.error as e:
            ...

小结
当前这个类对象中也没有run_from_argv这个方法,因此我们要往下看它的继承对象django.core.management.base.BaseCommand

 

django/core/management/base.py
class BaseCommand(object):

    def run_from_argv(self, argv):
        ...
        try:
            self.execute(*args, **cmd_options)
        except Exception as e:
            ...
        finally:
            connections.close_all()

    def execute(self, *args, **options):
        ...
        try:
            ...
            output = self.handle(*args, **options)
            ...
        finally:
            if saved_locale is not None:
                translation.activate(saved_locale)
        return output

runserver继承对象分布 依次按顺序如下

  • django.contrib.staticfiles.management.commands.runserver.Command
  • django.core.management.commands.runserver.Command
  • django.core.management.base.BaseCommand
  • object

当前类对象的run_from_argv方法中调用了self.execute方法,==由于请求的入口是django.contrib.staticfiles.management.commands.runserver.Command对象==,因此python并不会去执行BaseCommand.execute而是执行django.core.management.commands.runserver.Command.execute,最后通过super(Command, self).execute(*args, **options)来执行BaseComand.execute

代码流向
由于接下来的代码重度使用了继承、多态、接口等设计模式的方式来运作,没办法做过多解释,所以这里列出了它的一个基本流转过程。

  • BaseComand.execute方法中调用了self.handle(即:django.core.management.commands.runserver.Command.handle)。
  • Command.handle方法中调用了self.run(即:django.core.management.commands.runserver.Command.run)。
  • Command.run方法调用了self.inner_run(即:django.core.management.commands.runserver.Command.inner_run)。
  • Command.inner_run方法调用了self.get_handler(即:==django.contrib.staticfiles.management.commands.runserver.Command.get_handler==)
     

    这里要特别强调一下self.get_handler,它非常重要,三个重点:

    1. 因为它负责获取WSGIHandler。
    2. 由于请求入口是django.contrib.staticfiles.management.commands.runserver.Command,整好它本来就有get_handler这个方法,因此并没有采用django.core.management.commands.runserver.Command.get_handler
    3. self.get_handler并不会返回一个常规的WSGIHandler而是返回一个StaticFilesHandler
    4. StaticFilesHandler类对象继承WSGIHandler,它的目的是为了判断每个请求,如果是常规的url请求则直接分配到某个view中去执行,如果是静态文件规则那么将不会找view而是响应这个文件。
  • Command.inner_run方法调用了run(即:django.core.servers.basehttp.run),由于没有列出代码块,因此在下一个环节中进行说明。

小结
这部分代码实际上就是一个初始化过程,全部都为'runserver'服务,虽然很多代码我没有列出来,但是它确实做了一些,例如参数解析、端口指定检测、ipv4检测、ipv6检测、端口是否占用、线程检查等工作。

django/core/servers/basehttp.py
from wsgiref import simple_server
from django.utils.six.moves import socketserver


class WSGIServer(simple_server.WSGIServer, object):

    request_queue_size = 10

    def __init__(self, *args, **kwargs):
        if kwargs.pop('ipv6', False):
            self.address_family = socket.AF_INET6
        self.allow_reuse_address = kwargs.pop('allow_reuse_address', True)
        super(WSGIServer, self).__init__(*args, **kwargs)

    def server_bind(self):
        super(WSGIServer, self).server_bind()
        self.setup_environ()

    def handle_error(self, request, client_address):
        if is_broken_pipe_error():
            logger.info("- Broken pipe from %s\n", client_address)
        else:
            super(WSGIServer, self).handle_error(request, client_address)


class WSGIRequestHandler(simple_server.WSGIRequestHandler, object):
    def address_string(self):
        return self.client_address[0]


def run(addr, port, wsgi_handler, ipv6=False, threading=False):
    server_address = (addr, port)
    if threading:
        httpd_cls = type(str('WSGIServer'), (socketserver.ThreadingMixIn, WSGIServer), {})   # Work Here
    else:
        httpd_cls = WSGIServer
    httpd = httpd_cls(server_address, WSGIRequestHandler, ipv6=ipv6)
    if threading:
        httpd.daemon_threads = True

    httpd.set_app(wsgi_handler)
    httpd.serve_forever()

django.core.servers.basehttp.run工厂函数负责只会各个对象负责启动wsgi服务。
wsgi_handler参数,这里传递的是StaticFilesHandler

type(str('WSGIServer'), (socketserver.ThreadingMixIn, WSGIServer), {}) 是一种很特殊的写法,通过代码块中WSGIServer类对象可以看出它只继承了wsgiref.simple_server.WSGIServer、object这两个类对象,但是通过type这种写法相当于是强行赋予它一个socketserver.ThreadingMixIn继承对象,它的用意是每次调用这个对象的时候都会单独启用一个线程来处理。另外虽然 WSGIServer 只继承了 wsgiref.simple_server.WSGIServer、object两个对象,但是wsgiref.simple_server.WSGIServer却<递归式>的继承了一堆对象,下面完整的列出WSGIServer继承家族。

  • django.core.servers.basehttp.WSGIServer
  • wsgiref.simple_server.WSGIServersocketserver.ThreadingMixIn
  • http.server.HTTPServer
  • socketserver.TCPServer
  • socketserver.BaseServer
  • object

httpd_cls这个变量被定义完成之后,由于大量的继承关系,它其实已经不单纯的属于django,它是一个传统意义上的WSGI服务对象了。

httpd = httpd_cls(server_address, WSGIRequestHandler, ipv6=ipv6)这行代码非常重要,因为它是WSGI服务器与django之间相互通信的唯一枢纽通道,也就是说,当WSGI服务对象收到socket请求后,会将这个请求传递给django的WSGIRequestHandler(下节会列出WSGIRequestHandler是如何工作的)。

httpd.set_app(wsgi_handler)是将django.contrib.staticfiles.handlers.StaticFilesHandler 传递给WSGIServer当作一个application,当WSGIServer收到网络请求后,可以将数据分发给django.core.servers.basehttp.WSGIRequestHandler,最终由django.core.servers.basehttp.WSGIRequestHandler将数据传递给application(即:django.contrib.staticfiles.handlers.StaticFilesHandler)。

httpd.serve.forever()启动非堵塞网络监听服务。

总结

上面所有的过程都是django内部代码的为了启动服务而做的准备,简单的把流程给列出来。

  1. 解析运行 python manage.py 所提供的参数,例如: runserver.
  2. 根据参数 找到相对应的 命令管理工具。
  3. 加载所有的app。
  4. 检查端口、ipv4检测、ipv6检测、端口是否占用、线程检查、orm对象检查(表是否创建)。
  5. 实例化WSGIRequestHandler,并且将它注册到python Lib库中的WSGIServer中。
  6. 最后启动python Lib库中的WSGIServer。

 

 

WSGIServer To Django WSGIRequestHandler

接下来的部分是python Lib库中的WSGIServer运作过程中,如何将接收到的请求分发会django的WSGIRequestHandler。

C:/Python35/Lib/socketserver.py
class BaseServer:

    def __init__(self, server_address, RequestHandlerClass):
        self.server_address = server_address
        self.RequestHandlerClass = RequestHandlerClass
        self.__is_shut_down = threading.Event()
        self.__shutdown_request = False

    def serve_forever(self, poll_interval=0.5):
        self.__is_shut_down.clear()
        try:

            with _ServerSelector() as selector:
                selector.register(self, selectors.EVENT_READ)

                while not self.__shutdown_request:
                    ready = selector.select(poll_interval)
                    if ready:
                        self._handle_request_noblock()              # 这里

                    self.service_actions()
        finally:
            self.__shutdown_request = False
            self.__is_shut_down.set()

    def _handle_request_noblock(self):
        try:
            request, client_address = self.get_request()
        except OSError:
            return
        if self.verify_request(request, client_address):
            try:
                self.process_request(request, client_address)       # 这里
            except:
                self.handle_error(request, client_address)
                self.shutdown_request(request)
        else:
            self.shutdown_request(request)    

    def verify_request(self, request, client_address):
        """Verify the request.  May be overridden.

        Return True if we should proceed with this request.

        """
        return True            

    def process_request(self, request, client_address):
        self.finish_request(request, client_address)                # 这里
        self.shutdown_request(request)     

    def finish_request(self, request, client_address):
        self.RequestHandlerClass(request, client_address, self)     # 这里

上一小节最后一个动作是httpd.serve_forever,调用的是socketserver.BaseServer.serve_forever方法。该方法采用了selector网络模型进行等待数据,每0.5秒遍历一次文件描述符,当有数据进来时,ready变量会是一个socket请求对象,这时会将后续工作转交给self._handler_request_noblock方法(即:socketserver.BaseServer._handler_request_noblock)去处理。

socketserver.BaseServer._handler_request_noblock方法基本没做什么事情(self.verify_request压根就没有检查任何东西),直接就把后续工作转交给 socketserver.BaseServer.process_request 方法。

socketserver.BaseServer.process_request也没做什么事情,直接就将后续工作转交给socketserver.BaseServer.finish_request方法,只不过在最后加了一条关闭请求的命令。

socketserver.BaseServer.finish_request也没做什么事情,直接就将后续工作转交给socketserver.BaseServer.RequestHandlerClass

socketserver.BaseServer.RequestHandlerClass是由上一节httpd = httpd_cls(server_address, WSGIRequestHandler, ipv6=ipv6)传递过来的参数django.core.servers.basehttp.WSGIRequestHandler。 也就是说当执行self.RequestHandler(request, client_address, self)时等同于执行django.core.servers.basehttp.WSGIRequestHandler(request, client_address, self)

小结
serve_forever开启了一个while来无限监听网络层的scoket请求,当一条请求过来时,就层层转交到django.core.servers.basehttp.WSGIRequestHandler手中。

django.core.servers.basehttp.py 单独列出WSGIRequestHandler代码片段
class WSGIRequestHandler(simple_server.WSGIRequestHandler, object):
    def address_string(self):
        return self.client_address[0]

    def get_environ(self):
        for k, v in self.headers.items():
            if '_' in k:
                del self.headers[k]

        env = super(WSGIRequestHandler, self).get_environ()
        path = self.path
        if '?' in path:
            path = path.partition('?')[0]
        path = uri_to_iri(path).encode(UTF_8)
        env['PATH_INFO'] = path.decode(ISO_8859_1) if six.PY3 else path
        return env

    def handle(self):
        self.raw_requestline = self.rfile.readline(65537)
        if len(self.raw_requestline) > 65536:
            self.requestline = ''
            self.request_version = ''
            self.command = ''
            self.send_error(414)
            return

        if not self.parse_request():  # An error code has been sent, just exit
            return

        handler = ServerHandler(
            self.rfile, self.wfile, self.get_stderr(), self.get_environ()
        )

        handler.request_handler = self      # backpointer for logging
        handler.run(self.server.get_app())

接着上一节继续分析:
socketserver.BaseServer.RequestHandler(request, client_address, self)等同于django.core.servers.basehttp.WSGIRequestHandler(request, client_address, self)

首先django.core.servers.basehttp.WSGIRequestHandler的继承分布:

  • django.core.servers.basehttp.WSGIRequestHandler
  • wsgiref.simple_server.WSGIRequestHandler
  • http.server.BaseHTTPRequestHandler
  • socketserver.StreamRequestHandler
  • socketserver.BaseRequestHandler
  • object

从代码上看django.core.servers.basehttp.WSGIRequestHandler并没有init或者call方法,因此需要遍历所有父类对象。
最终在socketserver.BaseRequestHandler中看到了init实例初始化方法,它调用了self.handle方法(即回调了:django.core.servers.basehttp.WSGIRequestHandler.handle)。

handler = ServerHandler(self.rfile, self.wfile, self.get_stderr(), self.get_environ())实例化了ServerHandler对象。

handler.run(self.server.get_app()),意思是将django.contrib.staticfiles.handlers.StaticFilesHandler转交给ServerHandler去运行。

ServerHandler对象并没有run方法,它的继承分布:

  • django.core.servers.basehttp.ServerHandler
  • wsgiref.simple_server.ServerHandler
  • wsgiref.handlers.SimpleHandler
  • wsgiref.handlers.BaseHandler
  • object

最终在 wsgiref.handlers.BaseHandler 中找到了run方法。

wsgiref.handlers.py
class BaseHandler:
    def run(self, application):
        ...
        self.result = application(self.environ, self.start_response)    # 这里
        self.finish_response()

application(self.environ, self.start_response)也就相当于是django.contrib.staticfiles.handlers.StaticFilesHandler.__call__(self.environ, lf.start_response)

django.contrib.staticfiles.handlers.py
class StaticFilesHandler(WSGIHandler):
    def __call__(self, environ, start_response):
        if not self._should_handle(get_path_info(environ)):
            return self.application(environ, start_response)
        return super(StaticFilesHandler, self).__call__(environ, start_response)

通过层层流转,最终进入django的静态文件处理的Handler。

总结
environ这个变量在django的WSGIServer和WSGIRequestHandler中扮演这非常重要的角色,因为所有的客户端ip请求的URLcookiesessionheader等等信息都保存在其中。

WSGIServer: 用于处理socket请求和对接WSGIRequestHandler。
WSGIRequestHandler:针对environ进行预处理和对接WSGIServerHandler。
ServerHandler: 用于执行应用程序(application)和返回响应给WSGIServer。

© 著作权归作者所有

共有 人打赏支持
AllenOR灵感
粉丝 10
博文 2139
码字总数 82983
作品 0
程序员
Django之部署NGINX+uWSGI

参考地址:http://www.cnblogs.com/CongZhang/p/6548529.html http://www.cnblogs.com/alex3714/p/6538374.html http://uwsgi.readthedocs.io/en/latest/tutorials/Djangoandnginx.html ----......

LinQiH ⋅ 2017/11/07 ⋅ 0

打开Openstack dashboard出现Internal Server Error

这次的问题是在openstack搭建好之后,dashboard本身是能用的,但是在某一天后,突然发现打开dashboard后出现Internal Server Error. 于是乎在网上找答案但是没有,那就自己动手解决吧。 首先...

锅巴跑跑 ⋅ 2016/12/15 ⋅ 0

Nginx uWsgi Django环境搭建

由于一个监控项目(使用django开发)交接到所在项目组,需要了解python的web开发和环境搭建。 这里记录环境的搭建流程,以及遇到的问题和处理方案。 环境信息:CentOS 6.6 (内网IP:100.84....

-启曙- ⋅ 2015/12/31 ⋅ 2

caravel之架构与源码浅析

如果你打算改造和定制caravel,这篇文章可能对你有帮助 外围观察 上边的这张图列出了caravel用到的部分技术(由Wappalyzer分析得出) 包括: 后端 采用python的Flask框架(当前版本是Flask 0...

aibati2008 ⋅ 2016/10/11 ⋅ 0

Django + Uwsgi + Nginx 实现生产环境部署

今天老男孩IT教育Python教学导师吴sir带你用Django + Uwsgi + Nginx 实现生产环境部署 1、uwsgi 介绍 2、uwsgi安装使用 3、nginx安装配置 4、django with nginx 如何在生产上部署Django? Dja...

米斯特赛文 ⋅ 2017/04/06 ⋅ 0

Django 项目部署(apache)

Django 项目部署(apache) 终于完结,本机浏览器访问外网的(Django)网站连接问题,下面是总结最近一次部署Django基于ubuntu操作系统上的Apache服务器的成功案例。部署这个项目我走了很多的...

c0586 ⋅ 2017/02/05 ⋅ 0

Django + Uwsgi + Nginx 实现生产环境部署

今天老男孩IT教育Python教学总监alex带你用Django + Uwsgi + Nginx 实现生产环境部署 1、uwsgi 介绍 2、uwsgi安装使用 3、nginx安装配置 4、django with nginx 如何在生产上部署Django? Dja...

alex3714 ⋅ 2017/04/06 ⋅ 0

深入理解 Python WSGI:一起写一个 Web 服务器

导读: 本系列深入浅出的讲述了如何用 Python 从 0 开始,写一个 web 服务器,并让其与业界流行的 web 框架协同工作,最后还进一步完善了开头的 web 服务器 demo,让其可以支持多并发请求的处...

xrzs ⋅ 2015/08/03 ⋅ 0

在Mopaas上部署WSGI类型的(Django, Tornado, Flask)Python应用

第一部分:安装必要工具。 1.因为这是部署Python开发环境,所以安装pip可以简化一些软件的安装过程。(PIP对应Lua的luarocks) sudo apt-get install python-pip 安装三个Python框架 sudo p...

糖果L5Q ⋅ 2015/09/09 ⋅ 0

使用uwsgi和nginx部署django应用

概述 文章是uwsgi关于django部署的学习笔记,过程中涉及: 浏览器 nginx服务器 linux socket uwsgi服务 django应用 最终各个组件之间的关系是 当浏览器访问web页面时,如果请求的是静态文件,...

alazyer ⋅ 2016/03/02 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

RabbitMQ学习以及与Spring的集成(三)

本文介绍RabbitMQ与Spring的简单集成以及消息的发送和接收。 在RabbitMQ的Spring配置文件中,首先需要增加命名空间。 xmlns:rabbit="http://www.springframework.org/schema/rabbit" 其次是模...

onedotdot ⋅ 18分钟前 ⋅ 0

JAVA实现仿微信红包分配规则

最近过年发红包拜年成为一种新的潮流,作为程序猿对算法的好奇远远要大于对红包的好奇,这里介绍一种自己想到的一种随机红包分配策略,还请大家多多指教。 算法介绍 一、红包金额限制 对于微...

楠木楠 ⋅ 30分钟前 ⋅ 0

Python 数电表格格式化 xlutils xlwt xlrd的使用

需要安装 xlutils xlwt xlrd 格式化前 格式化后 代码 先copy读取的表格,然后按照一定的规则修改,将昵称中的学号提取出来替换昵称即可 from xlrd import open_workbookfrom xlutils.copy ...

阿豪boy ⋅ 59分钟前 ⋅ 0

面试题:使用rand5()生成rand7()

前言 读研究生这3 年,思维与本科相比变化挺大的,这几年除了看论文、设计方案,更重要的是学会注重先思考、再实现,感觉更加成熟吧,不再像个小P孩,人年轻时总会心高气傲。有1 道面试题:给...

初雪之音 ⋅ 59分钟前 ⋅ 0

Docker Toolbox Looks like something went wrong

Docker Toolbox 重新安装后提示错误:Looks like something went wrong in step ´Checking if machine default exists´ 控制面板-->程序与应用-->启用或关闭windows功能:找到Hyper-V,如果处......

随你疯 ⋅ 今天 ⋅ 0

Guacamole 远程桌面

本文将Apache的guacamole服务的部署和应用,http://guacamole.apache.org/doc/gug/ 该链接下有全部相关知识的英文文档,如果水平ok,可以去这里仔细查看。 一、简介 Apache Guacamole 是无客...

千里明月 ⋅ 今天 ⋅ 0

nagios 安装

Nagios简介:监控网络并排除网络故障的工具:nagios,Ntop,OpenVAS,OCS,OSSIM等开源监控工具。 可以实现对网络上的服务器进行全面的监控,包括服务(apache、mysql、ntp、ftp、disk、qmail和h...

寰宇01 ⋅ 今天 ⋅ 0

AngularDart注意事项

默认情况下创建Dart项目应出现以下列表: 有时会因为不知明的原因导致列表项缺失: 此时可以通过以下步骤解决: 1.创建项目涉及到的包:stagehand 2.执行pub global activate stagehand或pub...

scooplol ⋅ 今天 ⋅ 0

Java Web如何操作Cookie的添加修改和删除

创建Cookie对象 Cookie cookie = new Cookie("id", "1"); 修改Cookie值 cookie.setValue("2"); 设置Cookie有效期和删除Cookie cookie.setMaxAge(24*60*60); // Cookie有效时间 co......

二营长意大利炮 ⋅ 今天 ⋅ 0

【每天一个JQuery特效】淡入淡出显示或隐藏窗口

我是JQuery新手爱好者,有时间就练练代码,防止手生,争取每天一个JQuery练习,在这个博客记录下学习的笔记。 本特效主要采用fadeIn()和fadeOut()方法显示淡入淡出的显示效果显示或隐藏元...

Rhymo-Wu ⋅ 今天 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部