Django 多線程問題是怎麼回事?

首先介紹一下我個人的一些信息。

計算機專業,自學了點python,軟工課時和3個同學一起開發了一個雙系統app。因為我們都是新手,什麼都不會,看學長用的是Django,於是我們也用了。

作為新手,開發過程中遇到了很多不太了解的地方,磕磕碰碰還是用了兩個星期完成了後端的開發,但是遺留了很多困惑。

其中印象最深的就是多線程問題,當多個請求同時到來時,Django是如何處理的呢?這又會帶來什麼問題?

我自己也在網上搜索過,不過搜到的都是如何設置為多線程,可我的疑惑是這會帶來什麼問題,就很扎心(T▽T)


我來答下吧:

對於初學Web開發,理解一個web server如何能同事處理多個請求很重要。當然更重要的是,理解你通過瀏覽器發送的請求web server是怎麼處理的,然後怎麼返回給瀏覽器,瀏覽器才能展示的。

我到現在還記得大概在2010年左右,看了tomcat源碼之後,那種豁然開朗的感覺(對,當時我還在寫Java)。搞技術的人,總是希望花點時間,能夠更透徹的看透整個網路世界的構成,而不是那啥。

要理解web server如何能處理多個請求有兩個基本要素

  • 第一,知道怎麼通過socket編程,這也是我在視頻中強調的一點,理解這點之後再去看看WSGI,你就知道Python世界中大部分的框架怎麼運作了。
  • 第二,多線程編程,理解了這個,你才能知道怎麼著我起了一個web server,就能處理多個請求。 多進程也是一樣的邏輯。

通過這兩段代碼,應該很好理解:

呃,知乎的編輯器還是那麼難用,尤其是貼代碼時, @Pegasus Wang :)

想要copy代碼的話,去原文看吧,知乎這貼代碼很麻煩。

試試吧,與其著急去學習框架不如先弄懂這個。

參考

  • WSGI——Web框架基礎 - 《Django企業開發實戰草稿》
  • Python官方庫的socketserver源碼

原文地址:PythonWebServer如何同時處理多個請求 | the5fire的技術博客


好了,你更新了,我也更新下,算是最後一次更新

@黃哥

這裡我的確不嚴謹

應該嚴格意義上說是遵循 WSGI 的網關是掛在 Nginx 後面那一層

更正下

客戶端 &<-&> web 伺服器(Nginx 為例) &<-&> socket &<-&> 遵循 WSGI / ASGI 程序&<-&> Django

誒,我可沒胡噴你啊

我可一直遵循友好的技術討論的宗旨啊

誒,這微博可能是被盜號了?嘖嘖嘖,為人師表,好學校,惹不起,惹不起23333


@黃哥 更新了,我也更新一下吧

客戶端 &<-&> web 伺服器(Nginx 為例) &<-&> socket &<-&> uwsgi &<-&> Django 等我有空的時候寫一遍文章,講生成環境如何部署Django的問題。

這個順序非常正確

但是這裡還是有點錯誤

正確的順序應該是

客戶端 &<-&> web 伺服器(Nginx 為例) &<-&> socket &<-&> WSGI &<-&> Django

再次重申,uWSGI 只是 WSGI 的一種實現而已

詳情參見

https://www.python.org/dev/peps/pep-3333/www.python.org

https://www.python.org/dev/peps/pep-0333/www.python.org

中間的 WSGI 一層可以換成任意遵循 WSGI 的程序,比如常見的兩種 Gunicorn 以及 uWSGI

Django 具體怎麼支持並發的請求,取決於 WSGI 程序的實現

當然 Django 也支持處於草案階段的 ASGI

詳見

ASGI (Asynchronous Server Gateway Interface) Draft Specchannels.readthedocs.io

所以我還是不得不說,你回答里最主要的關於 load balance 的內容,還是和本題無關233333


回到題主的問題。

Django 並不負責處理多個請求的問題。之所以你可以直接跑起來 Django 項目,同時可以支持多個請求。是因為 Django 中自帶了一個 Gateway ,默認以多線程模型來處理請求,即一個請求一個線程。

