ヒトリ歩き

愚痴とかいろいろ書きます

リトライ処理をbackoffモジュールで簡単に実装する

リトライ処理を自分で作り込まずに、backoffモジュールを活用することでよりコードがシンプルになる。
これからはbackoffモジュールを活用していきたい。

backoffモジュールとは

backoffモジュールは条件が満たされるまでリトライするためのデコレータ。
ネットワークリソースやAPI、信頼性が低く、障害が発生する可能性のあるリソースにアクセスする際、使用することを目的としている。

使い方

backoff.on_exceptionデコレータにリトライする対象のExceptionクラスを定義。
第一パラメータのbackoff.expoはひとまずおまじないと思っていればいい。
下記の例だと、Exceptionが発生し続ければ、ずっとリトライする。

import requests
import backoff

@backoff.on_exception(backoff.expo,
                      Exception)
def handler():

    print("Start handler")
    try:
        response = requests.get("http://localhost:8081")
    except Exception as e:
        print("occur Exception")

        raise e
    
    print("End handler")

if __name__ == '__main__':
    handler()

リトライの設定

max_triesパラメータは、実行回数を設定する。
例えば、実行失敗時に、2回までリトライをさせる場合、最初の実行も回数に含めて3を設定する。

import requests
import backoff
import time

@backoff.on_exception(backoff.expo,
                      requests.exceptions.RequestException,
                      max_tries=5,
                      jitter=None)
def handler():

    print("Start handler")
    try:
        start = time.time()
        response = requests.get("http://localhost:8000", timeout=(5.0, 5.0))
    except Exception as e:
        end = time.time()
        print("occur Exception")
        print("Execute time = " + str(end - start))
        print(e)

        raise e
    
    end = time.time()
    print("End handler time = " + str(end - start))

if __name__ == '__main__':
    handler()

再実行時のリトライ間隔の設定

再実行時のリトライ間隔は、設定するWatiGeneratorで決まる。
backoff.expoを指定した場合、指数関数で増加する。デフォルトの場合、2のX乗。
backoff.fiboを指定した場合、フィボナッチ数列で増加する。
backoff.constantを指定した場合、リトライ間隔は一定となる。デフォルトは1秒。

backoff.constantのリトライの間隔を変更したい場合、intervalパラメータを指定する。
リトライの間隔を5秒に変更したい場合は、interval=5を指定する。

@backoff.on_exception(backoff.constant,
                      Exception,
                      jitter=None,
                      interval=5

リトライ間隔を1秒、10秒、20秒のようにカスタマイズしたい場合、intervalパラメータにリストで間隔を設定する。

@backoff.on_exception(backoff.constant,
                      Exception,
                      jitter=None,
                      interval=[1, 10, 20]

リトライ時のばらつきを含める

backoffモジュールにはリトライ時のタイミングにばらつきを持たせるために、jitterパラメータが用意されている。
複数のユーザが操作によるリトライが発生した場合、リトライによる際実行が同じになり、多くのリクエストが発生する。
そのため、jitterによる時間のずれを含めることで、リトライのタイミングをずらして再実行によるリクエストの増加を防ぐ。
実行のタイミングのずれを含めたくない場合、jitter=Noneの指定が必要になる。

beyondjapan.com

https://aws.typepad.com/sajp/2015/03/backoff.html

aws.amazon.com

最後に

backoffモジュールを使うことで簡単にリトライをするための実装が可能となる。
ほかにもon_predicateを使用して、ポーリングも続けることができる。
また、複数のデコレータを指定することもの可能だ。
backoffモジュールを使い倒すことで、リトライ系のことは何でもできそうな気がする。
これからは自分でリトライ処理は作り込まず、backoffモジュールを活用していきたい。