Python: URL轮询和发布架构

0 投票
1 回答
1050 浏览
提问于 2025-04-15 16:02

我遇到了一个简单的问题。我需要每分钟获取一次一个网址,检查是否有新内容,如果有,就把它发送到另一个网址。

我现在有一个每分钟运行一次的定时任务,基本上是这样:

for link in models.Link.objects.filter(enabled=True).select_related():
    # do it in two phases in case there is cross pollination

    # get posts
    twitter_posts, meme_posts = [], []
    if link.direction == "t2m" or link.direction == "both":
        twitter_posts = utils.get_twitter_posts(link)

    if link.direction == "m2t" or link.direction == "both":
        meme_posts = utils.get_meme_posts(link)

    # process them
    if len(twitter_posts) > 0:
        post_count += views.twitter_link(link, twitter_posts)

    if len(meme_posts) > 0:
        post_count += views.meme_link(link, meme_posts)

    count += 1

msg = "%s links crawled and %s posts updated" % (count, post_count)

这个方法对我现在的150个用户来说效果很好,但我对这种同步的方式有点担心。我已经设置了网址超时,但总有一天我的定时任务会超过1分钟,这样就会有很多任务同时运行,互相覆盖。

那么,我该怎么重写这个系统呢?

有几个问题:

  • 我不想对API请求太频繁,以免被封。所以我希望在任何时候最多只保持5个连接。
  • 用户在这个过程中不断注册,所以我需要一种方法来添加他们。
  • 我希望这个系统能够尽可能地扩展。
  • 我希望尽量重用现有的代码。

所以,我想到了一些想法:

  • 为每个link创建一个线程。
  • 使用python-twisted - 保持一个运行的进程,定时任务只需确保它在运行。
  • 使用stackless - 其实我对这个了解不多。
  • 问问StackOverflow :)

你会怎么做呢?

1 个回答

2

最简单的方法是使用一个长时间运行的进程,配合sched(在独立的线程中)来处理调度。你可以把请求放到一个队列里;然后准备一个固定大小的线程池(你可以在这里找到现成的线程池,但其实自己动手也很简单)来从队列中取请求,并通过另一个队列返回结果。如果需要,注册和其他系统功能可以由几个专门的线程来处理。

线程其实也没那么可怕,只要注意以下两点:(a)你不需要担心线程之间的同步问题(只要让它们通过本身线程安全的队列来沟通,绝对不要共享任何不是严格只读的结构或子系统),(b)线程的数量不要太多(可以为一些专门的功能,比如调度,使用几个专门的线程,同时为一般工作准备一个小的线程池——绝对不要为每个请求新建一个线程,这样会导致问题)。

使用Twisted可以让系统更具扩展性(而且硬件成本低),但如果你的架构依赖于线程(和队列),那么你就有了一种内置的方式来扩展系统(通过购买更多的硬件),以便使用非常相似的multiprocessing模块……几乎可以直接替换,而且扩展能力可以提升几个数量级!

撰写回答