粗读web框架之go gin和python django

栏目: IT技术 · 发布时间: 4年前

内容简介:===

为什么引入web框架

web应用的本质

  • 浏览器发送一个HTTP请求;
  • 服务器收到请求,生成一个HTML文档;
  • 服务器把HTML文档作为HTTP响应的Body发送给浏览器;
  • 浏览器收到HTTP响应,从HTTP Body取出HTML文档并显示;

涉及的问题

  • 解析http请求
  • 找到对应的处理函数
  • 生成并发送http响应

web框架工作流程

粗读web框架之go gin和python django

中间件

  • 中间件是请求或者应用开始和结束时注入代码的机制
  • 常见的web中间件: 鉴权、打印log、session、统计信息、处理数据库连接  等等

粗读web框架之go gin和python django

===

golang http 服务端编程

  • golang 的net/http包提供了http编程的相关接口,封装了内部TCP连接和报文解析的复杂琐碎的细节 
    go c.serve(ctx)最终会启动goroutine处理请求
  • 使用者只需要和 http.request  和  http.ResponseWriter  两个对象交互就行(也就是一个struct 实现net/http包中的Handler interface中的 ServeHttp方法 )
//E:\Go\src\net\http\server.go +82 
type Handler interface {
    ServeHTTP(ResponseWriter, *Request)
}

//纯 net.http包的server方法
package main

import (
    "io"
    "net/http"
)

type helloHandler struct{}

func (h *helloHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    w.Write([]byte("Hello, world!"))
}

func main() {
    http.Handle("/", &helloHandler{})
    http.ListenAndServe(":12345", nil)
}

//////////////////////////////////////////////////////////////////

import (
    "net/http"
)

type Handle struct{}

func (h Handle) ServeHTTP(response http.ResponseWriter, request *http.Request) {
    switch request.URL.Path {
    case "/info":
        response.Write([]byte("info"))
    default:

    }
}

func main() {
    http.ListenAndServe(":8888", Handle{})
}
  • net/http 的另外一个重要的概念  ,ServeMux (多路传输):ServeMux 可以注册多了 URL 和 handler 的对应关系,并自动把请求转发到对应的 handler 进行处理
  • // 下面看一个带路由的http server
    package main
    
    import (
        "io"
        "net/http"
    )
    
    func helloHandler(w http.ResponseWriter, r *http.Request) {
        io.WriteString(w, "Hello, world!\n")
    }
    
    func echoHandler(w http.ResponseWriter, r *http.Request) {
        io.WriteString(w, r.URL.Path)
    }
    
    func main() {
        mux := http.NewServeMux()
        mux.HandleFunc("/hello", helloHandler)
        mux.HandleFunc("/", echoHandler)
    
        http.ListenAndServe(":12345", mux)
    }
    
    // 其实ServeMux 也实现了Handler的ServeHTTP方法所以也是Handler接口
    //E:\Go\src\net\http\server.go 2382
    func (mux *ServeMux) ServeHTTP(w ResponseWriter, r *Request) {
        if r.RequestURI == "*" {
            if r.ProtoAtLeast(1, 1) {
                w.Header().Set("Connection", "close")
            }
            w.WriteHeader(StatusBadRequest)
            return
        }
        h, _ := mux.Handler(r)
        h.ServeHTTP(w, r)
    }
    
    
    // E:\Go\src\net\http\server.go +2219可以看到 net/http包中的 基于map 路由查找
    // Find a handler on a handler map given a path string.
    // Most-specific (longest) pattern wins.
    func (mux *ServeMux) match(path string) (h Handler, pattern string) {
        // Check for exact match first.
        v, ok := mux.m[path]
        if ok {
            return v.h, v.pattern
        }
    
        // Check for longest valid match.
        var n = 0
        for k, v := range mux.m {
            if !pathMatch(k, path) {
                continue
            }
            if h == nil || len(k) > n {
                n = len(k)
                h = v.h
                pattern = v.pattern
            }
        }
        return
    }

golang web框架 GIN golang gin web框架