如果你換用其餘的 Gateway ,比如 Gunicorn 或者 uWSGI ,那麼具體的處理模型,取決於各自的模型,可能是多線程,可能是協程。

至於多線程會帶來什麼問題

首先各自線程之間的調用棧彼此隔離,你沒必要太擔心,會有什麼額外的副作用。

然後,儘可能避免跨線程共享數據,如果非要跨線程,記得加鎖

差不多就是你這個問題的答案吧


其中印象最深的就是多線程問題,當多個請求同時到來時,Django是如何處理的呢?這又會帶來什麼問題?「 這是提問者的關鍵問題。 多個請求同時到來時, 是配置Nginx 或Apache 處理並發訪問。 上面一句話是關鍵點,有一個傢伙不看清楚,胡噴!

真是 @黃哥 沒想到你的節操比我想像的還低啊(不愧是好學校沒掛科的大佬,學不來,學不來)?

Nginx / Apache 做 load balance 只是面對多伺服器情況下,減輕單台伺服器壓力的手段,跟你一個機器如何處理多個請求並沒什麼關係。

Python 的 Web Framework 並不關心具體的並發模型,實際上基本各個語言的 Web Framework 都不關心。具體怎麼選擇相應的並發模型,線程,協程,還是其餘也好,是前端直接面對 Nginx 轉發過來/直接訪問(也就是 @黃哥 說的負載均衡,這一部分內容沒錯)的請求的 Gateway 負責的。

Python 中有一個規範叫 PEP 333 / PEP 3333 的 WSGI 的規範規定了 Gateway 一些操作。

其中並沒有規定具體的並發模型是什麼。


@黃哥

問個問題

多個請求同時到來時, 是配置nginx 或Apache 處理並發訪問。

利用niginx 的http 和反向代理,可以實現負載均衡

三台機器,8 個請求,每台機器的權重一樣,必然會一個機器掛多個請求,那麼這個時候怎麼處理?2333,


生產環境下,依賴於部署,如果用 uwsgi 的話,請求由 uwsgi 分發,一個 uwsgi 子進程對應一個 Django 的處理通道,所以具體幾個處理進程依賴於你的 uwsgi 配置;

開發環境下,runserver 默認是單進程多線程的,@連天 說的是錯的。django-admin and manage.py

The development server is multithreaded by default. Use the --nothreading option to disable the use of threading in the development server.


我只用 django 做過畢設,沒多少生產環境經驗,但是我覺得大部分答案估計你看了還是會比較懵逼(當然看完我這個你可能會更懵)。這裡我們自己擼一個簡單的多線程 wsgi server 來看下原理,還是需要深入源碼和 socket 編程你才能真正理解。 我們從 python 自帶的一個 wsgi server 看下如何實現多線程處理請求。首先你需要熟悉下 wsgi。 看一個最簡單的 wsgi app:

#!/usr/bin/env python
# -*- coding:utf-8 -*-

def application(environ, start_response):
status = 200 OK
headers = [(Content-Type, text/html; charset=utf8)]

start_response(status, headers)
return [b"&Hello&"]

if __name__ == __main__:
from wsgiref.simple_server import make_server
httpd = make_server(127.0.0.1, 8000, application)
httpd.serve_forever()

然後用你的開發工具跟進去 make_server 這個函數,看下它的定義:

# lib/python2.7/wsgiref/simple_server.py

def make_server(
host, port, app, server_class=WSGIServer, handler_class=WSGIRequestHandler
):
"""Create a new WSGI server listening on `host` and `port` for `app`"""
server = server_class((host, port), handler_class)
server.set_app(app)
return server

class WSGIServer(HTTPServer):

"""BaseHTTPServer that implements the Python WSGI protocol"""

application = None

def server_bind(self):
"""Override server_bind to store the server name."""
HTTPServer.server_bind(self)
self.setup_environ()

