Author Archive

Профилирование CPU с помощью Flame Graph

Flame Graph позволяет визуально увидеть чем были заняты процессоры в течении определенного периода.

Снять Flame Graph можно по классической инструкции: https://github.com/BrendanGregg/FlameGraph, что требует некоторых манипуляций.

Но можно пойти более легким путем, используя async-profiler:

  1. Скачиваем стабильный релиз в виде архива
  2. Распаковываем архив
  3. Выполняем команду:
    ./profiler.sh -d 30 -f /tmp/flamegraph.svg <PID>

    Где <PID> - это идентификатор Java-процесса.

    -d 30 - продолжительность профилирования, в секундах.

    /tmp/flamegraph.svg - файл сохранения Flame Graph.

Flame Graph можно прекрасно просматрировать в браузере без использования каких-либо дополнительных средств.

В этом можно убедиться на следующем примере Flame Graph (можно кликать на области): Your browser does not support SVG

Поиск искомого Java-процесса можно осуществляется через утилиту jps.

Скрипт, автоматизирующий поиск и снятие Flame Graph:

PID=`jps | grep YOUR_APP_NAME | awk '{print $1}'`
echo PID = $PID
FG=/tmp/flamegraph$PID.svg
echo FG = $FG
~/soft/async-profiler-1.6-macos-x64/profiler.sh -d 30 -f $FG $PID
p.s. Этому блогу исполнилось 7 лет!

Алгоритм Ахо-Корасик на Java

Пример реализации алгоритма Ахо-Корасика на Java.

При реализации использовался следующий материал:

  1. https://e-maxx.ru/algo/aho_corasick
  2. https://neerc.ifmo.ru/wiki/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%90%D1%85%D0%BE-%D0%9A%D0%BE%D1%80%D0%B0%D1%81%D0%B8%D0%BA

Данный алгоритм был опробован на задаче: https://www.hackerrank.com/challenges/determining-dna-health/problem

Пример использования:

List<AhoCorasick.Entry> entryList = new ArrayList<>();
 
List<String> list = Arrays.asList("b", "c", "aa", "d", "b");
 
for (String s : list) {
    AhoCorasick.Entry entry = new AhoCorasick.Entry();
    entry.setValue(s);
    entryList.add(entry);
}
 
 
AhoCorasick ahoCorasick = new AhoCorasick(entryList, 26);
 
ahoCorasick.solve("caaab");

Сама реализация алгоритма:

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
 
/**
 * Реализация алгоритма Ахо-Корасика
 */
public class AhoCorasick {
 
    private List<Entry> entryList;
    private int alphabetSize;
    private List<Vertex> vertexList;
    private int vertexCount;
 
    public AhoCorasick(List<Entry> entryList, int alphabetSize) {
        this.entryList = entryList;
        this.alphabetSize = alphabetSize;
    }
 
    // Строка набора
    public static class Entry {
        private String value;
 
        public String getValue() {
            return value;
        }
 
        public void setValue(String value) {
            this.value = value;
        }
 
        @Override
        public String toString() {
            return "Entry{" +
                    "value='" + value + '\'' +
                    '}';
        }
    }
 
    // Вершина бора (состояние конечного детерминированного автомата)
    public static class Vertex {
        private int next[];         // массив сыновей
        private boolean isLeaf;     // терминальная вершина
        private int parent;         // родительская вершина
        private int charIndexFromParent;// символ перехода от родителя
        private int suffixLink;     // суффиксная ссылка
        private int go[];           // массив переходов, используемый для вычисления суффиксных ссылок
 
        public Vertex(int alphabetSize) {
            this.next = newIntArray(alphabetSize);
            this.go = newIntArray(alphabetSize);
            this.parent = -1;
            this.suffixLink = -1;
        }
 
        private int[] newIntArray(int size) {
            int[] ints = new int[size];
            Arrays.fill(ints, -1);
            return ints;
        }
 
        @Override
        public String toString() {
            return "Vertex{" +
                    "next=" + Arrays.toString(next) +
                    ", go=" + Arrays.toString(go) +
                    ", isLeaf=" + isLeaf +
                    '}';
        }
    }
 
    public List<Entry> getEntryList() {
        return entryList;
    }
 
    // добавить строку в набор
    public boolean addEntry(Entry entry) {
        return entryList.add(entry);
    }
 
    // поиск всех строк из заданного набора в тексте
    public void solve(String text) {
        vertexList = new ArrayList<>();
        vertexList.add(new Vertex(alphabetSize));
        vertexList.add(new Vertex(alphabetSize));
        vertexCount = 1;
 
        for (Entry entry : entryList) {
            addToTrie(entry);
        }
 
        int v = 0;
        for (char ch : text.toCharArray()) {
            v = go(v, charToIndex(ch));
            Vertex vertex = vertexList.get(v);
            if(vertex.isLeaf) { // посещение терминальной вершине - это нахождение строки из заданного набора
                System.out.println(v + " " + vertex);
            }
        }
 
    }
 