//gin框架初始化的流程
1.初始化engine 
2.注册中间件
3.注册路由

//响应流程
1.路由,找到handle
2.将请求和响应用Context包装起来供业务代码使用
3.依次调用中间件和处理函数
4.输出结果

//gin 里面最重要的两个数据结构
//1.Context在中间件中传递本次请求的各种数据、管理流程,进行响应
//2.Engine gin 引擎,是框架的实例,它包含多路复用器,中间件和配置设置




// 下面看下open-falcon-api中的实际应用
//open-falcon-api里面注册路由和中间件
//E:\go_path\src\github.com\open-falcon\falcon-plus\modules\api\app\controller\graph\graph_routes.go
// 首先注册/api/v1/开头的path的路由组
// 然后Use 一个auth的中间件 ,作用是检查token
// 这个组后面的所有path 都使用这个中间件 
// 也就是访问 /graph/endpoint 时会过 3个中间件(log recovery  auth )+一个EndpointRegexpQuery处理函数
// 
func Routes(r *gin.Engine) {

    db = config.Con()
    authapi := r.Group("/api/v1")
    authapi.Use(utils.AuthSessionMidd)
    authapi.GET("/graph/endpointobj", EndpointObjGet)
    authapi.GET("/graph/endpoint", EndpointRegexpQuery)
    authapi.GET("/graph/endpoint_counter", EndpointCounterRegexpQuery)
    authapi.POST("/graph/history", QueryGraphDrawData)
    authapi.POST("/graph/lastpoint", QueryGraphLastPoint)
    authapi.POST("/graph/info", QueryGraphItemPosition)
    authapi.DELETE("/graph/endpoint", DeleteGraphEndpoint)
    authapi.DELETE("/graph/counter", DeleteGraphCounter)

    grfanaapi := r.Group("/api")
    grfanaapi.GET("/v1/grafana", GrafanaMainQuery)
    grfanaapi.GET("/v1/grafana/metrics/find", GrafanaMainQuery)
    grfanaapi.POST("/v1/grafana/render", GrafanaRender)
    grfanaapi.GET("/v1/grafana/render", GrafanaRender)

}


func AuthSessionMidd(c *gin.Context) {
    auth, err := h.SessionChecking(c)
    if !viper.GetBool("skip_auth") {
        if err != nil || auth != true {
            log.Debugf("error: %v, auth: %v", err, auth)
            c.Set("auth", auth)
            h.JSONR(c, http.StatusUnauthorized, err)
            c.Abort()
            return
        }
    }
    c.Set("auth", auth)
}


// E:\go_path\src\github.com\open-falcon\falcon-plus\vendor\github.com\gin-gonic\gin\context.go +715 最后会调用这里的Render方法
// Render writes the response headers and calls render.Render to render data.
func (c *Context) Render(code int, r render.Render) {
    c.Status(code)

    if !bodyAllowedForStatus(code) {
        r.WriteContentType(c.Writer)
        c.Writer.WriteHeaderNow()
        return
    }

    if err := r.Render(c.Writer); err != nil {
        panic(err)
    }
}

// 可以看到这里的bind 是在gin在解析请求payload是否和函数中要求的struct一致
// E:\go_path\src\github.com\open-falcon\falcon-plus\vendor\github.com\gin-gonic\gin\context.go +504
// bind会根据请求中的Content-Type header判断是json 还是xml
// 并且根据struct中的tag通过反射解析payload

// Bind checks the Content-Type to select a binding engine automatically,
// Depending the "Content-Type" header different bindings are used:
//     "application/json" --> JSON binding
//     "application/xml"  --> XML binding
// otherwise --> returns an error.
// It parses the request's body as JSON if Content-Type == "application/json" using JSON or XML as a JSON input.
// It decodes the json payload into the struct specified as a pointer.
// It writes a 400 error and sets Content-Type header "text/plain" in the response if input is not valid.
func (c *Context) Bind(obj interface{}) error {
    b := binding.Default(c.Request.Method, c.ContentType())
    return c.MustBindWith(obj, b)
}


