【问题标题】:Converting to Twisted Asynchronous Design转换为 Twisted 异步设计
【发布时间】:2010-08-11 22:54:30
【问题描述】:

好的,我在表达我正在处理的代码的问题时遇到了问题,但没有倾倒大量代码;所以这就是同步的(而不是从异步的角度询问它)。

对于类来说,什么时候应该通过方法参数访问变量,什么时候应该通过实例变量访问?

同时它看起来像这样...... 注意:实际的服务器 url 和解析是不同的,但只是使事情复杂化。同样在以下示例中,get_token 方法将会话作为参数,是否应该改为使用 self.session 来获取会话?

import urllib
import time

class SyncExampleClass(object):

    def __init__(self):
        self.session = None
        self.token = None
        self.session_time = -1


    def get_session(self):
        s = urllib.urlopen("http://example.com/session/").read()
        self.session_time = int(time.time())
        return s

    def get_token(self, session):
        t = urllib.urlopen("http://example.com/token/?session=%s" % session).read()
        return t

    def construct_api_call(self, api_method):
        # if the session is over an hour old (is that the correct sign?)
        if time.time() - 3600 > self.session_time or self.session is None:
            self.session = get_session()
            self.token = get_token(self.session)

        call = urllib.urlopen("http://example.com/api/?method=%s%session=%s&token=%s" % (api_method, self.session, self.token) ).read()
        return call

【问题讨论】:

  • 第一次在construct_api_call 中设置 self.session 后,如果距上次设置不到一小时,您将设置另一个 - 如果您有一个小时没有调用它,您' 将永远不会再设置另一个会话(或令牌)。非常奇怪的行为——你确定if 中的< 是你想要的吗?! (当你通过代码给出规范时,你最好加倍确保代码完全按照你的意愿做,当然,因为没有“自然语言”对应物来审查规范)。
  • @Alex Martelli 是的,你是对的;我的意思是如果会话超过一个小时。 (我在编辑中修复了它(所以如果你现在看它是正确的(我认为))
  • sessiontoken 变量实际上是 URL 的一部分,还是它们是 cookie?你的电话是urllib.urlopen 充其量会阻塞反应堆。你应该改用twisted.web.http.HTTPClient
  • 它们实际上是在实际实现中发布数据,这就是看起来没有所有不必要的复杂性的样子,我也知道 urllib 会阻塞,这就是为什么它是同步示例而不是异步示例。

标签: python asynchronous twisted


【解决方案1】:

鉴于这种情况,这只是一个隐含的解决方案的骨架。使用隐含且未经测试的代码提供解决方案似乎违背本能......

但是,如果我正在编写我认为您想要实现的目标,我可能会这样做:

from twisted.internet import defer
from twisted.web import client
from twisted.python import log
from urllib import urlencode
import time

class APIException(Exception):
    pass

class ASyncExampleClass(object):
    def __init__(self):
        self.session = None
        self.token = None

    @defer.inlineCallbacks
    def api_call(self, api_method,tries=3,timeout=10):
        attempt = 1
        while attempt <= tries:
            attempt += 1
            if self.session = None:
                yield sess_data = client.getPage("http://example.com/session/",timeout=timeout)
                self.session = extractSessionFromData(sess_data)
            if self.token = None:
                yield token_data = client.getPage("http://example.com/token/?%s" % urlencode(dict(session=self.session)),timeout=timeout)
                self.token = extractTokenFromData(token_data)
            # Place "the" call
            yield api_result = client.getPage("http://example.com/api/?%s" % urlencode(dict(api_method=api_method,session=self.session,token=self.token)),timeout=timeout)
            #
            if sessionInvalid(api_result):
                log.msg("Request for %s failed because invalid session %s" % (api_method,self.session))
                self.session = None
                self.token = None
                continue
            if tokenInvalid(api_result):
                log.msg("Request for %s failed because invalid token %s" % (api_method,self.token))
                self.token = None
                continue
            # Any other checks for valid result
            returnValue(api_result)
            break # Not sure if this is needed, not in an position to test readily.
        else:
            raise APIException("Tried and failed %s times to do %s" % (attempt - 1, api_method))

制作外部api的方法使用inlineCallbacks并自己处理获取和更新会话和令牌的逻辑。我假设当会话无效时,使用它获取的任何令牌也是无效的。它实现了一个重试循环,它还可以包含一个try/except 块,以便更好地处理 HTTP 异常。

您可以将twisted.web.client.getPage 与POST 一起使用,您提供的额外参数由HTTPClientFactory 处理。

另外,我不会费心为会话计时,只需在需要时更新即可。

【讨论】:

  • 非常感谢 MattH,这正是我一直在寻找的东西,我起初也避免使用 inlineCallbacks,但这让它们显得优雅而不是 hackish。 点击添加评论然后点赞:)
猜你喜欢
  • 2011-06-05
  • 2012-06-14
  • 1970-01-01
  • 2017-12-09
  • 1970-01-01
  • 2015-06-25
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多