转换为扭曲的异步设计

3 投票
1 回答
1011 浏览
提问于 2025-04-16 02:39

我在表达我遇到的代码问题时总是很难,不想一堆代码全扔出来;所以我想用同步的方式来说明这个问题(而不是从异步的角度来问)。

还有关于类的问题,什么时候应该通过方法的参数来访问一个变量,什么时候又应该通过实例变量来访问呢?

同步的代码大概是这样的…… 注意:实际的服务器网址和解析方式不同,但那样会让事情变得复杂。在下面的例子中,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

1 个回答

5

在这种情况下,这只是一个解决方案的框架,很多内容都是暗示的。提供一个代码解决方案似乎有点违背直觉,因为很多地方并没有经过测试...

不过,如果我是根据我认为你想要实现的目标来编写代码,我可能会这样做:

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来处理。

另外,我觉得没必要去计时会话,只要在需要的时候更新它就行。

撰写回答