错误重试的修饰器
    :param tries: 重试次数
    :param CatchExceptions: 需要重试的exception列表
    :param delay: 重试前等待
    :param backoff: 重试n次后，需要等待delay * n * backoff
    :return:


    @retry(5,ValueError)
    def test():
        raise ValueError

def retry(tries, CatchExceptions=(Exception,), delay=0.01, backoff=2):
    '''
    错误重试的修饰器
    :param tries: 重试次数
    :param CatchExceptions: 需要重试的exception列表
    :param delay: 重试前等待
    :param backoff: 重试n次后，需要等待delay * n * backoff
    :return:


    @retry(5,ValueError)
    def test():
        raise ValueError

    '''
    if backoff <= 1:
        raise ValueError("backoff must be greater than 1")

    if tries < 0:
        raise ValueError("tries must be 0 or greater")

    if delay <= 0:
        raise ValueError("delay must be greater than 0")

    def deco_retry(f):
        @wraps(f)
        def f_retry(*args, **kwargs):
            mdelay = delay
            retException = None
            for mtries in range(tries):
                try:
                    return f(*args, **kwargs)
                except CatchExceptions as ex:
                    logger.warning(
                        "function %s(%s, %s) try %d times error: %s\n" % (f.__name__, args, kwargs, mtries, str(ex)))
                    logger.warning("Retrying in %.4f seconds..." % (mdelay))

                    retException = ex
                    time.sleep(mdelay)
                    mdelay *= backoff
            raise retException

        return f_retry

    return deco_retry