Задать вопрос
  • Можно ли с помощью Kafka объединять результаты задач на базе общего id?

    Vamp
    @Vamp
    Вашу задачу в такой постановке вполне можно решить. Вот только склеиванием результатов придётся заниматься вручную.

    Создайте топик с результатами и в качестве ключа возьмите order_id. Далее считывайте результаты из топика и складывайте в коллекцию Map<Integer, Set<TaskResult>> (где Integer - order_id). Как только количество элементов в Set станет равным количеству ранее отправленных задач по данному order_id - можно считать, что все ответы получены и передавать их все разом на дальнейшую обработку.

    Останется только продумать крайние случаи. Например, нельзя до бесконечности ждать поступления всех результатов - external api может не ответить, а локальная задача вылететь с эксепшеном и не сгенерировать TaskResult. В этом случае количество ответов будет меньше количества отправленных задач. Придется прикручивать таймауты и/или отправлять задачи повторно. А что делать если вдруг ответов поступит больше, чем отправлялось запросов?

    Плюс ещё вопрос когда коммитить офсеты. Если сразу, то возникает опасность получить только половину результатов. Например, если сборщик результатов крашнется после того как соберёт первую половину и закоммитит её, то после рестарта он вычитает только вторую половину и никогда не соберёт полный ответ.

    Можно создавать отдельный топик под каждый order. Здесь упрощается обработка некоторых corner кейсов, но возникает проблема если order'ов много (сотни тысяч - миллионы).

    С kafka streams не работал, но бегло пробежав по документации, могу предположить, что комбинация groupByKey() + reduce() может решить вопрос меньшим количеством кода, чем у предыдущих двух вариантов.
    Ответ написан
    2 комментария
  • Apache Kafka как сделать управляющий канал?

    Vamp
    @Vamp
    Если хотите обойтись одним топиком, то делайте количество партиций по количеству агентов и подписывайте каждого агента на свою партицию.
    Ответ написан
  • Как послать сигнал на дочерний sh процесс?

    Vamp
    @Vamp
    Существует возможность заменить процесс sh процессом java при помощи команды exec:
    sh -c "exec java -jar ol.jar"

    В этом случае дочерний процесс займёт место родителя. В частности, получит PID bash процесса, который вам уже известен. Разумеется, если в скрипте после вызова java идут какие-то ещё команды, то они никогда не выполнятся, так как процесс bash перестаёт существовать в момент вызова exec.

    Если всё же необходимо выполнить какие-нибудь завершающие команды по окончании работы java процесса, то существует возможность повесить собственные обработчики сигналов:
    #!/bin/sh
    
    # Устанавливаем обработчик сигналов HUP INT QUIT TERM и псевдосигнала EXIT.
    # В обработчике отправляем сигнал TERM процессу, чей pid будет записан в
    # переменной CHILDPID.
    trap 'kill $CHILDPID' HUP INT QUIT TERM EXIT
    
    # Запускаем java в фоне.
    java -jar ol.jar &
    
    # Сохраняем PID только что запущенного дочернего java процесса.
    CHILDPID=$!
    
    # Следующая команда ставит процесс sh на паузу.
    # Пауза снимется после завершения фонового java процесса.
    # Обработчик сигналов при этом остаётся работоспособным и будет
    # корректно отрабатывать во время паузы.
    wait
    
    # В этом месте java уже завершилась и можно что-нибудь сделать.
    # Например, подчистить временные файлы.
    echo Cleanup actions
    Ответ написан
    Комментировать
  • Как реализовать "защиту" авторизации по номеру телефона?

    Vamp
    @Vamp
    1. Проверьте синтаксическую корректность номера телефона. Все мобильные номера в РФ начинаются на +79 и имеют длину ровно 11 цифр. Проверку можно даже добавить в веб форму на уровне js. Это нельзя назвать защитой от хулиганов, но она отсеет реальные ошибки и опечатки, облегчив жизнь обычным пользователям.

    2. Пробейте номер по базе россвязи (файл DEF-9xx). Так вы определите номера, на которые 100% не будет доставки. В отличии от проверки синтаксиса, не выдавайте пользователю ответ о некорректном номере. На все номера отвечайте "Одноразовый код отправлен, введите его сюда", но на невалидные номера не отправляйте сообщение.

    3. Добавьте ограничение на количество отправляемых форм в минуту с одного IP и количество отправляемых сообщений на один и тот же номер (независимо от IP).

    4. Оцените сколько может быть отправок форм в день и поставьте общий лимит на все отправки смс за день. Это единственный реальный способ контролировать атаку на сливание бюджета. Да, реальные пользователи пострадают при достижении лимита, но вы не должны доводить до него - настройте мониторинг количества отправляемых смс и алертинг при достижении порога в 90% от лимита, чтобы у вас было время среагировать на атаку и отбить её до полного исчерпания лимита. Либо если это всплеск реальных пользователей (например, неожиданно удачная реакция на рекламу), то у вас будет время скорректировать лимит. Можно рассчитывать лимит как 2 * среднее количество отправок смс за последние Х дней, чтобы не приходилось править его вручную по мере естественного роста посещаемости. Формулу и процент для алертинга, разумеется, подберёте под свои требования. Но можете взять и мои за основу.

    Отдельно хочу рассказать про так называемые прямые мобильные номера. Они выглядят как городские (например, +7495), но в реальности являются мобильными и могут принимать смски. Проверка в пункте 1 не пропускает такие номера и нет никакого способа проверить без отправки смс является ли отдельно взятый городской номер прямым мобильным. Прямых номеров мало по сравнению с настоящими мобильными или настоящими городскими. К тому же у каждого прямого номера есть мобильный аналог, начинающийся на +79, которым пользователь может воспользоваться для регистрации. Поэтому предлагаю просто забить на прямые номера, а в случае жалоб на невозможность регистрации с прямым номером, рассказывать про существование мобильного аналога, который может быть прописан где-то в договоре с оператором на оказание услуг связи или узнать в техподдержке оператора и с которым можно спокойно зарегистрироваться.

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

    Альтернативным вариантом является аренда входящего номера. В этом случае не вы отправляете сообщения пользователям, а они вам. Обычно за входящий номер берут фиксированную плату в месяц независимо от количества смс, так что не придётся в принципе волноваться за бюджет. Но тогда у вас будут в пролёте пользователи с отключенной услугой отправки смс. А таких немало, могу сказать. Благодаря интернет-мессенджерам.
    Ответ написан
    2 комментария
  • Запуск крон. Доступ запрещен?

    Vamp
    @Vamp
    Вам нужно явным образом вызвать интерпретатор php и передавать ему путь к скрипту:
    cd /var/www/no-name/data/www/oushd.ru/core/channels/channels/ && php /var/www/no-name/data/www/oushd.ru/core/channels/channels/test.php
    
    cd /var/www/no-name/data/www/oushd.ru/core/channels/channels/ && php /var/www/no-name/data/www/oushd.ru/core/channels/channels/channels.php
    Ответ написан
    1 комментарий
  • Какой подход для валидатора правильней?

    Vamp
    @Vamp
    Архитектурно правильнее возвращать как в примере с Yii2. Использование исключений для управления потоком выполнения программы считается серьёзным антипаттерном.

    Например:
    1. Исключения в данном случае - просто более хитрая замена оператору GOTO. Надеюсь, не нужно объяснять почему GOTO тоже считается антипаттерном?
    2. Программы, написанные в таком стиле, становится сложно читать и понимать.
    3. Меньшая эффективность в рантайме, так как большинство современных компиляторов не оптимизированы для случаев использования исключений в качестве управляющей логики.

    Более подробно по ссылке.
    Ответ написан
    5 комментариев
  • Как изменить настройки работающего контейнера Docker?

    Vamp
    @Vamp
    Единственный официальный способ изменить настройки контейнера - удалить и запустить новый с измененными настройками. Чтобы не терять настройки, удобнее всего записывать их в конфиги формата compose, с которыми работает утилита docker-compose.

    Чтобы не терять данные контейнера их нужно размещать в папках, подключаемых через volume. Всё остальное будет уничтожено при переделке контейнера.
    Ответ написан
    Комментировать
  • Как написать программу работающую со сборщиком мусора?

    Vamp
    @Vamp
    Задание поставлено очень широко. Листинг из примера ниже будет ему в точности соответствовать:

    // Работа с большими объектами
    byte[] b = new byte[65536];
    b[0] = 1;
    System.out.println(b[0]);
    
    // и с маленькими
    String s = "123";
    System.out.println(s.hashCode());
    
    // и с финализируемыми объектами
    Object f = new Object() {
        @Override
        protected void finalize() {
            System.out.println("I'm finalized!");
        }
    };
    // Строго говоря, объекты b и s так же являются финализируемыми,
    // так как наследуют метод finalize() от своего предка.
    // Так что f можно было даже не писать.
    
    System.gc();
    System.gc();
    System.gc();
    System.gc();
    // В этом месте объекты b, s и f пережили 4 цикла сборки.
    // Наверное... ¯\_(ツ)_/¯
    //
    // Более точно можно сказать, запустив программу с
    // аргументом -verbose:gc или с помощью утилиты visualvm
    Ответ написан
    6 комментариев
  • Есть ли разница в сложности освоения разных дистрибутивов, основанных на debian/ubuntu?

    Vamp
    @Vamp
    Как правило, трудностей не возникает. Но всё зависит от того, насколько сильно дистрибутив отличается от своей базы. Ubuntu, например, хоть и основан на Debian, но так сильно от него отличается, что искать решение проблем убунту в дебиановских доках практически бесполезно. Но Mint, основанный на Ubuntu, отличается незначительно, поэтому проблемы минта в доках убунты решаются без проблем.

    Хорошо работает подход при гуглении - добавлять название дистрибутива к поисковому запросу и если нет успеха, то добавить название базового дистрибутива.

    1. "[описание проблемы] kali"
    2. "[описание проблемы] debian"
    3. "[описание проблемы]" (если предыдущие два варианта облажались, гугление безотносительно дистрибутива - единственный оставшийся вариант)
    Ответ написан
    Комментировать
  • Почему не устанавливается расширение php-bcmath?

    Vamp
    @Vamp
    Потому что нужно устанавливать пакет php7.3-bcmath. На вашем же скриншоте видно, что установилась версия модуля для PHP 7.4: php-bcmath is already the newest version (2:7.4+73+ubuntu18.04.1+deb.sury.org+1)
    Ответ написан
    Комментировать
  • Стоит ли уменьшать количество подключенных файлов?

    Vamp
    @Vamp
    В линуксе (а большинство сайтов хостится именно на нем) контент часто запрашиваемых файлов кеширутся в память, так что нагрузка на диск от чтения скриптов будет генерироваться только первыми посетителями сайта. Поэтому не заморачивайтесь и делайте столько файлов, сколько нужно.

    Если хотите точно знать как сильно влияет на производительность то или иное ваше решение в проекте, берите apache bench, siege, yandex.tank и тестируйте. Сразу скажу, разницы между 11 и 5 файлами вы точно не увидите. Впрочем, как между 5 и 555. Но не во всех случаях ответ будет сразу очевиден, поэтому обязательно освойте какую-нибудь тулзу для нагрузочного тестирования и не гадайте на кофейной гуще.
    Ответ написан
    Комментировать
  • Как хранить подключения(Socket) клиентов и надо-ли это делать?

    Vamp
    @Vamp
    Хранить объект конечно нужно, иначе как клиенту отправлять данные?

    Начните с Map, а дальше видно будет. Ещё нужно обязательно предусмотреть регулярную очистку Map от закрывшихся сокетов, иначе получите утечку памяти.
    Ответ написан
    Комментировать
  • Как добавить HTTP заголовки к SOAP запросу?

    Vamp
    @Vamp
    __setSoapHeaders устанавливает заголовки soap запроса, а не http. Они размещаются в элементе Header. По вашему примеру кода получится что-то типа такого:
    <SOAP-ENV:Envelope
        xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
        xmlns:ns1="yourns"
        xmlns:ns2="http://soapinterop.org/echoheader/">
        <SOAP-ENV:Header>
            <ns2:token>token</ns2:token>
            <ns2:user-token>user_id</ns2:user-token>
        </SOAP-ENV:Header>
        <SOAP-ENV:Body>
            <!-- тело запроса -->
        </SOAP-ENV:Body>
    </SOAP-ENV:Envelope>


    Для добавления именно http заголовков нужно создать stream context:
    $aOptions['stream_context'] = stream_context_create([
        'http' => [
            'header' => "token: abc\r\nuser-token: 123"
        ]
    ]);
    
    $oSoapClient = new SoapClient( $url, $aOptions );
    $result = $oSoapClient->__doRequest( $sXml, $url, null, SOAP_1_1 );
    Ответ написан
    3 комментария
  • Возможен ли перехват tcp tls траффика в незашифрованном виде напрямую с устройства?

    Vamp
    @Vamp
    Перехват невозможен, так как шифрование трафика производится на уровне приложения, а не ОС. Ваш единственный способ перехвата - mitm. Но если приложение использует certificate/public key pinning, то и mitm вам не поможет.
    Ответ написан
    Комментировать
  • Что значить atime в stat?

    Vamp
    @Vamp
    Время последнего доступа кого угодно к файлу, на который вызываете stat(). Открытие файла и чтение данных из него обновляет метку atime. Причём последнее зарегистрированное время открытия/чтения необязательно могло быть сделано PHP скриптом.
    Ответ написан
    Комментировать
  • Объекты, имеющие поля сложных типов (Immutable или нет)?

    Vamp
    @Vamp
    В состояние объекта входит состояние объектов сложных типов. С формальной точки зрения объекты обоих ваших классов являются изменяемыми. Однако в JVM существует понятие effectively final - это мутабельный объект, состояние которого не изменяется, хоть и не форсируется модификаторами private и final. Если JVM поймёт, что конкретный объект вашего класса не меняется (другими словами, является effectively final), то к этому объекту могут применяться оптимизации, характерные для неизменяемых объектов.
    Ответ написан
    Комментировать
  • При инициализации класса, загружаются ли в память его instace-члены?

    Vamp
    @Vamp
    Могут загружаться, а могут и нет. JVMS не определяет должны ли загружаться все, часть или вообще никакие типы, на которые ссылается загружаемый класс. Так что данное поведение остаётся на усмотрение разработчика JVM.

    HotSpot, например, реализует ленивую загрузку классов. То есть не загружает классы, если в этом нет строгой необходимости. Он может даже не все static члены загрузить во время статической инициализации класса.

    Пример:
    import java.time.LocalDateTime;
    import java.util.concurrent.CountDownLatch;
    
    public class A {
    
        private static LocalDateTime ldt;
    
        private static CountDownLatch cdl;
    
        static {
            ldt = LocalDateTime.now();
            cdl = null;
        }
    
        public static void main(String[] args) {
            System.out.println("123");
        }
    
    }

    При запуске данного кода с аргументом JVM "-verbose:class" выведется список всех загруженных классов. Пример запуска на openjdk 12:

    ...
    [0,100s][info][class,load] java.time.temporal.TemporalAccessor source: shared objects file
    [0,100s][info][class,load] java.time.temporal.Temporal source: shared objects file
    [0,100s][info][class,load] java.time.temporal.TemporalAdjuster source: shared objects file
    [0,101s][info][class,load] java.time.chrono.ChronoLocalDateTime source: shared objects file
    [0,101s][info][class,load] java.time.LocalDateTime source: shared objects file
    ...

    Видно, что загрузился LocalDateTime и все имплементируемые им интерфейсы, но CountDownLatch в списке отсутствует, несмотря на то, что существует статическая переменная с этим типом.

    Аналогично происходит и с instance членами - классы, на которые они ссылаются, будут загружаться только по мере необходимости (использования). Вполне нормальна ситуация, когда объекты одного класса активно используются в программе, но при этом не все используемые им типы загружены. Более того, это относится не только к членам класса или объекта, но и к локальным переменным и даже просто коду:

    import java.math.BigDecimal;
    import java.time.LocalDateTime;
    
    public class A {
    
        public static void main(String[] args) {
            if ("world".equals(System.getenv("HELLO"))) {
                System.out.println(LocalDateTime.now());
            } else {
                System.out.println(BigDecimal.TEN);
            }
        }
    
    }

    В этом примере будет загружен класс BigDecimal, но не LocalDateTime. Если инвертировать условие или запустить код с выставленной переменной окружения HELLO=world, то в списке классов появится LocalDateTime, но BigDecimal будет отсутствовать.
    Ответ написан
    2 комментария
  • Почему рекурсия в java уходит в бесконечность?

    Vamp
    @Vamp
    Идеальная задачка для пошаговой отладки. Она есть в любой приличной IDE. Покажу на примере IDEA:

    1. Ставьте на строчке, которую хотите отладить, так называемую точку прерывания (breakpoint).

    5de7eb1ceef0b714535201.png

    2. Запускайте программу в отладочном режиме.

    5de7eb4300731205266204.png

    3. Программа начнёт выполняться как обычно, но когда исполнение дойдёт до строки, помеченной breakpoint'ом, выполнение остановится и в дебаг окне отобразится текущий стек трейс, значения локальных переменных и самое важное для вашей проблемы - кнопки пошагового продолжения.

    5de7ebd9dd5e7287922482.png(на скриншоте я уже проехал несколько шагов вперёд)

    Вот эти 5de7ecb0c3d98348194703.png
    Первая кнопка продвинет выполнение программы на одну строчку. Вторая сделает то же самое, но если в выполняемой строке вызывается какой-то метод, то отладка шагнёт внутрь метода и шаги продолжатся уже в теле вызываемого метода.

    Выполняя отладку по шагам, вы будете точно представлять себе что происходит внутри программы и появится понимание откуда растут ноги у бага. Чем раньше вы освоите отладку в пошаговом режиме, тем проще будет в будущем.
    Ответ написан
  • Тип данных с плавающей точкой в php?

    Vamp
    @Vamp
    Храните и отображайте в обычной строке. Влезет столько знаков, сколько нужно.
    Ответ написан
  • Где можно почитать/посмотреть про принципы проектирования и написания ПО для стратегически важных объектов?

    Vamp
    @Vamp
    Такие стандарты существуют. Наиболее известные - MISRA C и MISRA C++. Как можно догадаться, для языков С и С++, соответственно. Изначально созданы для автомобильной промышленности, но распространились и на аэрокосмическую отрасль, медицинскую технику, военных и прочие критичные к надёжности области. В том числе и АЭС.

    Из MISRA C выросли SEI CERT C/C++, AUTOSAR General Software Specification, JPL Institutional Coding Standard for the C (стандарт NASA) и ещё куча других.

    Вся суть этих стандартов в том, чтобы максимально ограничить используемые фичи языка - чтобы код был максимально прост (даже скорее примитивен) и доступен для анализа. Как правило, запрещено использовать бесконечные циклы, рекурсию, макросы, аллокации из кучи - вобщем, любые потенциально опасные операции, в которых чаще всего находятся ошибки. Так же накладываются ограничения на размер функции (в строках кода), цикломатическую сложность, покрытие автотестами и т.п.

    Помимо этого, существует ворох стандартов, описывающих подходы к организации процесса разработки и контроля качества. У каждой отрасли свои стандарты. Например, AS9100.

    Если вы интересуетесь вопросом чтобы применять подобные техники в повседневном программировании, то не стоит - никакого удовольствия и конкурентного преимущества вы от этого не получите. Если конечно ваше повседневное программирование не включает в себя разработку панели управления АЭС.

    Update 2019-12-30:
    На хабре появилась интересная статья с кратким обзором MISRA.
    Ответ написан
    Комментировать