Русский
Русский
English
Статистика
Реклама

Open-source

Перевод Flutter и настольные приложения

25.06.2020 12:16:27 | Автор: admin
Ни для кого не секрет то, что команда разработчиков Flutter стремится к тому, чтобы этот фреймворк позволял бы, пользуясь единой кодовой базой, создавать приложения для широкого разнообразия платформ. Сюда входят iOS, Android, Windows, Linux, macOS и веб. При этом такие приложения должны компилироваться в формат, специфичный для каждой из платформ, а их внешний вид не должен уступать внешнему виду хорошо сделанных игр. В Google фреймворк Flutter применяется во многих проектах: от Assistant до Stadia, от Cloud Search до Blogger. Flutter используются и другими компаниями от ByteDance до Grab, от Nubank до MGM Resorts. Всем им приносит пользу та гибкость и продуктивность разработки, которую даёт Flutter.

Многие из вас заинтересованы в развитии возможностей Flutter, касающихся настольных платформ, куда входят Windows, macOS, Linux. В опросах и на GitHub тема разработки настольных приложений относится к одной из самых заметных новых возможностей Flutter. В ближайшее время мы собираемся больше рассказать о том, чем мы занимаемся. Полагаем, стоит начать с обзора того, что сделано различными командами, отвечающими за возможности Flutter. Хотя поддержка настольных приложений пока находится на стадии ознакомительной технической версии, над соответствующими возможностями ведётся серьёзная работа.


Сборка приложений


Недавно мы выпустили профили и режимы сборки приложений для Windows и Linux в дополнение к существующей поддержке macOS. Например, если вы пользуетесь последней сборкой Flutter, Flutter-приложения можно компилировать в исполняемые файлы Windows командой flutter build windows. Для этого используется наш AOT-компилятор продакшн-уровня, который позволяет создавать машинный код для архитектуры x64, который можно выполнять на соответствующих компьютерах без установки Flutter.

Особенности настольных приложений


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

Мы, на уровне фреймворка, внесли во Flutter некоторые изменения, которые направлены на поддержку настольных приложений.

  • Когда вы создаёте новый проект в свежих сборках Flutter, вы увидите, что шаблон, применяемый по умолчанию, теперь включает в себя ссылку на свойство visualDensity, которое позволяет настраивать визуальную плотность расположения элементов управления на основе целевой платформы приложения. В макетах настольных приложений элементы управления упакованы плотнее, чем в мобильных приложениях. Например, это используется при настройке текстового поля (TextField), которое теперь предлагает варианты визуальной плотности compact, comfortable и standard, зависящие от параметров проекта.
  • Мы значительно улучшили поддержку мыши и клавиатуры. Сюда входит возможность работы с кодами клавиш клавиатуры в Windows, поддержка щелчка правой кнопкой мыши, поддержка изменения курсора и колёсика мыши.
  • Теперь можно, пользуясь классом Platform, узнавать о том, на какой платформе выполняется приложение. При работе приложения а Windows, macOS и Linux можно получить соответствующие результаты.
  • В самом свежем релизе мы добавили виджет NavigationRail, представляющий нечто вроде боковой панели. Этот виджет специально спроектирован для поддержки возможностей приложений, запускаемых на настольных ПК и на планшетах.

FFI


Команда Dart проделала большую работу по улучшению интерфейса внешних функций (Foreign Function Interface, FFI). Это способствует повышению скорости внедрения новых возможностей во Flutter. Например, для API, основанных на C, библиотека dart:ffi предоставляет прямой механизм для организации привязки к нативному коду. Среда выполнения Dart даёт возможность вызывать динамически связываемые библиотеки и выделять память в куче. В основе этого механизма лежат объекты Dart.

Вот фрагмент кода (здесь можно найти его полный вариант), представляющий собой простой пример, в котором показано обращение к традиционному Win32-API MessageBox(), полностью выполняемое средствами Dart-кода:

