2016-05-14 1 views
1

[Summary & Ответ: Очевидно, проблема в том, что для генерации генератора случайных чисел требуется много времени. См. Мой ответ ниже. ]Почему первый вызов scrypt() использует только 1% процессор и занимает полчаса в GCE?

В Google Compute Engine (GCE) самый первый запрос, который приложение моей виртуальной машины Java делает для функции хэширования scrypt, занимает много времени, потому что код еще не был скомпилирован Just-In-Time. Поэтому я разогреваю скриншот, создавая нужный код scrypt("pswd", 2,1,1) при запуске сервера. Однако, что происходит, это то, что CPU поднимается до 300% +, остается там на 10-20 секунд, а затем падает до 1%, хотя запрос на scrypt() еще не закончен. Теперь процессор остается на уровне 1% в течение многих минут (до получаса с 2 GCE vCPU), пока не будет завершен scrypt().

Почему это странное поведение?

Почему не будет scrypt() продолжать работать на 300% до тех пор, пока это не будет выполнено? Это не заканчивается память. Посмотрите на статистику Докера немного ниже.

После первого запроса scrypt() последующие запросы заканчиваются «немедленно». Например, это: SCryptUtil.scrypt("pswd", 65536, 8, 1) занимает < 0,2 секунды, хотя это делает гораздо больше работы, чем: SCryptUtil.scrypt("pswd", 2, 1, 1) , который (как уже упоминалось) мой самый первый Scrypt() вызов и обычно занимает несколько минут, с 4 GCE VCPU - и часто около получаса, с 2 GCE vCPU.

Я использую экземпляр GCE с 4 vCPU, 3.6 GB RAM. Докер 1.11.1. OpenJDK 1.8.0_77. В контейнере Docker Alpine Linux 3.3, хостеле Ubuntu 16.04 Docker. Невозможно воспроизвести это на моем ноутбуке; на моем ноутбуке, scrypt всегда быстр, не нуждается в разминке.

docker stats, через 5-10 секунд: (теперь edp_play_1, линия 2, использует 300% + ЦП)

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 через пол минуты: (теперь edp_play_1 использует только 0,97% 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.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 

Если вы хотите проверить в Scala & SBT, это то, что происходит у меня в 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 

Примечание: это не связано с докером. Я только что тестировал вне Docker, с openjdk 8, установленным непосредственно на экземпляре GCE, и результат тот же: scrypt(..) занимает 3 минуты в первый раз, но CPU составляет 90-100% бездействия. После этого запросы на скрипинг завершаются немедленно.

ответ

1

Проблема в том, что для генерации генератора случайных чисел требуется много времени. Scrypt делает это:

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)

Вызов nextBytes(salt) заставляет объект SecureRandom семян себя, и это занимает до получаса, в моем случае Google Compute Engine.

Это не связано с Java или Докер, вместо этого, смотрите здесь: (непосредственно на хост-машине, а не в какой-либо емкости Докер)

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

Это читает случайные символы из/разработчика/случайны, и я «У него было несколько минут, но через несколько минут вышло всего 3 символа. Так что это супер медленно.

Используя менее случайный, но быстрее,/Dev/urandom вместо этого, то это:

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

отпечатков 99999 символов сразу.

(я нашел выше < /dev/random ... команд здесь: https://unix.stackexchange.com/a/114883/128585)

На моем ноутбуке, однако, версия /dev/random/ сразу печатает 30-40 символов. Затем он блокирует и печатает один или несколько символов каждые 10 секунд или около того. Возможно, он получает случайность от меня, когда я использую мышь, клавиатуру или сеть.


Update

Что я сделал: Я теперь с помощью /dev/urandom вместо этого - насколько я читал в Интернете, что это совершенно нормально.

И я также начал использовать генератор случайных чисел оборудования; по-видимому, экземпляры GCE имеют это.

Смежные вопросы