type APIEndpointObjGetInputs struct {
    Endpoints []string `json:"endpoints" form:"endpoints"`
    Deadline  int64    `json:"deadline" form:"deadline"`
}


func EndpointObjGet(c *gin.Context) {
    inputs := APIEndpointObjGetInputs{
        Deadline: 0,
    }
    if err := c.Bind(&inputs); err != nil {
        h.JSONR(c, badstatus, err)
        return
    }
    if len(inputs.Endpoints) == 0 {
        h.JSONR(c, http.StatusBadRequest, "endpoints missing")
        return
    }

    var result []m.Endpoint = []m.Endpoint{}
    dt := db.Graph.Table("endpoint").
        Where("endpoint in (?) and ts >= ?", inputs.Endpoints, inputs.Deadline).
        Scan(&result)
    if dt.Error != nil {
        h.JSONR(c, http.StatusBadRequest, dt.Error)
        return
    }

    endpoints := []map[string]interface{}{}
    for _, r := range result {
        endpoints = append(endpoints, map[string]interface{}{"id": r.ID, "endpoint": r.Endpoint, "ts": r.Ts})
    }

    h.JSONR(c, endpoints)
}

//E:\go_path\src\github.com\open-falcon\falcon-plus\modules\api\main.go  +78
//初始化gin
routes := gin.Default()

//E:\go_path\src\github.com\open-falcon\falcon-plus\vendor\github.com\gin-gonic\gin\gin.go +148
// Default returns an Engine instance with the Logger and Recovery middleware already attached.
func Default() *Engine {
    debugPrintWARNINGDefault()
    engine := New()
    engine.Use(Logger(), Recovery())
    return engine
}

//E:\go_path\src\github.com\open-falcon\falcon-plus\vendor\github.com\gin-gonic\gin\gin.go +119 
// new方法 返回一个不带中间件的 单例engine ,并且把context 放在池中
func New() *Engine {
    debugPrintWARNINGNew()
    engine := &Engine{
        RouterGroup: RouterGroup{
            Handlers: nil,
            basePath: "/",
            root:     true,
        },
        FuncMap:                template.FuncMap{},
        RedirectTrailingSlash:  true,
        RedirectFixedPath:      false,
        HandleMethodNotAllowed: false,
        ForwardedByClientIP:    true,
        AppEngine:              defaultAppEngine,
        UseRawPath:             false,
        UnescapePathValues:     true,
        MaxMultipartMemory:     defaultMultipartMemory,
        trees:                  make(methodTrees, 0, 9),
        delims:                 render.Delims{Left: "{{", Right: "}}"},
        secureJsonPrefix:       "while(1);",
    }
    engine.RouterGroup.engine = engine
    engine.pool.New = func() interface{} {
        return engine.allocateContext()
    }
    return engine
}


//E:\go_path\src\github.com\open-falcon\falcon-plus\modules\api\app\controller\routes.go
//r.Run(port) 最后调用的是  net.http.ListenAndServe
func (engine *Engine) Run(addr ...string) (err error) {
    defer func() { debugPrintError(err) }()

    address := resolveAddress(addr)
    debugPrint("Listening and serving HTTP on %s\n", address)
    err = http.ListenAndServe(address, engine)
    return
}




//E:\Go\src\net\http\server.go +2686
func (sh serverHandler) ServeHTTP(rw ResponseWriter, req *Request) {
    handler := sh.srv.Handler
    if handler == nil {
        handler = DefaultServeMux
    }
    if req.RequestURI == "*" && req.Method == "OPTIONS" {
        handler = globalOptionsHandler{}
    }
    handler.ServeHTTP(rw, req)
}

//E:\go_path\src\github.com\open-falcon\falcon-plus\vendor\github.com\gin-gonic\gin\gin.go +321
//我们可以看到 在gin中实现了ServeHTTP方法  net.http.Handler