typedef MessageBoxNative = Int32 Function(IntPtr hWnd, Pointer<Utf16> lpText, Pointer<Utf16> lpCaption, Int32 uType);typedef MessageBoxDart = int Function(int hWnd, Pointer<Utf16> lpText, Pointer<Utf16> lpCaption, int uType);final user32 = DynamicLibrary.open('user32.dll');final win32MessageBox =user32.lookupFunction<MessageBoxNative, MessageBoxDart>('MessageBoxW');void showMessageBox(String message, String caption) => win32MessageBox(0,           // Нет окна-владельцаUtf16.toUtf16(message), // Текст сообщенияUtf16.toUtf16(caption), // Заголовок окна0            // Только кнопка OK);showMessageBox('Test Message', 'Window Caption'); // этот вызов выглядит как вызов обычной Dart-функции

Здесь у нас имеются typedef-псевдонимы, представляющие сигнатуры методов и для их нативного представления, и для их Dart-представления. Когда у нас есть эти псевдонимы, мы можем загрузить динамически связываемую библиотеку Windows, которая содержит реализацию функции. Для этого можно воспользоваться методом lookupFunction(), который делает мэппинг сигнатуры функции Dart на нативную функцию. И, наконец, мы, что не обязательно, описываем простую функциональную обёртку, которая упрощает работу с нативными механизмами в Dart-коде. В результате у нас получается нечто, подобное следующему рисунку.


Простой пример Windows-проекта, в котором использовано Win32-API MessageBox()

На самом деле, программисту необязательно делать всё это самому. Дело в том, что велика вероятность того, что кто-то уже подготовил код, упрощающий работу с необходимыми вам API. Здесь о FFI можно почитать подробнее.

Обновление модели плагинов


Flutter спроектирован с учётом того, чтобы у него было бы компактное ядро. Для оснащения фреймворка дополнительными возможностями соответствующий функционал оформляется в виде подключаемых плагинов и пакетов, дающих возможность интеграции с операционными системами, на которых работает Flutter. Плагины и пакеты могут создавать не только разработчики Flutter, но и другие программисты.

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

Здесь нам на помощь может прийти подход, в соответствии с которым некий базовый плагин описывает общий интерфейс, который может быть независимо реализован на разных платформах. В результате, как было описано в свежем материале про современную разработку плагинов, мы недавно внедрили во Flutter систему, которая упрощает совместную работу различных авторов над плагинами. Благодаря этой системе уже можно в явном виде указывать то, какие именно платформы поддерживаются конкретным плагином.

Мы начали использовать эту модель при работе над некоторыми базовыми плагинами. Примеры нового подхода к совместной разработке плагинов можно найти здесь.

Обратите внимание на то, что API плагинов для Windows и Linux пока ещё не устоялся, поэтому, хотя мы и советуем разработчикам знакомиться с новыми возможностями Flutter, мы пока не готовы к выпуску этих возможностей в продакшн. Мы, кроме того, работаем над добавлением на pub.dev тегов, относящихся к настольным платформам.

Запуск программ на Windows: Win32 и UWP


Один интересный аспект нашей работы, связанный с Windows, заключается в том, что здесь мы экспериментируем с различными подходами к архитектуре. Flutter, на любой платформе, внедряется в маленькое хост-приложение, являющееся контейнером (embedder) с применением подхода, напоминающего тот, который используется в игровых движках вроде Unity. Это приложение-embedder, особое для каждой платформы, предоставляет нам точку входа в проект. Оно согласовывает свои действия с операционной системой, работая со средствами рендеринга, с пользовательским вводом и с инструментами, обеспечивающими доступность контента. Оно поддерживает и цикл событий.


Windows даёт нам два способа создания подобного приложения. Первый это зрелая модель программирования Win32, которую можно использовать в качестве точки входа для Flutter-проектов. Этот подход предлагает максимальный уровень обратной совместимости с различными платформами, с такими, как Windows 7. Он позволяет создавать стандартные EXE-файлы, которые ожидает получить в результате работы над проектом множество программистов. В отличие от Win32, современная модель приложений UWP рекомендована для Windows 10. Эта модель предлагает заманчивые возможности по созданию Flutter-приложений для особых устройств, вроде Xbox, и для ОС Windows 10X, которая должна скоро выйти.

Мы, неофициально, работаем с различными разработчиками и исследуем разные решения. Мы с удовольствием более близко поработали бы с Microsoft ради того, чтобы повысить качество Windows-приложений, сделанных во Flutter. Так как семейство устройств Surface теперь включает в себя системы, основанные на Android и на Windows, мы думаем, что Flutter способен предложить Microsoft мощную платформу, которая позволяет создавать привлекательные нативные приложения для самых разных устройств.

Эксперименты с настольными приложениями


Так как возможности, о которых шла речь, пока находятся на стадии ознакомительной технической версии, API и вспомогательные инструменты всё ещё нестабильны. У нас запланировано ещё много дел, которые нужно доделать прежде чем мы переведём поддержку настольных приложений в разряд стабильных возможностей. Сюда входят, кроме того, улучшение доступности контента и локализации.

Если вы уже сейчас хотите испытать новые возможности, то вам нужно пользоваться каналом разработки. Поддержка Windows и Linux доступна в ветке master, в том месте, где идёт активная разработка Flutter. Поддержка macOS доступна в ветке dev. Там находятся более стабильные возможности, которые, правда, не рекомендуется применять в продакшне. Переключаться между каналами можно, используя команды flutter channel master или flutter channel dev. После этого нужно выполнить одну из следующих команд для того чтобы включить поддержку интересующей вас платформы:

C:\flutter> flutter config --enable-windows-desktop$ flutter config --enable-macos-desktop$ flutter config --enable-linux-desktop

Мы уже видели, как некоторые смелые разработчики начали создавать настольные приложения с помощью новых возможностей Flutter. Одно из первых таких приложений разработано для macOS. Оно называется Sharezone. Это планировщик для учеников, нацеленный на немецкий рынок образования. Сначала этот проект был выпущен в виде мобильного приложения. А недавно он вышел в настольном варианте и в варианте, предназначенном для веб.


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

Планируете ли вы использовать Flutter для разработки настольных приложений?



Подробнее..

USB Host, Blue Pill, метод деления отрезка пополам и цена на водку в СССР

19.03.2021 22:15:52 | Автор: admin

Написал недавно программный USB-HOST на esp32 для работы с клавиатурой/мышкой/джойстиком. Процессор быстрый, но нежный, 5 вольт на ножках не выдерживает. Поэтому решил переписать на stm32f103c8t6, широко известную в варианте отладочной платы "Blue Pill".

К сожалению , это весьма неторопливый по сегодняшним меркам процессор(72 MHz vs 240 у esp32 ), поэтому были сомнения , смогу ли я обеспечить необходимую точность временного интервала между битами при передаче (1.5 Mbps +/- 1.5%),что соответствует +/- 0.01uS то есть примерно один такт работы процессора. То есть процедура задежки типа :

static inline  void cpuDelay(uint32_t ticks){uint32_t stop =_getCycleCount32() + ticks;while((_getCycleCount32() - stop)&0x80000000u); // соntinue while overflow}

необходимую точность обеспечивать перестала, поэтому решил перейти к процедуре вида:

void cpuDelay(){__asm__ __volatile__("nop");__asm__ __volatile__("nop");__asm__ __volatile__("nop");__asm__ __volatile__("nop");}  

с необходимым числом "nop" - ов. Когда надоело настраивать задержку вручную и перекомпилировать код, решил написать процедуру подбирающую необходимую задержку, работающую в широком интервале частот процессора. Идея в следующем: вместо входа в процедуру с адресом cpuDelay войти в процедуру в середине, по указателю:

#define TNOP1  { __asm__ __volatile__("   nop"); }#define TNOP2  {TNOP1 TNOP1}#define TNOP4  {TNOP2 TNOP2}#define TNOP8  {TNOP4 TNOP4}#define TNOP16  {TNOP8 TNOP8}#define TNOP32  {TNOP16 TNOP16}#define TNOP64  {TNOP32 TNOP32}__volatile__ void cpuDelayBase(){TNOP64;TNOP64;TNOP64;TNOP64;END_BASE:;}void (*delay_pntA)() = &cpuDelayBase;#define cpuDelay(x) {(*delay_pntA)();}#define SIZEOF_NOP 2void setDelay(uint8_t ticks){delay_pntA = (&cpuDelayBase)+((256-ticks)*SIZEOF_NOP);}

Ну а теперь каждом запуске программы генерируется тестовая последовательность бит на 200,посылается, измеряется затраченное время и методом деления отрезка пополам процедура задержки приводится к требуемой и сохраняется в указателе delay_pntA.

Время передачи 6 бит должно составлять точно 4.0uS. 6 бит было удобно замерять на временной диаграмме передачи. В стартовой последовательности присутствует комбинация 6 фронтов , которые хорошо видно на диаграмме:

Диаграмма

При калибровке выяснилось, что есть небольшой оверхед на измерениях, и его нормированная величина 0.12uS отсюда целевое время 4.12 uS.

А причем здесь ВОДКА!!!???

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

Цену на водку люди знали лучше числа Пи. В частности, на пивзаводе где я подрабатывал электриком во время учебы, телефон фельдшера был 2-87, а электриков-механиков 3-62, а дирекции 4-12. И никто не забывал эти номера телефонов. Цену в 2.87 я не застал по причине слишком молодого возраста а 3.62 (Русская ) и 4.12 (Столичная) - уже вполне. Цена складывалась из цены собственно водки и залоговой цены бутылки в 12 копеек, поэтому такая и не круглая.

Водка русская ,этикетка:
Водка столичная , этикетка:

Это картинки отсюда

Итак 4.0 - содержание и 0.12-емкость итого 4.12 ---- Это цена водки Столичная в 1981 году.

результат прогона:

pins 8 9 1 1 is OK!cpu freq = 72.000000 MHzTIME_MULT = 43120 bits in 57.333332 uSec 2.093023 MHz  6 ticks in 2.866667 uS120 bits in 269.000000 uSec 0.446097 MHz  6 ticks in 13.450000 uS120 bits in 162.333328 uSec 0.739220 MHz  6 ticks in 8.116667 uS120 bits in 109.000000 uSec 1.100917 MHz  6 ticks in 5.450000 uS120 bits in 82.916664 uSec 1.447236 MHz  6 ticks in 4.145833 uS120 bits in 69.000000 uSec 1.739130 MHz  6 ticks in 3.450000 uS120 bits in 75.666664 uSec 1.585903 MHz  6 ticks in 3.783333 uS120 bits in 75.666664 uSec 1.585903 MHz  6 ticks in 3.783333 uS120 bits in 77.333336 uSec 1.551724 MHz  6 ticks in 3.866667 uS120 bits in 77.333336 uSec 1.551724 MHz  6 ticks in 3.866667 uSTRANSMIT_TIME_DELAY = 15 time = 4.145833 error = 0.627029%USB1: Ack = 0 Nack = 0 20 pcurrent->cb_Cmd = 2  state = 2 epCount = 0USB2: Ack = 0 Nack = 0 00 pcurrent->cb_Cmd = 0  state = 0 epCount = 0desc.bDeviceClass    = 00desc.bNumConfigurations = 01cfg.bLength         = 09cfg.wLength         = 59cfg.bNumIntf        = 02cfg.bCV             = 01cfg.bIndex          = 00cfg.bAttr           = a0cfg.bMaxPower       = 50pcurrent->epCount = 1pcurrent->epCount = 2desc.bDeviceClass    = 00desc.bNumConfigurations = 01cfg.bLength         = 09cfg.wLength         = 34cfg.bNumIntf        = 01cfg.bCV             = 01cfg.bIndex          = 00cfg.bAttr           = a0cfg.bMaxPower       = 50pcurrent->epCount = 1USB0: Ack = 6 Nack = 0 80 pcurrent->cb_Cmd = 14  state = 100 epCount = 2USB1: Ack = 6 Nack = 0 20 pcurrent->cb_Cmd = 14  state = 104 epCount = 1

P.S. Водка водкой, у нас в Израиле разрешили коноплю, забыл выложить исходники, вот.

P.P.S. не придирайтесь к коду. Это только proof of concept. Будет чиститься.

Подробнее..

Мониторинг 95 метрик PostgreSQL с помощью плагина Zabbix Agent 2

24.05.2021 20:06:11 | Автор: admin

В прошлом году популярный сервис мониторинга Zabbix представил Agent 2, призванный сократить число TCP-подключений и обеспечить удобную расширяемость за счёт плагинов на Golang.

Меня зовут Даша, и я один из разработчиков плагина мониторинга PostgreSQL для Zabbix Agent 2. В этой статье я расскажу об основных фишках использования Zabbix Agent 2 для мониторинга PostgreSQL, о принципе работы плагина, дам советы по его настройке, а также объясню на примере, как кастомизировать плагин.

Как появился плагин мониторинга PostgreSQL для Zabbix Agent 2?

В 2019 году Zabbix анонсировал выпуск нового Zabbix Agent 2. Он написан с нуля на Golang. Для мониторинга каждого приложения требуется отдельный плагин. Мы в Postgres Professional решили, что это отличная возможность применить наш многолетний опыт использования Zabbix для мониторинга PostgeSQL, и написали модуль мониторинга для Agent 2.

Как устроен мониторинг СУБД в Zabbix?

Начнём с небольшого введения в схему работы мониторинга Zabbix для новичков.

Интересную нам сейчас структуру можно разбить на две составляющие:

  1. Zabbix Server, который хранит и собирает данные.

  2. Агенты, которые устанавливаются на наблюдаемых объектах и собирают данные.

Для мониторинга каждого приложения в Zabbix Server требуется шаблон - XML-файл. В нём указаны ключи метрик (уникальные ID) и параметры их обработки.

Zabbix Agent 2 призван дать пользователю инструмент мониторинга из коробки, быстро и легко настраиваемый, а также с хорошей расширяемостью.

Как же работает PostgreSQL плагин для Zabbix Agent 2?

Есть основная функция, в которой по уникальному ключу вызываются обработчики для каждой метрики. Обработчик (handler) служит для сбора данных. Это файл, в котором указывается и выполняется SQL-запрос для получения одной или нескольких метрик. Результаты выполнения запроса записываются в переменную, которая относится к типу int, float или string. Если результат должен содержать значения сразу нескольких метрик, то он будет преобразован в JSON ещё на стадии получения запроса. Полученные результаты Zabbix Agent 2 периодически отдаёт Zabbix Server.

Плагин и обработчики находятся вот в этой папке: /plugins/postgres

Какими возможностями обладает модуль мониторинга PostgreSQL для Zabbix Agent 2?

  • Поддержка постоянного подключения к PostgreSQL.

  • Мониторинг нескольких экземпляров (instances) PostgreSQL одновременно.

  • Опции контроля и проверки метрик в реальном времени через командную строку.

  • Конфигурирование плагина через общий файл конфигурации агента.

  • Сохранение состояния между проверками.

  • Довольно простая кастомизация сбора существующих метрик.

  • Возможность писать новые плагины под свои требования.

К плагину есть официальный шаблон, который можно скачать по ссылке.

В нем есть базовые триггеры и комплексный экран, на котором отображается комбинация нескольких графиков. Всего плагин собирает более 95 метрик. Полный список всех метрик также можно найти по ссылке выше.

В веб-интерфейсе Zabbix Server можно редактировать шаблон и его составляющие для своих нужд. Что именно можно настроить?

  1. Изменить интервал сбора метрики.

  2. Добавить триггер для метрики.

  3. Добавить макрос или отредактировать существующий.

Как установить и использовать PostgreSQL-плагин для Zabbix Agent 2?

1. Создаем пользователя PostgreSQL для мониторинга:

CREATE USER 'zbx_monitor' IDENTIFIED BY '<password>';GRANT EXECUTE ON FUNCTION pg_catalog.pg_ls_dir(text) TO zbx_monitor;GRANT EXECUTE ON FUNCTION pg_catalog.pg_stat_file(text) TO zbx_monitor;

2. Редактируем pg_hba.conf, чтобы разрешить подключение от Zabbix Agent 2:

# TYPE DATABASE USER ADDRESS METHOD
host all zbx_monitor 127.0.0.1 md5

Больше информации о pg_hba.conf по ссылке.

Теперь остаётся задать параметры подключения к PostgreSQL для Zabbix Agent 2. Это можно сделать двумя способами:

  • использовать макросы для параметров подключения,

  • создать сессию.

Первый способ немного проще. Его достаточно, если нужно настроить мониторинг одного экземпляра PostgreSQL:

1. В шаблоне редактируем макрос {$PG.URI} , в котором указывается путь к PostgreSQL в формате <protocol(host:port)>.

2. Задаем макрос с именем пользователя и паролем ({$PG.USER} and {$PG.PASSWORD}). Так же можно указать макрос {$PG.DBNAME}. Этот параметр опционален для большинства метрик - если он не задан в ключе, то будет использовано имя базы, указанное в конфигурационном файле агента.

В шаблоне эти макросы уже указаны в параметрах всех ключей. Обратите внимание, что порядок указания параметров в ключе фиксирован.

Второй способ позволяет задавать параметры подключения к нескольким экземплярам PostgreSQL:

  1. Задаём параметры подключения для сессии в конфигурационном файле zabbix_agent2.conf в секции плагина Postgres: Postgres.Sessions.<Session_name>.URI,Postgres.Sessions.<Session_name>.User,Postgres.Sessions.<Session_name>.Password. Здесь вместо <Session_name> нужно указать уникальное имя новой сессии.

  2. Создаём макрос с именем сессии в шаблоне {$PG.<Session_name>}.

  3. Указываем макрос как единственный параметр для метрик в шаблоне.

Рассмотрим, как использовать плагин для сбора дополнительных метрик на примере добавления метрики uptime.

Для этого надо создать новый обработчик с запросом и добавить его ключ в основную функцию.

1. Создаем файл для получения новой метрики:

zabbix/src/go/plugins/postgres/handler_uptime.go

Подключаем пакет postgres и указываем ключ (ключи) метрик:

package postgresconst (keyPostgresUptime = "pgsql.uptime")

2. Объявляем обработчик (handler) c запросом, а так же переменную uptime, куда будет записан результат:

func uptimeHandler(ctx context.Context, conn PostgresClient,                    _ string, _ map[string]string, _ ...string) (interface{}, error){var uptime float64query := `SELECT date_part('epoch', now() - pg_postmaster_start_time());

3.Выполняем запрос, проверяем, возникла ли ошибка. Если все ОК, возвращаем переменную uptime с результатом.

row, err := conn.QueryRow(ctx, query)if err != nil {...}err = row.Scan(&uptime)if err != nil {...}return uptime, nil

4. Регистрируем ключ новой метрики:

var metrics = metric.MetricSet{....,keyPostgresUptime: metric.New("Returns uptime.",[]*metric.Param{paramURI, paramUsername, paramPassword,paramDatabase}, false),}

Собираем агент!

Новый функционал

В версии Zabbix 5.2 появилась возможность вычислять метрики, собирая результаты пользовательских запросо из отдельных SQL-файлов. При этом можно создавать даже динамические запросы. В этой версии также обновлена архитектура модуля и исправлены мелкие ошибки.Продемонстрируем, как добавлять кастомные метрики через SQL - файл на примере простого запроса с одним параметром:

  1. Создадим SQL-файл с запросом.

    $touch custom1.sql$echo SELECT id FROM my_table WHERE id=$1; > custom1.sql
    

    Тут в $1 будет передан параметр при выполнении запроса.

  2. В zabbix_agent2.conf заполним параметр Plugins.Postgres.CustomQueriesPath, указав путь к директории с SQL-файлом.

    Plugins.Postgres.CustomQueriesPath=/path/to/the/file

  3. В шаблоне для ключа pgsql.query.custom укажем имя SQL-файла и добавим дополнительные параметры для запроса, т.е. тот, который заменит $1. Стоит отметить, что для названия SQL - файла и для параметров можно также создавать макросы в шаблоне.

Дополнительные материалы

Презентации Zabbix Online Meetup, который проходил 19 июня

Статья Вадима Ипатова - одного из разработчиков Zabbix Agent 2

Шаблон для плагина мониторинга PostgreSQL

Zabbix Git для тех, кто хочет видеть больше реальных примеров и посмотреть на все SQL-запросы для получения метрик

Видео доклада на PGConf.Online 2021 "Обзор новой функциональности и настройка Zabbix Agent 2 для мониторинга PostgreSQL"

Остались вопросы?

Все вопросы можно задавать в комментариях.

Подробнее..

Appwrite, open-source бэкэнд-платформа

10.06.2021 10:07:14 | Автор: admin


В последние пару лет намечается тенденция на упрощение работы с бэкэндом, появляется всё больше low- и no-code обёрток для тех, кто хочет сэкономить на стоимости разработки. Экосистемы AWS и GCP задают стандарты в этой области, и все новые проекты стараются копировать их фичи. Проблема в том, что многие молодые проекты предлагают свою панельку с доступом к базе, cloud functions и базовой аналитикой, а потом продают её на сервисной основе. Идея так себе, потому что имея функционал, такой же как у гигантов, они, не имея такого же кредита доверия, пытаются сразу заработать на клиентах. Гораздо больше шансов на успех у тех, кто ведёт полную разработку в open-source, поощряет self-hosting и не пытается сходу монетизировать свой проект, взимая деньги только за использование в облаке. Пример такого подхода Appwrite, и благодаря ему они всего за два года обзавелись большим комьюнити и поддержкой, не успев полностью выпустить продукт и не начав зарабатывать на нём.

Функционал




Appwrite интегрируется с мобильными и веб-клиентами (всякий JS, Flutter, Swift, Objective-C, Kotlin) и предоставляет нужную среду для бэкэнд-задач (Node, .NET, Python, PHP, Ruby, Deno, ограниченно на Go и Java). Разбираем основной функционал:

Авторизация


Авторизация работает по паролю, OAuth и JWT. Вместе с последним в релизе 0.8 появились анонимные сессии в современном вебе это мастхэв фича, которой не хватает во многих аналогах. Список OAuth провайдеров захардкожен, новых нельзя добавить из интерфейса, но можно форкнуть репозиторий и дописать вручную. Сейчас доступны:

  • Amazon
  • Apple
  • BitBucket
  • Bitly
  • Box.com
  • Discord
  • Dropbox
  • Facebook
  • GitHub
  • GitLab
  • Google
  • LinkedIn
  • Microsoft
  • PayPal
  • Salesforce
  • Slack
  • Spotify
  • Tradeshift
  • Twitch
  • VK
  • Yahoo
  • Yandex
  • WordPress


Аккаунт


Appwrite поддерживает API, по которому клиент взаимодействует с учеткой пользователя, хранящейся на сервере. Пользователи могут в реальном времени обновлять информацию в аккаунте, и сохранять личные предпочтения, вроде языка интерфейса, настроек времени или темы. Поля могут быть какими угодно, и включать в себя метаданные типа геолокации, операционной системы, юзерагента и так далее. Есть отдельный эндпойнт для отображения логинов, смены пароля и прочих действий, связанных с безопасностью аккаунта.

База данных


Судя по беглому обзору зависимостей, для большинства задач здесь используется MariaDB, при этом все данные хранятся в JSON в виде коллекций. Допускается использование для коллекций как строгой структуры, так и динамической, обе с любой степенью вложенности. Есть готовые фильтры с глубоким поиском, валидация и контроль доступа с гибкой системой разрешений.

Хранилище


Для всех пользовательских файлов есть готовое хранилище, также с фильтрацией и контролем доступа и действий на уровне отдельных пользователей и групп:

  var appwrite = new window.Appwrite();  appwrite    .setEndpoint('https://localhost/v1')    .setProject('[PROJECT-ID]')  ;  var file = document.getElementById('file-input').files[0];  let read = ['*']; // wildcard read access  let write = ['user:self']; // write access only to me  appwrite.storage.createFile(file, read, write)    .then(function (response) {      console.log('file uploaded successfully');    }, function (error) {      console.log(error);    });


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

Вебхуки


Кастомное поведение после триггера нужного эндпойнта. Вы знаете, как работают вебхуки.



Список событий очень большой, покрываются разделы account, database, functions, storage, users и teams.

Задачи и функции


Серверные функции, запускающиеся по расписанию или по определённым хукам. Задачи поддерживают кастомные заголовки HTTP и авторизацию для разных сценариев. Функции исполняют кастомный код в разных окружениях (Node, .NET, Python, PHP, Ruby, Deno), с нужными ограничениями по правам и доступом к указанным переменным окружения. Очень гибкая штука.

Установка


Она тут предельно простая, путь от голой VPS до работающей платформы займёт минуту.
Appwrite работает в контейнерах и может быть использован любой системой оркестрации, мы же рассмотрим самую базовую установку.

Если нет докера, ставим его:

  sudo apt-get update  sudo apt-get install \    apt-transport-https \    ca-certificates \    curl \    gnupg \    lsb-release  curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg  echo \    "deb [arch=amd64 signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu \    $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null  sudo apt-get update  sudo apt-get install docker-ce docker-ce-cli containerd.io


Затем качаем и запускаем установку:

  docker run -it --rm \    --volume /var/run/docker.sock:/var/run/docker.sock \    --volume "$(pwd)"/appwrite:/usr/src/code/appwrite:rw \    --entrypoint="install" \    appwrite/appwrite:0.8.0


Проходим установочный скрипт (если хочется просто пощупать, достаточно указать везде дефолтные значения) и переходим в панельку (она будет жить на тех портах, которые мы укажем при установке, по дефолту localhost:80 / :443).

Заключение


Appwrite не похожа на другие Backend-as-a-Service проекты, стремящиеся к быстрой монетизации. Здесь разработчики сначала фокусируются на задачах, реализации и тестировании фич, и ещё даже не подошли к сервисной модели (которая, тем не менее, планируется). Проект опенсорсный и поддержка self-hosting заявлена вечная, так что сейчас Appwrite выглядит отличным решением, которое можно как минимум потестить на пет-проекте.

Ссылки:




На правах рекламы


Наши эпичные серверы вы можете использовать для любых задач разработки, размещения сайтов, использования под VPN и даже получить удалённую машину на Windows! Идей может быть много и любую из них поможем воплотить в реальность!

Присоединяйтесь к нашему чату в Telegram.

Подробнее..

Приглашаем на конкурс разработки open-source пакетов на Robot Operating System

02.10.2020 18:16:15 | Автор: admin
Как ни странно современная мировая робототехника на данный момент развивается на таком феномене как ROS и open-source. Да, по каким то причинам это в России непонято и малоизвестно. Но мы русскоязычное сообщество ROS, пытаемся это изменить и поддержать тех энтузиастов-робототехников, которые пишут открытый код для роботов. В этой статье хотелось бы раскрыть работу над таким начинанием в виде конкурса ROS пакетов, который идет сейчас.




Краткая справка для тех, кто в первый раз сталкивается с аббревиатурой ROS: ROS это программный фреймворк для написания различных компонентов роботов, который ускоряет разработку в разы. Но это не все, ROS это на самом деле целая идеология разработки роботов и глобальное сообщество робототехников, разделяющих ценности open-source, переиспользования компонентов, обмена знаниями и опытом. Например, вот эти роботы сделаны с использованием ROS:


Мы провели уже три российских ROS Meetup, наше сообщество в telegram более 700 человек. У нас есть ROS комитет, который занимается организацией мероприятий. Мы неоднократно дискутировали на тему того, как же нам развивать сообщество, много копий было сломлено, прежде чем мы пришли к консенсусу. Все сошлись на том, что нас мотивирует соперничество: кто лучший робототехник, ученый, кто лучше пишет код, кто лучше знает Machine Learning и т.д. Поэтому нам показалось логичным провести конкурс, где робототехники и программисты смогут проявить свои способности и поделиться своими достижениями с другими. Так в общих обсуждениях родились положения Конкурса открытых пакетов ROS. И мы приглашаем принять в этой нашей инициативе участие как в качестве судей жюри, так и участников конкурса.

Основными контрибьюторами ROS пакетов например являются такие университеты как Stanford, MIT, TUM. Студенты у которых таким образом образуется хорошее портфолио на github, потом попадают на работу в такие компании как Intel, Samsung, Google, которые также являются контрибьюторами в ROS.

Многие студенты и ученые которые писали ROS в WillowGarage, через некоторое время организовали свои стартапы известные в робототехнике например Fetch Robotics, Savioke.

Если посмотреть на статистику трафика официального репозитория пакетов ROS, то видно, что Россия за последние годы выпала даже из топ-15. И все, что есть популярного в сообществе, оно только от зарубежных авторов. Пора изменить эту ситуацию, так как у нас в стране тоже есть талантливые робототехники.

Больше информации о статистике вы можете получить в этом отчете и на сайте аналитики.

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

Нас поддержала компания NVIDIA, за что ей огромная благодарность. Теперь у нас есть три приза для победителей: Jetson XAVIER, TX2, Nano.


Хочется отметить, что мы про открытое сообщество, состоящее из активных членов, которые самостоятельно и независимо строят русское сообщество робототехников, поэтому мы проводим открытые собрания в ZOOM, в которых может принять участие любой желающий. Предлагаю в комментариях открыть дискуссию по поводу развития сообщества, проведения ROS meetup'ов, конкурса пакетов. Что вы об этом думаете? Какие у вас предложения? Критика? Кто хочет присоединиться? Пишите, мы будем рады любому мнению и предложению!

Правила участия в конкурсе:


  1. Подать заявку с темой вашего пакета. Форма регистрации.
  2. Написать пакет под ROS (принимаются как пакеты под ROS1 так и под ROS2) на свободную тематику(жюри будет оценивать пакеты в первую очередь с точки зрения их применимости в робототехнике, беспилотных автомобилях).
  3. Выложить пакет с описанием на Github, снабдив исчерпывающей документацией (в т.ч. и на русском языке).
  4. Приложить видео-презентацию работы вашего пакета.
  5. Приложить набор тестов с инструкцией по запуску, демонстрирующих исполнение его функционала. Например это может быть .bag файл или модель робота в среде Gazebo, на которых можно запустить предложенный пакет.


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

Критерии оценки:


  1. Практическая значимость
    Оценивается актуальность, общая полезность пакета для ROS сообщества в целом, применимость в практических задачах, связанных с робототехникой, а также его пере используемость в задачах других участников сообщества. Т.е. если вы делаете пакет управления ракетным двигателем на основе ROS, то его практическая значимость для сообщества явно не высока (на данном этапе развития технологий).
  2. Технический уровень (элегантность решения). Сложность и обоснованность примененных технических решений. Применение нелинейных алгоритмов управления и сложного математического аппарата будет плюсом, но только в том случае, если оно оправдано и ведет к улучшению технических характеристик. И наоборот, усложнение ради усложнения, будет приводить к снижению оценок.
  3. Уровень исполнения
    Работоспособность заявленного функционала. Репрезентативность предложенных тестов. Исчерпываемость технической документации и читаемость кода также оценивается данным критерием.


Этапы оценки


  1. На показе MVP жюри оценивают видео-презентации по первым двум критериям по 20 балльной шкале.
  2. При сдаче результатов присланные результаты оцениваются по каждому критерию по 100 балльной шкале.
  3. Финальный результат получается путем сложения всех полученных оценок.


Народное голосование


Третий победитель конкурса будет выбран по итогам голосования участников сообщества. Этот участник будет награжден Nvidia Jetson Nano (голосование проводится среди участников, не получивших награды от жюри).

Положения конкурса открытых пакетов ROS.

Подведение итогов конкурса будет приурочено к следующему ROS Meetup. Предварительная дата ROS Meetup начало декабря. Так что у тебя есть все шансы принять участие и выиграть приз!

В организационный комитет конкурса входит Лаборатория робототехники Сбер.

Предварительный состав жюри: Алексей Бурков, Александр Богословский(МИРЭА), Лада Толстенко(МИСиС), Кирилл Артемов(ИТМО). Приглашаются дополнительные участники в жюри.

И напоминаем, что найти единомышленников по разработке на ROS в России, узнать последние ROS-новости можно в нашем телеграм-канале ROS Russia community.
Подробнее..

BigBug Data анализируем исходный код Apache Flink

15.12.2020 10:11:51 | Автор: admin
image1.png

Приложения, использующиеся в области Big Data, обрабатывают огромные объемы информации, причем часто это происходит в реальном времени. Естественно, такие приложения должны обладать высокой надежностью, чтобы никакая ошибка в коде не могла помешать обработке данных. Для достижения высокой надежности необходимо пристально следить за качеством кода проектов, разрабатываемых для этой области. Решением данной проблемы и занимается статический анализатор PVS-Studio. Сегодня в качестве подопытного для анализатора был выбран проект Apache Flink, разработанный организацией Apache Software Foundation одним из лидеров на рынке ПО для Big Data.

Что же такое Apache Flink? Это open-source фреймворк для распределенной обработки больших объемов данных. Он был разработан как альтернатива Hadoop MapReduce в 2010 году в Техническом университете Берлина. Основу фреймворка составляет движок распределенного исполнения приложений пакетной и потоковой обработки данных. Написан этот движок на языках Java и Scala. Сегодня Apache Flink возможно использовать в проектах, написанных с использованием языков Java, Scala, Python и даже SQL.

Анализ проекта


Загрузив исходный код проекта, я запустил сборку проекта командой 'mvn clean package -DskipTests', указанной в инструкции на GitHub. Пока шла сборка, я при помощи утилиты CLOC выяснил что в проекте 10838 Java-файлов, в которых имеется около 1.3 миллионов строк кода. Причем тестовых Java файлов оказалось аж 3833 штуки, а это больше 1/3 всех Java файлов. Также я заметил, что в проекте используется статический анализатор кода FindBugs и утилита Cobertura, предоставляющая информацию о покрытии кода тестами. Учитывая все это, становится ясно, что разработчики Apache Flink тщательно следили за качеством кода и покрытием тестами при разработке.

После удачной сборки я открыл проект в IntelliJ IDEA и запустил анализ при помощи плагина PVS-Studio for IDEA and Android Studio. Предупреждения анализатора распределились следующим образом:

  • 183 High;
  • 759 Medium;
  • 545 Low.

Примерно 2/3 срабатываний анализатора PVS-Studio выданы на тестовые файлы. Если учесть этот факт и размер кодовой базы проекта, то можно сказать, что разработчикам Apache Flink удалось сохранить качество кода на высоте.

Изучив предупреждения анализатора более подробно, я выбрал самые интересные на мой взгляд. Так давайте же посмотрим, что же удалось найти PVS-Studio в данном проекте!


Всего лишь немного невнимательности


V6001 There are identical sub-expressions 'processedData' to the left and to the right of the '==' operator. CheckpointStatistics.java(229)

@Overridepublic boolean equals(Object o) {  ....  CheckpointStatistics that = (CheckpointStatistics) o;  return id == that.id &&    savepoint == that.savepoint &&    triggerTimestamp == that.triggerTimestamp &&    latestAckTimestamp == that.latestAckTimestamp &&    stateSize == that.stateSize &&    duration == that.duration &&    alignmentBuffered == that.alignmentBuffered &&    processedData == processedData &&                // <=    persistedData == that.persistedData &&    numSubtasks == that.numSubtasks &&    numAckSubtasks == that.numAckSubtasks &&    status == that.status &&    Objects.equals(checkpointType, that.checkpointType) &&    Objects.equals(      checkpointStatisticsPerTask,       that.checkpointStatisticsPerTask);}

На фоне других выражений в return данная ошибка не сильно бросается в глаза. При переопределении метода equals для класса CheckpointStatistics программист допустил ошибку в выражении processedData == processedData, которое не имеет смысла, потому что всегда истинно. Аналогично остальным выражениям в return должны были сравниваться поля текущего объекта this и объекта that: processedData == that.processedData. Данная ситуация это один из типичных паттернов ошибок, найденных в функциях сравнения, которые подробно описаны в статье "Зло живет в функциях сравнения". Вот так и получается, что всего лишь "немного невнимательности" сломало логику проверки эквивалентности объектов класса CheckpointStatistics.

Выражение всегда истинно


V6007 Expression 'input2.length > 0' is always true. Operator.java(283)

public static <T> Operator<T> createUnionCascade(Operator<T> input1,                                                  Operator<T>... input2) {  if (input2 == null || input2.length == 0)   {    return input1;                                // <=  }   else if (input2.length == 1 && input1 == null)   {    return input2[0];  }  ....  if (input1 != null)   {    ....  }   else if (input2.length > 0 && input2[0] != null) // <=  {    ....  }   else   {    ...  }}

В этом методе анализатор оказался внимательнее человека, о чем и решил сообщить в своей своеобразной манере, указав на то, что выражение input2.length > 0 всегда будет истинно. Причина в том, что если длина массива input2 будет равна 0, то условие input2 == null || input2.length == 0 первого if в методе будет истинно, и выполнение метода будет прервано, так и не дойдя до строчки с выражением input2.length > 0.

Всевидящий анализатор


V6007 Expression 'slotSharingGroup == null' is always false. StreamGraphGenerator.java(510)

private <T> Collection<Integer> transformFeedback(...){  ....  String slotSharingGroup = determineSlotSharingGroup(null, allFeedbackIds);  if (slotSharingGroup == null)  {    slotSharingGroup = "SlotSharingGroup-" + iterate.getId();  }  ....}

Анализатор сообщил, что выражение slotSharingGroup == null всегда ложно. А это наталкивает на мысль о том, что метод determineSlotSharingGroup никогда не вернет null. Неужели анализатор настолько умный, что смог вычислить все значения, которые может вернуть этот метод? Давайте-ка лучше все перепроверим сами:

public class StreamGraphGenerator {  ....  public static final String DEFAULT_SLOT_SHARING_GROUP = "default";  ....  private String determineSlotSharingGroup(String specifiedGroup,                                            Collection<Integer> inputIds)   {    if (specifiedGroup != null)    {      return specifiedGroup; // <= 1    }    else    {      String inputGroup = null;      for (int id: inputIds)      {        String inputGroupCandidate = streamGraph.getSlotSharingGroup(id);        if (inputGroup == null)        {          inputGroup = inputGroupCandidate;        }        else if (!inputGroup.equals(inputGroupCandidate))        {          return DEFAULT_SLOT_SHARING_GROUP; // <= 2        }      }      return inputGroup == null              ? DEFAULT_SLOT_SHARING_GROUP              : inputGroup; // <= 3    }  }  ...}

По порядку пройдемся по всем return и посмотрим, что же может вернуть данный метод:

  • В первом return вернется аргумент метода specifiedGroup, но только если он не будет равен null.
  • return в цикле for вернет значение статического финального поля DEFAULT_SLOT_SHARING_GROUP, инициализированного строковым литералом;
  • И последний return в методе вернет значение переменной inputGroup, если оно не будет равно null. В противном случае вернется значение поля DEFAULT_SLOT_SHARING_GROUP.

Получается, что анализатор действительно смог вычислить невозможность возврата null из метода determineSlotSharingGroup и предупредил нас об этом, указав на бессмысленность проверки slotSharingGroup == null. И хотя данная ситуация не является ошибочной, однако подобная дополнительная защита анализатора сможет обнаружить ошибку в каком-нибудь другом случае. Например, когда необходимо, чтобы метод возвращал null при определенных условиях.

Собери их всех


V6007 Expression 'currentCount <= lastEnd' is always true. CountSlidingWindowAssigner.java(75)

V6007 Expression 'lastStart <= currentCount' is always true. CountSlidingWindowAssigner.java(75)

@Overridepublic Collection<CountWindow> assignWindows(....) throws IOException {  Long countValue = count.value();  long currentCount = countValue == null ? 0L : countValue;  count.update(currentCount + 1);  long lastId = currentCount / windowSlide;  long lastStart = lastId * windowSlide;  long lastEnd = lastStart + windowSize - 1;  List<CountWindow> windows = new ArrayList<>();  while (lastId >= 0 &&          lastStart <= currentCount &&          currentCount <= lastEnd)   {    if (lastStart <= currentCount && currentCount <= lastEnd) // <=    {      windows.add(new CountWindow(lastId));    }    lastId--;    lastStart -= windowSlide;    lastEnd -= windowSlide;  }  return windows;}

Анализатор предупреждает, что выражения currentCount <= lastEnd и lastStart <= currentCount всегда истинны. И ведь действительно, если посмотреть на условие цикла while, то там имеются точно такие же выражения. Это значит, что внутри цикла эти выражения всегда будут истинны, поэтому в список windows будут добавлены все объекты типа CountWindow созданные в цикле. Вариантов появления этой бессмысленной проверки множество, и первое, что приходит в голову, либо артефакт рефакторинга, либо перестраховка разработчика. Но это может быть и ошибка, если хотелось проверить что-то иное...

Некорректный порядок аргументов


V6029 Possible incorrect order of arguments passed to method: 'hasBufferForReleasedChannel', 'hasBufferForRemovedChannel'. NettyMessageClientDecoderDelegateTest.java(165), NettyMessageClientDecoderDelegateTest.java(166)

private void testNettyMessageClientDecoding(       boolean hasEmptyBuffer,       boolean hasBufferForReleasedChannel,       boolean hasBufferForRemovedChannel) throws Exception {  ....  List<BufferResponse> messages = createMessageList (    hasEmptyBuffer,    hasBufferForReleasedChannel,    hasBufferForRemovedChannel);  ....}

Отсутствие в Java возможности вызова метода с именованными параметрами иногда играет злую шутку с разработчиками. Именно это и произошло при вызове метода createMessageList, на который указал анализатор. При взгляде на определение этого метода становится ясно, что параметр hasBufferForRemovedChannel должен передаваться в метод перед параметром hasBufferForReleasedChannel:

private List<BufferResponse> createMessageList(  boolean hasEmptyBuffer,  boolean hasBufferForRemovedChannel,  boolean hasBufferForReleasedChannel) {  ....  if (hasBufferForReleasedChannel) {    addBufferResponse(messages,                       releasedInputChannelId,                       Buffer.DataType.DATA_BUFFER,                       BUFFER_SIZE,                       seqNumber++);  }  if (hasBufferForRemovedChannel) {    addBufferResponse(messages,                       new InputChannelID(),                       Buffer.DataType.DATA_BUFFER,                       BUFFER_SIZE,                       seqNumber++);  }  ....  return messages;}

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

Ох уж этот копипаст


V6032 It is odd that the body of method 'seekToFirst' is fully equivalent to the body of another method 'seekToLast'. RocksIteratorWrapper.java(53), RocksIteratorWrapper.java(59)

public class RocksIteratorWrapper implements RocksIteratorInterface, Closeable {  ....  private RocksIterator iterator;  ....  @Override  public void seekToFirst() {    iterator.seekToFirst(); // <=    status();   }    @Override  public void seekToLast() {    iterator.seekToFirst();  // <=    status();  }    ....}

Тела методов seekToFirst и seekToLast совпадают. Причем оба метода используются в коде.

Что-то здесь нечисто! И действительно, если посмотреть какие методы имеются у объекта iterator, то станет понятно, какую ошибку помог найти анализатор:

public class RocksIterator extends AbstractRocksIterator<RocksDB>{  ....}public abstract class AbstractRocksIterator<...> extends ...{  ....  public void seekToFirst() // <=  {    assert this.isOwningHandle();    this.seekToFirst0(this.nativeHandle_);  }    public void seekToLast() // <=  {    assert this.isOwningHandle();    this.seekToLast0(this.nativeHandle_);  }  ....}

Получается, что метод seekToLast класса RocksIteratorWrapper был создан копипастом метода seekToFirst этого же класса. Однако по каким-то причинам разработчик забыл заменить вызов метода seekToFirst у iterator на seekToLast.

Путаница с форматными строками


V6046 Incorrect format. A different number of format items is expected. Arguments not used: 1. UnsignedTypeConversionITCase.java(102)

public static void prepareMariaDB() throws IllegalStateException {  ....  if (!initDbSuccess) {    throw new IllegalStateException(      String.format(        "Initialize MySQL database instance failed after {} attempts," + // <=        " please open an issue.", INITIALIZE_DB_MAX_RETRY));  }}

Форматные строки метода String.format и логгеров в Java различаются. В отличие от форматной строки метода String.format, где места подстановки аргументов указываются при помощи символа '%', в форматных строках логгеров вместо этого используется комбинация символов '{}'. Из-за этой путаницы и произошла эта ошибка. В качестве форматной строки в метод String.format передается строка, которая, скорее всего, была скопирована из другого места, где она использовалась в каком-нибудь логгере. В результате, в сообщении исключения IllegalStateException не произойдет подстановки значения поля INITIALIZE_DB_MAX_RETRY вместо '{}', и тот, кто поймает или залоггирует это исключение, так и не узнает сколько попыток подключения к БД было произведено.

Ненормальное распределение


V6048 This expression can be simplified. Operand 'index' in the operation equals 0. CollectionUtil.java(76)

public static <T> Collection<List<T>> partition(Collection<T> elements,                                                 int numBuckets) {  Map<Integer, List<T>> buckets = new HashMap<>(numBuckets);    int initialCapacity = elements.size() / numBuckets;  int index = 0;  for (T element : elements)   {    int bucket = index % numBuckets;                                 // <=    buckets.computeIfAbsent(bucket,                             key -> new ArrayList<>(initialCapacity))           .add(element);   }  return buckets.values();}

Метод partition разделяет элементы из коллекции elements на несколько сегментов, после чего возвращает эти сегменты. Однако из-за ошибки, на которую указал анализатор, никакого разделения происходить не будет. Выражение, при помощи которого определяют номер сегмента index % numBuckets, всегда будет равно 0, потому что index всегда равен 0. Изначально я подумал, что код этого метода был подвергнут рефакторингу, в результате которого забыли добавить увеличение переменной index в цикле for. Но, просмотрев коммит, где этот метод был добавлен, выяснилось, что эта ошибка появилась вместе с этим методом. Исправленный вариант кода:

public static <T> Collection<List<T>> partition(Collection<T> elements,                                                 int numBuckets) {  Map<Integer, List<T>> buckets = new HashMap<>(numBuckets);    int initialCapacity = elements.size() / numBuckets;  int index = 0;  for (T element : elements)   {    int bucket = index % numBuckets;     buckets.computeIfAbsent(bucket,                             key -> new ArrayList<>(initialCapacity))           .add(element);    index++;  }  return buckets.values();}

Несовместимый тип


V6066 The type of object passed as argument is incompatible with the type of collection: String, ListStateDescriptor<NextTransactionalIdHint>. FlinkKafkaProducer.java(1083)

public interface OperatorStateStore {  Set<String> getRegisteredStateNames();}public class FlinkKafkaProducer<IN> extends ....{  ....  private static final   ListStateDescriptor<FlinkKafkaProducer.NextTransactionalIdHint>  NEXT_TRANSACTIONAL_ID_HINT_DESCRIPTOR = ....;  @Override  public void initializeState(FunctionInitializationContext context)....   {    ....    if (context.getOperatorStateStore()               .getRegisteredStateNames()               .contains(NEXT_TRANSACTIONAL_ID_HINT_DESCRIPTOR))    // <=    {       migrateNextTransactionalIdHindState(context);    }    ....  }}

Выражение, на которое указал анализатор, всегда будет ложно, а значит вызова метода migrateNextTransactionalIdHindState никогда не произойдет. Как же так случилось, что кто-то ищет в коллекции типа Set<String> элемент совсем другого типа ListStateDescriptor<FlinkKafkaProducer.NextTransactionalIdHint>? Без помощи анализатора такая ошибка, скорее всего, очень долго бы жила в коде, так как в глаза она не бросается и без тщательной проверки данного метода её просто невозможно найти.

Неатомарное изменение переменной


V6074 Non-atomic modification of volatile variable. Inspect 'currentNumAcknowledgedSubtasks'. PendingCheckpointStats.java(131)

boolean reportSubtaskStats(JobVertexID jobVertexId, SubtaskStateStats subtask) {  TaskStateStats taskStateStats = taskStats.get(jobVertexId);  if (taskStateStats != null && taskStateStats.reportSubtaskStats(subtask)) {    currentNumAcknowledgedSubtasks++;                // <=    latestAcknowledgedSubtask = subtask;    currentStateSize += subtask.getStateSize();      // <=    long processedData = subtask.getProcessedData();    if (processedData > 0) {      currentProcessedData += processedData;         // <=    }    long persistedData = subtask.getPersistedData();    if (persistedData > 0) {      currentPersistedData += persistedData;         // <=    }    return true;  } else {    return false;  }}

Плюс еще 3 предупреждения анализатора в том же самом методе:

  • V6074 Non-atomic modification of volatile variable. Inspect 'currentStateSize'. PendingCheckpointStats.java(134)
  • V6074 Non-atomic modification of volatile variable. Inspect 'currentProcessedData'. PendingCheckpointStats.java(138)
  • V6074 Non-atomic modification of volatile variable. Inspect 'currentPersistedData'. PendingCheckpointStats.java(143)

Анализатор подсказал, что аж 4 volatile поля в методе изменяются неатомарно. И анализатор, как всегда, оказывается прав, потому что операции ++ и +=, на самом деле, это последовательность из нескольких операций чтения-изменения-записи. Как известно, значение volatile поля видно всем потокам, а это значит, что из-за состояния гонки часть изменений поля может быть утеряна. Более подробную информацию об этом вы можете прочитать в описании диагностики.

Заключение


В Big Data проектах надежность является одним из ключевых требований, поэтому за качеством кода в них необходимо пристально следить. В этом разработчикам Apache Flink помогали несколько инструментов, а также они написали значительное количество тестов. Однако даже в таких условиях анализатор PVS-Studio смог найти ошибки. От ошибок невозможно полностью избавиться, но использование различных инструментов статического анализа кода регулярно позволит приблизится к этому идеалу. Да-да, именно регулярно. Только при регулярном использовании статический анализ показывает свою эффективность, о чём более подробно рассказано в этой статье.


Если хотите поделиться этой статьей с англоязычной аудиторией, то прошу использовать ссылку на перевод: Valery Komarov. Big / Bug Data: Analyzing the Apache Flink Source Code.
Подробнее..

Начало работы с Windows Terminal

22.12.2020 10:22:28 | Автор: admin
Привет, Хабр! Сегодня делимся гайдом по началу работы с Windows Terminal. Да, поскольку он о начале работы с инструментом, в основном в материале описываются какие-то базовые моменты. Но я думаю, что и профессионалы смогут подчерпнуть для себя что-то полезное, как минимум из списка полезных ссылок в конце статьи. Заглядывайте под кат!



Установка


Windows Terminal доступен в двух разных сборках: Windows Terminal и Windows Terminal Preview. Обе сборки доступны для загрузки в Microsoft Store и на странице выпусков GitHub.

Требования


Для запуска любой сборки Windows Terminal на вашем компьютере должна быть установлена Windows 10 1903 или более поздняя версия.

Windows Terminal Preview


Windows Terminal Preview это сборка, в которой в первую очередь появляются новые функции. Эта сборка предназначена для тех, кто хочет видеть новейшие функции сразу после их выпуска. Эта сборка имеет ежемесячный цикл выпуска с новейшими функциями каждый месяц.

Windows Terminal


Терминал Windows это основная сборка продукта. Функции, которые поступают в Windows Terminal Preview, появляются в Windows Terminal через месяц эксплуатации. Это позволяет проводить обширное тестирование ошибок и стабилизацию новых функций. Эта сборка предназначена для тех, кто хочет получить функции после того, как они были изучены и протестированы сообществом Preview.

Первый запуск


После установки терминала вы можете запустить приложение и сразу приступить к работе с командной строкой. По умолчанию терминал включает профили Windows PowerShell, Command Prompt и Azure Cloud Shell в раскрывающемся списке. Если на вашем компьютере установлены дистрибутивы Подсистемы Windows для Linux (WSL), они также должны динамически заполняться как профили при первом запуске терминала.

Профили


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



Дефолтный профиль


При первом запуске Windows Terminal в качестве профиля по умолчанию устанавливается Windows PowerShell. Профиль по умолчанию это профиль, который всегда открывается при запуске терминала, и это профиль, который открывается при нажатии кнопки новой вкладки. Вы можете изменить профиль по умолчанию, установив defaultProfile на имя вашего предпочтительного профиля в файле settings.json.

"defaultProfile": "PowerShell"

Добавление нового профиля


Новые профили можно добавлять динамически с помощью терминала или вручную. Терминал Windows автоматически создаст профили для распределений PowerShell и WSL. Эти профили будут иметь свойство source, которое сообщает терминалу, где он может найти соответствующий исполняемый файл.

Если вы хотите создать новый профиль вручную, вам просто нужно сгенерировать новый guid, указать name и предоставить исполняемый файл для свойства commandline.

Примечание. Вы не сможете скопировать свойство source из динамически созданного профиля. Терминал просто проигнорирует этот профиль. Вам нужно будет заменить source на commandline и предоставить исполняемый файл, чтобы дублировать динамически созданный профиль.

Структура Settings.json


В Терминал Windows включены два файла настроек. Один из них defaults.json, который можно открыть, удерживая клавишу Alt и нажав кнопку Настройки в раскрывающемся списке. Это неизменяемый файл, который включает в себя все настройки по умолчанию, которые поставляются с терминалом. Второй файл settings.json, в котором вы можете применить все свои пользовательские настройки. Доступ к нему можно получить, нажав кнопку Настройки в раскрывающемся меню.

Файл settings.json разделен на четыре основных раздела. Первый это объект глобальных настроек, который находится в верхней части файла JSON внутри первого {. Примененные здесь настройки повлияют на все приложение.

Следующим основным разделом файла является объект profiles. Объект profiles разделен на два раздела: defaults и list. Вы можете применить настройки профиля к объекту defaults, и они будут применяться ко всем профилям в вашем list. list содержит каждый объект профиля, который представляет профили, описанные выше, и это элементы, которые появляются в раскрывающемся меню вашего терминала. Настройки, примененные к отдельным профилям в списке, имеют приоритет над настройками, примененными в разделе defaults.

Далее в файле расположен массив schemes. Здесь можно разместить собственные цветовые схемы. Отличный инструмент, который поможет вам создать свои собственные цветовые схемы, это terminal.sexy.

Наконец, в нижней части файла находится массив actions. Перечисленные здесь объекты добавляют действия в ваш терминал, которые можно вызывать с клавиатуры и/или находить внутри палитры команд.

Базовая кастомизация


Вот несколько основных настроек, которые помогут вам начать настройку вашего терминала.

Фон


Одна из самых популярных настроек настраиваемое фоновое изображение. Это настройка профиля, поэтому ее можно либо поместить внутри объекта defaults внутри объекта profiles, чтобы применить ко всем профилям, либо внутри определенного объекта профиля.

"backgroundImage": "C:\Users\admin\background.png"

Параметр backgroundImage принимает расположение файла изображения, которое вы хотите использовать в качестве фона вашего профиля. Допустимые типы файлов: .jpg, .png, .bmp, .tiff, .ico и .gif.



Цветовая схема


Список доступных цветовых схем можно найти на нашем сайте документации. Цветовые схемы применяются на уровне профиля, поэтому вы можете поместить настройку внутри значений по умолчанию или в конкретный объект профиля.

"colorScheme": "COLOR SCHEME NAME"

Этот параметр принимает название цветовой схемы. Вы также можете создать свою собственную цветовую схему и поместить ее в список schemes, а затем установить в настройках профиля имя этой новой схемы, чтобы применить ее.

Начертание шрифта


По умолчанию Windows Terminal использует Cascadia Mono в качестве шрифта. Начертание шрифта это настройка уровня профиля. Вы можете изменить шрифт, установив fontFace на имя шрифта, который вы хотите использовать.

"fontFace": "FONT NAME"`

Совет: Терминал Windows также поставляется с начертанием шрифта Cascadia Code, который включает программные лигатуры (см. Gif ниже). Если вы используете Powerline, Cascadia Code также поставляется в PL-версии, которую можно загрузить с GitHub.



Полезные ресурсы


Докуметация Windows Terminal
Скотт Хансельман: как сделать красивым Windows Terminal с помощью Powerline, шрифтов Nerd, кода Cascadia, WSL и oh-my-posh
Скотт Хансельман: Как настроить терминал с помощью Git Branch, Windows Terminal, PowerShell, + Cascadia Code!
Скотт Хансельман: Windows Terminal Feature PREVIEW Кастомизируйте свои привязки клавиш, цветовые схемы, панели, и многое другое!
>_TerminalSplash темы Windows Terminal
Подробнее..

Приглашаем на Live-Вебинар Автоматизация процессов с GitLab CICD 29 Окт., 1500 -1600 (MST)

06.10.2020 12:23:44 | Автор: admin

Расширяем знания и переходим на следующий уровень.




Вы только начинаете изучать основные принципы Continuous Integration / Continuous Delivery или написали уже не один десяток пайплайнов? Вне зависимости от уровня Ваших знаний, присоединяйтесь к нашему вебинару, чтобы на практике разобраться, почему тысячи организаций по всему миру выбирают GitLab в качестве ключевого инструмента для автоматизации IT процессов.

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

В этом вебинаре мы разберем:
  • Основные элементы автоматизации в GitLab CI/CD
  • Принципы Pipelines-as-Code
  • AutoDevOps автоматический полноценный CI/CD конвейер, который самостоятельно автоматизирует весь процесс
  • Расширенные настройки и оптимизацию GitLab CI/CD

Также мы вметсе на конкретных примерах покажем, как использование GitLab из коробки поможет значительно ускорить цикл разработки программного обеспечения.
Подробнее..

Опыт Тинькофф Оплаты улучшили мобильный SDK и сделали оплату в интернете еще удобнее

23.10.2020 10:09:29 | Автор: admin
image

Привет! В начале лета мы обновили Tinkoff Acquiring SDK и зарелизили вторую версию для Android и iOS. Tinkoff Acquiring SDK это open-source-библиотека для мобильных платформ. С ней удобно принимать оплату в приложениях. В статье хочу рассказать, чем это круто для владельцев интернет-бизнеса и их покупателей.

Что улучшили


Проблемы, которые были в прошлой версии:

  1. Сложная архитектура библиотеки. Из-за этого разработчики тратили много времени на поиск и исправление ошибок.
  2. Избыточный API библиотеки. Разработчики часто ошибались при интеграции SDK.
  3. Не соблюдаются требования Apple Pay и Google Pay по настройке этих видов оплаты.


Что мы сделали, чтобы это исправить, и что ещё нового появилось:

  1. Полностью переписали код для обеих операционных систем. Провели рефакторинг кода и добавили много полезных функций. Теперь библиотеки работают на Kotlin и Swift.
  2. Добавили Систему быстрых платежей. Можно платить через СБП по QR-коду или через Мгновенный счёт по кнопке на экране оплаты.
  3. Поддерживаем решение all in one. Всё в одном месте: привязка карт, бесконтактная оплата, быстрые платежи по QR-коду.
  4. Обновили дизайн. Добавили темную тему. Но и не забыли про кастомизацию темы на Android, которая была в прошлой версии.
  5. Продолжаем поддерживать предыдущую версию. Подумали обо всех клиентах, не обделили старых и новых SDK работает одинаково и в первой, и во второй версии. Но рекомендуем все же обновиться, чтобы пользоваться новыми функциями.

А теперь обо всем по порядку.

Для чего нужен Tinkoff Acquiring SDK


SDK интегрирует в любое мобильное приложение интернет-эквайринг и подходит бизнесу, который принимает онлайн-платежи в приложениях: интернет-магазинам, маркетплейсам, тревел-компаниям, клинингу, стриминговым площадкам, фриланс-биржам.

Решение помогает покупателям привязать карту в приложении и оплачивать покупки в пару кликов, а продавцам сделать приложение более функциональным и не терять клиентов на этапе оформления.

Вот что умеет SDK:

Сканировать и распознавать банковские карты с помощью камеры и NFC и сохранять их. Покупатель один раз привязывает карту в приложении, а потом вводит только CVC-код, чтобы оплатить покупки.

Принимать все виды платежей в приложении: по банковским картам, через личный кабинет tinkoff.ru, Apple Pay и Google Pay. В том числе рекуррентные платежи то есть автоматические, по подписке, без повторного ввода реквизитов.
imageimage

Оплачивать с помощью Системы быстрых платежей (СБП). SDK помогает принимать платежи через СБП: покупатель оплачивает товары по QR-коду, в котором содержится вся информация о заказе. Реализуем два сценария: оплату в клиентском приложении и выставление счёта.

Поддерживать онлайн-кассу и фискализацию. Всё в соответствии с ФЗ-54.

Настраивать окно оплаты под дизайн приложения. Доступно в приложениях на Android.


Архитектурные улучшения


Проблема. Архитектура кода SDK устроена так, что поддерживать и внедрять в него новые функции сложно и долго.
Решение. Разработчики SDK провели полный рефакторинг старого кода библиотек:

код написан на современных языках Kotlin и Swift. Это ускорило процесс разработки и помогло выстроить удобную архитектуру, которую легко поддерживать и расширять: мы быстрее внедряем новые функции, а клиенты быстрее получают обновления. В первой версии код написан на Java и Objective-C;

на Android настройки экрана созданы с помощью Kotlin DSL. А вся многопоточная работа построена на корутинах: они помогают обрабатывать запросы в сеть, размер библиотеки при этом увеличивается менее чем на 1 МБ;

в проекте сократилось количество кода. Используем Generic enum, обработку ошибок с помощью throws и расширения некоторых базовых классов. Поддерживать проект стало проще;

обновили API библиотек. Методы вызова экранов и основных функций для оплаты вынесли в отдельный класс. Это единая точка входа библиотеки: гораздо проще вызывать методы API из одного места, чем из раскиданных по коду.

Структуру методов спроектировали так, чтобы работа с API была удобной и понятной. Методы хорошо задокументированы и соответствуют привычным стандартам кода на Java, Kotlin, Swift.

Например, так выглядит формирование настроек экрана оплаты и его запуск в SDK Android:
val tinkoffAcquiring = TinkoffAcquiring("terminalKey", "password", "publicKey")        val paymentOptions = PaymentOptions()                .setOptions {                    orderOptions {                        orderId = "777"                        amount = Money.ofCoins(1000)                        title = "Order Title"                        description = "Order Description"                    }                    customerOptions {                        customerKey = "user-key"                        checkType = CheckType.HOLD.toString()                        email = "useremail@gmail.com"                    }                    featuresOptions {                        localizationSource = AsdkSource(Language.RU)                        useSecureKeyboard = true                    }                }tinkoffAcquiring.openPaymentScreen(this, paymentOptions, PAYMENT_REQUEST_CODE)


Пример SDK iOS:
let credentional = AcquiringSdkCredential(terminalKey: "terminalKey", password: "terminalPassword", publicKey: "testPublicKey")let acquiringSDKConfiguration = AcquiringSdkConfiguration(credential: credentional)if let sdk = try? AcquiringUISDK.init(configuration: acquiringSDKConfiguration) {sdk.presentPaymentView(on: self, paymentData: createPaymentData(), configuration: acquiringViewConfiguration()) { [weak self] (response) inself?.responseReviewing(response)}}


SDK не ограничивает приложения клиентов: например, принимать оплату можно не только через экраны библиотеки. Иногда владельцам бизнеса и их разработчикам нужно больше возможностей в приложении.

Например, на нашем экране оплаты совершаются два запроса на сервер, а продавцу нужна дополнительная обработка заказа в приложении. Поэтому один из запросов должен выполняться до открытия экрана SDK, а второй уже на экране. Мы можем такое реализовать.

Для этого в SDK есть отдельный модуль Core. Он не зависит от платформы разработки, и с помощью него можно вызывать методы Tinkoff Acquiring API. А модуль с UI-частью предлагает другие решения без экрана SDK дополнительные методы, которые запускают экран оплаты с определенного этапа.


Google Pay и Apple Pay


Проблема. Google и Apple требуют, чтобы их способы оплаты находились не рядом с картами на экране оплаты SDK, а как отдельные кнопки в приложении, которые нужно делать самостоятельно. Разработчикам клиента приходится выполнять больше работы по настройке Google Pay и Apple Pay.
Решение. В обновленном SDK улучшили поддержку приема платежей через Google Pay и Apple Pay.

Мы учитываем требование Google и Apple, но при этом упрощаем задачу разработчикам. Большую часть настроек оплаты берет на себя SDK, а разработчикам клиента нужно вызвать всего несколько методов.

Эти методы выполняют:

  • настройку сервисов для платежа Google Pay и Apple Pay;
  • обработку доступности сервисов на девайсе;
  • вызов сервиса для оплаты;
  • обработку результата оплаты.

Все, что нужно разработчику сделать по гайдлайнам сервиса кнопку, установить настройки платежа и передать токен в SDK для завершения оплаты через интернет-эквайринг Тинькофф.


Система быстрых платежей


Это новая функция на рынке финтеха и новая технология, поэтому разработчикам пришлось реализовывать ее в SDK с нуля. СБП помогает принимать платежи по QR-коду и моментально зачислять деньги на счет продавца они приходят сразу же, даже в выходные и праздники.

Как это работает:

  1. При оплате заказа покупатель нажимает Оплатить через СБП.
  2. Покупатель попадает в банковское приложение и там подтверждает списание денег. Сумма будет указана в платежной форме.
  3. После оплаты деньги сразу приходят на расчетный счет продавца.

imageimage

Подключить новый способ оплаты клиенты могут в личном кабинете Тинькофф Оплаты. После этого в мобильном SDK у клиентов откроется возможность принимать платежи через СБП по кнопке и QR-коду.

Мгновенный счёт. На экране оплаты появляется кнопка с логотипом СБП.

Настройка экрана fpsEnabled включает кнопку для быстрой оплаты в Android SDK:
var paymentOptions = PaymentOptions().setOptions {    orderOptions { /*options*/ }    customerOptions { /*options*/ }    featuresOptions {        fpsEnabled = true             }}

В iOS SDK нужно передать buttonPaySPB:
let viewConfigration = AcquiringViewConfigration.init()viewConfigration.fields = []viewConfigration.fields.append(AcquiringViewConfigration.InfoFields.buttonPaySPB)


По статическому QR-коду. Продавец должен открыть экран оплаты на своем устройстве, а покупатель отсканировать QR-код с экрана. Это можно сделать в банковском приложении или любым QR-ридером откроется мобильное приложение банка, покупатель введет сумму заказа и оплатит товар.

Для экрана оплаты по QR-коду в SDK есть соответствующий метод. Разработчику приложения нужно создать кнопку в разметке и вызвать метод.

На Android:
tinkoffAcquiring.openStaticQrScreen(context, screenOptions, STATIC_QR_REQUEST_CODE)

Метод на iOS:
sdk.presentPaymentSbpQrImage(on: self, paymentData: createPaymentData(), configuration: acquiringViewConfiguration()) { [weak self] (response) inself?.responseReviewing(response)}


Обновленный дизайн и темная тема


Было:
imageimage

Стало:
imageimage

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

Еще экраны SDK теперь поддерживают горизонтальную ориентацию девайса и темную тему. Чтобы поддерживать настройки клиентского приложения, в Android SDK режим темной темы можно указать в настройках экранов. Возможные значения: Всегда включена, Всегда выключена, Переключение в зависимости от настроек девайса или режима экономии заряда батареи. В iOS SDK темная тема переключается в зависимости от настроек самого приложения.


Локализация экранов


Проблема. Использовали стандартную локализацию из SDK, в зависимости от установленного на устройстве языка. Некоторым клиентам нужны были расширенные настройки локализации экранов.
Решение. Запустили расширенную поддержку локализации экранов для Android SDK. Теперь она не зависит от локали девайса. Можно создать свою локализацию на любой язык или поставить стандартную локализацию SDK. На обеих платформах по-прежнему поддерживается русская и английская локализация.


Как начать пользоваться


Чтобы интегрировать SDK в приложение, сначала нужно подключить интернет-эквайринг Тинькофф Оплаты. Это быстро: владелец бизнеса подает заявку, заполняет анкету. Когда мы проверим данные и убедимся, что все в порядке, можно будет начать использовать SDK. Обычно на все это уходит два дня или меньше.

Методы подключения библиотек, документацию и исходный код смотрите на нашем GitHub:

Android SDK

iOS SDK

Если есть любые вопросы по библиотеке или нужна помощь с переходом на новую версию, напишите нам, поможем разобраться: oplata@tinkoff.ru. Или создайте Issue в соответствующем разделе на GitHub.
Подробнее..

HTTP-клиент на стероидах

14.09.2020 14:13:59 | Автор: admin

Бывает так, что в проект не хочется тащить мощный и тяжелый HTTP-клиент, а хочется взять что-то легкое, но его функционала не достаточно. Для решения таких компромиссов у меня есть небольшие классы декораторы, которые я опубликовал на всеобщее обозрение под лицензией MIT.

Смена версии протокола

К нам пришёл HTTP/2, но не каждый сервер, как и не каждый клиент его поддерживает. Если попробовать отправить запрос, принудительно указав версию протокола 2, можно получить от сервера ошибку 505 HTTP Version Not Supported. Пакет webclient/ext-protocol-version решает эту, возможно надуманную, проблему. При получении ответа 505 клиент повторит запрос, но уже с версией протокола, указанной в ответе сервера.

<?phpuse Webclient\Extension\ProtocolVersion\Client;use Psr\Http\Client\ClientInterface;use Psr\Http\Message\RequestInterface;/**  * @var ClientInterface $client Ваш PSR-18 совместимый HTTP Client. */$http = new Client($client);/** @var RequestInterface $request */$response = $http->sendRequest($request);

Редиректы

Очень редко, но встречается, когда клиент не умеет следовать редиректам при ответе с кодом 3xx. В этом случае поможет пакет webclient/ext-redirect. Тут всё элементарно, Передаём в конструктор наш клиент и максимальное количество допустимых редиректов на один запрос.

<?phpuse Webclient\Extension\Redirect\Client;use Psr\Http\Client\ClientInterface;use Psr\Http\Message\RequestInterface;/**  * @var ClientInterface $client Ваш PSR-18 совместимый HTTP Client. * @var int $maxRedirects Максимальное количество допустимых редиректов. */$http = new Client($client, $maxRedirects);/** @var RequestInterface $request */$response = $http->sendRequest($request);

Логирование

Иногда нам необходимо логировать запросы и ответы. Пакет webclient/ext-log позволяет настроить логирование так, как этого требует проект. Помимо клиента, вам понадобится PSR-3 совместимый логгер. Для формирования строки лога используется интерфейс Webclient\Extension\Log\Formatter\Formatter, а для формирования ID запроса (для поиска в логах пары запрос-ответ) - Webclient\Extension\Log\IdGenerator\IdGenerator. По одной реализации этих интерфейсов поставляется из коробки:

  • Webclient\Extension\Log\IdGenerator\UniqueIdGenerator - элементарный генератор на основе uniqid()

  • Webclient\Extension\Log\Formatter\RawHttpFormatter - логирование запросов и ответов в виде RAW-текста.

<?phpuse Psr\Http\Client\ClientInterface;use Psr\Http\Message\RequestInterface;use Psr\Log\LoggerInterface;use Psr\Log\LogLevel;use Webclient\Extension\Log\Client;use Webclient\Extension\Log\Formatter\Formatter;use Webclient\Extension\Log\IdGenerator\IdGenerator;/**  * @var ClientInterface $client Ваш PSR-18 совместимый HTTP Client. * @var LoggerInterface $logger Ваш PSR-3 совместимый логгер. * @var IdGenerator|null $idGenerator Ваш ID-генератор. *      При передаче null будет использоваться  *      Webclient\Extension\Log\IdGenerator\UniqueIdGenerator. * @var Formatter|null $formatter Ваш форматировщик лога.  *      При передаче null будет использоваться  *      Webclient\Extension\Log\Formatter\RawHttpFormatter. */$http = new Client(    $client,    $logger,    $idGenerator,    $formatter,    LogLevel::INFO, // Уровень логирования запросов    LogLevel::INFO, // Уровень логирования информационных ответов (Коды 1xx)    LogLevel::INFO, // Уровень логирования успешных ответов (Коды 2xx)    LogLevel::INFO, // Уровень лоигрования ответов с редиректом (Коды 3xx)    LogLevel::ERROR, // Уровень логирования ответов об ошибках клиента (Коды 4xx)    LogLevel::ERROR, // Уровень логирования ответов об ошибках сервера (Коды 5xx)    LogLevel::WARNING // Уровень логирования исключений HTTP клиента);/** @var RequestInterface $request */$response = $http->sendRequest($request);

Куки

Бывают проекты, в которых не достаточно просто отправить запрос и получить ответ. Иногда нужно поддерживать сессию (ну или ещё что-то). Чтобы добавить вашему клиенту поддержку печенек, нужно отнаследоваться от абстрактного класса Webclient\Extension\Cookie\Cookie\Storage из пакета webclient/ext-cookie, либо воспользоваться одной из поставляемых с пакетом реализацией:

  • Webclient\Extension\Cookie\Cookie\ArrayStorage - держит куки в памяти. Слетает после завершения скрипта;

  • Webclient\Extension\Cookie\Cookie\NetscapeCookieFile - хранит куки в файле в соответствии с форматом Netscape.

<?phpuse Psr\Http\Client\ClientInterface;use Psr\Http\Message\RequestInterface;use Webclient\Extension\Cookie\Client;use Webclient\Extension\Cookie\Cookie\Storage;/**  * @var ClientInterface $client Ваш PSR-18 совместимый HTTP Client. * @var Storage $storage Хранилище куков. *      Вы можете отнаследоваться от этого класса для реализации своего хранилища. */$http = new Client($client, $storage);/** @var RequestInterface $request */$response = $http->sendRequest($request);

Кэширование

О кэшировании, его плюсах и минусах сказано уже много. Если вам хочется разгрузить какой-то из своих редко обновляемых микросервисов (и нагрузить кэш), поможет пакет webclient/ext-cache. Чтобы завернуть в него свой клиент, вам понадобится реализация Psr\SimpleCache\CacheInterface из PSR-6, Psr\Http\Message\ResponseFactoryInterface и Psr\Http\Message\StreamFactoryInterface из PSR-18.

Кэширование происходит на основе соответствующих заголовков HTTP-запросов и ответов.

<?phpuse Psr\Http\Client\ClientInterface;use Psr\Http\Message\RequestInterface;use Psr\Http\Message\ResponseFactoryInterface;use Psr\Http\Message\StreamFactoryInterface;use Psr\SimpleCache\CacheInterface;use Webclient\Extension\Cache\Client;/**  * @var ClientInterface $client Ваш PSR-18 совместимый HTTP Client. * @var CacheInterface $cache Ваш PSR-6 совместимый кэш. * @var ResponseFactoryInterface $responseFactory  *      Ваша PSR-17 совместимая фабрика ответов. * @var StreamFactoryInterface $streamFactory *      Ваша PSR-17 совместимая фабрика потоков. * @var string Строка, уникальная для приватного кэша (например, ID сессии). */$http = new Client(  $client,  $cache,  $responseFactory,  $streamFactory,  $privateKey);/** @var RequestInterface $request */$response = $http->sendRequest($request);

На этом расширения для клиентов заканчиваются, но статья продолжается. У меня есть ещё парочка инструментов, которые могут кому-то облегчить жизнь.

Создание запросов с файлами

Как известно, в PSR-7 есть два вида запросов - обычный запрос и серверный запрос. Они оба реализуют интерфейс Psr\Http\Message\RequestInterface (Psr\Http\Message\ServerRequestInterface его расширяет). При отправке файлов на сервер мы не можем просто взять серверный запрос, выполнить его метод withUploadedFiles($files) и передать полученный объект в HTTP-клиент. Для того, чтобы клиент корректно отправил запрос с файлами, эти файлы должны быть записаны в поток тела запроса. Библиотека webclient/helper-form предназначена для упрощения создания таких запросов. Для работы вам понадобятся реализации интерфейсов Psr\Http\Message\ResponseFactoryInterface и Psr\Http\Message\StreamFactoryInterface.

фейковый клиент

В проектах бывает необходимость запросов к сервисам, которые не желательно дергать лишний раз (например, платные API), но код, использующий такие обращения, тестировать нужно.

Для такого тестирования можно воспользоваться webclient/fake-http-client, который является реализацией Psr\Http\Client\ClientInterface, но под капотом вместо запроса к серверу вызывает Psr\Http\Server\RequestHandlerInterface из PSR-15 (преобразовав при необходимости Psr\Http\Message\RequestInterface в Psr\Http\Message\ServerRequestInterface). Реализация Psr\Http\Server\RequestHandlerInterface остаётся за вами - эмулируйте поведение, как вам нужно для тестирования.

<?phpuse Webclient\Fake\Client;use Psr\Http\Message\RequestInterface;use Psr\Http\Server\RequestHandlerInterface;/**  * @var RequestHandlerInterface $handler Ваш обработчик запроса.  * @var array $serverParams Параметры сервера, которые будут добавлены  *      при преобразовании из Psr\Http\Message\RequestInterface в *      Psr\Http\Message\ServerRequestInterface. */$client = new Client($handler, $serverParams);/** * @var RequestInterface $request Ваш HTTP-запрос */$response = $client->sendRequest($request);

Если вы передаете объект Psr\Http\Message\ServerRequestInterface клиенту и хотите, чтобы обработчик получил его как есть, добавьте атрибут Webclient\Fake\Client::NOREPLACEATTRIBUTE (иначе будет создан новый объект запроса).

<?phpuse Webclient\Fake\Client;use Psr\Http\Server\RequestHandlerInterface;/**  * @var Client $client.  * @var ServerRequestInterface $request. */$request = $request->withAttribute(Client::NOREPLACEATTRIBUTE, true);$response = $client->sendRequest($request);

Чтобы хоть чуть-чуть упростить вам написание обработчика, в пакете поставляется класс Webclient\Fake\Handler\SimpleRoutingHandler - обработчик с примитивным роутингом.

<?phpuse Webclient\Fake\Client;use Webclient\Fake\Handler\SimpleRoutingHandler;use Psr\Http\Message\RequestInterface;use Psr\Http\Server\RequestHandlerInterface;/**  * @var RequestHandlerInterface $notFoundHandler Обработчик запросов,  *      для которых не нашлось роута. * @var RequestHandlerInterface $entityCreatedHandler Обработчик запросов *      для эмуляции созания сущности (POST /entities). * @var RequestHandlerInterface $entityHandler Обработчик запросов *      для получения сущности (GET /entities/1). * @var RequestHandlerInterface $entityDeletedHandler обработчик запросов *      для удаления сущности (DELETE /entities/2). * @var RequestInterface $errorRequest Запрос несуществующего URI (GET /users). * @var RequestInterface $entityCreatingRequest Запрос создания  *      сущности (POST /entities). * @var RequestInterface $entityRequest Запрос получения  *      сущности (GET /entities/1). * @var RequestInterface $entityDeletingRequest Запрос удаления  *      сущности (DELETE /entities/2). */$handler = new SimpleRoutingHandler($notFoundHandler);$handler    ->route(['GET', 'HEAD'], '/entities/1', $entityHandler)    ->route(['POST'], '/entities', $entityCreatedHandler)    ->route(['DELETE'], '/entities/2', $entityDeletedHandler);$client = new Client($handler);$resp1 = $client->sendRequest($errorRequest); // Вернёт ошибку 404$resp2 = $client->sendRequest($entityCreatingRequest); // Вернёт ответс кодом 201$resp3 = $client->sendRequest($entityRequest); // Вернёт ответ с кодом 200$resp4 = $client->sendRequest($entityDeletingRequest); // Вернёт ответ с кодом 204

Надеюсь, вы не зря потратили время на прочтение этой статьи и кому-то приглянутся предоставленные инструменты. Спасибо за внимание!

Подробнее..

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru