你應該使用 Nginx + UWSGI

oschina發表於2013-07-09

  經過大量的實驗(在 disqus.comgetsentry.com上),我可以確切的說:uwsgi應該成為Python世界的標準。 把它和nginx結合,在基於 Python的Web應用程式上你能獲得線上程(或非執行緒)之上更好的效能體驗。

  更新:忽略古老的說法“你給任何度量是慢”,我在這裡說的請求是指後端節點,他們處理輸入事件(從20KB到1MB大小的請求),在網路跳過數跳經過各種授權和配額策略,並最形成一些佇列操作。解除安裝儘可能多的工作負載。(本段翻譯有問題,請參考原文,譯者注)

 服務策略

  目前已經有相當數量的方法可以用來執行Python應用程式。我不打算使用mod_wsgi,最重要的,我並不想說明事件模型如何工作。我不相信在Python的世界它們依舊使用,所以這篇文章的主題也不是關於傳統的執行緒(或多程式)的Python應用程式。

  相反,我將專注於兩個最流行且我最熟悉的解決方案:gunicorn和uwsgi。

 Gunicorn(Python UNIX平臺的wsgi伺服器)

  回顧過去,Python的Web伺服器的解決方案基本上只有mod_wsgi。其中最流行的(或理解為時尚)的方法是最近Gunicorn。

  實際上,我仍然建議使用gunicorn,這樣可以極大的減少不便:它可以漂亮的嵌入Django而且設定簡單。

  它也有10%的配置選項和uwsgi一致(這對某些人來說是件好事),除此之外,比較看來,它提供了與uwsgi(或任何其他Python Web伺服器)幾乎相同的基本特性。

 uwsgi

  在我看來這是唯一的選擇,從Gunicorn到uwsgi。將有更高效能的,有更多極易明白的配置選項,通過協議可以與nginx互動也增加了優勢。
它的配置也是相當簡單,找到一篇文章相關文章就可以了,後來更多。
我開始使用uwsgi來跑一些應用,使用–processes=10和–threads=10來測試伺服器的多CPU,目的有兩個:

  • 支援情況
  • 測試降低記憶體使用量的可能性
  • 測試執行緒安全的支援情況

  (對於這些測試是否值得,DISQUS是 單執行緒執行的,我想保持儘可能的精簡,把每個節點的能力發揮到極致)

 不斷趨向成功的迭代

  我們使API平均響應時間降到40ms以內,我非常自豪。這裡我說的API相應時間是指:從請求擊中了Python伺服器到伺服器返回響應到代理所花費的時間。

  不幸的是,當我們始獲得越來越大的流量並出現訪問尖峰後響應時間出現問題了,波動的響應時間不再符合我們開始的設想,儘管服務節點上我們仍然有大約30%的記憶體和60%的資源空餘。

  在不少調整後,我們停用了大量uwsgi程式的方法,讓nginx的負載均衡它們(之前是讓uwsgi本身負載平衡)。

  這意味著什麼呢,是不是做uwsgi過程= 10,我們執行10個單獨的uwsgi例項代替–processes=10。

  其結果是一個美麗的,一致的20ms的平均響應時間。

  API響應時間

 將他們組合在一起

  我喜歡著手去做而非空談,這裡我給大家一些我們線上伺服器的實際設定。

 nginx

  配置的第一塊是Nginx的,我們需要實際計算並新增uwsgi的程式 後端數量,所以事情有點複雜。

  我們首先建立在我們的網頁配置列表:

# recipes/web.rb

hosts = (0..(node[:getsentry][:web][:processes] - 1)).to_a.map do |x|
  port = 9000 + x
  "127.0.0.1:#{port}"
end

template "#{node['nginx']['dir']}/sites-available/getsentry.com" do
  source "nginx/getsentry.erb"
  owner "root"
  group "root"
  variables(
    :hosts => hosts
  )
  mode 0644
  notifies :reload, "service[nginx]"
end

  Nginx的配置很簡單:

# templates/getsentry.erb

upstream internal {
<% @hosts.each do |host| %>
  server <%= host %>;
<% end %>
}

server {
  location / {
    uwsgi_pass         internal;

    uwsgi_param   Host                 $host;
    uwsgi_param   X-Real-IP            $remote_addr;
    uwsgi_param   X-Forwarded-For      $proxy_add_x_forwarded_for;
    uwsgi_param   X-Forwarded-Proto    $http_x_forwarded_proto;

    include uwsgi_params;
  }
}

  現在,我們已經設定了uwsgi的主機數量並分配了權重值,從9000埠開始,它們都是被uwsgi配置使用的套接字地址。

 uwsgi

  另一方面,我們使用supervisor來控制uwsg程式,這也非常簡單:

# recipes/web.rb

command = "/srv/www/getsentry.com/env/bin/uwsgi -s 127.0.0.1:90%(process_num)02d --need-app --disable-logging --wsgi-file getsentry/wsgi.py --processes 1 --threads #{node['getsentry']['web']['threads']}"

supervisor_service "web" do
  directory "/srv/www/getsentry.com/current/"
  command command
  user "dcramer"
  stdout_logfile "syslog"
  stderr_logfile "syslog"
  startsecs 10
  stopsignal "QUIT"
  stopasgroup true
  killasgroup true
  process_name '%(program_name)s %(process_num)02d'
  numprocs node['getsentry']['web']['processes']
end

 位置的選擇

  除非有人想出了一個非常有說服力的論據:為什麼應該有另一種方式(或某種該情形下不能工作的情況),我希望能聽到這種模式因為Python的世界變得更標準。最起碼,我希望看到關於如何提高uwsgi內程式管理的一些辯論的火花。

  如果你要精簡這個帖子,留下這句話:uwsgi執行緒(或非執行緒)服務是Python的Web應用的程式唯一選擇。

  (我匆匆寫了這篇文章來說明今天的一些研究結果,所以難免簡單,錯別字請諒解)

  英文原文:You Should Be Using Nginx + UWSGI

相關文章