// ServeHTTP conforms to the http.Handler interface. 
// 这里使用sync.pool cache context数据结构,避免频繁GC,每次使用都初始化
//一个struct实现了 interface中的方法 就说明这个struct是这个类型
func (engine *Engine) ServeHTTP(w http.ResponseWriter, req *http.Request) {
    c := engine.pool.Get().(*Context)
    c.writermem.reset(w)
    c.Request = req
    c.reset()

    engine.handleHTTPRequest(c)
    
    engine.pool.Put(c)
}



// gin 里面处理请求的核心方法
// 根据一些配置去 压缩前缀树 radix.tree中找到对应的handleChain 然后执行
// 注意这句handlers, params, tsr := root.getValue(path, c.Params, unescape)
// 路由查找的过程是 从基数树的根节点一直匹配到和请求一致的节点找到对应的handlerchain
// 注册路由 E:\go_path\src\github.com\open-falcon\falcon-plus\vendor\github.com\gin-gonic\gin\gin.go +243 
// 从下面的addRoute方法中可以看到gin 为每个HttpMethod  GET POST PUT DELETE都注册了一颗tree
// 并且有priority 即最长的路径优先匹配
/*
func (engine *Engine) addRoute(method, path string, handlers HandlersChain) {
    assert1(path[0] == '/', "path must begin with '/'")
    assert1(method != "", "HTTP method can not be empty")
    assert1(len(handlers) > 0, "there must be at least one handler")

    debugPrintRoute(method, path, handlers)
    root := engine.trees.get(method)
    if root == nil {
        root = new(node)
        engine.trees = append(engine.trees, methodTree{method: method, root: root})
    }
    root.addRoute(path, handlers)
}
*/


func (engine *Engine) handleHTTPRequest(c *Context) {
    httpMethod := c.Request.Method
    path := c.Request.URL.Path
    unescape := false
    if engine.UseRawPath && len(c.Request.URL.RawPath) > 0 {
        path = c.Request.URL.RawPath
        unescape = engine.UnescapePathValues
    }

    // Find root of the tree for the given HTTP method
    t := engine.trees
    for i, tl := 0, len(t); i < tl; i++ {
        if t[i].method != httpMethod {
            continue
        }
        root := t[i].root
        // Find route in tree
        handlers, params, tsr := root.getValue(path, c.Params, unescape)
        if handlers != nil {
            c.handlers = handlers
            c.Params = params
            c.Next()
            c.writermem.WriteHeaderNow()
            return
        }
        if httpMethod != "CONNECT" && path != "/" {
            if tsr && engine.RedirectTrailingSlash {
                redirectTrailingSlash(c)
                return
            }
            if engine.RedirectFixedPath && redirectFixedPath(c, root, engine.RedirectFixedPath) {
                return
            }
        }
        break
    }

    if engine.HandleMethodNotAllowed {
        for _, tree := range engine.trees {
            if tree.method == httpMethod {
                continue
            }
            if handlers, _, _ := tree.root.getValue(path, nil, unescape); handlers != nil {
                c.handlers = engine.allNoMethod
                serveError(c, http.StatusMethodNotAllowed, default405Body)
                return
            }
        }
    }
    c.handlers = engine.allNoRoute
    serveError(c, http.StatusNotFound, default404Body)
}

python django  (django框架复杂,先简单看一下)

# 入口文件 
def execute_from_command_line(argv=None):
    """
    A simple method that runs a ManagementUtility.
    """
    utility = ManagementUtility(argv)
    utility.execute()


