У меня есть приложение, которое порождает несколько кратковременных java (IBM Java 8) программ, в которых им нужно сделать SSL-запрос к серверу. Я сталкиваюсь с проблемой, когда, если я запускаю несколько экземпляров этих программ параллельно, все они занимают одинаковое количество времени и намного больше времени для запуска. В некоторых случаях это почти не экономит время, как если бы я запускал их серийно. Например, если запуск 1 экземпляра занимает 3 секунды, если я запускаю 5 параллельно, все они могут запустить 15 секунд.
Я заметил, что это не проблема для Windows систем. Я не очень знаком с библиотеками безопасности Java, и я нашел этот пост Медленная инициализация SecureRandom , которая, кажется, может быть причиной root, но я не смог получить фрагменты кода работать на Cipher.getInstance()
вызов.
Чтобы продемонстрировать проблему, я перевел ее к следующему фрагменту кода:
import java.io.IOException;
import java.io.InputStreamReader;
import java.lang.management.ManagementFactory;
import java.security.Provider;
import java.security.Security;
import java.util.ArrayList;
import java.util.IntSummaryStatistics;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import javax.crypto.Cipher;
public class CipherTesting {
private static final int DEFAULT = 20;
public static void main( String[] args ) throws Exception {
int num = parseArg( args );
if ( num > 1 ) {
ThreadPoolExecutor executor = (ThreadPoolExecutor) Executors.newFixedThreadPool( num );
List<Future<String>> results = new ArrayList<>();
for ( int i = 0 ; i < num ; i++ ) {
results.add( executor.submit( new Callable<String>() {
@Override
public String call() throws Exception {
String output = execute( "java", "-cp", System.getProperty( "java.class.path" ),
CipherTesting.class.getSimpleName(), "" + 1 );
return output;
}
} ) );
}
executor.shutdown();
executor.awaitTermination( 1, TimeUnit.MINUTES );
List<Integer> times = new ArrayList<>( num );
for ( Future<String> result : results ) {
System.err.println( result.get() );
times.add( Integer.parseInt( result.get().split( ":" )[1].trim() ) );
}
IntSummaryStatistics stats = times.stream().mapToInt( ( x ) -> x ).summaryStatistics();
System.out.println( stats.toString() );
} else {
test();
}
}
private static void test() {
try {
Provider provider = new com.ibm.crypto.plus.provider.IBMJCEPlusFIPS();
Security.insertProviderAt( provider, 1 );
long start = System.currentTimeMillis();
Cipher.getInstance( "AES/CBC/PKCS5Padding", provider.getClass().getSimpleName() );
long end = System.currentTimeMillis();
System.out.println( "JVM" + ManagementFactory.getRuntimeMXBean().getName().replaceAll( "@.+", "" )
+ " Time: " + ( end - start ) );
} catch ( Exception e ) {
e.printStackTrace();
}
}
private static int parseArg( String[] args ) {
if ( args.length == 0 ) {
return DEFAULT;
} else if ( args.length == 1 && args[0].trim().matches( "\\d+" ) ) {
return Integer.parseInt( args[0] );
} else {
System.out.println( "first argument must be a number" );
System.exit( 1 );
return 0;
}
}
private static String execute( String... commands ) throws IOException {
Runtime rt = Runtime.getRuntime();
Process proc = rt.exec( commands );
BufferedReader stdInput = new BufferedReader( new InputStreamReader( proc.getInputStream() ) );
// Read the output from the command
String s = null;
StringBuilder sb = new StringBuilder();
while ( ( s = stdInput.readLine() ) != null ) {
sb.append( s );
}
return sb.toString();
}
}
Если вы не хотите устанавливать IBM Java, я протестировал это в IBM java docker контейнер с помощью команды:
λ docker run -it --rm -v %CD%:/example ibmjava:8 java -cp /example/bin/ CipherTestingSimple 20
JVM76 Time: 15469
JVM77 Time: 17598
JVM80 Time: 15173
JVM55 Time: 14612
JVM78 Time: 16281
JVM58 Time: 17610
JVM49 Time: 17600
JVM48 Time: 16641
JVM75 Time: 16497
JVM54 Time: 19725
JVM59 Time: 16494
JVM61 Time: 17435
JVM56 Time: 18056
JVM73 Time: 16212
JVM81 Time: 18385
JVM74 Time: 17136
JVM60 Time: 17857
JVM57 Time: 17073
JVM72 Time: 16422
JVM79 Time: 15348
IntSummaryStatistics{count=20, sum=337624, min=14612, average=16881.200000, max=19725}
Vs on windows:
JVM5476 Time: 906
JVM34144 Time: 3576
JVM28468 Time: 2751
JVM36084 Time: 2741
JVM16700 Time: 2560
JVM8640 Time: 2454
JVM34112 Time: 3140
JVM33364 Time: 3362
JVM17132 Time: 3999
JVM14160 Time: 3683
JVM11816 Time: 933
JVM33252 Time: 2878
JVM13660 Time: 2315
JVM12068 Time: 2416
JVM24240 Time: 3218
JVM30032 Time: 2965
JVM32316 Time: 3081
JVM14436 Time: 4532
JVM12764 Time: 2793
JVM14692 Time: 962
IntSummaryStatistics{count=20, sum=55265, min=906, average=2763.250000, max=4532}
Я также заметил аналогичную проблему с вызовами SSLSocketFactory.createSocket()
, но надеюсь, что решение этой проблемы шифра также решит это.
Заранее большое спасибо.
Редактировать 1 05/04/2020 @ rustyx
root@9a230345867e:/# cat $JAVA_HOME/lib/security/java.security | grep securerandom.source=
securerandom.source=file:/dev/urandom
Я также убил одного из детей java обрабатывает (после ожидания около 7 секунд) сигнал -3
, который включает вывод:
JVMDUMP039I Processing dump event "user", detail "" at 2020/05/04 16:23:13 - please wait.
JVMDUMP032I JVM requested Java dump using '//javacore.20200504.162313.2812.0001.txt' in response to an event
JVMDUMP010I Java dump written to //javacore.20200504.162313.2812.0001.txt
JVMDUMP013I Processed dump event "user", detail "".
Выход этого javacore можно найти здесь .
Редактировать 2 05/04/2020 Следует отметить, что код, использованный при создании javacore, был закомментирован в следующих строках. Сделал это во время тестирования кода на open java и забыл отменить его. Поведение остается прежним, но прикинуть стоит упомянуть.
Provider provider = new com.ibm.crypto.plus.provider.IBMJCEPlusFIPS();
Security.insertProviderAt( provider, 1 );