mvc框架打造筆記之wsgi協(xié)議的優(yōu)缺點(diǎn)以及接口實(shí)現(xiàn)-創(chuàng)新互聯(lián)

前言:

創(chuàng)新互聯(lián)公司長(zhǎng)期為上千客戶提供的網(wǎng)站建設(shè)服務(wù),團(tuán)隊(duì)從業(yè)經(jīng)驗(yàn)10年,關(guān)注不同地域、不同群體,并針對(duì)不同對(duì)象提供差異化的產(chǎn)品和服務(wù);打造開放共贏平臺(tái),與合作伙伴共同營(yíng)造健康的互聯(lián)網(wǎng)生態(tài)環(huán)境。為市中企業(yè)提供專業(yè)的網(wǎng)站建設(shè)、網(wǎng)站制作,市中網(wǎng)站改版等技術(shù)服務(wù)。擁有10多年豐富建站經(jīng)驗(yàn)和眾多成功案例,為您定制開發(fā)。

又是WSGI ,這是我曾經(jīng)比較熟悉的協(xié)議,以前針對(duì)實(shí)現(xiàn)了wsgi server的unicorn和uwsgi都寫過源碼解析的文章。  其實(shí)他們的實(shí)現(xiàn)也很簡(jiǎn)單,就是給flask、django這樣的application傳遞environ,start_response 。

什么是WSGI協(xié)議,什么是WSGI Server,他們的區(qū)別是什么?

上線的架構(gòu)圖很容易誤導(dǎo)別人,乍一看有nginx這樣的web服務(wù)器,又有g(shù)unicorn這樣的wsgi server。  我們先說明wsgi 跟 wsgi server的關(guān)系,wsgi是個(gè)協(xié)議,是web底層跟application解耦的協(xié)議。wsgi server是自己做web服務(wù)器借用wsgi協(xié)議來調(diào)用application。 我們需要明確一點(diǎn),nginx是無法直接跟flask application做通信,需要借用wsgi server。flask本身也有個(gè)web服務(wù)器是werkzeug,so 才能啟動(dòng)服務(wù)并監(jiān)聽端口。記得以前uwsgi沒名氣的時(shí)候,我們都在使用apache + mode_wsgi模式,apache也無法直接跟tornado通信,是借用mod_wsgi把torando做成了unix socket服務(wù),說白了也是啟動(dòng)了一個(gè)服務(wù),靠apache來轉(zhuǎn)發(fā)。

nginx、apache在這里只是啟動(dòng)了proxy的作用,那為什么不直接把uwsgi和gunicorn給暴露出去,因?yàn)閚ginx的靜態(tài)文件處理能力極強(qiáng)。

WSGI怎么工作的

wsgi主要是兩層,服務(wù)器方 和 應(yīng)用程序 :

1  服務(wù)器方:從底層解析http解析,然后調(diào)用應(yīng)用程序,給應(yīng)用程序提供(環(huán)境信息)和(回調(diào)函數(shù)), 這個(gè)回調(diào)函數(shù)是用來將應(yīng)用程序設(shè)置的http header和status等信息傳遞給服務(wù)器方.

2  應(yīng)用程序:用來生成返回的header,body和status,以便返回給服務(wù)器方。

WSGI把來自socket的數(shù)據(jù)包解析為http格式,然后進(jìn)而變化為environ變量,這environ變量里面有wsgi本身的信息(比如 host, post,進(jìn)程模式等),還有client的header及body信息。start_respnse是一個(gè)函調(diào)函數(shù),必須要附帶兩個(gè)參數(shù),一個(gè)是status(http狀態(tài)),response_headers(響應(yīng)的header頭信息)。

像flask、django、tornado都會(huì)暴露WSGI協(xié)議入口,我們只需要自己實(shí)現(xiàn)WSGI協(xié)議,wsgi server然后給flask傳遞environ,及start_response, 等到application返回值之后,我再socket send返回客戶端。

WSGI的優(yōu)點(diǎn)、缺點(diǎn)是什么?

優(yōu)點(diǎn):

多樣的部署選擇和組件之間的高度解耦

由于上面提到的高度解耦特性,理論上,任何一個(gè)符合WSGI規(guī)范的App都可以部署在任何一個(gè)實(shí)現(xiàn)了WSGI規(guī)范的Server上,這給Python Web應(yīng)用的部署帶來了極大的靈活性。


Flask自帶了一個(gè)基于Werkzeug的調(diào)試用服務(wù)器。根據(jù)Flask的文檔,在生產(chǎn)環(huán)境不應(yīng)該使用內(nèi)建的調(diào)試服務(wù)器,而應(yīng)該采取以下方式之一進(jìn)行部署:


GUNICORN


UWSGI

缺點(diǎn):


沒有

我們?cè)趙sgi層可以做什么時(shí)尚的操作:

  1. 黑白名單規(guī)則防御.
  2. 可以通過重寫環(huán)境變量,根據(jù)目標(biāo)URL,將請(qǐng)求消息路由到不同的應(yīng)用對(duì)象。這意思就是說,實(shí)現(xiàn)一套類似nginx location proxy的規(guī)則,可以把阻塞高性能轉(zhuǎn)給tornado的app. 當(dāng)然這是理想化的操作.
  3. 允許在一個(gè)進(jìn)程中同時(shí)運(yùn)行多個(gè)應(yīng)用程序或應(yīng)用框架.
  4. 負(fù)載均衡和遠(yuǎn)程處理,通過在網(wǎng)絡(luò)上轉(zhuǎn)發(fā)請(qǐng)求和響應(yīng)消息.