    // добавить в бор
    private void addToTrie(Entry entry) {
        int v = 0;
        for (char ch : entry.value.toCharArray()) {
            int sym = charToIndex(ch);
            if(vertexList.get(v).next[sym] == -1) {
                Vertex vertex = vertexList.get(vertexCount);
                vertex.suffixLink = -1;
                vertex.parent = v;
                vertex.charIndexFromParent = sym;
 
                vertexList.add(new Vertex(alphabetSize));
                vertexList.get(v).next[sym] = vertexCount++;
            }
            v = vertexList.get(v).next[sym];
        }
        vertexList.get(v).isLeaf = true;
    }
 
    private int charToIndex(char ch) {
        return ch - 'a';
    }
 
    // обычный переход
    private int go(int v, int sym) {
        Vertex vertex = vertexList.get(v);
        if(vertex.go[sym] == -1) {
            if(vertex.next[sym] != -1) {
                vertex.go[sym] = vertex.next[sym];
            } else {
                vertex.go[sym] = v == 0 ? 0 : go(goBySuffixLink(v), sym);
            }
        }
        return vertex.go[sym];
    }
 
    // переход по суффиксной ссылке (к вершине, в которой оканчивается наидлиннейший собственный суффикс строки)
    private int goBySuffixLink(int v) {
        Vertex vertex = vertexList.get(v);
        if(vertex.suffixLink == -1) {
            if(v == 0 || vertex.parent == 0) {
                vertex.suffixLink = 0;
            } else {
                vertex.suffixLink = go(goBySuffixLink(vertex.parent), vertex.charIndexFromParent);
            }
        }
        return vertex.suffixLink;
    }
 
}

Sublime gcc docker

При разработке на C++ может возникнуть ситуация, когда ваша ОС не подходит.

Например, в моем случае у меня MacBook, но нужно использовать epoll, который не подджерживается в Mac OS.

Можно выйти из ситуации следующим путем, если вы пишите и билдите код в редакторе Sublime.

В Sublime выберите Tools > Build System > New Build System...

Откроется шаблон, замените его содержимое на следующее:

{
 "shell_cmd": "docker run --rm -v \"${file_path}\":/usr/src/myapp -w /usr/src/myapp gcc:4.9 g++ -std=c++11 \"${file_name}\" -o \"${file_base_name}\" && docker run --rm -v \"${file_path}\":/usr/src/myapp -w /usr/src/myapp gcc:4.9 ./\"${file_base_name}\"",
 "file_regex": "^(..[^:]*):([0-9]+):?([0-9]+)?:? (.*)$",
 "working_dir": "${file_path}",
 "selector": "source.c++",
 "variants":
 [
  {
   "name": "Run",
   "shell_cmd": "docker run --rm -v \"${file_path}\":/usr/src/myapp -w /usr/src/myapp gcc:4.9 g++ -std=c++11 \"${file_name}\" -o \"${file_base_name}\" && \"${file_base_name}\" && docker run --rm -v \"${file_path}\":/usr/src/myapp -w /usr/src/myapp gcc:4.9 ./\"${file_base_name}\""
  }
 ]
}

Сохраните файл, будет предложено наименование файла в формате YOUR_NAME.sublime-build

Чтобы воспользовать новым вариантом сборки, выберите Tools -> Build System -> YOUR_NAME.

Сделайте Build.

Если нужно отредактировать файл YOUR_NAME.sublime-build, установите плагин PackageResourceViewer.

Понимание OpenID Connect (OIDC)

Превосходная статья по пониманию OpenID Connect: https://habr.com/ru/post/422765/

В данной статье в пункте 6 указано, что хранение токенов (access_token и refresh_token) в браузере не очень секьюрно, лучше это делать на уровне бэкенда. Но это требует сессию, к которой будут крепится токены.

Сессия хранится в Cookie, Cookie передаются во всех запросах к одному хосту, даже с других сайтов, поэтому нужно защить запросы еще CSRF-токеном.

Также сессии нужно передавать между бэкендами для балансировки нагрузки.

Сессии хранятся в памяти, поэтому большое кол-во пользователей может исчерпать память.

Понимание Kerberos

Отличное видео-объяснение протокола Kerberos

https://www.youtube.com/watch?v=_44CHD3Vx-0 (Продолжительность: 6 минут)

Цербер (Кербер) - трехголовый пес.

Примечательно, что протокол построен на симметричной криптографии с использованием трех различных закрытых ключей.

TAG:

Отличный курс по Active Directory

Всегда казалось, что Active Directory - это что-то супер сложное для понимания.

Но найдя понятный и простой курс по AD, уже так не думаю 🙂

Курс состоит всего из 4 модулей общей продолжительностью чуть больше часа.

