部署
Sanic有三個服務選項:內置web服務器、ASGI web服務器或gunicorn。
Sanic自己的web服務器是最快的選擇,它可以安全地在互聯網上運行。不過,將Sanic放在反向代理后面也是非常常見的,如Nginx部署中所示。
Snaic webserver
定義sanic.Sanic
實例后,我們可以使用以下關鍵字參數調用run方法:
host
(默認為"127.0.0.1"
):托管服務器的地址。port
(默認為8000
):用於托管服務器的端口。unix
(默認為None
): 服務器所在的Unix套接字名稱(而不是TCP)。debug
(默認為False
):啟用調試輸出(降低服務器速度)。ssl
(默認為None
):SSLContext用於對工作人員進行SSL加密。sock
(默認為None
):服務器接受其連接的套接字。worker
(默認值為1
):要產生的工作進程數。loop
(默認為None
):異步兼容的事件循環。如果未指定,Sanic將創建其自己的事件循環。protocol
(默認為HttpProtocol
):asyncio.protocol
的子類。access_log
(默認為True
):啟用登錄以處理請求(顯着降低服務器速度)。
app.run(host='0.0.0.0', port=1337, access_log=False)
在上面的示例中,我們決定關閉訪問日志以提高性能。
Workers
默認情況下,Sanic僅使用一個CPU內核偵聽主進程。要提高效率,只需在運行參數中指定workers數。
app.run(host='0.0.0.0', port=1337, workers=4)
Sanic將自動啟動多個進程並在它們之間路由流量。我們建議您使用盡可能多的核心。
命令啟動
如果您喜歡使用命令行參數,則可以通過執行模塊來啟動Sanic Web服務器。例如,如果您在名為server.py的文件中將Sanic初始化為app,則可以這樣運行服務器:
sanic server.app --host=0.0.0.0 --port=1337 --workers=4
它也可以直接作為模塊調用。
python -m sanic server.app --host=0.0.0.0 --port=1337 --workers=
通過這種方式運行sanic,無需在Python文件中調用app.run
。
如果這樣做,請確保將其包裝起來,以便僅在由解釋器直接運行時才執行。
if __name__ == '__main__':
app.run(host='0.0.0.0', port=1337, workers=4)
ASGI
Sanic也符合ASGI。這意味着您可以使用首選的ASGI Web服務器來運行Sanic。ASGI的三個主要實現是Daphne, Uvicorn和 Hypercorn。
按照他們的文檔來運行它們的正確方法,但是它看起來應該像這樣:
daphne myapp:app
uvicorn myapp:app
hypercorn myapp:app
使用ASG時需要注意的幾件事
-
使用Sanic Web服務器時,Websockets將使用websockets軟件包運行。在ASGI模式下,由於websocket是在ASGI服務器中管理的,因此不需要此軟件包。
-
ASGI壽命協議https://asgi.readthedocs.io/en/latest/specs/lifespan.html僅支持兩個服務器事件:啟動和關閉。Sanic有四個:啟動之前,啟動之后,關閉之前和關閉之后。因此,在ASGI模式下,啟動和關閉事件將連續運行,而實際上不會圍繞服務器進程的開始和結束運行(因為現在由ASGI服務器控制)。因此,最好使用after_server_start和before_server_stop。
Sanic在Trio上運行的實驗支持包括:
hypercorn -k trio myapp:app
Gunicorn
Gunicorn“ Green Unicorn”是用於UNIX的WSGI HTTP服務器。這是從Ruby的Unicorn項目移植過來的pre-fork
worker模型。
為了在Gunicorn上運行Sanic應用程序,您需要對Gunicorn worker-class參數使用特殊的sanic.worker.GunicornWorker
:
gunicorn myapp:app --bind 0.0.0.0:1337 --worker-class sanic.worker.GunicornWorker
如果您的應用程序遭受內存泄漏的困擾,您可以將Gunicorn配置為在處理了給定數量的請求之后正常重啟工作器。這是幫助限制內存泄漏影響的便捷方法。
有關更多信息,請參見Gunicorn Docs。
其他部署注意事項
禁用調試日志記錄以提高性能
為了提高性能,請在運行參數中添加debug = False
和access_log = False
。
app.run(host='0.0.0.0', port=1337, workers=4, debug=False, access_log=False)
通過Gunicorn運行,您可以設置環境變量SANIC_ACCESS_LOG ="False"
env SANIC_ACCESS_LOG="False" gunicorn myapp:app --bind 0.0.0.0:1337 --worker-class sanic.worker.GunicornWorker --log-level warning
或者您可以直接重寫應用程序配置
app.config.ACCESS_LOG = False
異步支持和共享循環
如果您需要與其他應用程序(特別是循環)共享Sanic進程,則此方法非常適合。但是,請注意,此方法不支持使用多個進程,並且通常不是運行該應用程序的首選方法。
這是一個不完整的示例(請參閱示例中的run_async.py
了解更多實用信息):
server = app.create_server(host="0.0.0.0", port=8000, return_asyncio_server=True)
loop = asyncio.get_event_loop()
task = asyncio.ensure_future(server)
loop.run_forever()
注意:使用此方法,調用app.create_server()
將觸發before_server_start
服務器事件,但不會觸發after_server_start
,before_server_stop
或after_server_stop
服務器事件。
對於更高級的用例,您可以使用AsyncioServer對象觸發這些事件,該對象是通過等待服務器任務返回的。
這是一個不完整的示例(請參閱示例中的run_async_advanced.py
了解更完整的內容):
serv_coro = app.create_server(host="0.0.0.0", port=8000, return_asyncio_server=True)
loop = asyncio.get_event_loop()
serv_task = asyncio.ensure_future(serv_coro, loop=loop)
server = loop.run_until_complete(serv_task)
server.after_start()
try:
loop.run_forever()
except KeyboardInterrupt as e:
loop.stop()
finally:
server.before_stop()
# Wait for server to close
close_task = server.close()
loop.run_until_complete(close_task)
# Complete all tasks on the loop
for connection in server.connections:
connection.close_if_idle()
server.after_stop()
Nginx部署
概述
盡管Sanic可以直接在Internet上運行,但在它前面使用Nginx這樣的代理服務器可能會很有用。這對於在同一個IP上運行多個虛擬主機、在單個Sanic應用程序旁邊為nodej或其他服務提供服務特別有用,而且還允許高效地為靜態文件提供服務。SSL和HTTP/2也很容易在這樣的代理上實現。
我們將Sanic應用程序設置為僅在127.0.0.1:8000
本地提供服務,而Nginx安裝負責向域上的公共互聯網提供服務example.com網站. 靜態文件將從/var/www/
提供。
代理Sanic應用程序
該應用程序需要設置一個用於識別可信代理的密鑰,這樣才能識別真實的客戶端IP和其他信息。這可以防止任何人在互聯網上發送假請求頭欺騙他們的IP地址和其他細節。選擇任意隨機字符串並在應用程序和Nginx配置中進行配置。
from sanic import Sanic
from sanic.response import text
app = Sanic("proxied_example")
app.config.FORWARDED_SECRET = "YOUR SECRET"
@app.get("/")
def index(request):
# This should display external (public) addresses:
return text(
f"{request.remote_addr} connected to {request.url_for('index')}\n"
f"Forwarded: {request.forwarded}\n"
)
if __name__ == '__main__':
app.run(host='127.0.0.1', port=8000, workers=8, access_log=False)
由於這將是一個系統服務,請將代碼保存到/srv/sanicexample/sanicexample.py
.
要進行測試,請在終端中運行應用程序。
Nginx配置
需要相當多的配置來允許快速透明代理,但在大多數情況下,這些都不需要修改,所以請接受我的建議。
upstream
服務器需要在一個單獨的upstream
塊中配置,以啟用HTTP keep alive,這可以極大地提高性能,因此我們使用它,而不是在proxy_pass
指令中直接提供upstream
地址。在本例中,upstream
部分由server_name
命名,即公共域名,然后在主機頭中傳遞給Sanic。您可以根據需要更改名稱。還可以提供多個服務器用於負載平衡和故障切換。
根據網站的真實域名更改example.com
的兩個匹配項,使用你的應用程序選擇的秘密替換YOUR SECRET
。
upstream example.com {
keepalive 100;
server 127.0.0.1:8000;
#server unix:/tmp/sanic.sock;
}
server {
server_name example.com;
listen 443 ssl http2 default_server;
listen [::]:443 ssl http2 default_server;
# Serve static files if found, otherwise proxy to Sanic
location / {
root /var/www;
try_files $uri @sanic;
}
location @sanic {
proxy_pass http://$server_name;
# Allow fast streaming HTTP/1.1 pipes (keep-alive, unbuffered)
proxy_http_version 1.1;
proxy_request_buffering off;
proxy_buffering off;
# Proxy forwarding (password configured in app.config.FORWARDED_SECRET)
proxy_set_header forwarded "$proxy_forwarded;secret=\"YOUR SECRET\"";
# Allow websockets
proxy_set_header connection "upgrade";
proxy_set_header upgrade $http_upgrade;
}
}
為了避免cookie可見性問題和搜索引擎上的地址不一致,最好將所有訪問者重定向到一個真正的域,始終使用HTTPS:
# Redirect all HTTP to HTTPS with no-WWW
server {
listen 80 default_server;
listen [::]:80 default_server;
server_name ~^(?:www\.)?(.*)$;
return 301 https://$1$request_uri;
}
# Redirect WWW to no-WWW
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name ~^www\.(.*)$;
return 301 $scheme://$1$request_uri;
}
上面的配置部分可以放在/etc/nginx/sites available/default
或其他站點配置中(如果創建新的站點,請確保將它們符號鏈接到啟用的站點)。
確保在主配置中配置了SSL證書,或者將SSL_certificate
和SSL_certificate_key
指令添加到偵聽SSL的每個服務器部分。
另外,將所有這些內容復制並粘貼到nginx/conf.d/forwarded.conf
# RFC 7239 Forwarded header for Nginx proxy_pass
# Add within your server or location block:
# proxy_set_header forwarded "$proxy_forwarded;secret=\"YOUR SECRET\"";
# Configure your upstream web server to identify this proxy by that password
# because otherwise anyone on the Internet could spoof these headers and fake
# their real IP address and other information to your service.
# Provide the full proxy chain in $proxy_forwarded
map $proxy_add_forwarded $proxy_forwarded {
default "$proxy_add_forwarded;by=\"_$hostname\";proto=$scheme;host=\"$http_host\";path=\"$request_uri\"";
}
# The following mappings are based on
# https://www.nginx.com/resources/wiki/start/topics/examples/forwarded/
map $remote_addr $proxy_forwarded_elem {
# IPv4 addresses can be sent as-is
~^[0-9.]+$ "for=$remote_addr";
# IPv6 addresses need to be bracketed and quoted
~^[0-9A-Fa-f:.]+$ "for=\"[$remote_addr]\"";
# Unix domain socket names cannot be represented in RFC 7239 syntax
default "for=unknown";
}
map $http_forwarded $proxy_add_forwarded {
# If the incoming Forwarded header is syntactically valid, append to it
"~^(,[ \\t]*)*([!#$%&'*+.^_`|~0-9A-Za-z-]+=([!#$%&'*+.^_`|~0-9A-Za-z-]+|\"([\\t \\x21\\x23-\\x5B\\x5D-\\x7E\\x80-\\xFF]|\\\\[\\t \\x21-\\x7E\\x80-\\xFF])*\"))?(;([!#$%&'*+.^_`|~0-9A-Za-z-]+=([!#$%&'*+.^_`|~0-9A-Za-z-]+|\"([\\t \\x21\\x23-\\x5B\\x5D-\\x7E\\x80-\\xFF]|\\\\[\\t \\x21-\\x7E\\x80-\\xFF])*\"))?)*([ \\t]*,([ \\t]*([!#$%&'*+.^_`|~0-9A-Za-z-]+=([!#$%&'*+.^_`|~0-9A-Za-z-]+|\"([\\t \\x21\\x23-\\x5B\\x5D-\\x7E\\x80-\\xFF]|\\\\[\\t \\x21-\\x7E\\x80-\\xFF])*\"))?(;([!#$%&'*+.^_`|~0-9A-Za-z-]+=([!#$%&'*+.^_`|~0-9A-Za-z-]+|\"([\\t \\x21\\x23-\\x5B\\x5D-\\x7E\\x80-\\xFF]|\\\\[\\t \\x21-\\x7E\\x80-\\xFF])*\"))?)*)?)*$" "$http_forwarded, $proxy_forwarded_elem";
# Otherwise, replace it
default "$proxy_forwarded_elem";
}
對於不使用conf.d和可用站點的安裝,上述所有配置也可以放在main的http部分中nginx.conf文件.
更改后重新加載Nginx配置:
sudo nginx -s reload
現在你應該可以在https://example.com/
連接到你的app。任何404錯誤等都將由Sanic的錯誤頁面處理,並且每當一個靜態文件出現在給定的路徑上時,Nginx將為其提供服務。
SSL證書
如果您還沒有在服務器上配置有效的證書,現在是這樣做的好時機。安裝certbot,python3-certbot-nginx
,然后運行
certbot --nginx -d example.com -d www.example.com
https://www.nginx.com/blog/using-free-ssltls-certificates-from-lets-encrypt-with-nginx/
作為服務執行
這部分是針對基於systemd的Linux發行版的。創建單元文件/etc/systemd/system/sanicexample.service
:
[Unit]
Description=Sanic Example
[Service]
User=nobody
WorkingDirectory=/srv/sanicexample
ExecStart=/usr/bin/env python3 sanicexample.py
Restart=always
[Install]
WantedBy=multi-user.target
然后重新加載服務文件,啟動服務並在引導時啟用:
sudo systemctl daemon-reload
sudo systemctl start sanicexample
sudo systemctl enable sanicexample