2016-05-14 2 views
1

[Résumé &] réponse: Apparemment, le problème est qu'il faut beaucoup de temps pour semer le générateur de nombres aléatoires. Voir ma réponse ci-dessous.Pourquoi le 1er appel de scrypt() n'utilise que 1% de CPU et prend une demi-heure en GCE?

Dans Google Compute Engine (GCE), la toute première requête de mon application Java Virtual Machine pour la fonction de hachage par mot de passe scrypt prend longtemps car le code n'a pas encore été compilé Just-In-Time. Donc, je réchauffe scrypt, en faisant un factice scrypt("pswd", 2,1,1) appel au démarrage du serveur. Cependant, ce qui se passe, c'est que le CPU monte à 300% +, reste là pendant 10-20 secondes, puis redescend à 1%, bien que la requête à scrypt() ne soit pas encore terminée. Maintenant, le CPU reste à 1%, pendant de nombreuses minutes (jusqu'à une demi-heure, avec 2 GCE vCPU), jusqu'à ce que finalement scrypt() soit fait.

Pourquoi ce comportement étrange?

Pourquoi scrypt() ne continuera pas à fonctionner à 300% de CPU jusqu'à ce que ce soit fait? Ce n'est pas à court de mémoire. Regardez les statistiques de Docker un peu plus bas.

Après la première requête scrypt(), les requêtes suivantes se terminent "immédiatement". Par exemple, ceci: SCryptUtil.scrypt("pswd", 65536, 8, 1) prend < 0,2 secondes, même si elle fait beaucoup plus de travail que: SCryptUtil.scrypt("pswd", 2, 1, 1) qui (comme indiqué) est mon premier scrypt() appel et prend généralement quelques minutes, avec 4 GCE vCPU - et souvent autour d'une demi-heure, avec 2 GCE vCPU. J'utilise une instance GCE avec 4 vCPU, 3,6 Go de RAM. Docker 1.11.1. OpenJDK 1.8.0_77. Dans un conteneur Docker Alpine Linux 3.3, hôte Ubuntu 16.04 Docker. Impossible de reproduire ceci sur mon ordinateur portable; sur mon ordinateur portable, scrypt est toujours rapide, n'a pas besoin d'échauffement.

docker stats, après 5-10 secondes: (maintenant edp_play_1, ligne 2, utilise 300 +% CPU)

CONTAINER   CPU %    MEM USAGE/LIMIT  MEM %    NET I/O    BLOCK I/O   PIDS 
edp_nginx_1   0.02%    55.92 MB/104.9 MB 53.33%    6.191 kB/2.897 kB 0 B/0 B   6 
edp_play_1   315.12%    914.7 MB/2.831 GB 32.31%    43.4 kB/66.09 kB 0 B/2.58 MB  67 
edp_postgres_1  0.33%    29.84 MB/314.6 MB 9.49%    529.1 kB/307.9 kB 0 B/327.7 kB  17 
edp_redis_1   0.08%    6.513 MB/52.43 MB 12.42%    4.984 kB/1.289 kB 0 B/0 B   3 

docker stats après une demi-minute: (maintenant edp_play_1 utilise uniquement CPU 0,97% - et reste comme ce, jusqu'à une demi-heure, jusqu'à ce que fait)

CONTAINER   CPU %    MEM USAGE/LIMIT  MEM %    NET I/O    BLOCK I/O   PIDS 
edp_nginx_1   0.02%    55.92 MB/104.9 MB 53.33%    6.341 kB/3.047 kB 0 B/0 B   6 
edp_play_1   0.97%    1.011 GB/2.831 GB 35.71%    130.2 kB/215.2 kB 0 B/5.546 MB  66 
edp_postgres_1  0.28%    29.84 MB/314.6 MB 9.49%    678.2 kB/394.7 kB 0 B/458.8 kB  17 
edp_redis_1   0.06%    6.513 MB/52.43 MB 12.42%    4.984 kB/1.289 kB 0 B/0 B   3 

Si vous voulez tester à Scala & SBT, c'est ce qui se passe pour moi dans GCE:

scala> import com.lambdaworks.crypto.SCryptUtil 
import com.lambdaworks.crypto.SCryptUtil 

scala> def time[R](block: => R): R = { val t0 = System.nanoTime() ; val result = block ; val t1 = System.nanoTime() ; println("Elapsed time: " + (t1 - t0) + "ns") ; result ; } 
time: [R](block: => R)R 

scala> time { SCryptUtil.scrypt("dummy password 1", 2, 1, 1) } 
Elapsed time: 313823ns <-- 5 minutes 
res0: String = $s0$10101$2g6nrD0f5gDOTuP44f0mKg==$kqEe4TWSFXwtwGy3YgmIcqAhDvjMS89acST7cwPf/n4= 

scala> time { SCryptUtil.scrypt("dummy password 1", 2, 1, 1) } 
Elapsed time: 178461ns 
res1: String = $s0$10101$C0iGNvfP+ywAxDS0ARoqVw==$k60w5Jpdt28PHGKT0ypByPocCyJISrq+T1XwmPlHR5w= 

scala> time { SCryptUtil.scrypt("dummy password 1", 65536, 8, 1) } 
Elapsed time: 130900544ns <-- 0.1 seconds 
res2: String = $s0$100801$UMTfIuBRY6lO1asECmVNYg==$b8i7GABgeczVHKVssJ8c2M7Z011u0TMBtVF4VSRohKI= 

scala> 313823L/1e9 
res3: Double = 313.823

scala> 130900544L/1e9 
res4: Double = 0.130900544 

Remarque: Ceci n'est pas lié à Docker. Je viens de tester en dehors de Docker, avec openjdk 8 installé directement sur l'instance GCE, et le résultat est le même: scrypt(..) prend comme 3 minutes la première fois, mais le processeur est de 90-100% inactif. Par la suite, les demandes de scrypt sont complétées immédiatement.

Répondre

1

Le problème est qu'il faut beaucoup de temps pour semer le générateur de nombres aléatoires. Scrypt fait ceci:

public static String scrypt(String passwd, int N, int r, int p) { 
    try { 
     byte[] salt = new byte[16]; 
     SecureRandom.getInstance("SHA1PRNG").nextBytes(salt); <--- look 

     byte[] derived = SCrypt.scrypt(passwd.getBytes("UTF-8"), salt, N, r, p, 32); 

(here)

L'appel à nextBytes(salt) fait l'objet SecureRandom à lui-même semence, et cela peut prendre jusqu'à une demi-heure, sur mon exemple Google Compute Engine.

Ce n'est pas lié à Java ou Docker, au lieu, regardez ici: (directement sur la machine hôte, pas à l'intérieur tout conteneur Docker)

# < /dev/random stdbuf -o0 strings --bytes 1 | stdbuf -o0 tr -d '\n\t ' 

Ce lit les caractères aléatoires/dev/random, et je Cela fait maintenant quelques minutes que ça fonctionne, mais seulement 3 caractères ont été sortis jusqu'ici, après quelques minutes. Donc c'est super lent.

En utilisant le moins aléatoire, mais plus rapide,/dev/urandom à la place, alors ceci:

# < /dev/urandom stdbuf -o0 strings --bytes 1 | stdbuf -o0 tr -d '\n\t ' 

imprime immédiatement 99999 caractères.

(je trouve les < /dev/random ... commandes ci-dessus ici: https://unix.stackexchange.com/a/114883/128585)

Sur mon ordinateur portable, cependant, la version /dev/random/ imprime immédiatement 30-40 caractères. Ensuite, il bloque et imprime un ou quelques caractères toutes les 10 secondes environ. Peut-être que cela m'apparaît aléatoire quand j'utilise la souris, le clavier ou le réseau.


Mise à jour

Ce que je l'ai fait: J'utilise maintenant /dev/urandom au lieu - pour autant que je l'ai lu sur Internet, c'est tout à fait bien.

Et j'ai également commencé à utiliser un générateur de nombres aléatoires matériel; apparemment les instances GCE ont ceux-ci.

apt install rng-tools # start using any hardware rand num gen, on Ubuntu