Название модуля Ссылка на youtube Продолжительность
Введение в Active Directory https://www.youtube.com/watch?v=fojHlsyGQqA 20 мин.
Создание первого домена https://www.youtube.com/watch?v=rJJKYe5VECk 10 мин.
Подразделения, группы, учетные записи пользователей https://www.youtube.com/watch?v=ZTnLg9CzZZQ 27 мин.
Включение компьютера в домен https://www.youtube.com/watch?v=JMfe3_OmnRA 10 мин.

Исследование Keycloak

Если у вас уже установлен keycloak и настроен realm, то запросить open-id конфигурацию данного realm можно командой:

curl http://{KEYCLOAK_HOST}:{KEYCLOAK_PORT}/auth/realms/{YOUR_REALM}/.well-known/openid-configuration | jq '.'

Результат будет представлен в виде JSON. Чтобы удобочитаемо отформатировать JSON, можно воспользоваться утилитой jq (https://stedolan.github.io/jq/), которую нужно будет дополнительно установить.

Небольшое описание полученных данных

issuer - базовый адрес realm'а;

authorization_endpoint - конечная точка авторизации; Получить публичный ключ для проверки подписи токена:

curl http://{KEYCLOAK_HOST}:{KEYCLOAK_PORT}/auth/realms/{YOUR_REALM}/protocol/openid-connect/certs | jq '.'

TAG: ,

Highload табличка

Часто возникает необходимость оценки скорости работы системы или какой-нибудь утилитки. Приходится брать калькулятор делить/умножать на секунды, минуты, часы и дни. Расчеты получаются, конечно же, приблизительные. Поэтому хотелось бы иметь примерную табличку, чтобы реже пользоваться калькулятором.
Кол-во операций,
транзакций и т.д.
За 24 часа
(86 400 секунд)
За 10 часов
(36 000 секунд)
За 10 часов
(600 минут)
1 000 000 11.6 в секунду 27.8 в секунду 1 666.7 в минуту
10 000 000 115.7 в секунду 277.8 в секунду 16 666.7 в минуту
100 000 000 1 157.4 в секунду 2 777.8 в секунду 166 666.7 в минуту
300 000 000 3 472.2 в секунду 8 333.3 в секунду 500 000.0 в минуту
500 000 000 5 787.0 в секунду 13 888.9 в секунду 833 333.3 в минуту
700 000 000 8 101.9 в секунду 19 444.4 в секунду 1 166 666.7 в минуту
1 000 000 000 11 574.1 в секунду 27 777.8 в секунду 1 666 666.7 в минуту

Слетели и не работают bash alias

В один день на серваке почему-то перестали работать алиасы (alias)

Долго так промучались.

Пока не нашел такое решение.

Выполняем команду и алиасы работают:

source ~/.bashrc

Чтобы это работало при новом входе:

Добавляем:

source ~/.bashrc

в файл ~/.bash_profile

Сборка и установка турника 3 в 1

Выложим на ровную поверхность компоненты турника:

Распакуем все детали:

Наденем ручки. Половину ручек нужно разрезать пополам. Чтобы дело пошло быстрее смачиваем ручки водой, а сам турник мыльным раствором:

В сборке турник выглядит следующим образом: <<<тут будет фото>>>

Размечаем на стене отверстия для креплений гвоздем или чем-то еще, чтобы была выемка для точного высверливания отверстия. Вcего четыре крепления, каждое крепится двумя анкерами. Расстояние между центрами креплений 45см (желательно пользоваться уровнем(ватерпасом)):

Под местом сверления вешаем пакет на скотч, чтобы меньше пылить и мусорить:

При сверлении отверстий возникла 1-ая проблема стена оказалась не бетонной, а из газобетона (газоблока). Такая стена менее прочная и легко сверлится. С турником шли анкера 12x70, стало понятно, что такие анкера не удержат турник. Пришлось гуглить и смотреть ютуб...

В итоге решил вешать турник на анкера 12x100, но перед этим залить в отверстия "жидкие гвозди" (строительный/монтажный клей). Как выяснилось, применение "жидких гвоздей" значительно усиливает соединение анкера со стеной, заполняя микрополости.

Далее возникла следующая проблема: в строй-магазине были только анкера 12x120, что даже лучше (чем длинне анкер, тем больше площадь соприкосновения со стеной), но продавец положил шурупы к анкеру длинной 100мм. Обнаружив это только дома, пришлось искать шурупы под анкера на 120, не найдя таких, решил укоротить анкера до 100мм, отрезав верхнюю часть:

После высверливания 8 отверстий 12x100, обязательно выскабливаем остатки пыли ершиком или как я - шляпкой шурупа меньшего диаметра:

Дополнительно, можно пройтись пылесосом:

Все отверстия готовы:

Далее по очереди работаем над каждым креплением: заливаем клей в два отверстия, забиваем два анкера и на два шурупа прикручиваем крепление.

Отверстие лучше заполнять клеем на половину, иначе избыток клея заполнит все пространство, сочясь из середины анкера, и будет очень мешать забить анкер.

Все крепления прикручены:

Ждем 3-5 суток до полного отвердевания клея...