Eu vi algumas implementações disso online, em particular Retrying with style , que é uma postagem de dois anos e tem algumas coisas que me incomodaram:
- está bloqueando, Futuros onde não estão em escala no momento,
- ele usa variáveis mutáveis, que não são intrinsecamente ruins em tal contexto contido; mas meu editor os destaca em vermelho e eu não gosto de ter pedaços vermelhos em meu código: D
Portanto, criei uma nova implementação disponível nesta essência .
A ideia é que você poderia ter um bloco de código que falha por qualquer tipo de razão que não seja determinística. Por exemplo, uma chamada para o banco de dados pode falhar porque a fila de conexão está cheia, ou uma solicitação para um serviço da web pode falhar porque a “internet está ocupada”.
Em vez de escrever um loop e muitos códigos relevantes para a parte de nova tentativa, você pode escrever um wrapper de bloco em scala que cuidará de tudo em segundo plano para você:
val myResult = retry(10) {
...
makeWSCall()
...
}
Isso é o que o pequeno código que coloquei nesta essência faz. Não vou copiar tudo aqui porque é muito longo, mas aqui estão alguns comentários sobre como funciona:
- Eu uso a
Promise
para eventualmente incorporar um valor em aFuture
. O futuro dessa promessa é retornado diretamente e as tentativas são executadas de forma assíncrona. - se uma exceção for detectada, ele tentará novamente, a menos que tenha esgotado o número fornecido de novas tentativas, caso em que falhará no futuro.
- assim que o resultado puder ser calculado sem exceções, ele será retornado no futuro.
Como o bloco retorna a Future
, as novas tentativas são executadas de forma assíncrona, com sorte não bloqueando o fluxo de dados. Você pode usar a Future
API para manipular o valor eventualmente retornado:
myResult.map(_ * 2)
Você também pode usar os métodos de recuperação integrados de Future
para lidar com um resultado com falha (muitas tentativas):
val myResult = retry(10) {
...
makeWSCall()
...
} recover {
case t: Throwable => 0
}
A retry
função também fornece alguns parâmetros opcionais avançados:
- além da contagem máxima obrigatória de novas tentativas, você pode definir um prazo opcional após o qual ele deve simplesmente desistir:
val myResult = retry(10, Some(10 seconds fromNow)) {
...
makeWSCall()
...
}
- por padrão, o bloco será repetido com um back-off exponencial , já que é freqüentemente usado para lidar com recursos que não gostam de ser sobrecarregados, você pode alterar este padrão se desejar
val myResult = retry(10, backoff = (r) => 100 milliseconds) {
...
makeWSCall()
...
}
- se você deseja que o bloco falhe sem nova tentativa para exceções específicas, você pode especificar uma função de filtragem com o
ignoreThrowable
parâmetro opcional