def execute(self):
        """
        Given the command-line arguments, figure out which subcommand is being
        run, create a parser appropriate to that command, and run it.
        """
        try:
            subcommand = self.argv[1]
        except IndexError:
            subcommand = 'help'  # Display help if no arguments were given.

        # Preprocess options to extract --settings and --pythonpath.
        # These options could affect the commands that are available, so they
        # must be processed early.
        parser = CommandParser(usage='%(prog)s subcommand [options] [args]', add_help=False, allow_abbrev=False)
        parser.add_argument('--settings')
        parser.add_argument('--pythonpath')
        parser.add_argument('args', nargs='*')  # catch-all
        try:
            options, args = parser.parse_known_args(self.argv[2:])
            handle_default_options(options)
        except CommandError:
            pass  # Ignore any option errors at this point.

        try:
            settings.INSTALLED_APPS
        except ImproperlyConfigured as exc:
            self.settings_exception = exc
        except ImportError as exc:
            self.settings_exception = exc

        if settings.configured:
            # Start the auto-reloading dev server even if the code is broken.
            # The hardcoded condition is a code smell but we can't rely on a
            # flag on the command class because we haven't located it yet.
            if subcommand == 'runserver' and '--noreload' not in self.argv:
                try:
                    autoreload.check_errors(django.setup)()
                except Exception:
                    # The exception will be raised later in the child process
                    # started by the autoreloader. Pretend it didn't happen by
                    # loading an empty list of applications.
                    apps.all_models = defaultdict(OrderedDict)
                    apps.app_configs = OrderedDict()
                    apps.apps_ready = apps.models_ready = apps.ready = True

                    # Remove options not compatible with the built-in runserver
                    # (e.g. options for the contrib.staticfiles' runserver).
                    # Changes here require manually testing as described in
                    # #27522.
                    _parser = self.fetch_command('runserver').create_parser('django', 'runserver')
                    _options, _args = _parser.parse_known_args(self.argv[2:])
                    for _arg in _args:
                        self.argv.remove(_arg)

            # In all other cases, django.setup() is required to succeed.
            else:
                django.setup()

        self.autocomplete()

        if subcommand == 'help':
            if '--commands' in args:
                sys.stdout.write(self.main_help_text(commands_only=True) + '\n')
            elif not options.args:
                sys.stdout.write(self.main_help_text() + '\n')
            else:
                self.fetch_command(options.args[0]).print_help(self.prog_name, options.args[0])
        # Special-cases: We want 'django-admin --version' and
        # 'django-admin --help' to work, for backwards compatibility.
        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)

#C:\Users\Administrator\AppData\Local\Programs\Python\Python37\Lib\site-packages\django\core\management\__init__.py +301
'''
#1.fetch_command 最终会调用C:\Users\Administrator\AppData\Local\Programs\Python\Python37\Lib\site-packages\django\core\management\__init__.py 的find_commands()
最终会找到 django\core\management\commands 下面的所有的command
    check
    compilemessages
    createcachetable
    dbshell
    diffsettings
    dumpdata
    flush
    inspectdb
    loaddata
    makemessages
    makemigrations
    migrate
    runserver
    sendtestemail
    shell
    showmigrations
    sqlflush
    sqlmigrate
    sqlsequencereset
    squashmigrations
    startapp
    startproject
    test
    testserver

2. run_from_argv 调 execute() 再调用handle()
'''

            
# 最常用的runserver
#C:\Users\Administrator\AppData\Local\Programs\Python\Python37\Lib\site-packages\django\core\management\commands\runserver.py + 
# execute()-->handle()-->run()-->inner_run()-->get_wsgi_application() #WSGIHandler 在这里加载中间件
# C:\Users\Administrator\AppData\Local\Programs\Python\Python37\Lib\site-packages\django\core\handlers\wsgi.py
class WSGIHandler(base.BaseHandler):
    request_class = WSGIRequest

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.load_middleware()

    def __call__(self, environ, start_response):
        set_script_prefix(get_script_name(environ))
        signals.request_started.send(sender=self.__class__, environ=environ)
        request = self.request_class(environ)
        response = self.get_response(request)

        response._handler_class = self.__class__

        status = '%d %s' % (response.status_code, response.reason_phrase)
        response_headers = [
            *response.items(),
            *(('Set-Cookie', c.output(header='')) for c in response.cookies.values()),
        ]
        start_response(status, response_headers)
        if getattr(response, 'file_to_stream', None) is not None and environ.get('wsgi.file_wrapper'):
            response = environ['wsgi.file_wrapper'](response.file_to_stream)
        return response


    def inner_run(self, *args, **options):
        # If an exception was silenced in ManagementUtility.execute in order
        # to be raised in the child process, raise it now.
        autoreload.raise_last_exception()

        threading = options['use_threading']
        # 'shutdown_message' is a stealth option.
        shutdown_message = options.get('shutdown_message', '')
        quit_command = 'CTRL-BREAK' if sys.platform == 'win32' else 'CONTROL-C'

        self.stdout.write("Performing system checks...\n\n")
        self.check(display_num_errors=True)
        # Need to check migrations here, so can't use the
        # requires_migrations_check attribute.
        self.check_migrations()
        now = datetime.now().strftime('%B %d, %Y - %X')
        self.stdout.write(now)
        self.stdout.write((
            "Django version %(version)s, using settings %(settings)r\n"
            "Starting development server at %(protocol)s://%(addr)s:%(port)s/\n"
            "Quit the server with %(quit_command)s.\n"
        ) % {
            "version": self.get_version(),
            "settings": settings.SETTINGS_MODULE,
            "protocol": self.protocol,
            "addr": '[%s]' % self.addr if self._raw_ipv6 else self.addr,
            "port": self.port,
            "quit_command": quit_command,
        })

        try:
            handler = self.get_handler(*args, **options)
            run(self.addr, int(self.port), handler,
                ipv6=self.use_ipv6, threading=threading, server_cls=self.server_cls)
        except socket.error as e:
            # Use helpful error messages instead of ugly tracebacks.
            ERRORS = {
                errno.EACCES: "You don't have permission to access that port.",
                errno.EADDRINUSE: "That port is already in use.",
                errno.EADDRNOTAVAIL: "That IP address can't be assigned to.",
            }
            try:
                error_text = ERRORS[e.errno]
            except KeyError:
                error_text = e
            self.stderr.write("Error: %s" % error_text)
            # Need to use an OS exit because sys.exit doesn't work in a thread
            os._exit(1)
        except KeyboardInterrupt:
            if shutdown_message:
                self.stdout.write(shutdown_message)
            sys.exit(0)
            

