SlideShare a Scribd company logo
1 of 45
Download to read offline
Кирилл Голоднов 
Java-разработчик в Яндекс.Метрика 
О бесконечно долгой работе дочерних процессов Java- приложения
3 
Содержание 
•Постановка задачи и описание проблемы 
•Пример воспроизводимого тест-кейса 
•Анализ «зависания» тестового процесса – почему это происходит и как это исправить 
•Почему «зависают» процессы, когда ваш код в этом точно не виноват, и как с этим бороться
4 
Постановка проблемы 
•Есть сервис, который обрабатывает файлы, используя запуск внешних нативных процессов. 
•Проблема: на некоторых файлах дочерний процесс зависает.
5 
Маленькое наблюдение 
•Зависают те процессы, которые выводят в stdout много информации
6 
Как работать с процессом 
•Чаще всего, с процессом работают так: 
Process process = new ProcessBuilder(cmd) 
.environment(envp) 
.directory(dir) 
.start(); 
process.waitFor(); 
if (process.exitValue() == 0) { 
// Getting out and error streams of this process 
InputStream output = process.getInputStream(); 
InputStream error = process.getErrorStream(); 
// Skipped: processing data in process streams 
} else { 
// Skipped: external process returned non-zero code… 
} 
•Вместо ProcessBuilder в данном случае можно просто Runtime.getRuntime().exec(cmd, envp, dir).
7 
Тестовый процесс 
~$ s=$(printf "%-20s" '~') ; echo "${s// /'~'}" 
~~~~~~~~~~~~~~~~~~~~
8 
Тестовый дочерний процесс 
String[] cmd = { 
"/bin/bash", 
"-c", 
"s=$(printf "%-" + n + "s" '~') ; echo "${s// /'~'}"" 
}; 
Process process = new ProcessBuilder(cmd).start(); 
process.waitFor();
9 
Результаты работы процесса 
•Процесс отрабатывает для n = 10, 20, 30, 40, 1000, 10000. 
•Процесс зависает для n = 100000, 200000, 1000000 и т.д.
10 
Что значит «процесс зависает»? 
•Утверждение «процесс зависает для n = 100000, 1000000» является сомнительным как минимум по трём причинам: 
- Проблема остановки неразрешима (Тьюринг, 1936) 
- Процесс просто работает долго? Swapping и/или внутренняя жизнь ОС, внезапно возникшая внешняя нагрузка на сервер и т.д. 
- Процесс-таки отработал (и очень быстро), а Java не смогла получить его поток на чтение (in.readLine())?
11 
Процесс «зависает»? 
•Правильно написать: 
«Исходное Java-приложение испытывает проблемы с производительностью для n = 100000, 1000000, а именно – не успевает отработать за k секунд».
12 
Процесс в ps 
•Это снимок примерно сразу после вызова внешнего процесса: 
~$ ps -ef | grep bash 
kirill 4158 4148 95 01:08 pts/1 00:00:08 /bin/bash -c s=$(printf "%-100000s" '~') ; echo "${s// /'~'}"
13 
Процесс в ps 
•Это снимок через некоторое время: 
~$ ps -ef | grep bash 
kirill 4158 4148 10 01:08 pts/1 00:00:14 /bin/bash -c s=$(printf "%-100000s" '~') ; echo "${s// /'~'}"
14 
Процесс в top 
•Это снимок примерно сразу после вызова внешнего процесса: 
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND 
4158 kirill 20 0 6196 2032 1148 R 94.8 0.1 0:12.39 /bin/bash -c s=$(printf "%-100000s" '~') ; echo "${s// /'~'}"
15 
Процесс в top 
•Это снимок через некоторое время: 
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND 
4158 kirill 20 0 6196 1872 1172 S 0.0 0.1 0:14.06 /bin/bash -c s=$(printf "%-100000s" '~') ; echo "${s// /'~'}"
16 
Процесс в /proc/…/stat 
~$ less /proc/4158/stat 
4158 (bash) S 4148 4148 …
17 
Граничное значение параметра n 
•Процесс в состоянии sleeping (не использует CPU), который непонятно что делает. 
•Граничное значение n – это n = 65535 (двоичный поиск).
18 
Источник проблемы 
•Буфер ограниченного размера, через который общаются процессы.
19 
Что делать с процессом в Java? 
•Надо вычитывать stdout дочернего процесса во время работы. 
•Желательно избежать вызова process.waitFor().
20 
IOUtils.copy из Apache commons-io 
public static long copyLarge(InputStream input, OutputStream output, 
byte[] buffer) throws IOException { 
long count = 0; 
int n = 0; 
while (EOF != (n = input.read(buffer))) { 
output.write(buffer, 0, n); 
count += n; 
} 
return count; 
}
21 
Копируем байты из stdout 
Process process = new ProcessBuilder(cmd).start(); 
ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); 
IOUtils.copy(process.getInputStream(), outputStream); 
process.waitFor();
22 
Забыли про stderr 
String[] cmd = { 
"/bin/bash", 
"-c", 
"s=$(printf "%-" + n + "s" '~') ; echo "${s// /'~'}" >&2" }; 
Process process = new ProcessBuilder(cmd).start(); 
ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); 
IOUtils.copy(process.getInputStream(), outputStream); 
process.waitFor();
23 
IOUtils.copy 
•Отказываемся от IOUtils.copy. 
•Надо придумать что-то другое.
24 
«Новый» метод 
•В Java 8 в java.lang.Process появился «новый» метод: 
public boolean isAlive() { 
try { 
exitValue(); 
return false; 
} catch (IllegalThreadStateException e) { 
return true; 
} 
} 
•В java.lang.UNIXProcess этот метод переопределяется: 
@Override 
public synchronized boolean isAlive() { 
return !hasExited; 
}
25 
Использование «нового» метода 
•Пока процесс жив, вычитываем неблокирующим чтением его stdout и stderr. 
•Между парами чтений делаем паузы.
26 
Неблокирующее перемещение байтов 
private static int moveBytes(InputStream src, OutputStream dst, 
byte[] buffer) throws IOException { 
if (src.available() > 0) { 
int read = src.read(buffer); 
if (read > 0) { 
dst.write(buffer, 0, read); 
} 
return read; 
} else { 
return 0; 
} 
}
27 
Использование moveBytes 
final byte[] buffer = new byte[1 << 15]; 
while (process.isAlive()) { 
while (Math.max( 
moveBytes(process.getInputStream(), outputStream, buffer), 
moveBytes(process.getErrorStream(), errorStream, buffer)) > 0) { 
} 
Thread.sleep(10); 
} 
// Process has terminated, let's read remain data from process: 
while (moveBytes(process.getInputStream(), outputStream, buffer) > 0) { 
} 
while (moveBytes(process.getErrorStream(), errorStream, buffer) > 0) { 
}
28 
Чтение через дополнительные потоки 
Pair<InputStream, ByteArrayOutputStream>[] pairs = new Pair[]{ 
Pair.of(process.getInputStream(), new ByteArrayOutputStream()), 
Pair.of(process.getErrorStream(), new ByteArrayOutputStream()) 
}; 
for (Pair<InputStream, ByteArrayOutputStream> pair : pairs) { 
executor.submit(() -> IOUtils.copy(pair.getKey(), pair.getValue())); 
} 
process.waitFor();
29 
Без pipe проблемы нет 
•Этот процесс работает нормально: 
new ProcessBuilder(cmd). 
redirectOutput(ProcessBuilder.Redirect.INHERIT). 
redirectError(ProcessBuilder.Redirect.INHERIT). 
start(); 
•Если использовать вместо INHERIT – WRITE/APPEND, то тоже всё хорошо.
30 
Пойдём далее 
•Пусть проблема с буфером уже решена. 
•Любой процесс, получающий и обрабатывающий данные из интернета, может зависнуть с достаточно большой вероятностью.
31 
Пример с phantomjs 
~$ ps -ef | grep phantomjs 
metrika 2516 789 0 Sep17 ? 00:00:00 /usr/lib/phantomjs … 
metrika 5930 2516 0 Sep17 ? 00:00:00 /usr/lib/phantomjs … 
metrika 16135 1 0 May19 ? 00:00:00 /usr/lib/phantomjs … 
metrika 17031 16135 0 May19 ? 00:00:00 /usr/lib/phantomjs …
32 
Пример с urllib.open try: usock = urllib.urlopen(c_url) except: out.write('Error while opening ' + c_url)
33 
Причины зависания процессов 
•Ошибки в программном коде внешних программ и библиотек. 
•Ошибки того, кто вызывает процесс.
34 
Последствия зависания процессов 
•Перестаёт контролироваться Java-процессом. 
•Если таких процессов много, растёт длина CPU Run Queue. Увеличиваются затраты памяти, возможен swapping.
35 
Пример с ZooKeeper 
if (lock.acquire(5, TimeUnit.SECONDS)) { 
try { 
<hanging or looping process> 
} finally { 
lock.release(); 
} 
}
36 
Что же делать? 
•Построить свой алгоритм, который будет определять, завис ли процесс, на основе его состояний (D/R/S/T/Z). 
•Снимать процесс по таймауту, из Java.
37 
Apache commons-exec 
•Библиотека от Apache для работы с процессами. 
•Совместима с JDK 1.3.
38 
commons-exec: CommandLine 
CommandLine cmd = CommandLine.parse("/bin/bash"); 
// Failed to parse string: "/bin/bash -c 'echo 1'" 
cmd.addArguments(new String[] { "-c", 
"s=$(printf "%-" + n + "s" '~') ; echo "${s// /'~'}"" }, 
false); 
DefaultExecutor executor = new DefaultExecutor(); 
executor.setExitValue(0);
39 
commons-exec: PumpStreamHandler 
ByteArrayOutputStream out = new ByteArrayOutputStream(); 
ByteArrayOutputStream err = new ByteArrayOutputStream(); 
PumpStreamHandler pumpStreamHandler = new PumpStreamHandler(out, err); 
executor.setStreamHandler(pumpStreamHandler);
40 
commons-exec: ExecuteWatchdog 
ExecuteWatchdog watchdog = new ExecuteWatchdog(10000); 
executor.setWatchdog(watchdog); 
executor.execute(cmd);
41 
Apache commons-exec 
•Справляется с большинством задач, связанных с процессами. 
•Хорошо спроектирована по архитектуре кода.
42 
Apache commons-exec 
•Работает через Process.destroy(). 
•От ExecuteWatchdog сложно наследоваться.
43 
Введём метод destroyForcibly() 
private static boolean destroyForcibly(Process process) { 
if (process != null && process.isAlive()) { 
process.destroyForcibly(); 
return true; 
} 
return false; 
}
44 
Используем метод destroyForcibly() 
try { 
// Skipped: process initialization 
while (process.isAlive() && (timeout <= 0 || System.currentTimeMillis() < finish)) { 
// Skipped: get data from process streams (stdout and stderr) 
} 
// Skipped: get data from process streams for the last time 
if (destroyForcibly(process)) { 
throw new TimeoutException("Process " + Arrays.toString(cmd) 
+ " has not finished its work and was destroyed" 
+ " after " + (System.currentTimeMillis() - start) + " ms"); 
} 
} catch (InterruptedException | IOException | RuntimeException | Error e) { 
destroyForcibly(process); 
throw e; 
}
Кирилл Голоднов 
Java-разработчик 
kyrylhol@yandex-team.ru 
http://kyryloholodnov.wordpress.com

More Related Content

What's hot

Практика Lock-free. RealTime-сервер
Практика Lock-free. RealTime-серверПрактика Lock-free. RealTime-сервер
Практика Lock-free. RealTime-серверPlatonov Sergey
 
Мониторинг ожиданий в PostgreSQL / Курбангалиев Ильдус (Postgres Professional)
Мониторинг ожиданий в PostgreSQL / Курбангалиев Ильдус (Postgres Professional)Мониторинг ожиданий в PostgreSQL / Курбангалиев Ильдус (Postgres Professional)
Мониторинг ожиданий в PostgreSQL / Курбангалиев Ильдус (Postgres Professional)Ontico
 
ПВТ - осень 2014 - Лекция 6 - Атомарные операции. Внеочередное выполнение инс...
ПВТ - осень 2014 - Лекция 6 - Атомарные операции. Внеочередное выполнение инс...ПВТ - осень 2014 - Лекция 6 - Атомарные операции. Внеочередное выполнение инс...
ПВТ - осень 2014 - Лекция 6 - Атомарные операции. Внеочередное выполнение инс...Alexey Paznikov
 
Многопоточность, работа с сетью (Lecture 12 – multithreading, network)
Многопоточность, работа с сетью (Lecture 12 – multithreading, network)Многопоточность, работа с сетью (Lecture 12 – multithreading, network)
Многопоточность, работа с сетью (Lecture 12 – multithreading, network)Noveo
 
Tdd webpack + testem + mocha + chai
Tdd webpack + testem + mocha + chaiTdd webpack + testem + mocha + chai
Tdd webpack + testem + mocha + chaiMichael Chernobrov
 
JPoint 2016 - Etudes of DIY Java profiler
JPoint 2016 - Etudes of DIY Java profilerJPoint 2016 - Etudes of DIY Java profiler
JPoint 2016 - Etudes of DIY Java profilerAnton Arhipov
 
Caching data outside Java Heap and using Shared Memory in Java
Caching data outside Java Heap and using Shared Memory in JavaCaching data outside Java Heap and using Shared Memory in Java
Caching data outside Java Heap and using Shared Memory in JavaAndrei Pangin
 
Отладка и устранение проблем в PostgreSQL Streaming Replication.
Отладка и устранение проблем в PostgreSQL Streaming Replication.Отладка и устранение проблем в PostgreSQL Streaming Replication.
Отладка и устранение проблем в PostgreSQL Streaming Replication.Alexey Lesovsky
 
2014-10-04 02 Владислав Безверхий. Mocha - покрой frontend по полной
2014-10-04 02 Владислав Безверхий. Mocha - покрой frontend по полной2014-10-04 02 Владислав Безверхий. Mocha - покрой frontend по полной
2014-10-04 02 Владислав Безверхий. Mocha - покрой frontend по полнойОмские ИТ-субботники
 
Михаил Давыдов - JavaScript. Асинхронность
Михаил Давыдов - JavaScript. АсинхронностьМихаил Давыдов - JavaScript. Асинхронность
Михаил Давыдов - JavaScript. АсинхронностьYandex
 
Web осень 2013 лекция 2
Web осень 2013 лекция 2Web осень 2013 лекция 2
Web осень 2013 лекция 2Technopark
 
Библиотеки для передачи данных (Lecture 13 – multithreading, network (libs))
Библиотеки для передачи данных (Lecture 13 – multithreading, network (libs))Библиотеки для передачи данных (Lecture 13 – multithreading, network (libs))
Библиотеки для передачи данных (Lecture 13 – multithreading, network (libs))Noveo
 
PostgreSQL Vacuum: Nine Circles of Hell
PostgreSQL Vacuum: Nine Circles of HellPostgreSQL Vacuum: Nine Circles of Hell
PostgreSQL Vacuum: Nine Circles of HellAlexey Lesovsky
 
ПВТ - весна 2015 - Лекция 5. Многопоточное программирование в С++. Синхрониза...
ПВТ - весна 2015 - Лекция 5. Многопоточное программирование в С++. Синхрониза...ПВТ - весна 2015 - Лекция 5. Многопоточное программирование в С++. Синхрониза...
ПВТ - весна 2015 - Лекция 5. Многопоточное программирование в С++. Синхрониза...Alexey Paznikov
 
Михаил Давыдов — JavaScript: Асинхронность
Михаил Давыдов — JavaScript: АсинхронностьМихаил Давыдов — JavaScript: Асинхронность
Михаил Давыдов — JavaScript: АсинхронностьYandex
 
ПВТ - осень 2014 - Лекция 5 - Многопоточное программирование в языке С++. Р...
ПВТ - осень 2014 - Лекция 5 - Многопоточное программирование в языке С++.   Р...ПВТ - осень 2014 - Лекция 5 - Многопоточное программирование в языке С++.   Р...
ПВТ - осень 2014 - Лекция 5 - Многопоточное программирование в языке С++. Р...Alexey Paznikov
 
RDSDataSource: Чистые тесты на Swift
RDSDataSource: Чистые тесты на SwiftRDSDataSource: Чистые тесты на Swift
RDSDataSource: Чистые тесты на SwiftRAMBLER&Co
 
Asynchrony and coroutines
Asynchrony and coroutinesAsynchrony and coroutines
Asynchrony and coroutinescorehard_by
 

What's hot (20)

Практика Lock-free. RealTime-сервер
Практика Lock-free. RealTime-серверПрактика Lock-free. RealTime-сервер
Практика Lock-free. RealTime-сервер
 
Мониторинг ожиданий в PostgreSQL / Курбангалиев Ильдус (Postgres Professional)
Мониторинг ожиданий в PostgreSQL / Курбангалиев Ильдус (Postgres Professional)Мониторинг ожиданий в PostgreSQL / Курбангалиев Ильдус (Postgres Professional)
Мониторинг ожиданий в PostgreSQL / Курбангалиев Ильдус (Postgres Professional)
 
ПВТ - осень 2014 - Лекция 6 - Атомарные операции. Внеочередное выполнение инс...
ПВТ - осень 2014 - Лекция 6 - Атомарные операции. Внеочередное выполнение инс...ПВТ - осень 2014 - Лекция 6 - Атомарные операции. Внеочередное выполнение инс...
ПВТ - осень 2014 - Лекция 6 - Атомарные операции. Внеочередное выполнение инс...
 
бегун
бегунбегун
бегун
 
Многопоточность, работа с сетью (Lecture 12 – multithreading, network)
Многопоточность, работа с сетью (Lecture 12 – multithreading, network)Многопоточность, работа с сетью (Lecture 12 – multithreading, network)
Многопоточность, работа с сетью (Lecture 12 – multithreading, network)
 
Tdd webpack + testem + mocha + chai
Tdd webpack + testem + mocha + chaiTdd webpack + testem + mocha + chai
Tdd webpack + testem + mocha + chai
 
JPoint 2016 - Etudes of DIY Java profiler
JPoint 2016 - Etudes of DIY Java profilerJPoint 2016 - Etudes of DIY Java profiler
JPoint 2016 - Etudes of DIY Java profiler
 
Caching data outside Java Heap and using Shared Memory in Java
Caching data outside Java Heap and using Shared Memory in JavaCaching data outside Java Heap and using Shared Memory in Java
Caching data outside Java Heap and using Shared Memory in Java
 
Отладка и устранение проблем в PostgreSQL Streaming Replication.
Отладка и устранение проблем в PostgreSQL Streaming Replication.Отладка и устранение проблем в PostgreSQL Streaming Replication.
Отладка и устранение проблем в PostgreSQL Streaming Replication.
 
2014-10-04 02 Владислав Безверхий. Mocha - покрой frontend по полной
2014-10-04 02 Владислав Безверхий. Mocha - покрой frontend по полной2014-10-04 02 Владислав Безверхий. Mocha - покрой frontend по полной
2014-10-04 02 Владислав Безверхий. Mocha - покрой frontend по полной
 
Михаил Давыдов - JavaScript. Асинхронность
Михаил Давыдов - JavaScript. АсинхронностьМихаил Давыдов - JavaScript. Асинхронность
Михаил Давыдов - JavaScript. Асинхронность
 
Web осень 2013 лекция 2
Web осень 2013 лекция 2Web осень 2013 лекция 2
Web осень 2013 лекция 2
 
Библиотеки для передачи данных (Lecture 13 – multithreading, network (libs))
Библиотеки для передачи данных (Lecture 13 – multithreading, network (libs))Библиотеки для передачи данных (Lecture 13 – multithreading, network (libs))
Библиотеки для передачи данных (Lecture 13 – multithreading, network (libs))
 
PostgreSQL Vacuum: Nine Circles of Hell
PostgreSQL Vacuum: Nine Circles of HellPostgreSQL Vacuum: Nine Circles of Hell
PostgreSQL Vacuum: Nine Circles of Hell
 
ПВТ - весна 2015 - Лекция 5. Многопоточное программирование в С++. Синхрониза...
ПВТ - весна 2015 - Лекция 5. Многопоточное программирование в С++. Синхрониза...ПВТ - весна 2015 - Лекция 5. Многопоточное программирование в С++. Синхрониза...
ПВТ - весна 2015 - Лекция 5. Многопоточное программирование в С++. Синхрониза...
 
Михаил Давыдов — JavaScript: Асинхронность
Михаил Давыдов — JavaScript: АсинхронностьМихаил Давыдов — JavaScript: Асинхронность
Михаил Давыдов — JavaScript: Асинхронность
 
ПВТ - осень 2014 - Лекция 5 - Многопоточное программирование в языке С++. Р...
ПВТ - осень 2014 - Лекция 5 - Многопоточное программирование в языке С++.   Р...ПВТ - осень 2014 - Лекция 5 - Многопоточное программирование в языке С++.   Р...
ПВТ - осень 2014 - Лекция 5 - Многопоточное программирование в языке С++. Р...
 
Java 8 puzzlers
Java 8 puzzlersJava 8 puzzlers
Java 8 puzzlers
 
RDSDataSource: Чистые тесты на Swift
RDSDataSource: Чистые тесты на SwiftRDSDataSource: Чистые тесты на Swift
RDSDataSource: Чистые тесты на Swift
 
Asynchrony and coroutines
Asynchrony and coroutinesAsynchrony and coroutines
Asynchrony and coroutines
 

Similar to JavaDay'14

Асинхронность и сопрограммы
Асинхронность и сопрограммыАсинхронность и сопрограммы
Асинхронность и сопрограммыPlatonov Sergey
 
Mike ponomarenko java17-fork-v1.2
Mike ponomarenko java17-fork-v1.2Mike ponomarenko java17-fork-v1.2
Mike ponomarenko java17-fork-v1.2Alex Tumanoff
 
Инструментация среды исполнения в арсенале тестировщика
Инструментация среды исполнения в арсенале тестировщикаИнструментация среды исполнения в арсенале тестировщика
Инструментация среды исполнения в арсенале тестировщикаSQALab
 
PHP Tricks
PHP TricksPHP Tricks
PHP TricksBlackFan
 
статический анализ кода
статический анализ кодастатический анализ кода
статический анализ кодаAndrey Karpov
 
Статический анализ кода
Статический анализ кода Статический анализ кода
Статический анализ кода Pavel Tsukanov
 
Сергей Шамбир, Адаптация Promise/A+ для взаимодействия между C++ и Javascript
Сергей Шамбир, Адаптация Promise/A+ для взаимодействия между C++ и JavascriptСергей Шамбир, Адаптация Promise/A+ для взаимодействия между C++ и Javascript
Сергей Шамбир, Адаптация Promise/A+ для взаимодействия между C++ и JavascriptSergey Platonov
 
Cache2012 administrationbasics
Cache2012 administrationbasicsCache2012 administrationbasics
Cache2012 administrationbasicsDenis Pavlov
 
20130429 dynamic c_c++_program_analysis-alexey_samsonov
20130429 dynamic c_c++_program_analysis-alexey_samsonov20130429 dynamic c_c++_program_analysis-alexey_samsonov
20130429 dynamic c_c++_program_analysis-alexey_samsonovComputer Science Club
 
Node.js введение в технологию, КПИ #ITmeetingKPI
Node.js введение в технологию, КПИ  #ITmeetingKPINode.js введение в технологию, КПИ  #ITmeetingKPI
Node.js введение в технологию, КПИ #ITmeetingKPITimur Shemsedinov
 
Григорий Демченко — Асинхронное программирование и сопрограммы
Григорий Демченко — Асинхронное программирование и сопрограммыГригорий Демченко — Асинхронное программирование и сопрограммы
Григорий Демченко — Асинхронное программирование и сопрограммыYandex
 
Инструментируй это
Инструментируй этоИнструментируй это
Инструментируй этоRoman Dvornov
 
Лекция 6. Стандарт OpenMP
Лекция 6. Стандарт OpenMPЛекция 6. Стандарт OpenMP
Лекция 6. Стандарт OpenMPMikhail Kurnosov
 
Web осень 2013 лекция 5
Web осень 2013 лекция 5Web осень 2013 лекция 5
Web осень 2013 лекция 5Technopark
 
Android: Как написать приложение, которое не тормозит
Android: Как  написать приложение, которое не тормозитAndroid: Как  написать приложение, которое не тормозит
Android: Как написать приложение, которое не тормозитElena Kotina
 
Семинар 1. Многопоточное программирование на OpenMP (часть 1)
Семинар 1. Многопоточное программирование на OpenMP (часть 1)Семинар 1. Многопоточное программирование на OpenMP (часть 1)
Семинар 1. Многопоточное программирование на OpenMP (часть 1)Mikhail Kurnosov
 

Similar to JavaDay'14 (20)

Parallel STL
Parallel STLParallel STL
Parallel STL
 
Асинхронность и сопрограммы
Асинхронность и сопрограммыАсинхронность и сопрограммы
Асинхронность и сопрограммы
 
Mike ponomarenko java17-fork-v1.2
Mike ponomarenko java17-fork-v1.2Mike ponomarenko java17-fork-v1.2
Mike ponomarenko java17-fork-v1.2
 
Инструментация среды исполнения в арсенале тестировщика
Инструментация среды исполнения в арсенале тестировщикаИнструментация среды исполнения в арсенале тестировщика
Инструментация среды исполнения в арсенале тестировщика
 
PHP Tricks
PHP TricksPHP Tricks
PHP Tricks
 
статический анализ кода
статический анализ кодастатический анализ кода
статический анализ кода
 
Статический анализ кода
Статический анализ кода Статический анализ кода
Статический анализ кода
 
Сергей Шамбир, Адаптация Promise/A+ для взаимодействия между C++ и Javascript
Сергей Шамбир, Адаптация Promise/A+ для взаимодействия между C++ и JavascriptСергей Шамбир, Адаптация Promise/A+ для взаимодействия между C++ и Javascript
Сергей Шамбир, Адаптация Promise/A+ для взаимодействия между C++ и Javascript
 
Cache2012 administrationbasics
Cache2012 administrationbasicsCache2012 administrationbasics
Cache2012 administrationbasics
 
20130429 dynamic c_c++_program_analysis-alexey_samsonov
20130429 dynamic c_c++_program_analysis-alexey_samsonov20130429 dynamic c_c++_program_analysis-alexey_samsonov
20130429 dynamic c_c++_program_analysis-alexey_samsonov
 
Node.js введение в технологию, КПИ #ITmeetingKPI
Node.js введение в технологию, КПИ  #ITmeetingKPINode.js введение в технологию, КПИ  #ITmeetingKPI
Node.js введение в технологию, КПИ #ITmeetingKPI
 
What's in a metrics? Ruby Russia 2018
What's in a metrics? Ruby Russia 2018What's in a metrics? Ruby Russia 2018
What's in a metrics? Ruby Russia 2018
 
Григорий Демченко — Асинхронное программирование и сопрограммы
Григорий Демченко — Асинхронное программирование и сопрограммыГригорий Демченко — Асинхронное программирование и сопрограммы
Григорий Демченко — Асинхронное программирование и сопрограммы
 
Инструментируй это
Инструментируй этоИнструментируй это
Инструментируй это
 
Async
AsyncAsync
Async
 
Luxoft async.net
Luxoft async.netLuxoft async.net
Luxoft async.net
 
Лекция 6. Стандарт OpenMP
Лекция 6. Стандарт OpenMPЛекция 6. Стандарт OpenMP
Лекция 6. Стандарт OpenMP
 
Web осень 2013 лекция 5
Web осень 2013 лекция 5Web осень 2013 лекция 5
Web осень 2013 лекция 5
 
Android: Как написать приложение, которое не тормозит
Android: Как  написать приложение, которое не тормозитAndroid: Как  написать приложение, которое не тормозит
Android: Как написать приложение, которое не тормозит
 
Семинар 1. Многопоточное программирование на OpenMP (часть 1)
Семинар 1. Многопоточное программирование на OpenMP (часть 1)Семинар 1. Многопоточное программирование на OpenMP (часть 1)
Семинар 1. Многопоточное программирование на OpenMP (часть 1)
 

JavaDay'14

  • 1.
  • 2. Кирилл Голоднов Java-разработчик в Яндекс.Метрика О бесконечно долгой работе дочерних процессов Java- приложения
  • 3. 3 Содержание •Постановка задачи и описание проблемы •Пример воспроизводимого тест-кейса •Анализ «зависания» тестового процесса – почему это происходит и как это исправить •Почему «зависают» процессы, когда ваш код в этом точно не виноват, и как с этим бороться
  • 4. 4 Постановка проблемы •Есть сервис, который обрабатывает файлы, используя запуск внешних нативных процессов. •Проблема: на некоторых файлах дочерний процесс зависает.
  • 5. 5 Маленькое наблюдение •Зависают те процессы, которые выводят в stdout много информации
  • 6. 6 Как работать с процессом •Чаще всего, с процессом работают так: Process process = new ProcessBuilder(cmd) .environment(envp) .directory(dir) .start(); process.waitFor(); if (process.exitValue() == 0) { // Getting out and error streams of this process InputStream output = process.getInputStream(); InputStream error = process.getErrorStream(); // Skipped: processing data in process streams } else { // Skipped: external process returned non-zero code… } •Вместо ProcessBuilder в данном случае можно просто Runtime.getRuntime().exec(cmd, envp, dir).
  • 7. 7 Тестовый процесс ~$ s=$(printf "%-20s" '~') ; echo "${s// /'~'}" ~~~~~~~~~~~~~~~~~~~~
  • 8. 8 Тестовый дочерний процесс String[] cmd = { "/bin/bash", "-c", "s=$(printf "%-" + n + "s" '~') ; echo "${s// /'~'}"" }; Process process = new ProcessBuilder(cmd).start(); process.waitFor();
  • 9. 9 Результаты работы процесса •Процесс отрабатывает для n = 10, 20, 30, 40, 1000, 10000. •Процесс зависает для n = 100000, 200000, 1000000 и т.д.
  • 10. 10 Что значит «процесс зависает»? •Утверждение «процесс зависает для n = 100000, 1000000» является сомнительным как минимум по трём причинам: - Проблема остановки неразрешима (Тьюринг, 1936) - Процесс просто работает долго? Swapping и/или внутренняя жизнь ОС, внезапно возникшая внешняя нагрузка на сервер и т.д. - Процесс-таки отработал (и очень быстро), а Java не смогла получить его поток на чтение (in.readLine())?
  • 11. 11 Процесс «зависает»? •Правильно написать: «Исходное Java-приложение испытывает проблемы с производительностью для n = 100000, 1000000, а именно – не успевает отработать за k секунд».
  • 12. 12 Процесс в ps •Это снимок примерно сразу после вызова внешнего процесса: ~$ ps -ef | grep bash kirill 4158 4148 95 01:08 pts/1 00:00:08 /bin/bash -c s=$(printf "%-100000s" '~') ; echo "${s// /'~'}"
  • 13. 13 Процесс в ps •Это снимок через некоторое время: ~$ ps -ef | grep bash kirill 4158 4148 10 01:08 pts/1 00:00:14 /bin/bash -c s=$(printf "%-100000s" '~') ; echo "${s// /'~'}"
  • 14. 14 Процесс в top •Это снимок примерно сразу после вызова внешнего процесса: PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND 4158 kirill 20 0 6196 2032 1148 R 94.8 0.1 0:12.39 /bin/bash -c s=$(printf "%-100000s" '~') ; echo "${s// /'~'}"
  • 15. 15 Процесс в top •Это снимок через некоторое время: PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND 4158 kirill 20 0 6196 1872 1172 S 0.0 0.1 0:14.06 /bin/bash -c s=$(printf "%-100000s" '~') ; echo "${s// /'~'}"
  • 16. 16 Процесс в /proc/…/stat ~$ less /proc/4158/stat 4158 (bash) S 4148 4148 …
  • 17. 17 Граничное значение параметра n •Процесс в состоянии sleeping (не использует CPU), который непонятно что делает. •Граничное значение n – это n = 65535 (двоичный поиск).
  • 18. 18 Источник проблемы •Буфер ограниченного размера, через который общаются процессы.
  • 19. 19 Что делать с процессом в Java? •Надо вычитывать stdout дочернего процесса во время работы. •Желательно избежать вызова process.waitFor().
  • 20. 20 IOUtils.copy из Apache commons-io public static long copyLarge(InputStream input, OutputStream output, byte[] buffer) throws IOException { long count = 0; int n = 0; while (EOF != (n = input.read(buffer))) { output.write(buffer, 0, n); count += n; } return count; }
  • 21. 21 Копируем байты из stdout Process process = new ProcessBuilder(cmd).start(); ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); IOUtils.copy(process.getInputStream(), outputStream); process.waitFor();
  • 22. 22 Забыли про stderr String[] cmd = { "/bin/bash", "-c", "s=$(printf "%-" + n + "s" '~') ; echo "${s// /'~'}" >&2" }; Process process = new ProcessBuilder(cmd).start(); ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); IOUtils.copy(process.getInputStream(), outputStream); process.waitFor();
  • 23. 23 IOUtils.copy •Отказываемся от IOUtils.copy. •Надо придумать что-то другое.
  • 24. 24 «Новый» метод •В Java 8 в java.lang.Process появился «новый» метод: public boolean isAlive() { try { exitValue(); return false; } catch (IllegalThreadStateException e) { return true; } } •В java.lang.UNIXProcess этот метод переопределяется: @Override public synchronized boolean isAlive() { return !hasExited; }
  • 25. 25 Использование «нового» метода •Пока процесс жив, вычитываем неблокирующим чтением его stdout и stderr. •Между парами чтений делаем паузы.
  • 26. 26 Неблокирующее перемещение байтов private static int moveBytes(InputStream src, OutputStream dst, byte[] buffer) throws IOException { if (src.available() > 0) { int read = src.read(buffer); if (read > 0) { dst.write(buffer, 0, read); } return read; } else { return 0; } }
  • 27. 27 Использование moveBytes final byte[] buffer = new byte[1 << 15]; while (process.isAlive()) { while (Math.max( moveBytes(process.getInputStream(), outputStream, buffer), moveBytes(process.getErrorStream(), errorStream, buffer)) > 0) { } Thread.sleep(10); } // Process has terminated, let's read remain data from process: while (moveBytes(process.getInputStream(), outputStream, buffer) > 0) { } while (moveBytes(process.getErrorStream(), errorStream, buffer) > 0) { }
  • 28. 28 Чтение через дополнительные потоки Pair<InputStream, ByteArrayOutputStream>[] pairs = new Pair[]{ Pair.of(process.getInputStream(), new ByteArrayOutputStream()), Pair.of(process.getErrorStream(), new ByteArrayOutputStream()) }; for (Pair<InputStream, ByteArrayOutputStream> pair : pairs) { executor.submit(() -> IOUtils.copy(pair.getKey(), pair.getValue())); } process.waitFor();
  • 29. 29 Без pipe проблемы нет •Этот процесс работает нормально: new ProcessBuilder(cmd). redirectOutput(ProcessBuilder.Redirect.INHERIT). redirectError(ProcessBuilder.Redirect.INHERIT). start(); •Если использовать вместо INHERIT – WRITE/APPEND, то тоже всё хорошо.
  • 30. 30 Пойдём далее •Пусть проблема с буфером уже решена. •Любой процесс, получающий и обрабатывающий данные из интернета, может зависнуть с достаточно большой вероятностью.
  • 31. 31 Пример с phantomjs ~$ ps -ef | grep phantomjs metrika 2516 789 0 Sep17 ? 00:00:00 /usr/lib/phantomjs … metrika 5930 2516 0 Sep17 ? 00:00:00 /usr/lib/phantomjs … metrika 16135 1 0 May19 ? 00:00:00 /usr/lib/phantomjs … metrika 17031 16135 0 May19 ? 00:00:00 /usr/lib/phantomjs …
  • 32. 32 Пример с urllib.open try: usock = urllib.urlopen(c_url) except: out.write('Error while opening ' + c_url)
  • 33. 33 Причины зависания процессов •Ошибки в программном коде внешних программ и библиотек. •Ошибки того, кто вызывает процесс.
  • 34. 34 Последствия зависания процессов •Перестаёт контролироваться Java-процессом. •Если таких процессов много, растёт длина CPU Run Queue. Увеличиваются затраты памяти, возможен swapping.
  • 35. 35 Пример с ZooKeeper if (lock.acquire(5, TimeUnit.SECONDS)) { try { <hanging or looping process> } finally { lock.release(); } }
  • 36. 36 Что же делать? •Построить свой алгоритм, который будет определять, завис ли процесс, на основе его состояний (D/R/S/T/Z). •Снимать процесс по таймауту, из Java.
  • 37. 37 Apache commons-exec •Библиотека от Apache для работы с процессами. •Совместима с JDK 1.3.
  • 38. 38 commons-exec: CommandLine CommandLine cmd = CommandLine.parse("/bin/bash"); // Failed to parse string: "/bin/bash -c 'echo 1'" cmd.addArguments(new String[] { "-c", "s=$(printf "%-" + n + "s" '~') ; echo "${s// /'~'}"" }, false); DefaultExecutor executor = new DefaultExecutor(); executor.setExitValue(0);
  • 39. 39 commons-exec: PumpStreamHandler ByteArrayOutputStream out = new ByteArrayOutputStream(); ByteArrayOutputStream err = new ByteArrayOutputStream(); PumpStreamHandler pumpStreamHandler = new PumpStreamHandler(out, err); executor.setStreamHandler(pumpStreamHandler);
  • 40. 40 commons-exec: ExecuteWatchdog ExecuteWatchdog watchdog = new ExecuteWatchdog(10000); executor.setWatchdog(watchdog); executor.execute(cmd);
  • 41. 41 Apache commons-exec •Справляется с большинством задач, связанных с процессами. •Хорошо спроектирована по архитектуре кода.
  • 42. 42 Apache commons-exec •Работает через Process.destroy(). •От ExecuteWatchdog сложно наследоваться.
  • 43. 43 Введём метод destroyForcibly() private static boolean destroyForcibly(Process process) { if (process != null && process.isAlive()) { process.destroyForcibly(); return true; } return false; }
  • 44. 44 Используем метод destroyForcibly() try { // Skipped: process initialization while (process.isAlive() && (timeout <= 0 || System.currentTimeMillis() < finish)) { // Skipped: get data from process streams (stdout and stderr) } // Skipped: get data from process streams for the last time if (destroyForcibly(process)) { throw new TimeoutException("Process " + Arrays.toString(cmd) + " has not finished its work and was destroyed" + " after " + (System.currentTimeMillis() - start) + " ms"); } } catch (InterruptedException | IOException | RuntimeException | Error e) { destroyForcibly(process); throw e; }
  • 45. Кирилл Голоднов Java-разработчик kyrylhol@yandex-team.ru http://kyryloholodnov.wordpress.com