Вычисления в гриде или майнинг «красивых» хешей, такую задачу я решил проверить для вычисления в гриде Apache Ignite. Ранее я пробовал и писал Ignite как Sql БД, но для себя я понял что это пока удобная опция в этой вычислительной системе (к SQL на Ignite я еще вернусь), именно так как вычислительная система я себе ее представляю с возможностью быстрой и не дорогой масштабируемостью. Вот это и посмотрим, как можно быстро и недорого нарастить вычисления, или нет, например нарастить вычисления 1 мощного компьютера добавляя к нему несколько слабых.
Задача такая, для блоков данных-транзакций вычислить хеш, но не простой, а с некоторой сложностью, например содержащий подряд семь символов 'А'. Что бы это было возможно, к блоку данных будем приклеивать постоянно увеличивающейся в цикле число, пока не будет получен хеш заданной сложности. Да, это похоже как делают майнеры добывая крипто валюту. Поскольку у меня несколько транзакций я их буду отправлять в вычислительный грид. Вычислительный грид это ноды Ignite, запущенные экземпляры на разных компьютерах, они сами себя обнаруживают и образуют грид. Распределяться эти вычисления между нодами будут равномерно и автоматически.
И так мои вычислительные мощности, в домашних условиях это:
Intel Core I5-7400 3,5 Ггц 8 Гб. ОЗУ
Intel Core I3-6100 3,7 Ггц 8 Гб. ОЗУ
Intel Core 2Duo E6550 2,3 Ггц 8 Гб. ОЗУ
На каждом из них будет запускаться Ignite.
Результат вычислений для 11-и транзакций на одной ноде (один компьютер), код выше
Приятно что мощности со времен 2Duo выросли. А вот так это выглядело на картинках с I5
А теперь начнем наращивать мощность вычисления т.е. запускать на нескольких компьютерах Ignite server. Поскольку пример реализован так, что ожидает выполнение всех задач, то окончанием будет вычислением на последней ноде, и если мы подключаем слабые компьютеры к сильным, результат будет падать (по крайне мере вначале).
Результат вычислений на нескольких нодах.
Видно например что добавив к I5, I3 результат стал ниже чем для одного I5, но лучше чем один I3. Таким образом время вычисления для данной реализации будет мериться по слабой ноде и равно времени за которое нода обработает свою порцию задач. Что бы получить время вычисления в комбинации I5, I3 стала лучше чем на одном I5, нужно понять на каком количестве транзакций I3 покажет время лучшее чем I5 для всех. Экспериментально быстро установил, что I3 порцию из 4-5 транзакций обрабатывает также или лучше как I5 все 11 транзакций, таким образом такие порции возможны когда в гриде будут 3 ноды — I5 + I3 +I3, мои ожидания, что мой грид вычислит эту задачу ~30 сек.(против 40 сек. на одном I5), вот такая масштабируемость.
Ну а добавляя к слабым компьютерам в гриде мощные, конечно сразу получаем увеличение. Один 2Duo считал 140 сек, а в гриде с другими за 68 сек.
Так выглядит одна из консолей Ignite для трех нод в гриде
Показывает 3 сервера, один клиент который рассылает на них задачи, CPU показывает как сумму с трех компьютеров, ну память тоже как сумму. Видно что нода получила 4-ре задачи из 11-и (транзакций), по окончании остались три сервера.
В целом распределенные задачи решаются здорово, предлагаются различные шаблоны, с разными возможностями. Далее хочу вернуться к SQL в Ignite и поработать с кешам, напишу…
Материалы:
Ignite Getting Started
Задача такая, для блоков данных-транзакций вычислить хеш, но не простой, а с некоторой сложностью, например содержащий подряд семь символов 'А'. Что бы это было возможно, к блоку данных будем приклеивать постоянно увеличивающейся в цикле число, пока не будет получен хеш заданной сложности. Да, это похоже как делают майнеры добывая крипто валюту. Поскольку у меня несколько транзакций я их буду отправлять в вычислительный грид. Вычислительный грид это ноды Ignite, запущенные экземпляры на разных компьютерах, они сами себя обнаруживают и образуют грид. Распределяться эти вычисления между нодами будут равномерно и автоматически.
И так мои вычислительные мощности, в домашних условиях это:
Intel Core I5-7400 3,5 Ггц 8 Гб. ОЗУ
Intel Core I3-6100 3,7 Ггц 8 Гб. ОЗУ
Intel Core 2Duo E6550 2,3 Ггц 8 Гб. ОЗУ
На каждом из них будет запускаться Ignite.
Вот один из шаблонов которые предлагает Ignite для вычисления в гриде
//Стартуем клиента
Ignition.setClientMode(true);
try (Ignite ignite = Ignition.start("examples/config/example-ignite.xml")) {
//Подготовка к вычислениям, создание задач
//Проходим через все задания и создаем задачу на выполнение, добавляем в список
for (final T item: list) {
calls.add(new IgniteCallable<String>() {
@Override public String call() throws Exception {
//Вот этот код будет выполняться на нодах, на разных компьютерах в сети
return result;
}
});
}
// Запускаем список заданий на ноды грида
Collection<String> res = ignite.compute().call(calls);
// Получаем результат от каждой ноды
for (String hash : res) {
System.out.println(">>> Check all nodes for output : " + result);
}
}
Вот код который будет вычисляться на нодах грида (public String call())
calls.add(new IgniteCallable<String>() {
@Override public String call() throws Exception {
System.out.println();
System.out.println(">>> Id transaction=#" + transaction.getId() + " on this node from ignite job.");
MessageDigest md = MessageDigest.getInstance("MD5");
String transactHash;
// ищем красивый хеш
do {
md.update(transaction.getDifficultyContent().getBytes());
byte[] digest = md.digest();
transactHash = DatatypeConverter.printHexBinary(digest).toUpperCase();
// увеличиваем сложность
transaction.setDifficulty(transaction.getDifficulty() + 1);
} while (!transactHash.contains("AAAAAAA"));
return transactHash;
}
});
Полный код
public class MyComputeCallable {
// Данные для транзакции
public static final String LOREM_IPSUM = "Lorem ipsum dolor sit amet, consetetur sadipscing elitr,# " +
"sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.#" +
"At vero eos et accusam et justo duo dolores et ea rebum.#" +
"Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.";
/**
* Executes example.
*
* @param args Command line arguments, none required.
* @throws IgniteException If example execution failed.
*/
public static void main(String[] args) throws IgniteException {
String[] loremIpsum = LOREM_IPSUM.split("#");
List<Transaction> transactionList = new ArrayList<>();
for (int i= 0; i <= 10; i++) {
transactionList.add(i, new Transaction(i, loremIpsum[i % 4]));
}
Ignition.setClientMode(true);
try (Ignite ignite = Ignition.start("examples/config/example-ignite.xml")) {
System.out.println();
System.out.println(">>> Compute callable example started.");
Collection<IgniteCallable<String>> calls = new ArrayList<>();
// Iterate through all words in the sentence and create callable jobs.
for (final Transaction transaction : transactionList) {
calls.add(new IgniteCallable<String>() {
@Override public String call() throws Exception {
System.out.println();
System.out.println(">>> Id transaction=#" + transaction.getId() + " on this node from ignite job.");
MessageDigest md = MessageDigest.getInstance("MD5");
String transactHash;
do {
md.update(transaction.getDifficultyContent().getBytes());
byte[] digest = md.digest();
transactHash = DatatypeConverter.printHexBinary(digest).toUpperCase();
// увилмваем сложность
transaction.setDifficulty(transaction.getDifficulty() + 1);
} while (!transactHash.contains("AAAAAAA"));
return transactHash;
}
});
}
// Execute collection of callables on the ignite.
long millis = System.currentTimeMillis();
Collection<String> res = ignite.compute().call(calls);
System.out.println();
// individual received from remote nodes.
for (String hash : res) {
System.out.println(">>> Check all nodes for output hash: " + hash);
}
System.out.println(">>> Total msec: " + (System.currentTimeMillis() - millis));
}
}
}
//----------------------- Transaction ---------------------------------
public class Transaction {
private int difficulty;
private int id;
private String content;
public Transaction(int id, String content) {
this.id = id;
this.content = content;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public int getDifficulty() {
return difficulty;
}
public void setDifficulty(int difficulty) {
this.difficulty = difficulty;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public String getDifficultyContent() {
return "" + difficulty + content;
}
}
Результат вычислений для 11-и транзакций на одной ноде (один компьютер), код выше
Компьютер |
Результат msec. |
---|---|
I5 |
40 909 |
I3 |
57 162 |
2 Duo |
140 673 |
Приятно что мощности со времен 2Duo выросли. А вот так это выглядело на картинках с I5
Консоль Ignite
Видно что к серверу подключился клиент (srevers=1, clients=1), на ноду прилетели транзакции для вычислений (всего 11) >>> Id transaction…
по окончании клиент отключился (srevers=1, clients=0)
Видно что к серверу подключился клиент (srevers=1, clients=1), на ноду прилетели транзакции для вычислений (всего 11) >>> Id transaction…
по окончании клиент отключился (srevers=1, clients=0)
Вывод программы (красивые хеши)
А вот вычисленные «красивые» хеши
А вот вычисленные «красивые» хеши
А теперь начнем наращивать мощность вычисления т.е. запускать на нескольких компьютерах Ignite server. Поскольку пример реализован так, что ожидает выполнение всех задач, то окончанием будет вычислением на последней ноде, и если мы подключаем слабые компьютеры к сильным, результат будет падать (по крайне мере вначале).
Результат вычислений на нескольких нодах.
Ноды |
Результат msec. |
---|---|
I5+I3 |
44 389 |
I5+I3+2Duo |
68 892 |
Видно например что добавив к I5, I3 результат стал ниже чем для одного I5, но лучше чем один I3. Таким образом время вычисления для данной реализации будет мериться по слабой ноде и равно времени за которое нода обработает свою порцию задач. Что бы получить время вычисления в комбинации I5, I3 стала лучше чем на одном I5, нужно понять на каком количестве транзакций I3 покажет время лучшее чем I5 для всех. Экспериментально быстро установил, что I3 порцию из 4-5 транзакций обрабатывает также или лучше как I5 все 11 транзакций, таким образом такие порции возможны когда в гриде будут 3 ноды — I5 + I3 +I3, мои ожидания, что мой грид вычислит эту задачу ~30 сек.(против 40 сек. на одном I5), вот такая масштабируемость.
Ну а добавляя к слабым компьютерам в гриде мощные, конечно сразу получаем увеличение. Один 2Duo считал 140 сек, а в гриде с другими за 68 сек.
Так выглядит одна из консолей Ignite для трех нод в гриде
Показывает 3 сервера, один клиент который рассылает на них задачи, CPU показывает как сумму с трех компьютеров, ну память тоже как сумму. Видно что нода получила 4-ре задачи из 11-и (транзакций), по окончании остались три сервера.
В целом распределенные задачи решаются здорово, предлагаются различные шаблоны, с разными возможностями. Далее хочу вернуться к SQL в Ignite и поработать с кешам, напишу…
Топология
Материалы:
Ignite Getting Started
Oldron
В xml конфиге ignite только настройки TcpDiscovery? Или ещё что-то?
arylkov Автор
из поставки — apache-ignite-fabric-2.3.0-bin\examples\config\example-default.xml