Задать вопрос
  • Какие книги по операционным системам для изучения?

    Vamp
    @Vamp
    Мне очень понравилась книга Bryant, O'Hallaron - Computer Systems: A Programmer's Perspective. Мало воды, чёткая и конкретная. Там идёт довольно глубокое погружение в тему, так что не очень подходит под критерий "в общих чертах", но я всё равно посоветую. Книга толстая - чуть более 1100 страниц. Сам я её читал уже дважды и в будущем планирую ещё раз прочитать.
    Ответ написан
    Комментировать
  • Почему PHP считает int числа после математических операций как float?

    Vamp
    @Vamp
    Все потому, что в операции деления у вас получается float, так как операнды не поделились друг на друга без остатка. Умножение int на float в дальнейшем тоже приводится к умножению float на float.
    Ответ написан
    Комментировать
  • Где хранить ключи api используемые для тестирования открытого по?

    Vamp
    @Vamp
    Стандартной практикой является коммит в репозиторий файла phpunit.xml.dist в который прописываются все настройки по умолчанию, кроме чувствительных к безопасности (api ключи, пароли и пр). Файл phpunit.xml добавляется в .gitignore. Таким образом, если пользователю нужно кастомизировать какие-то параметры, он просто копирует файл phpunit.xml.dist в phpunit.xml и добавляет свои секреты в последний. Это безопасно, так как phpunit.xml заигнорен.

    То что вы описываете - это уже не unit, а интеграционное тестирование. Скрипты для интеграционного тестирования обычно делают в отдельном testsuite и скипаются по умолчанию. Для запуска юнит тестов не должна требоваться какая-либо подготовка - только "склонировал репозиторий, запустил". Для интеграционных подготовка уже нужна. Например, юзер должен будет модифицировать phpunit.xml (не .dist!), вписав туда свой тестовый api ключ.

    Соответственно, для юнит тестов достаточно в тестируемый код передать мокнутого http клиента, который будет возвращать ответ, который вернуло бы реальное стороннее api (в guzzle это делается с помощью MockHandler). Поэтому если надо просто запустить юнит тест, то ничего делать не нужно - phpunit автоматически подхватит phpunit.xml.dist, если не найдет phpunit.xml. Если надо запустить интеграционные, то юзер должен будет скопировать phpunit.xml.dist в phpunit.xml и добавить туда api ключ.
    Ответ написан
    1 комментарий
  • Как хранить ключи в Python?

    Vamp
    @Vamp
    Храните свои ключи в SharedPreferences, EncryptedSharedPreferences (если sdk 23 и выше) или KeyChain.
    Ответ написан
    Комментировать
  • Как реализуются лицензии по ядрам?

    Vamp
    @Vamp
    определяется лицензия -> макс кол-во ядер и на основании этого создается пул потоков заданного размера
    Так оно и есть.
    Ответ написан
    Комментировать
  • В чём преимущество Clang перед GCC? Что использовать для кросс-компиляции?

    Vamp
    @Vamp
    Зачем Clang использует gcc? Для компиляции?
    Сам компилятор не используется. Только линкер и стандартная библиотека.

    Почему считается, что использовать Clang для (кросс-)компиляции, и в CMake лучше, если он использует тот же gcc?
    Потому что clang строго следует букве стандарта, в отличии от gcc, который трактует стандарт вольно и в целом ориентирован на поддержку экзотических костылей и исторически сложившихся традиций. Поэтому при использовании clang проще писать кроссплатформенный код.
    Ответ написан
    4 комментария
  • Как использовать разные буферы для чтения и записи в FileChannel и AsynchronousFileChannel?

    Vamp
    @Vamp
    Буфер нужно переключать между режимами чтение/запись:

    FileChannel channel = FileChannel.open(Path.of("test.txtx"),StandardOpenOption.CREATE, 
    				StandardOpenOption.WRITE, StandardOpenOption.READ);
    		
    ByteBuffer buffer = ByteBuffer.allocate(15);
    buffer.put("hello world".getBytes()); // записали в буфер данные
    buffer.flip(); // <--- переключили буфер из режима записи в режим чтения
    channel.write(buffer, 0); // теперь channel.write сможет прочитать записанные в 
                              // буфере данные и записать их в файл
    		
    ByteBuffer buffer2 = ByteBuffer.allocate(15);
    channel.read(buffer2, 0); // channel.read прочитал данные из файла и записал их в буфер
    buffer2.flip(); // <--- переключаем из записи в чтение
    System.out.println(StandardCharsets.UTF_8.decode(buffer2)); // decode читает данные из буфера
                                                                // и составляет из них строку
    
    // Здесь ничего не выводилось, потому что без flip ничего и не записалось в файл.
    System.out.println(new String(buffer2.array())); // ничего не выведет
    Ответ написан
    Комментировать
  • Почему PHP выполняет математические операции неправильно?

    Vamp
    @Vamp
    Потому что типом данных double нет возможности представить число 0.58. Поэтому компьютер берёт наиболее близкое к 0.58 число, которое double может выразить.

    Если вам нужны точные вычисления, то следует воспользоваться специальными функциями:

    $format = bcdiv("580000000", "1000000000", 2); // "0.58"
    
    var_dump(bcmul($format, "100")); // string(2) "58"


    Существует даже специальный сайт, рассказывающий о данной особенности математики чисел с плавающей запятой: https://0.30000000000000004.com/
    Ответ написан
    Комментировать
  • Как ограничить подключение к MongoDB?

    Vamp
    @Vamp
    На уровне ОС это можно сделать через фаервол.
    На уровне базы через настройку authenticationRestrictions в аккаунте пользователя.
    Ответ написан
  • Как пробросить сервер за NAT провайдера?

    Vamp
    @Vamp
    У cloudflare есть решение для этой задачи - cloudflare tunnel.

    Cloudflare Tunnel provides you with a secure way to connect your resources to Cloudflare without a publicly routable IP address. With Tunnel, you do not send traffic to an external IP — instead, a lightweight daemon in your infrastructure (cloudflared) creates outbound-only connections to Cloudflare's global network.
    Ответ написан
    1 комментарий
  • Как исправить проблему с mail php и заголовками для gmail?

    Vamp
    @Vamp
    Заголовки To и Subject подставляются автоматически функцией mail. Вам не нужно добавлять их самостоятельно. Просто удалите строки:

    $headers .= "To: $to\r\n";
    $headers .= "Subject: $subject\r\n";
    Ответ написан
    1 комментарий
  • Как деплоить php приложение вместе с docker?

    Vamp
    @Vamp
    Это типичный вариант развёртывания приложения в докере. Единственный его минус - некоторое время даунтайма между моментом когда опустился старый контейнер и поднялся новый. Для решения этой проблемы есть разные техники. Например, blue-green deployment. Это когда есть два контейнера, условно называемые green и blue. И трафик льётся только на один. Скажем, на green. В момент деплоя обновляется blue контейнер и после того как он полностью будет готов - переключаем трафик на blue. В следующий раз деплой начнётся с обновления green.

    Переключение трафика делается разными способами. Самый безопасный - переписать конфиг nginx и релоаднуть его. Или менее безопасный, зато без необходимости править конфиг, - добавить оба конейнера в одну upstream группу и опустить green контейнер после обновления blue.

    Довольно муторное дело. Но в оркестраторах типа openshift и kubernetes такой деплоймент процесс уже встроен из коробки. Но вот сами оркестраторы довольно тяжелая штука. Для них нужен отдельный выделенный человек, который будет заниматься только ими. Так что я советую начинать присматриваться к оркестраторам только когда количество серваков перевалит за 50. С меньшим количеством это просто нерентабельно.

    Другой вариант развёртывания - контейнер с пхп поднят постоянно, но код проекта монтируется в контейнер через volume. Далее ci джоба по sftp заливает новый код в соседнюю папку и переключает симлинк. Симлинк переключается атомарно и контейнер сразу начинает работать с новым кодом. Ничего больше делать не надо. Я рекомендую такой подход когда на проекте менее 50 серверов.
    Ответ написан
    4 комментария
  • Как работать с DI-контейнером?

    Vamp
    @Vamp
    В самом контейнере задаются только примитивные типы данных?

    В DI контейнере обычно регистрируют объекты, общие для всего приложения, а не для конкретного запроса. Например, коннект к базе, роутер, кеш. Автовайринг примитивных типов в DI - прямая дорога к путанице и сложным для обнаружения ошибкам.

    А все объектные зависимости решаются с помощью автовайринга через Reflection API?

    Можно и так. А можно руками связи выставить. Или аннотациями. Разные способы бывают, смотря какие поддерживаются выбранной вами реализацией DI.

    К примеру, у меня есть контроллер.

    В случае с контроллерами обычно делают front controller, на который сваливаются абсолютно все запросы, а дальше он сам определяет какой из контроллеров нужно создать и передаёт запрос на обработку ему.

    Сам вид принимает шаблоны, это обычные строки с разванием шаблонов, которые нужно подключить.

    Если строки с названием шаблонов фиксированы, хранятся в конфиге/базе и одинаковы для всех страниц и всех пользователей, то можно шаблон и в DI зарегистрировать. В противном случае лучше создавать его во фронт контроллере. Либо зарегистрировать в DI фабрику шаблонов, которая будет создавать объекты вью в зависимости от переданных параметров. Аналогично и с моделью.

    Модель принимает класс соединения с БД, а БД принимает массив с настройками подключения.

    В контейнере можно зарегистрировать объект Settings, в котором хранятся данные для подключения к базе и прочие глобальные настройки, а затем инжектить его в класс для работы с базой. Ещё контейнер может сам выступать хранилищем настроек и инжектить их (в том числе примитивные). Так сделано в DI компоненте Symfony.

    Есть еще класс пагинации, он тоже должен быть в контроллере и его конструктор принимает три параметра типа int.

    Опять же, если эти три параметра не зависят от запроса, то можно зарегистрировать пагинатор в DI. Если зависят, то можно из конструктора перенести эти параметры в метод и вызывать его в контроллере. Например: $page = $this->paginator->calulatePage($one, $two, $three)
    Ответ написан
    2 комментария
  • Как отправить длинное SMS с помощью SMPP?

    Vamp
    @Vamp
    Отправляйте текст через message_payload, а не short_message. В этом случае делением на сегменты будет заниматься провайдер.

    Пример Данилы лучше не использовать. Нельзя втупую резать текст по 153 символа. А ещё там косяк с кодировкой.

    Я не вижу никаких ошибок, потому что либа даже не отправляет мое SMS.

    Дебажить SMPP - это боль и страдания (не зависит от используемого языка). Лучше всего снять дамп трафика и смотреть на сырые пдухи в wireshark. В идеале отправлять смски через другой протокол. Многие провайдеры предоставляют HTTP API.
    Ответ написан
    Комментировать
  • Какой софт поставить на домашний файловый сервер из древнего железа?

    Vamp
    @Vamp
    Я на свой древневековый ноутбук с 32-битным процессором (intel atom) поставил openmediavault. На слабом железе хорошо работает.

    Не верю в стабильность внешних ресурсов.

    А в стабильность хлама значит верите? По опыту эксплуатации хлама скажу, что лучше его выкинуть/продать и взять что-нибудь свежее взамен. Посмотрите мини-пк или одноплатники. Будет надёжнее, меньше места занимать, меньше энергии потреблять. Я в итоге заменил ноутбук на raspberry pi с m.2 модулем, куда вставил терабайтный SSD.
    Ответ написан
  • Какой оптимальный способ попасть в домашнюю сеть снаружи?

    Vamp
    @Vamp
    Самый удобный вариант - развернуть частный VPN типа zerotier или tailscale.
    Ответ написан
  • Как запустить парсер NBBC под PHP8?

    Vamp
    @Vamp
    Нужно ещё конструкторы в классах переименовать в __construct() и тогда останется только два теста, которые заваливаются из-за того, что htmlspecialchars по умолчанию применяет ENT_QUOTES.
    Ответ написан
    Комментировать
  • Низкая скорость отдачи с VPS на конкретный хост?

    Vamp
    @Vamp
    Разница почти наверняка из-за неудачного маршрута PC2->PC1. Маршрут можно посмотреть командой traceroute или mtr (в ubuntu пакет называется mtr-tiny). Далее можно попробовать написать письмо в техподдержку хостера PC2, приложив трейсы (с обоих сторон) и результаты iperf3 (так же с обоих сторон). Тогда хостер может подправить маршрутизацию и скорость восстановится.
    Ответ написан
    5 комментариев
  • Как правильно должно быть развернута инфрастуктура с Docker, Kubernetes?

    Vamp
    @Vamp
    1. Потому что докер объединил уже имеющиеся в линуксе способы изоляции процессов (chroot, cgroups, namespaces) в удобное для использования приложение.

    2. Гипервизором выступает ядро линукса. Поэтому накладные расходы на контейнеризацию значительно ниже, чем на виртуализациию.
    Ответ написан
  • Как избавиться от требования обработки исключения в Project Reactor?

    Vamp
    @Vamp
    неужели есть только один вариант это обрабатывать с поощью try/catch внутри каждого блока что бы избежать Unhandled exceptions?

    Вобщем-то да. Можно вынести try/catch в отдельный универсальный метод, который оборачивает исключение в RuntimeException:

    private @NotNull Mono<JsonObject> invoke(Channel channel, Class<?> clazz, Method method, Object... args) {
        return Mono.fromCallable(() -> clazz.getDeclaredConstructor(Server.class, Channel.class))
            .map(constructor -> ex(() -> constructor.newInstance(this.manager.getServer(), channel)))
            .flatMap(obj -> new GenericData<JsonObject>().invoke(method, obj, args))
            .onErrorResume(Mono::error);
    }
    
    private <T> T ex(Callable<T> code) {
        try {
            return code.call();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }


    Конкретно в вашем примере можно вызов конструктора переместить в callable, из которого делается Mono, так как из Callable разрешено стрелять проверяемыми исключениями, в отличии от Function, который принимает map:

    private @NotNull Mono<JsonObject> invoke(Channel channel, Class<?> clazz, Method method, Object... args) {
        return Mono.fromCallable(() -> {
                var constructor = clazz.getDeclaredConstructor(Server.class, Channel.class);
                return constructor.newInstance(this.manager.getServer(), channel);
            })
            .flatMap(obj -> new GenericData<JsonObject>().invoke(method, obj, args))
            .onErrorResume(Mono::error);
    }
    Ответ написан
    Комментировать