部署

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时需要注意的几件事

使用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-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')}
"
        f"Forwarded: {request.forwarded}
"
    )

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