def setup_environ(self):
# Set up base environment
env = self.base_environ = {}
env[SERVER_NAME] = self.server_name
env[GATEWAY_INTERFACE] = CGI/1.1
env[SERVER_PORT] = str(self.server_port)
env[REMOTE_HOST]=
env[CONTENT_LENGTH]=
env[SCRIPT_NAME] =

def get_app(self):
return self.application

def set_app(self,application):
self.application = application

看到這個 WSGIServer 定義了嗎,繼承了一個 HttpServer。我們再繼續追一下其定義:

# lib/python2.7/BaseHTTPServer.py

class HTTPServer(SocketServer.TCPServer):

allow_reuse_address = 1 # Seems to make sense in testing environment

def server_bind(self):
"""Override server_bind to store the server name."""
SocketServer.TCPServer.server_bind(self)
host, port = self.socket.getsockname()[:2]
self.server_name = socket.getfqdn(host)
self.server_port = port

到這裡,我們繼續追,看到 TcpServer 定義:

# lib/python2.7/SocketServer.py

class TCPServer(BaseServer):
"""這裡我省略了定義"""

你還可以發現一個 ThreadingTCPServer 類:我們看下它的定義

class ThreadingTCPServer(ThreadingMixIn, TCPServer): pass

好了,怎麼多線程處理請求呢?看下這個 ThreadingMixIn 類是如何實現的:

class ThreadingMixIn:
"""Mix-in class to handle each request in a new thread."""

# Decides how threads will act upon termination of the
# main process
daemon_threads = False

def process_request_thread(self, request, client_address):
"""Same as in BaseServer but as a thread.

In addition, exception handling is done here.

"""
try:
self.finish_request(request, client_address)
self.shutdown_request(request)
except:
self.handle_error(request, client_address)
self.shutdown_request(request)

def process_request(self, request, client_address):
"""Start a new thread to process the request."""
t = threading.Thread(target = self.process_request_thread,
args = (request, client_address))
t.daemon = self.daemon_threads
t.start()

看到嗎,其實就是對於每個新請求,會啟動一個新的線程來處理 socket 請求。假如讓我們自己實現一個多線程 socket server 應該怎麼實現呢?先來寫一個簡單的單線程 socker echo server:

from socket import * # 偷懶這麼寫
s = socket(AF_INET, SOCK_STREAM)
s.bind(("", 9000))
s.listen(5)
while True:
c, a = s.accept()
print "Received connection from", a
c.send("Hello %s\n" % a[0])
c.close()

你可以用telnet之類的工具連上該埠看效果。 這樣一次只能處理一個請求,如果想每次來一個請求用一個線程處理呢?我們可以這樣做:

import threading
from socket import *

