天天看點

go替代python運維_粗讀web架構之go gin和python django

為什麼引入web架構

web應用的本質

浏覽器發送一個HTTP請求;

伺服器收到請求,生成一個HTML文檔;

伺服器把HTML文檔作為HTTP響應的Body發送給浏覽器;

浏覽器收到HTTP響應,從HTTP Body取出HTML文檔并顯示;

涉及的問題

解析http請求

找到對應的處理函數

生成并發送http響應

*

web架構工作流程

go替代python運維_粗讀web架構之go gin和python django
go替代python運維_粗讀web架構之go gin和python django

中間件

中間件是請求或者應用開始和結束時注入代碼的機制

常見的web中間件: 鑒權、列印log、session、統計資訊、處理資料庫連接配接  等等

go替代python運維_粗讀web架構之go gin和python django
go替代python運維_粗讀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{})

}

go替代python運維_粗讀web架構之go gin和python django

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

}

go替代python運維_粗讀web架構之go gin和python django

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) 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)

}

go替代python運維_粗讀web架構之go gin和python django

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

go替代python運維_粗讀web架構之go gin和python django