【问题标题】:How to implement retry logic?如何实现重试逻辑?
【发布时间】:2022-09-25 19:20:20
【问题描述】:

我正在尝试使用 pytransitions 从初始化状态实现重传逻辑。总结就是在初始化状态下,如果对方1秒后没有响应就重发数据包。这与我在这里看到的非常相似:https://github.com/pytransitions/transitions/pull/461

我尝试了这个补丁,即使我看到发生超时/失败,我的回调也只是第一次被调用。对于 before/after 和 on_enter/exit 也是如此。无论我尝试了什么,我都无法让重传再次发生。有任何想法吗?

    标签: pytransitions


    【解决方案1】:

    即使这个问题有点过时,我还是想发布一个答案,因为 Retry 状态已添加到 transitions 在发布 0.9 中。 Retry 本身只会计算重新进入状态的频率,这意味着当转换源和目标相等时计数器将增加,否则将重置。它完全是被动的,需要另一种方法来触发事件。除了Retry 之外,通常使用Timeout 状态扩展来实现此目的。在下面的示例中,状态机装饰有RetryTimeout 状态扩展,允许使用几个关键字来定义状态:

    • timeout - 进入状态后触发超时前的时间(以秒为单位)
    • on_timeout- 触发 timeout 时调用的回调
    • retries - 重新进入状态时调用失败回调之前的重试次数
    • on_failure - 当重入计数器到达 retries 时调用的回调

    该示例将重新输入pinging,除非随机生成的介于 0 和 1 之间的数字大于 0.8。这可以解释为一个服务器,它粗略地只回答每五个请求。当您执行该示例时,达到“已初始化”所需的重试可能会有所不同,甚至在达到retries 时会失败。

    from transitions import Machine
    from transitions.extensions.states import add_state_features, Retry, Timeout
    
    import random
    import time
    
    # create a custom machine with state extension features and also 
    # add enter callbacks for the states 'pinging', 'initialized' and 'init_failed'
    @add_state_features(Retry, Timeout)
    class RetryMachine(Machine):
        def on_enter_pinging(self):
            print("pinging server...")
            if random.random() > 0.8:
                self.to_initialized()
    
        def on_enter_initialized(self):
            print("server answered")
    
        def on_enter_init_failed(self):
            print("server did not answer!")
    
    
    states = ["init",
              {"name": "pinging",
               "timeout": 0.5,  # after 0.5s we assume the "server" wont answer
               "on_timeout": "to_pinging",  # when timeout enter 'pinging' again
               "retries": 3,  # three pinging attempts will be conducted
               "on_failure": "to_init_failed"},
              "initialized",
              "init_failed"]
    
    # we don't pass a model to the machine which will result in the machine 
    # itself acting as a model; if we add another model, the 'on_enter_<state>'
    # methods must be defined on the model and not machine
    m = RetryMachine(states=states, initial="init")
    assert m.is_init()
    m.to_pinging()
    while m.is_pinging():
        time.sleep(0.2)
    

    【讨论】: