回退配置
(从1.9.15-dev起可用)
如果你需要一个”重置到出厂默认设置”,或者”在用户把配置弄得一团糟的时候显示一个欢迎页面”的场景,那么回退配置就是你的银弹。
简单的例子
一个非常普遍的问题是screwing-up the port on which the instance is listening.
为了模拟这样的错误,我们试着以非特权用户来绑定80端口:
- uwsgi --uid 1000 --http-socket :80
uWSGI将会退出:
- bind(): Permission denied [core/socket.c line 755]
在内部 (从内核的角度),实例退出,状态为1
现在,我们想让实例在用户提供的配置失败的时候自动绑定到端口8080.
让我们来定义一个回退配置 (你可以将其保存为safe.ini):
- [uwsgi]
- print = Hello i am the fallback config !!!
- http-socket = :8080
- wsgi-file = welcomeapp.wsgi
现在,我们可以重新运行(损坏的)实例:
- uwsgi --fallback-config safe.ini --uid 1000 --http-socket :80
现在,错误将如下:
- bind(): Permission denied [core/socket.c line 755]
- Thu Jul 25 21:55:39 2013 - !!! /home/roberto/uwsgi/uwsgi (pid: 7409) exited with status 1 !!!
- Thu Jul 25 21:55:39 2013 - !!! Fallback config to safe.ini !!!
- [uWSGI] getting INI configuration from safe.ini
- *** Starting uWSGI 1.9.15-dev-a0cb71c (64bit) on [Thu Jul 25 21:55:39 2013] ***
- ...
正如你所见,实例检测到退出码1,并且用一个新的配置对自身打了二进制补丁 (无需更改pid,或者调用fork())
已破坏应用
另一个常见的问题是不能加载应用,但并非让整个站点挂掉,而是想要加载一个备用应用:
- uwsgi --fallback-config safe.ini --need-app --http-socket :8080 --wsgi-file brokenapp.py
这里,键是–need-app。如果实例一直未能加载至少一个应用,那么它将会调用exit(1)。
多种回退层次
你的回退配置文件也可以指定一个fallback-config指令,允许多种回退层次。当心循环!!!
它是如何工作的
目标是在进程本身被销毁之前捕获到其退出码 (不想要调用另一个fork(),或者销毁已打开的文件描述符)
uWSGI大量使用atexit()钩子,因此我们仅需将回退处理器作为第一个进行注册 (钩子是以相反的顺序执行的)。
除此之外,我们需要在我们的atexit()钩子中获取退出码,这个默认不支持 (on_exit()函数现在已弃用)。
解决方法是用uwsgi_exit(x)对exit(x)“打补丁”,它是一个简单的设置uwsgi.last_exit_code内存指针的封装器。
现在,钩子仅需检测uwsgi.last_exit_code == 1,并最终再次execve()二进制文件,将回退配置传递给它
- char *argv[3];
- argv[0] = uwsgi.binary_path;
- argv[1] = uwsgi.fallback_config;
- argv[2] = NULL;
- execvp(uwsgi.binary_path, argv);
注意事项
试着尽可能快地将–fallback-config放到你的配置树中。在回退文件被注册之前,各种配置解析器可能会失败 (调用exit(1))