def handle_client(c):
# 處理 client 請求
c.send("Hello
")
c.close()
return

s = socket(AF_INET, SOCK_STREAM)
s.bind(("", 9000))
s.listen(5)
while True:
c, a = s.accept()
t = threading.Thread(target=handle_client,
args=(c,))

是不是很簡單,當然 socket 編程還是偏底層,我們剛才看到了 python 提供了 SocketServer 模塊來簡化 socket 編程。我們使用 SocketServer 模塊來發送數據:

import SocketServer
import time

class TimeHandler(SocketServer.BaseRequestHandler):
def handle(self):
# self.request 是一個 client socket 對象
self.request.sendall(time.ctime() + "
")

serv = SocketServer.TCPServer(("", 8889), TimeHandler)
serv.serve_forever()

它的執行原理是這樣的:

  • server 循環等待請求到來
  • 對每個 socket 連接,server 創建一個新的 handler 類實例
  • handle() 方法調用處理 client socket 對象,比如發送數據
  • handle() 方法返回後,連接關閉,同時 handler 實例對象銷毀

但是這個 server 的處理能力很差,一次只能處理一個請求,我們看下這個模塊提供了幾個類:

  • TCPServer: 同步的 tcp server
  • ForkingTCPServer: 多進程 tcp server
  • ThreadingTCPServer: 多線程 tcp server

怎麼實現一個多線程 tcp server 呢?很簡單:

# 改成 ThreadingTCPServer 就行了,代碼其他部分不動
serv = SocketServer.ThreadingTCPServer(("",8000),TimeHandler)
serv.serve_forever()

這樣一來,新的請求就能被新的線程去處理了。我們就通過線程來增強了並發能力,當然線程開銷比較大,不如用協程(抽空會寫個用協程實現非同步的socker server)。 如果你瀏覽該模塊,還能看到兩個 Mixin:

  • ForkingMixin
  • ThreadingMixIn

我們只要繼承它們就很容易實現一個多進程或者多線程的 server,比如實現一個多線程的 HTTPServer

from BaseHTTPServer import HTTPServer
from SimpleHTTPServer import SimpleHTTPRequestHandler
from SocketServer import ThreadingMixIn

class ThreadedHTTPServer(ThreadingMixIn, HTTPServer):
pass

serv = ThreadedHTTPServer(("", 8080), SimpleHTTPRequestHandler)

好了,看了這麼多讓我們改造下 Python 自帶的 wsgi server 為多線程的:

import time
import SocketServer
import socket
from SimpleHTTPServer import SimpleHTTPRequestHandler
from SocketServer import ThreadingMixIn

class HTTPServer(SocketServer.TCPServer):

allow_reuse_address = 1 # Seems to make sense in testing environment

def server_bind(self):
"""Override server_bind to store the server name."""
SocketServer.TCPServer.server_bind(self)
host, port = self.socket.getsockname()[:2]
self.server_name = socket.getfqdn(host)
self.server_port = port

class ThreadedHTTPServer(ThreadingMixIn, HTTPServer):
pass

class ThreadWSGIServer(ThreadedHTTPServer):

"""BaseHTTPServer that implements the Python WSGI protocol"""

application = None

def server_bind(self):
"""Override server_bind to store the server name."""
HTTPServer.server_bind(self)
self.setup_environ()

def setup_environ(self):
# Set up base environment
env = self.base_environ = {}
env[SERVER_NAME] = self.server_name
env[GATEWAY_INTERFACE] = CGI/1.1
env[SERVER_PORT] = str(self.server_port)
env[REMOTE_HOST] =
env[CONTENT_LENGTH] =
env[SCRIPT_NAME] =

def get_app(self):
return self.application

def set_app(self, application):
self.application = application

def application(environ, start_response):
time.sleep(10) # 注意這裡的 sleep
status = 200 OK
headers = [(Content-Type, text/html; charset=utf8)]

start_response(status, headers)
return [b"&Hello&"]

if __name__ == __main__:
from wsgiref.simple_server import make_server
httpd = make_server(127.0.0.1, 8000, application, server_class=ThreadWSGIServer)
httpd.serve_forever()

對了,我們怎麼證明這個真是多線程的 wsgi server 了呢,很簡單。你看我加了個 sleep(10)。 如果你在之前的單線程 wsgi server 跑,你可以開倆個終端去 curl,curl 完一個趕緊切到另一個 curl 。 單線程你會發現幾乎是 10 + 10 秒,但是下邊這個多線程 wsgi server 你會發現大概只用10秒,兩個請求是並發的(當然我這種測試很 low,你可以隨便用一個網路測試工具)。雖然我沒看過 django wsgi server 的實現,但是它自己實現的開發伺服器原理應該是類似的。

如果你能堅持看到這裡,是更明白了呢還是更懵了呢?

你可以看下PegasusWang/notebooks 這個是手擼 web 框架的教程,看完你就對 wsgi,如何自己寫 框架以及 gunicorn 部署有點概念了。


提問者的提問標題是「Django 多線程問題是怎麼回事?」

從下面的關鍵點看,提問者應該是對Django 如何支持並發,比較困惑。

其中印象最深的就是多線程問題,當多個請求同時到來時,Django是如何處理的呢?這又會帶來什麼問題?「 這是提問者的關鍵問題。

多個請求同時到來時, 是配置Nginx 或Apache 處理並發訪問。

這個地方沒有展開講,

客戶端 &<-&> web 伺服器(Nginx 為例) &<-&> socket &<-&> uwsgi或Gunicorn&<-&> Django

等我有空的時候寫一遍文章,講生產環境如何部署Django的問題。

wsgi 是協議,uwsgi 是具體實現,不用具體實現部署,用協議部署,要抬杠可以,但不要

胡噴!

上面一句話是關鍵點,有一個傢伙不看清楚,胡噴!

1、如果並發不大,可以這樣配置,看文檔

How to use Django with uWSGI

2、如果並發量大,需要採用分散式,根據dns根據不同的地區調度到就近的伺服器。

利用Nginx 的http 和反向代理,可以實現負載均衡。

高並發解決方案--負載均衡

1,什麼是負載均衡?

當一台伺服器的性能達到極限時,我們可以使用伺服器集群來提高網站的整體性能。那麼,在伺服器集群中,需要有一台伺服器充當調度者的角色,用戶的所有請求都會首先由它接收,調度者再根據每台伺服器的負載情況將請求分配給某一台後端伺服器去處理。

那麼在這個過程中,調度者如何合理分配任務,保證所有後端伺服器都將性能充分發揮,從而保持伺服器集群的整體性能最優,這就是負載均衡問題。

下面詳細介紹負載均衡的四種實現方式。

(一)HTTP重定向實現負載均衡

過程描述

當用戶向伺服器發起請求時,請求首先被集群調度者截獲;調度者根據某種分配策略,選擇一台伺服器,並將選中的伺服器的IP地址封裝在HTTP響應消息頭部的Location欄位中,並將響應消息的狀態碼設為302,最後將這個響應消息返回給瀏覽器。

當瀏覽器收到響應消息後,解析Location欄位,並向該URL發起請求,然後指定的伺服器處理該用戶的請求,最後將結果返回給用戶。

在使用HTTP重定向來實現伺服器集群負載均衡的過程中,需要一台伺服器作為請求調度者。用戶的一項操作需要發起兩次HTTP請求,一次向調度伺服器發送請求,獲取後端伺服器的IP,第二次向後端伺服器發送請求,獲取處理結果。

調度策略

調度伺服器收到用戶的請求後,究竟選擇哪台後端伺服器處理請求,這由調度伺服器所使用的調度策略決定。

  1. 隨機分配策略

    當調度伺服器收到用戶請求後,可以隨機決定使用哪台後端伺服器,然後將該伺服器的IP封裝在HTTP響應消息的Location屬性中,返回給瀏覽器即可。
  2. 輪詢策略(RR)

    調度伺服器需要維護一個值,用於記錄上次分配的後端伺服器的IP。那麼當新的請求到來時,調度者將請求依次分配給下一台伺服器。

由於輪詢策略需要調度者維護一個值用於記錄上次分配的伺服器IP,因此需要額外的開銷;此外,由於這個值屬於互斥資源,那麼當多個請求同時到來時,為了避免線程的安全問題,因此需要鎖定互斥資源,從而降低了性能。而隨機分配策略不需要維護額外的值,也就不存在線程安全問題,因此性能比輪詢要高。

優缺點分析

採用HTTP重定向來實現伺服器集群的負載均衡實現起來較為容易,邏輯比較簡單,但缺點也較為明顯。

在HTTP重定向方法中,調度伺服器只在客戶端第一次向網站發起請求的時候起作用。當調度伺服器向瀏覽器返迴響應信息後,客戶端此後的操作都基於新的URL進行的(也就是後端伺服器),此後瀏覽器就不會與調度伺服器產生關係,進而會產生如下幾個問題:

  • 由於不同用戶的訪問時間、訪問頁面深度有所不同,從而每個用戶對各自的後端伺服器所造成的壓力也不同。而調度伺服器在調度時,無法知道當前用戶將會對伺服器造成多大的壓力,因此這種方式無法實現真正意義上的負載均衡,只不過是把請求次數平均分配給每台伺服器罷了。
  • 若分配給該用戶的後端伺服器出現故障,並且如果頁面被瀏覽器緩存,那麼當用戶再次訪問網站時,請求都會發給出現故障的伺服器,從而導致訪問失敗。

(二)DNS負載均衡

DNS是什麼?

在了解DNS負載均衡之前,我們首先需要了解DNS域名解析的過程。

我們知道,數據包採用IP地址在網路中傳播,而為了方便用戶記憶,我們使用域名來訪問網站。那麼,我們通過域名訪問網站之前,首先需要將域名解析成IP地址,這個工作是由DNS完成的。也就是域名伺服器。

我們提交的請求不會直接發送給想要訪問的網站,而是首先發給域名伺服器,它會幫我們把域名解析成IP地址並返回給我們。我們收到IP之後才會向該IP發起請求。

那麼,DNS伺服器有一個天然的優勢,如果一個域名指向了多個IP地址,那麼每次進行域名解析時,DNS只要選一個IP返回給用戶,就能夠實現伺服器集群的負載均衡。

具體做法

首先需要將我們的域名指向多個後端伺服器(將一個域名解析到多個IP上),再設置一下調度策略,那麼我們的準備工作就完成了,接下來的負載均衡就完全由DNS伺服器來實現。

當用戶向我們的域名發起請求時,DNS伺服器會自動地根據我們事先設定好的調度策略選一個合適的IP返回給用戶,用戶再向該IP發起請求。

調度策略

一般DNS提供商會提供一些調度策略供我們選擇,如隨機分配、輪詢、根據請求者的地域分配離他最近的伺服器。

優缺點分析

DNS負載均衡最大的優點就是配置簡單。伺服器集群的調度工作完全由DNS伺服器承擔,那麼我們就可以把精力放在後端伺服器上,保證他們的穩定性與吞吐量。而且完全不用擔心DNS伺服器的性能,即便是使用了輪詢策略,它的吞吐率依然卓越。

此外,DNS負載均衡具有較強了擴展性,你完全可以為一個域名解析較多的IP,而且不用擔心性能問題。

但是,由於把集群調度權交給了DNS伺服器,從而我們沒辦法隨心所欲地控制調度者,沒辦法定製調度策略。

DNS伺服器也沒辦法了解每台伺服器的負載情況,因此沒辦法實現真正意義上的負載均衡。它和HTTP重定向一樣,只不過把所有請求平均分配給後端伺服器罷了。

此外,當我們發現某一台後端伺服器發生故障時,即使我們立即將該伺服器從域名解析中去除,但由於DNS伺服器會有緩存,該IP仍然會在DNS中保留一段時間,那麼就會導致一部分用戶無法正常訪問網站。這是一個致命的問題!好在這個問題可以用動態DNS來解決。

動態DNS

動態DNS能夠讓我們通過程序動態修改DNS伺服器中的域名解析。從而當我們的監控程序發現某台伺服器掛了之後,能立即通知DNS將其刪掉。

綜上所述

DNS負載均衡是一種粗獷的負載均衡方法,這裡只做介紹,不推薦使用。

(三)反向代理負載均衡

什麼是反向代理負載均衡?

反向代理伺服器是一個位於實際伺服器之前的伺服器,所有向我們網站發來的請求都首先要經過反向代理伺服器,伺服器根據用戶的請求要麼直接將結果返回給用戶,要麼將請求交給後端伺服器處理,再返回給用戶。

之前我們介紹了用反向代理伺服器實現靜態頁面和常用的動態頁面的緩存。接下來我們介紹反向代理伺服器更常用的功能——實現負載均衡。

我們知道,所有發送給我們網站的請求都首先經過反向代理伺服器。那麼,反向代理伺服器就可以充當伺服器集群的調度者,它可以根據當前後端伺服器的負載情況,將請求轉發給一台合適的伺服器,並將處理結果返回給用戶。

優點

  1. 隱藏後端伺服器。

    與HTTP重定向相比,反向代理能夠隱藏後端伺服器,所有瀏覽器都不會與後端伺服器直接交互,從而能夠確保調度者的控制權,提升集群的整體性能。
  2. 故障轉移

    與DNS負載均衡相比,反向代理能夠更快速地移除故障結點。當監控程序發現某一後端伺服器出現故障時,能夠及時通知反向代理伺服器,並立即將其刪除。
  3. 合理分配任務

    HTTP重定向和DNS負載均衡都無法實現真正意義上的負載均衡,也就是調度伺服器無法根據後端伺服器的實際負載情況分配任務。但反向代理伺服器支持手動設定每台後端伺服器的權重。我們可以根據伺服器的配置設置不同的權重,權重的不同會導致被調度者選中的概率的不同。

缺點

  1. 調度者壓力過大

    由於所有的請求都先由反向代理伺服器處理,那麼當請求量超過調度伺服器的最大負載時,調度伺服器的吞吐率降低會直接降低集群的整體性能。
  2. 制約擴展

    當後端伺服器也無法滿足巨大的吞吐量時,就需要增加後端伺服器的數量,可沒辦法無限量地增加,因為會受到調度伺服器的最大吞吐量的制約。

粘滯會話

反向代理伺服器會引起一個問題。若某台後端伺服器處理了用戶的請求,並保存了該用戶的session或存儲了緩存,那麼當該用戶再次發送請求時,無法保證該請求仍然由保存了其Session或緩存的伺服器處理,若由其他伺服器處理,先前的Session或緩存就找不到了。

解決辦法1:

可以修改反向代理伺服器的任務分配策略,以用戶IP作為標識較為合適。相同的用戶IP會交由同一台後端伺服器處理,從而就避免了粘滯會話的問題。

解決辦法2:

可以在Cookie中標註請求的伺服器ID,當再次提交請求時,調度者將該請求分配給Cookie中標註的伺服器處理即可。

2,負載均衡組件

1.1、apache

—— 它是Apache軟體基金會的一個開放源代碼的跨平台的網頁伺服器,屬於老牌的web伺服器了,支持基於Ip或者域名的虛擬主機,支持代理伺服器,支持安全Socket層(SSL)等等,目前互聯網主要使用它做靜態資源伺服器,也可以做代理伺服器轉發請求(如:圖片鏈等),結合tomcat等servlet容器處理jsp。

1.2、ngnix

—— 俄羅斯人開發的一個高性能的 HTTP和反向代理伺服器。由於Nginx 超越 Apache 的高性能和穩定性,使得國內使用 Nginx 作為 Web 伺服器的網站也越來越多,其中包括新浪博客、新浪播客、網易新聞、騰訊網、搜狐博客等門戶網站頻道等,在3w以上的高並發環境下,ngnix處理能力相當於apache的10倍。

參考:apache和tomcat的性能分析和對比(Nginx 0.8.x + PHP 5.2.13(FastCGI)搭建勝過Apache十倍的Web伺服器(第6版)[原創])

1.3、lvs

—— Linux Virtual Server的簡寫,意即Linux虛擬伺服器,是一個虛擬的伺服器集群系統。由畢業於國防科技大學的章文嵩博士於1998年5月創立,可以實現LINUX平台下的簡單負載均衡。了解更多,訪問官網:http://zh.linuxvirtualserver.org/。

1.4、HAProxy

—— HAProxy提供高可用性、負載均衡以及基於TCP和HTTP應用的代理,支持虛擬主機,它是免費、快速並且可靠的一種解決方案。HAProxy特別適用於那些負載特大的web站點, 這些站點通常又需要會話保持或七層處理。HAProxy運行在當前的硬體上,完全可以支持數以萬計的並發連接。並且它的運行模式使得它可以很簡單安全的整合進您當前的架構中, 同時可以保護你的web伺服器不被暴露到網路上.

1.5、keepalived

—— 這裡說的keepalived不是apache或者tomcat等某個組件上的屬性欄位,它也是一個組件,可以實現web伺服器的高可用(HA high availably)。它可以檢測web伺服器的工作狀態,如果該伺服器出現故障被檢測到,將其剔除伺服器群中,直至正常工作後,keepalive會自動檢測到並加入到伺服器群裡面。實現主備伺服器發生故障時ip瞬時無縫交接。它是LVS集群節點健康檢測的一個用戶空間守護進程,也是LVS的引導故障轉移模塊(director failover)。Keepalived守護進程可以檢查LVS池的狀態。如果LVS伺服器池當中的某一個伺服器宕機了。keepalived會通過一 個setsockopt呼叫通知內核將這個節點從LVS拓撲圖中移除。

1.6、memcached

—— 它是一個高性能分散式內存對象緩存系統。當初是Danga Interactive為了LiveJournal快速發展開發的系統,用於對業務查詢數據緩存,減輕資料庫的負載。其守護進程(daemon)是用C寫的,但是客戶端支持幾乎所有語言(客戶端基本上有3種版本[memcache client for Java;spymemcached;xMecache]),服務端和客戶端通過簡單的協議通信;在memcached裡面緩存的數據必須序列化。

1.7、terracotta

—— 是一款由美國Terracotta公司開發的著名開源Java集群平台。它在JVM與Java應用之間實現了一個專門處理集群功能的抽象層,允許用戶在不改變系統代碼的情況下實現java應用的集群。支持數據的持久化、session的複製以及高可用(HA)。詳細參考:Terracotta 3.2.1簡介 (二)


生產環境下,nginx將請求分發給多個後端uwsgi進程,而不是線程...

謝謝張傑指正,開發伺服器從1.4開始就是多線程的了。

Django 1.4 release notes


題主你是想問兩個請求同時過來,並且操作同一條數據,django會怎麼處理呢,這個意思嗎?

兩個請求同時過來,你可以理解成兩人django同時,分別處理這兩個請求,並操作同一條數據,最後保存。

比如都是操作 {id: 1, text: origin} 這條數據,第一個請求把text變成text1, 第二個請求是把text變成text2。他們都是先讀資料庫,保存到內存,修改數據,寫回資料庫,返回數據,結束請求。

所以當第一個請求讀取的時候,得到的數據是 {id: 1, text: origin}

第二個請求讀取,也是得到數據 {id: 1, text: origin}

當第一個請求寫入的時候, 資料庫數據 {id: 1, text: text1}, 數據 {id: 1, text: text1},

當第二個請求寫入的時候,資料庫數據 {id: 1, text: text2},數據 {id: 1, text: text2},

結果就是第一個請求返回 {id: 1, text: text1} 第二個請求返回 {id: 1, text: text2}。資料庫數據 {id: 1, text: text2},

另外django的更新是全局的更新。比如第一個請求是把數據 {id: 1, text1: a, text2: b}變成 {id: 1, text1: A, text2: b}, 第二個請求是把數據 {id: 1, text1: a, text2: b} 變成 {id: 1, text1: a, text2: B}。兩個請求同時發過來,最後一個save的,會保留所有數據,變成 {id: 1, text1: a, text2: B} 或者 {id: 1, text1: A, text2: b} 而不會變成 {id: 1, text1: A, text2: B}。如果真的有這種需求,就需要用[transactions](Database transactions)保存資料庫操作的時候,其他線程不操作。只要記住django的執行順序: 1從資料庫讀數據到內存,2在內存中修改數據,3把內存中數據全部保存到資料庫,這樣你就能理解多線程時django的執行結果了。


計算機不是理科,計算機特么就是個「工科」,大多數問題都有非常確定的設計意圖和解決方案。

在這麼個前提下,計算機的問題討論或求碼(呸...),求答疑解惑難道不應該是:感謝安利,答主好人,一生平安么???

這特么是怎樣的一種畫風?居然吵起來了,當然,也偶然碰到一些「名校高學歷」同行的叱責,難以理解....,特么的,多看幾本書務實把示例讀透會死啊???說笑的,不讀也沒關係,大多數原理,在應用的時候並沒什麼卵用....

回歸主題,多用戶處理,可以看看SOCKET原理,線程原理,分散式原理,其實之上所有的應用方案只是這些原理的實現。這些方案要怎麼理解?首先看原理,再看方案源代碼。


推薦閱讀:

Django框架應用中models.py文件與資料庫操作問題?
Django中提示TemplateDoesNotExist?

TAG:Django框架 | Python框架 |