我們用python具體實(shí)現(xiàn)這個(gè)wsgi server及協(xié)議.

#xiaorui.cc
 
import socket
import StringIO
import sys
 
class WSGIServer(object):
 
 address_family = socket.AF_INET
 socket_type = socket.SOCK_STREAM
 request_queue_size = 1
 
 def __init__(self, server_address):
  # 創(chuàng)建一個(gè)可用的socket
  self.listen_socket = listen_socket = socket.socket(
   self.address_family,
   self.socket_type
  )
  #socket的工作模式
  listen_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
  # Bind
  listen_socket.bind(server_address) #綁定地址
  # Activate
  listen_socket.listen(self.request_queue_size) #監(jiān)聽文件描述符
  # Get server host name and port
  host, port = self.listen_socket.getsockname()[:2]
  self.server_name = socket.getfqdn(host)
  self.server_port = port
  self.headers_set = []
 
 def set_app(self, application):
  self.application = application
 
 def serve_forever(self): #啟動(dòng)WSGI server服務(wù),不停的監(jiān)聽并獲取socket數(shù)據(jù)。
  listen_socket = self.listen_socket
  while True:
    self.client_connection, client_address = listen_socket.accept()
   self.handle_one_request() #處理新連接
 
 def handle_one_request(self): #主要處理函數(shù)
  self.request_data = request_data = self.client_connection.recv(1024)
  print(''.join(
   '< {line}\n'.format(line=line)
   for line in request_data.splitlines()
  ))
 
  self.parse_request(request_data)
  env = self.get_environ()
 
  #給flask\tornado傳遞兩個(gè)參數(shù),environ,start_response
  result = self.application(env, self.start_response)
 
  # Construct a response and send it back to the client
  self.finish_response(result)
 
 def parse_request(self, text): #處理socket的http協(xié)議
  request_line = text.splitlines()[0]
  request_line = request_line.rstrip('\r\n')
  # Break down the request line into components
  (self.request_method, # GET
   self.path,   # /hello
   self.request_version # HTTP/1.1
   ) = request_line.split()
 
 def get_environ(self): #獲取environ數(shù)據(jù)
  env = {}
  env['wsgi.version']  = (1, 0)
  env['wsgi.url_scheme'] = 'http'
  env['wsgi.input']  = StringIO.StringIO(self.request_data)
  env['wsgi.errors']  = sys.stderr
  env['wsgi.multithread'] = False
  env['wsgi.multiprocess'] = False
  env['wsgi.run_once']  = False
  env['REQUEST_METHOD'] = self.request_method # GET
  env['PATH_INFO']   = self.path    # /hello
  env['SERVER_NAME']  = self.server_name  # localhost
  env['SERVER_PORT']  = str(self.server_port) # 8888
  return env
 
 def start_response(self, status, response_headers, exc_info=None): #創(chuàng)建回調(diào)函數(shù).
  server_headers = [
   ('Date', 'Tue, 31 Mar 2015 12:54:48 GMT'),
   ('Server', 'WSGIServer 0.3'),
  ]
  self.headers_set = [status, response_headers + server_headers]
 
 def finish_response(self, result): #把a(bǔ)pplication返回給WSGI的數(shù)據(jù)返回給客戶端。
  try:
   status, response_headers = self.headers_set
   response = 'HTTP/1.1 {status}\r\n'.format(status=status)
   for header in response_headers:
    response += '{0}: {1}\r\n'.format(*header)
   response += '\r\n'
   for data in result:
    response += data
   # Print formatted response data a la 'curl -v'
   print(''.join(
    '> {line}\n'.format(line=line)
    for line in response.splitlines()
   ))
   self.client_connection.sendall(response)
  finally:
   self.client_connection.close()
 
SERVER_ADDRESS = (HOST, PORT) = '', 8888
 
def make_server(server_address, application):
 server = WSGIServer(server_address)
 server.set_app(application)
 return server
 
if __name__ == '__main__':
 if len(sys.argv) < 2:
  sys.exit('Provide a WSGI application object as module:callable')
 app_path = sys.argv[1]
 module, application = app_path.split(':')
 module = __import__(module) #動(dòng)態(tài)加載模塊
 application = getattr(module, application) #使用自省的模式加載application的WSGI協(xié)議入口。
 httpd = make_server(SERVER_ADDRESS, application)
 print('WSGIServer: Serving HTTP on port {port} ...\n'.format(port=PORT))
 httpd.serve_forever()

文章題目:mvc框架打造筆記之wsgi協(xié)議的優(yōu)缺點(diǎn)以及接口實(shí)現(xiàn)-創(chuàng)新互聯(lián)
標(biāo)題鏈接:http://bm7419.com/article46/cedpeg.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供用戶體驗(yàn)、企業(yè)建站、網(wǎng)站導(dǎo)航、移動(dòng)網(wǎng)站建設(shè)微信小程序、動(dòng)態(tài)網(wǎng)站

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來源: 創(chuàng)新互聯(lián)

成都網(wǎng)頁設(shè)計(jì)公司