原文网址

https://www.jianshu.com/p/17d78b52c732?utm_campaign=maleskine&utm_content=note&utm_medium=seo_notes&utm_source=recommendation

环境说明

  • [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。

django源码分析的更多相关文章

  1. django源码分析 python manage.py runserver

    django是一个快速开发web应用的框架, 笔者也在django框架上开发不少web应用,闲来无事,就想探究一下django底层到底是如何实现的,本文记录了笔者对django源码的分析过程 I be ...

  2. Django源码分析之启动wsgi发生的事

    前言 ​ 好多人对技术的理解都停留在懂得使用即可,因而只会用而不会灵活用,俗话说好奇害死猫,不然我也不会在凌晨1.48的时候决定写这篇博客,好吧不啰嗦了 ​ 继续上一篇文章,后我有个问题(上文:&qu ...

  3. django源码分析——本地runserver分析

    本文环境python3.5.2,django1.10.x系列 1.根据上一篇文章分析了,django-admin startproject与startapp的分析流程后,根据django的官方实例此时 ...

  4. 2、Django源码分析之启动wsgi发生了哪些事

    一 前言 Django是如何通过网络socket层接收数据并将请求转发给Django的urls层? 有的人张口就来:就是通过wsgi(Web Server Gateway Interface)啊! D ...

  5. django源码分析——处理请求到wsgi及视图view

    本文环境python3.5.2,django1.10.x系列 根据前上一篇runserver的博文,已经分析了本地调试服务器的大致流程,现在我们来分析一下当runserver运行起来后,django框 ...

  6. django源码分析 LazySetting对象

    一.django中通过LazySetting对象来获取项目的配置,LazySetting对象有什么特性?为什么使用这个对象? LazySetting顾名思义,就是延迟获取配置内容.比如,我们定义了一个 ...

  7. django源码分析——静态文件staticfiles中间件

    本文环境python3.5.2,django1.10.x系列 1.在上一篇文章中已经分析过handler的处理过程,其中load_middleware就是将配置的中间件进行初始化,然后调用相应的设置方 ...

  8. Django源码分析之程序执行入口分析

    一般我们开启一个django项目,最简单的方法是进入project 目录,这时目录结构是这样的 然后我们执行python manage.py runserver,程序就开始执行了. 那django是如 ...

  9. django源码分析 请求流程

    一.从浏览器发出一个请求,到返回响应内容,这个过程是怎么样的? 1. 浏览器解析输入的url 2. 查找url对应的ip地址 3. 通过ip地址访问我们的服务器 1.  请求进入wsgi服务器(我在这 ...

随机推荐

  1. WebStorm 关联 TFS(转)

    1.下载插件 TFS integration  2.链接TFS 服务器 3.创建工作区 4. 5.选择一个 工作环境 6.最重要的有点是在VCS里面要选择一个默认的提交方式!!!

  2. $.each() 与 $(selector).each()的区别

    $.each( dataArr,function(i,item){}) 可用于遍历任何的集合(无论是数组或对象).  如果是数组,回调函数每次传入数组的索引(也就是i)和对应的值(item)(值亦可以 ...

  3. Freeswitch 入门

    让我们从最初的运行开始,一步一步进入 FreeSWITCH 的神秘世界. 命令行参数 一般来说,FreeSWITCH 不需要任何命令行参数就可以启动,但在某些情况下,你需要以一些特殊的参数启动.在此, ...

  4. [jquery]为jQuery.ajax添加onprogress事件

    原理: 给XMLHttpRequest对象的upload属性绑定onprogress方法监听上传过程 var xhr = new XMLHttpRequest();  xhr.upload.onpro ...

  5. 洛谷P3247 [HNOI2016]最小公倍数 [分块,并查集]

    洛谷 思路 显然,为了达到这个最小公倍数,只能走\(a,b\)不是很大的边. 即,当前询问的是\(A,B\),那么我们只能走\(a\leq A,b\leq B\)的边. 然而,为了达到这最小公倍数,又 ...

  6. a href=#与 a href=javascript:void(0) 的区别(转)

    a href="#"> 点击链接后,页面会向上滚到页首,# 默认锚点为 #TOP <a href="javascript:void(0)" onCl ...

  7. 信息摘要算法之六:HKDF算法分析与实现

    HKDF是一种特定的键衍生函数(KDF),即初始键控材料的功能,KDF从其中派生出一个或多个密码强大的密钥.在此我们想要描述的是基于HMAC的HKDF. 1.HKDF概述 密钥派生函数(KDF)是密码 ...

  8. 使用Eclipse进行Makefile项目

    最近在MCU on Eclipse网站上看到Erich Styger所写的一篇有关在Eclipse中使用Makefile创建项目的文章,文章讲解清晰明了非常不错,所以呢没人将其翻译过来供各位同仁参考. ...

  9. http之cdn介绍

    百度百科:CDN的全称是Content Delivery Network,即内容分发网络.CDN是构建在网络之上的内容分发网络,依靠部署在各地的边缘服务器,通过中心平台的负载均衡.内容分发.调度等功能 ...

  10. nginx安装目录详解(针对centos)