Python: URL轮询和发布架构
我遇到了一个简单的问题。我需要每分钟获取一次一个网址,检查是否有新内容,如果有,就把它发送到另一个网址。
我现在有一个每分钟运行一次的定时任务,基本上是这样:
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模块……几乎可以直接替换,而且扩展能力可以提升几个数量级!