# C:\Users\Administrator\AppData\Local\Programs\Python\Python37\Lib\site-packages\django\core\handlers\base.py
class BaseHandler:
    _view_middleware = None
    _template_response_middleware = None
    _exception_middleware = None
    _middleware_chain = None

    def load_middleware(self):
        """
        Populate middleware lists from settings.MIDDLEWARE.

        Must be called after the environment is fixed (see __call__ in subclasses).
        """
        self._view_middleware = []
        self._template_response_middleware = []
        self._exception_middleware = []

        handler = convert_exception_to_response(self._get_response)
        for middleware_path in reversed(settings.MIDDLEWARE):
            middleware = import_string(middleware_path)
            try:
                mw_instance = middleware(handler)
            except MiddlewareNotUsed as exc:
                if settings.DEBUG:
                    if str(exc):
                        logger.debug('MiddlewareNotUsed(%r): %s', middleware_path, exc)
                    else:
                        logger.debug('MiddlewareNotUsed: %r', middleware_path)
                continue

            if mw_instance is None:
                raise ImproperlyConfigured(
                    'Middleware factory %s returned None.' % middleware_path
                )

            if hasattr(mw_instance, 'process_view'):
                self._view_middleware.insert(0, mw_instance.process_view)
            if hasattr(mw_instance, 'process_template_response'):
                self._template_response_middleware.append(mw_instance.process_template_response)
            if hasattr(mw_instance, 'process_exception'):
                self._exception_middleware.append(mw_instance.process_exception)

            handler = convert_exception_to_response(mw_instance)

        # We only assign to this when initialization is complete as it is used
        # as a flag for initialization being complete.
        self._middleware_chain = handler
        
