一个python实现重试机制的简要实践

最近在写接口测试脚本时,遇到如下一个测试场景

1、A系统会创建一条数据,创建成功后会把数据推到B系统;

2、由于是两个系统之间通信,数据不会立刻从A系统同步到B系统,中间有一个短暂的时间差;

我要调试的接口有2个,一是在A系统调用一个接口,生成数据;二是在B系统调用另一个接口处理数据。

实际操作后,发现一个问题:由于调用完A接口后,会立刻调用B接口,从代码层面看,这个时间差很短,虽然A系统已经把数据生成了,但在这么短时间内还没推送到B系统,导致调用B接口时,查不到这条数据,就会报错

第一个解决方案

开始想到的解决方案是使用time.sleep(),当调用A接口后,等待一段时间,如 time.sleep(5),死等5s,然后再调用B接口

因为等待5s后,数据一般能够从A系统推送到B系统

当然如果5s后还没有同步到B系统,调用B接口时仍然会报错,所以这并不是一个很好的解决方案

第二个解决方案

互联网冲浪一番后发现了python有一个库可以实现重试机制:tenacity

下面是找到的一些参考博客,可以看一下基础用法:https://www.cnblogs.com/wuzhibinsuib/p/13443622.html

接下来说一下自己的实验结果以及理解

它的简单用法是给需要重试的代码加上@retry修饰器,代码抛出异常会被装饰器捕获并进行重试

这里的关键是捕获到到代码抛出的异常

例1【如果报错会一直重试】

@retry
def test_retry1():
    print("等待重试.....")
    raise Exception  # 通过raise直接返回一个错误

@retry
def test_retry2():
    print("等待重试.....")
    return "hello" + 1  # 人为制造一个错误,这里我是把字符串和整数相加,因为类型不同,肯定会报错,所以会触发重试

上述2段代码运行后会一直打印“等待重试”,直至手工停止运行

例2【设置最大重试次数stop_after_attempt】

@retry(stop=stop_after_attempt(5))
def test_retry():
    print("等待重试.....")
    return "hello" + 1 

用 stop 接收 stop_after_attempt,当重试指定次数时,结束重试,如下重试了5次

例3【设置最大重时间,如果失败,则重试,一直重试5s】 

@retry(stop=stop_after_delay(5))
def test_retry():
    print("等待重试.....")
    return "hello" + 1 

例4【出现特定错误后重试】

@retry(retry=retry_if_exception_type(TypeError))
def test_retry1():
    print("等待重试.....")
    return "hello" + 1  # 捕获类型错误,当出现类型错误时重试

@retry(retry=retry_if_exception_type(SyntaxError))
def test_retry2():
    print("等待重试.....")
    raise SyntaxError  # 捕获语法错误,当出现语法错误时重试

例5【满足自定义的条件后重试】

# 首先定义了一个函数symbol,它的作用是判断传入的值是否为None;它返回一个布尔值,如果结果value=None,则返回true,否则返回False
def symbol(value):
    return value is None


# 装饰器中retry=retry_if_result(symbol),表示把test_retry函数的结果传入symbol,判断test_retry的结果是否为None,
# 如果=None,就进行重试(retry),如果不等于None,就结束并返回函数值(所以达成重试的条件是test_retry的结果是否为条件函数定义的结果)
@retry(stop=stop_after_attempt(3), retry=retry_if_result(symbol), reraise=True) def test_retry(): print("等待重试.....") return None
symbol()函数是定义的条件函数,test_retry()函数是希望重试的函数,它俩通过装饰器中的retry_if_result()来关联,具体含义可以看下上述代码的注释

接下来开始处理我的接口测试脚本,用到是上面例5的自定义条件重试

首先处理需要重试的方法,我规定了当这个方法没有接收到推送过来的数据时,返回None

    def seal_regist(code):

        seal_data = self.get_seal_data(code)

        try:
            if seal_data["data"]["list"]:

                r = requests.post(url, json=payload, headers=headers)
                return r.json()

            else:
                print("列表中没有这条待用印数据{},请检查系统~".format(code))
                return None

        except Exception as e:
            raise e

定义一个条件函数

def test_retry(value):
    """重试条件函数"""
    return value is None

给 seal_regist()函数加上retry装饰器

@retry(stop=stop_after_delay(10), retry=retry_if_result(test_retry))
    def seal_regist(code):
      .....
      .....

如果 seal_regist()返回None则重试,最大重试时间为10s

ps.因为重试函数中需要用到登陆cookie,之前是把登陆获取cookie的方法写到里面的,但是如果加上重试机制的话,当开始重试时会一直重新登录获取cookie,提示登陆频繁并导致登陆接口调用失败,所以为了避免这种情况,我把获取登陆cookie的方法放到了外面,这样无论重试几次都用开始获取到的一个cookie即可(所以如果有遇到和我类似情况的,把那些类似只需获取一次数据的方法放到外面,避免重复请求接口引发异常)

原文地址:https://www.cnblogs.com/hanmk/p/15525986.html