• <noscript id="e0iig"><kbd id="e0iig"></kbd></noscript>
  • <td id="e0iig"></td>
  • <option id="e0iig"></option>
  • <noscript id="e0iig"><source id="e0iig"></source></noscript>
  • 粗讀web框架之go gin和python django

    為什么引入web框架

       web應用的本質

    • 瀏覽器發送一個HTTP請求;
    • 服務器收到請求,生成一個HTML文檔;
    • 服務器把HTML文檔作為HTTP響應的Body發送給瀏覽器;
    • 瀏覽器收到HTTP響應,從HTTP Body取出HTML文檔并顯示;

       涉及的問題  

    • 解析http請求
    • 找到對應的處理函數
    • 生成并發送http響應

    web框架工作流程

     

     


    中間件

    • 中間件是請求或者應用開始和結束時注入代碼的機制
    • 常見的web中間件: 鑒權、打印log、session、統計信息、處理數據庫連接  等等

     


    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
    

     

    版權聲明:本文為qq_26193209原創文章,遵循 CC 4.0 BY-SA 版權協議,轉載請附上原文出處鏈接和本聲明。
    本文鏈接:https://blog.csdn.net/qq_26193209/article/details/91363681

    智能推薦

    web框架之Django

    web框架和Django流程 1.web框架要點 1.Web應用程序處理流程 2.Web應用程序框架的意義 用于搭建Web應用程序 免去不同Web應用相同代碼部分的重復編寫,只需關心Web應用核心的業務邏輯實現 3.web應用程序的本質 接收并解析HTTP請求,獲取具體的請求信息 處理本次HTTP請求,即完成本次請求的業務邏輯處理 構造并返回處理結果——HTTP響應 4.w...

    Web框架之Django

    一、Django的簡介 Django是一個開放源代碼的Web應用框架,由Python寫成。采用了MT’V的框架模式,即模型M,模板T和視圖V。它最初是被開發來用于管理勞倫斯出版集團旗下的一些以新聞內容為主的網站的,即是CMS(內容管理系統)軟件。并于2005年7月在BSD許可證下發布。這套框架是以比利時的吉普賽爵士吉他手Django Reinhardt來命名的。 二、Django開發環...

    HTML中常用操作關于:頁面跳轉,空格

    1.頁面跳轉 2.空格的代替符...

    freemarker + ItextRender 根據模板生成PDF文件

    1. 制作模板 2. 獲取模板,并將所獲取的數據加載生成html文件 2. 生成PDF文件 其中由兩個地方需要注意,都是關于獲取文件路徑的問題,由于項目部署的時候是打包成jar包形式,所以在開發過程中時直接安照傳統的獲取方法沒有一點文件,但是當打包后部署,總是出錯。于是參考網上文章,先將文件讀出來到項目的臨時目錄下,然后再按正常方式加載該臨時文件; 還有一個問題至今沒有解決,就是關于生成PDF文件...

    電腦空間不夠了?教你一個小秒招快速清理 Docker 占用的磁盤空間!

    Docker 很占用空間,每當我們運行容器、拉取鏡像、部署應用、構建自己的鏡像時,我們的磁盤空間會被大量占用。 如果你也被這個問題所困擾,咱們就一起看一下 Docker 是如何使用磁盤空間的,以及如何回收。 docker 占用的空間可以通過下面的命令查看: TYPE 列出了docker 使用磁盤的 4 種類型: Images:所有鏡像占用的空間,包括拉取下來的鏡像,和本地構建的。 Con...

    猜你喜歡

    requests實現全自動PPT模板

    http://www.1ppt.com/moban/ 可以免費的下載PPT模板,當然如果要人工一個個下,還是挺麻煩的,我們可以利用requests輕松下載 訪問這個主頁,我們可以看到下面的樣式 點每一個PPT模板的圖片,我們可以進入到詳細的信息頁面,翻到下面,我們可以看到對應的下載地址 點擊這個下載的按鈕,我們便可以下載對應的PPT壓縮包 那我們就開始做吧 首先,查看網頁的源代碼,我們可以看到每一...

    Linux C系統編程-線程互斥鎖(四)

    互斥鎖 互斥鎖也是屬于線程之間處理同步互斥方式,有上鎖/解鎖兩種狀態。 互斥鎖函數接口 1)初始化互斥鎖 pthread_mutex_init() man 3 pthread_mutex_init (找不到的情況下首先 sudo apt-get install glibc-doc sudo apt-get install manpages-posix-dev) 動態初始化 int pthread_...

    統計學習方法 - 樸素貝葉斯

    引入問題:一機器在良好狀態生產合格產品幾率是 90%,在故障狀態生產合格產品幾率是 30%,機器良好的概率是 75%。若一日第一件產品是合格品,那么此日機器良好的概率是多少。 貝葉斯模型 生成模型與判別模型 判別模型,即要判斷這個東西到底是哪一類,也就是要求y,那就用給定的x去預測。 生成模型,是要生成一個模型,那就是誰根據什么生成了模型,誰就是類別y,根據的內容就是x 以上述例子,判斷一個生產出...

    styled-components —— React 中的 CSS 最佳實踐

    https://zhuanlan.zhihu.com/p/29344146 Styled-components 是目前 React 樣式方案中最受關注的一種,它既具備了 css-in-js 的模塊化與參數化優點,又完全使用CSS的書寫習慣,不會引起額外的學習成本。本文是 styled-components 作者之一 Max Stoiber 所寫,首先總結了前端組件化樣式中的最佳實踐原則,然后在此基...

    精品国产乱码久久久久久蜜桃不卡