#get_handler 函数最终会返回一个 WSGIHandler 的实例。WSGIHandler 类只实现了 def __call__(self, environ, start_response) , 使它本身能够成为 WSGI 中的应用程序, 并且实现 __call__ 能让类的行为跟函数一样。        
class WSGIHandler(base.BaseHandler):
    request_class = WSGIRequest

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.load_middleware()

    def __call__(self, environ, start_response):
        set_script_prefix(get_script_name(environ))
        signals.request_started.send(sender=self.__class__, environ=environ)
        request = self.request_class(environ)
        response = self.get_response(request)

        response._handler_class = self.__class__

        status = '%d %s' % (response.status_code, response.reason_phrase)
        response_headers = [
            *response.items(),
            *(('Set-Cookie', c.output(header='')) for c in response.cookies.values()),
        ]
        start_response(status, response_headers)
        if getattr(response, 'file_to_stream', None) is not None and environ.get('wsgi.file_wrapper'):
            response = environ['wsgi.file_wrapper'](response.file_to_stream)
        return response
        
# C:\Users\Administrator\AppData\Local\Programs\Python\Python37\Lib\socketserver.py + 215
    def serve_forever(self, poll_interval=0.5):
        """
        处理一个http请求直到关闭
        """
        self.__is_shut_down.clear()
        try:
            # XXX: Consider using another file descriptor or connecting to the
            # socket to wake this up instead of polling. Polling reduces our
            # responsiveness to a shutdown request and wastes cpu at all other
            # times.
            with _ServerSelector() as selector:
                selector.register(self, selectors.EVENT_READ)

                while not self.__shutdown_request:
                    ready = selector.select(poll_interval)
                    if ready:
                        #如果 fd可用调用处理方法
                        self._handle_request_noblock()

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



    def _handle_request_noblock(self):
        """Handle one request, without blocking.

        I assume that selector.select() has returned that the socket is
        readable before this function was called, so there should be no risk of
        blocking in get_request().
        """
        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 Exception:
                self.handle_error(request, client_address)
                self.shutdown_request(request)
            except:
                self.shutdown_request(request)
                raise
        else:
            self.shutdown_request(request)
            
    
    def process_request(self, request, client_address):
        """Call finish_request.

        Overridden by ForkingMixIn and ThreadingMixIn.

        """
        self.finish_request(request, client_address)
        self.shutdown_request(request)
    

    #finish_request 最后调用这个BaseRequestHandler
    class BaseRequestHandler:
    '''
    
    '''

    def __init__(self, request, client_address, server):
        self.request = request
        self.client_address = client_address
        self.server = server
        self.setup()
        try:
            self.handle()
        finally:
            self.finish()
            
            
    # C:\Users\Administrator\AppData\Local\Programs\Python\Python37\Lib\site-packages\django\core\servers\basehttp.py +156        
    def handle(self):
    '''
    这里对请求长度做限制
    parse_request对http解包
    '''

    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())
    
    #get_app 返回之前装配的WSGIAPP最终
    class WSGIHandler(base.BaseHandler):
    request_class = WSGIRequest

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.load_middleware()

    def __call__(self, environ, start_response):
        set_script_prefix(get_script_name(environ))
        signals.request_started.send(sender=self.__class__, environ=environ)
        request = self.request_class(environ)
        response = self.get_response(request)

        response._handler_class = self.__class__

        status = '%d %s' % (response.status_code, response.reason_phrase)
        response_headers = [
            *response.items(),
            *(('Set-Cookie', c.output(header='')) for c in response.cookies.values()),
        ]
        start_response(status, response_headers)
        if getattr(response, 'file_to_stream', None) is not None and environ.get('wsgi.file_wrapper'):
            response = environ['wsgi.file_wrapper'](response.file_to_stream)
        return response

以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Pro Django

Pro Django

Marty Alchin / Apress / 2008-11-24 / USD 49.99

Django is the leading Python web application development framework. Learn how to leverage the Django web framework to its full potential in this advanced tutorial and reference. Endorsed by Django, Pr......一起来看看 《Pro Django》 这本书的介绍吧!

JSON 在线解析
JSON 在线解析

在线 JSON 格式化工具

RGB转16进制工具
RGB转16进制工具

RGB HEX 互转工具

RGB CMYK 转换工具
RGB CMYK 转换工具

RGB CMYK 互转工具