Lorsque le multithreading a été effectué en utilisant Thread.new ()
, une erreur de pénurie de ressources s'est produite dans Heroku.
can't create Thread: Resource temporarily unavailable (ThreadError)
J'ai créé une gestion des erreurs de thread et un mécanisme de nouvelle tentative qui utilise les threads au mieux.
Au fait, le nombre de threads de processus pouvant être exécutés par Heroku est assez limité, alors faites attention à la différence avec l'environnement local.
https://devcenter.heroku.com/articles/limits#processes-threads
À partir de 2020/08
free, hobby and standard-1x dynos support no more than 256
standard-2x and private-s dynos support no more than 512
performance-m and private-m dynos support no more than 16384
performance-l and private-l dynos support no more than 32768
Si votre environnement local est MacOS, vous pouvez utiliser la commande sysctl kern.num_taskthreads
pour connaître le nombre maximum de threads par processus.
$ sysctl kern.num_taskthreads
kern.num_taskthreads: 4096
Il y a une forte possibilité qu'une erreur de conception soit commise lors du processus de manque de threads lorsque la charge est légèrement plus élevée que d'habitude.
Tout d'abord, considérez comment réduire le nombre de threads à configurer, par exemple si le traitement peut être délégué à un autre serveur ou s'il existe un moyen de traiter un petit nombre de requêtes en raison des spécifications de l'API.
def retry_threads(times: 3)
try = 0
begin
try += 1
Thread.new { yield }
rescue ThreadError
sleep(1 * try)
retry if try < times
raise
end
end
S'il n'y a pas de thread disponible, attendez quelques secondes et réessayez.
Le nombre de secondes d'attente est variable, par exemple 1 seconde pour la première tentative et 2 secondes pour la deuxième tentative, afin d'éviter une perte de temps.
Il existe également un moyen de gérer les nouvelles tentatives en détail en définissant le temps d'attente en microsecondes.
Utilisation réelle
def heavy_task(url)
#Traitement lourd
end
# urls = ["...","...",...]
threads = []
urls.all each do |url|
threads << retry_threads{ heavy_task(url) }
end
threads.each(&:join)
Nous avons étudié dans quelle mesure la tolérance pour le threading était réellement augmentée par ce mécanisme de nouvelle tentative.
def heavy_task
sleep(10)
end
def retry_threads(times: 3)
try = 0
begin
try += 1
Thread.new { yield }
rescue ThreadError
sleep(1 * try)
retry if try < times
p $count
raise
end
end
def no_retry_threads()
begin
Thread.new { yield }
rescue ThreadError
p $count
raise
end
end
$count = 0
#Aucune nouvelle tentative
loop do
no_retry_threads{ heavy_task }
$count += 1
end
#Avec réessayer
loop do
retry_threads{ heavy_task }
$count += 1
end
La valeur du nombre de tâches qui ont pris 10 secondes dans l'environnement local (nombre maximum de threads: 4096).
Aucune nouvelle tentative | Avec réessayer |
---|---|
4094 | 212888 |
Recommended Posts