从uWSGI主循环调用Python代码

2 投票
1 回答
639 浏览
提问于 2025-04-30 04:58

我写了一个简单的uWSGI插件。

#include <uwsgi.h>

static void cycle() {
      uwsgi_log("In master cycle\n");
}

struct uwsgi_plugin master_plugin = {
        .name = "master",
        .master_cycle = cycle,
};

现在我想在主循环线程中调用一些Python代码,这个线程和我的WSGI应用程序是在同一个Python解释器中初始化的。有没有uWSGI的API可以做到这一点?我可以使用Python插件吗:https://github.com/unbit/uwsgi/blob/master/plugins/python/python_plugin.c?如果可以的话,请给我一个代码示例。

更新

以下是一些评论中的背景信息:

  1. 我想实现一个内存缓存,利用写时复制的特性,所以我会把数据加载到主进程中,然后重新分叉工作进程,这样它们都能获取到最新的数据。
  2. 我不能使用uWSGI的缓存,因为序列化和反序列化大约需要200毫秒,而我缓存的对象是复杂的Python对象,比如SQLAlchemy模型实例,实际上是一系列这样的实例。用协议2序列化的示例列表大约占用6MB的空间。
  3. 我希望缓存的实现尽可能简单,就像缓存装饰器一样,这样被缓存的方法就不需要关心自己是否被缓存。这是为了向后兼容,并且将来添加缓存方法时也更简单。
  4. 我不能一次性使用uwsgi.reload()重新加载所有工作进程,原因有两个:1)重新分叉所有工作进程(20-30个进程)可能会对性能产生影响,因此我希望分批次在时间上重新分叉工作进程(比如每5秒一次);2)可能会有几个缓存有不同的过期时间,完全重新加载会让它们全部失效,并给数据库带来不必要的负担。
  5. 最开始,我在主进程中创建了一个新线程,定期重新验证缓存,并向工作进程发送HUP信号以优雅地重新分叉。问题是我无法完全控制工作进程何时会真正死亡和重生,所以在我的线程更新缓存的过程中,工作进程可能会分叉,导致获取到损坏的数据。有没有办法在不进行线程同步的情况下提供一致的数据?
  6. 为了解决上面的问题,我想在主循环中加入一些代码,检查与更新线程相关的信号量,并在缓存更新完成之前暂停主循环。这样可以确保在缓存更新期间不会发生分叉。
暂无标签

1 个回答

1

根据你的需求,我觉得你可以试试uWSGI 2.1的fork服务器功能:

https://github.com/unbit/uwsgi-docs/blob/master/ForkServer.rst

这里有关于如何在Python中使用这个功能的说明(这个功能最初是为Perl开发的):

https://github.com/unbit/uwsgi-docs/blob/master/examples/CPythonForkServer.rst

这个讨论串里也有一些相关的说明:

http://lists.unbit.it/pipermail/uwsgi/2014-June/007444.html

简单来说,你会有两个“无关”的实例,一个是用来不断构建缓存的,另一个是从第一个实例分叉出来的,用于你需要的时候。

最后,如果你真的想在主循环中运行自定义的Python代码,这里有一段代码可以作为起点:

#include <uwsgi.h>
void PyRun_SimpleString(char *);

static void cycle() {
      uwsgi_log("In master cycle\n");
        PyRun_SimpleString("print \"aaa\"");
}

struct uwsgi_plugin master_plugin = {
        .name = "master",
        .master_cycle = cycle,
};

撰写回答