Sanic官翻-部署


部署

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, UvicornHypercorn

按照他們的文檔來運行它們的正確方法,但是它看起來應該像這樣:

daphne myapp:app
uvicorn myapp:app
hypercorn myapp:app

使用ASG時需要注意的幾件事

  1. 使用Sanic Web服務器時,Websockets將使用websockets軟件包運行。在ASGI模式下,由於websocket是在ASGI服務器中管理的,因此不需要此軟件包。

  2. 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-forkworker模型。

為了在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 = Falseaccess_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_startbefore_server_stopafter_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_certificateSSL_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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM