Изучаем Java. По вопросам сотрудничества: @seniorvladislav
ACID (Atomicity, Consistency, Isolation, Durability)
Это набор свойств, которые обеспечивают надежность и целостность транзакций в базах данных. Хотя ACID является широко применяемым концептом в области баз данных, в Java есть различные способы обеспечения ACID-транзакций в приложениях, работающих с базами данных.
Одним из распространенных способов работы с ACID-транзакциями в Java является использование JDBC
(Java Database Connectivity
), который предоставляет стандартный способ взаимодействия с базами данных в Java. В JDBC
есть возможность создания транзакций, используя методы commit()
и rollback()
.
ACID-транзакции можно использовать с различными базами данных, такими как MySQL, PostgreSQL, Oracle и другими, поддерживающими JDBC.
ScheduledThreadPoolExecutor
ScheduledThreadPoolExecutor является реализацией интерфейса ScheduledExecutorService и представляет собой пул потоков, который позволяет выполнять задачи по расписанию. Он предоставляет механизм для запуска задач через определенные интервалы времени или с определенной задержкой.
В этом примере создается ScheduledThreadPoolExecutor
с двумя рабочими потоками. Затем мы запускаем задачу delayedTask
с задержкой в 5 секунд с помощью метода schedule
. Затем мы запускаем периодическую задачу periodicTask
, которая будет выполняться каждую секунду, начиная сразу же, используя метод scheduleAtFixedRate
.
После запуска задач мы ждем 10 секунд, чтобы дать им выполняться. Затем мы вызываем shutdown()
для остановки пула потоков.
Prototype
В Java паттерн Prototype используется для создания новых объектов путем клонирования существующих объектов, вместо создания новых объектов с помощью конструкторов. Prototype
позволяет создавать копии объектов с минимальными затратами и избегать сложной логики инициализации объектов.
Для реализации этого паттерна необходимо, чтобы классы имели возможность клонирования, что достигается реализацией интерфейса Cloneable
и переопределением метода clone()
.
В этом примере класс Sheep
имеет два приватных поля name
и color
, а также соответствующие геттеры и сеттеры. Класс также имплементирует интерфейс Cloneable
и переопределяет метод clone()
для создания копии объекта.
В main
мы создаем оригинальный объект, клонируем его с помощью метода clone()
и получаем клонированный объект clonedSheep
. Затем мы изменяем его параметры и выводим результат.
Java Bean
JavaBeans - это стандартный подход к созданию переносимых и повторно используемых компонентов. Они представляют собой классы, которые следуют определенным соглашениям и обеспечивают удобный способ управления состоянием и поведением объектов.
Основные характеристики JavaBeans:
1. Класс должен быть публичным и иметь открытый конструктор без аргументов.
2. Приватные поля класса должны быть доступны с помощью геттеров и сеттеров (getter
и setter
методы).
3. Класс должен быть сериализуемым, то есть должен реализовывать интерфейс Serializable
.JavaBeans
также могут иметь дополнительные свойства, такие как события и методы уведомления, которые позволяют другим компонентам реагировать на изменения состояния.
synchronized
Ключевое слово synchronized используется для обозначения синхронизированных блоков кода или методов. Оно обеспечивает монопольный доступ к определенному коду, чтобы только один поток исполнения мог выполнять этот код в данный момент времени.
Синхронизация помогает предотвратить состояние гонки (race condition), когда несколько потоков пытаются одновременно изменить общие ресурсы.
Важно отметить, что синхронизация может повлечь за собой ухудшение производительности, поэтому следует использовать ее только там, где это необходимо для правильной работы с общими ресурсами и избегать излишней синхронизации.
Java NIO
Java NIO (New I/O) — это пакет, предоставляющий альтернативные механизмы ввода-вывода, которые были введены в Java начиная с версии 1.4. Он предоставляет более эффективные и мощные средства для обработки операций ввода-вывода, особенно в контексте сетевого программирования и работы с файлами.Java NIO
позволяет более эффективно управлять данными благодаря следующим особенностям:
Буферизация: данные обрабатываются с использованием промежуточных контейнеров для данных, которые могут быть эффективно обработаны (Buffers).
Неблокирующие операции ввода-вывода: операции ввода-вывода не блокируют поток выполнения, позволяя ему продолжать работу с другими операциями или потоками.
Каналы и селекторы: селекторы отслеживают готовые к операциям чтения/записи каналы, что позволяет обрабатывать несколько операций ввода-вывода с помощью одного потока.
ForkJoinPool
Это класс, введенный в JDK 7, который представляет собой специализированный пул потоков, предназначенный для параллельного выполнения рекурсивных задач.ForkJoinPool
автоматически управляет созданием и использованием потоков для выполнения подзадач и обеспечивает эффективное распределение работы между ними.
Принцип работы основан на делении большой задачи на более мелкие подзадачи и их последующем объединении для получения результата. Каждая подзадача может быть выполнена независимо друг от друга, что позволяет эффективно использовать доступные ресурсы процессора.
LinkedHashMap
Это класс, который представляет собой реализацию коллекции Map, обеспечивающую упорядоченное хранение элементов с помощью двусвязного списка. Он расширяет класс HashMap
и наследует его функциональность, но в отличие от HashMap
, гарантирует, что порядок элементов будет соответствовать порядку их добавления или доступа.
Обратите внимание, что порядок элементов в LinkedHashMap
соответствует порядку их добавления. Если мы изменяем значение существующего ключа, порядок элементов не меняется. LinkedHashMap
также поддерживает удаление элементов и другие методы, определенные в интерфейсе Map
.
Узнаем размер файла с помощью Java
Здесь создается объект класса File, указывающий на файл, размер которого нужно узнать.
Затем вызывается метод length(), который возвращает размер файла в байтах. Результат выводится на консоль с помощью метода println().
♨️ Тест на знание языка Java
— Ответьте на 21 вопрос и проверьте, насколько хорошо вы знаете язык Java и готовы освоить Spring. Сможете сдать — пройдёте на продвинутый онлайн-курс "Разработчик на Spring" Framework со скидкой!
👉 ПРОЙТИ ТЕСТ ОНЛАЙН-КУРСА "Разработчик на Spring Framework"
Курс доступен в рассрочку.
🎁 Пройдете успешно тест, получите доступ к открытым урокам курса.Нативная интеграция. Информация о продукте www.otus.ru
ТОП 3 канала для тех, кто хочет выйти на новый уровень в программировании
Этичный Хакер - Уроки по хакингу. Канал c инструкциями по взлому, деанону, социальной инженерии и созданию вирусов.
Python и 1000 программ - канал для всех, кто хочет освоить самый перспективный язык 2023 года. Гайды для новичков, шпаргалки, фишки, программы и многое другое.
IT Pirate - самый большой канал со сливами курсов. Каталог 10000+, удобная навигация по языкам и школам.
Распределенные транзакции с использованием Saga Pattern в Java
В микросервисной архитектуре, когда дело доходит до управления транзакциями, возникает проблема - как обеспечить согласованность данных между различными сервисами. Saga Pattern - это подход, который можно использовать для решения этой проблемы.
Обзор Saga Pattern: Saga - это долгосрочная транзакция, которая включает в себя несколько подтранзакций, каждая из которых может быть выполнена в разных сервисах. Если одна из подтранзакций завершается с ошибкой, то Saga выполняет компенсационные транзакции, чтобы отменить все предыдущие изменения.
Saga Pattern в Java: Вы можете реализовать Saga Pattern в Java с использованием библиотеки Eventuate Tram Sagas, которая предлагает надежный и легко использовать фреймворк для Saga Pattern.
В этом примере создается класс CreateOrderSagaData, который содержит данные, необходимые для Saga.
Saga Pattern - это мощный инструмент для обеспечения согласованности данных в микросервисной архитектуре. Разумное использование этого подхода может значительно улучшить надежность вашего приложения.
Применение Quarkus в микросервисной архитектуре
Quarkus - это полноценный стек для написания Java приложений, предлагающий удивительные время загрузки и использование памяти, особенно полезные для контейнеров и облачных рабочих нагрузок.
Обзор Quarkus: Quarkus предлагает поддержку широкого спектра стандартных библиотек и фреймворков, таких как Hibernate, Apache Camel, MicroProfile и многих других. Он поддерживает горячую перезагрузку, что увеличивает скорость разработки.
Микросервисы с Quarkus: Quarkus подходит для создания микросервисов, благодаря его небольшому размеру, быстрой загрузке и эффективному использованию памяти. Он поддерживает как императивный, так и реактивный стиль программирования, позволяя вам выбрать наиболее подходящий подход для ваших потребностей.
В этом примере демонстрируется использование событий Quarkus для отслеживания жизненного цикла приложения.
GitHub ресурс для изучения:
Quarkus Getting Started: Ссылка на репозиторий
Quarkus предлагает современные возможности для создания высокопроизводительных микросервисов на Java.
Использование JMH (Java Microbenchmark Harness) для производительности Java
JMH - это часть проекта OpenJDK, предназначенная для написания, запуска и анализа бенчмарков (тестов производительности) в языке Java и других языках, работающих на JVM.
Давайте рассмотрим, как мы можем создать свой собственный бенчмарк с использованием JMH.
В примере State(Scope.Thread) указывает, что экземпляр класса MyBenchmark будет создан для каждого потока, выполняющего тест производительности. Аннотация говорит JMH, что метод testMethod является методом бенчмарка.
Результаты тестов JMH могут быть сложными для интерпретации, поскольку они включают множество различных метрик. Основные метрики, на которые следует обратить внимание, - это "ops/time", которое показывает количество операций, выполненных за единицу времени, и "time/op", которое показывает время выполнения одной операции.
В заключение, JMH - это мощный инструмент для тестирования производительности в Java. Он позволяет разработчикам писать точные и надежные тесты производительности, которые могут помочь оптимизировать и улучшить их код.
Оптимизация производительности Java с использованием GraalVM
GraalVM - это высокопроизводительная среда исполнения, которая предоставляет значительные преимущества для Java и других JVM-языков. Она включает в себя JIT-компилятор Just-in-Time, который может существенно увеличить скорость выполнения Java-приложений, и Ahead-of-Time (AOT) компилятор, который может сократить время запуска Java-приложений и их потребление памяти.
В примере мы сначала устанавливаем путь к директории GraalVM, а затем собираем наш проект с помощью Maven, используя профиль "native", который активирует AOT-компиляцию.
Используя GraalVM, вы можете получить преимущества как JIT, так и AOT компиляции, чтобы улучшить производительность вашего Java-приложения.
GraalVM открывает новые возможности для оптимизации производительности Java-приложений. Это один из инструментов, которые должны быть в арсенале каждого senior Java разработчика, особенно если вы работаете с микросервисами или функциями в облаке.
#вопросы_с_собеседований
Какие возможности Spring предоставляет для коммуникации с базой данных?
Spring предоставляет мощный набор инструментов для работы с базами данных. Ниже перечислены некоторые из них:-
Spring JDBC
: Абстракции над JDBC (Java Database Connectivity), что упрощает взаимодействие с базой данных через простые API.-
Spring Data JPA
: Spring абстракция над JPA (Java Persistence API) и упрощает взаимодействие с объектно-реляционными базами данных (ORM).-
Spring ORM
: Spring ORM интегрирует ORM-фреймворки, такие как Hibernate
, с Spring Framework
. Он обеспечивает удобную интеграцию между Spring и ORM-фреймворками, предоставляя возможности управления транзакциями, упрощенный доступ к данным и возможности кэширования.-
Spring Data JDBC
: Альтернативный подход к доступу к данным с использованием JDBC. В отличие от ORM, Spring Data JDBC предлагает простую и прямолинейную модель доступа к данным без использования объектно-реляционного отображения (ORM).-
Spring Transactions
: Spring предоставляет механизм управления транзакциями для работы с базами данных. Он позволяет объявлять транзакции с помощью аннотаций или XML-конфигурации.-
Spring Batch
: Spring Batch предоставляет возможности для пакетной обработки данных, включая чтение, обработку и запись данных в базу данных.
#вопросы_с_собеседований
Когда лучше использовать наследование, а не агрегацию?
Использование наследования целесообразно, когда существует отношение "является" (is-a) между классами. То есть, если один класс является специализацией или расширением другого класса.
Наследование позволяет унаследовать свойства и методы родительского класса, а также добавить или изменить их в производном классе. Это способствует повторному использованию кода и созданию иерархий классов.
Агрегацию следует использовать, когда существует отношение "имеет" (has-a) между классами. Это означает, что один класс содержит экземпляр другого класса в качестве своего члена.
Агрегация позволяет создавать более сложные объекты, состоящие из других объектов, и устанавливать между ними отношения. В отличие от наследования, агрегация не создает иерархических связей между классами.
POJO
Термин POJO
означает Plain Old Java Object
, что переводится как "простой старый Java-объект". POJO - это обычный Java-класс, который не зависит от каких-либо фреймворков или библиотек и не имеет особых ограничений или требований к своей структуре.POJO
классы часто используются в различных контекстах, таких как модель данных, передача данных или DTO
(Data Transfer Object). Они служат для хранения данных и обеспечивают доступ к этим данным через геттеры и сеттеры.
В этом примере класс Person
имеет два приватных поля name
и age
, а также соответствующие геттеры и сеттеры для доступа к этим полям. Геттеры используются для получения значений полей, а сеттеры - для установки новых значений.
#вопросы_с_собеседований
Что такое optimistic и pessimistic locking?
Это две стратегии управления параллельным доступом к данным в многопоточной или распределенной среде.Optimistic locking
— это подход, основанный на предположении, что конфликты при доступе к данным происходят редко.
При оптимистической блокировке ресурс не блокируется для других потоков во время чтения. Когда поток пытается изменить ресурс, происходит проверка наличия конфликтов. Обычно используются механизмы версионирования (например, с помощью временных меток или счетчиков) для обнаружения изменений.Pessimistic locking
— это подход, при котором ресурс блокируется для других потоков во время чтения или записи.
При пессимистической блокировке поток, запрашивающий доступ к ресурсу, получает эксклюзивное право на его использование до тех пор, пока не освободит его. Pessimistic locking
обеспечивает более жесткую синхронизацию и может быть полезен в случаях, когда конфликты доступа к данным возникают часто.
Для реализации блокировки можно использовать различные механизмы, такие как synchronized
блоки, ключевое слово volatile
, Lock
или ReadWriteLock
интерфейсы.
#вопросы_с_собеседований
В чем разница между Thread.sleep() и Thread.yield()?Thread.sleep()
: Этот метод приостанавливает выполнение текущего потока на заданное количество времени. Время задается в миллисекундах и указывается в качестве аргумента метода. При вызове Thread.sleep()
, поток переходит в состояние ожидания и не выполняет код в течение указанного периода времени. После истечения времени поток снова становится готовым к выполнению. Вызов Thread.sleep()
может сгенерировать исключение InterruptedException
, поэтому необходимо обрабатывать его или пробрасывать.Thread.yield()
: Этот метод предоставляет намек планировщику потоков на то, что поток, вызвавший yield(), готов уступить процессорное время другим потокам с тем же приоритетом. Однако это рекомендация, а не гарантия. Вызов Thread.yield()
приводит к тому, что текущий поток переходит из исполняемого в состояние готовности, чтобы другие потоки могли получить доступ к процессору. Однако, если нет других готовых потоков с тем же приоритетом, поток может продолжить выполнение.
#вопросы_с_собеседований
Расскажите о Java Memory Model
JMM определяет, как Java-программы взаимодействуют с памятью в многопоточной среде.
Основные концепции в Java Memory Model
включают в себя:
- Main Memory (Главная память): представляет собой общую память, к которой имеют доступ все потоки выполнения. В ней хранятся все переменные и объекты.
- Working Memory (Рабочая память): локальная память, доступная каждому потоку выполнения. Она содержит кэшированные значения переменных, с которыми работает поток.
- Переменные: в JMM
переменные делятся на два типа: общие (shared) и локальные (local). Общие переменные могут быть доступны из разных потоков, а локальные переменные являются локальными для каждого потока.
- Запись в память (Write): операция записи значения переменной из рабочей памяти в главную память. Это обеспечивает видимость изменений переменной другим потокам.
- Чтение из памяти (Read): операция чтения значения переменной из главной памяти в рабочую память потока. Это обеспечивает актуальность данных для потока.
При разработке многопоточных приложений необходимо ознакомиться с JMM
, чтобы гарантировать правильное взаимодействие и избежать таких ошибок, как состояние race condition или deadlock.
Хотите работать в крупных IT-компаниях? Научитесь планировать архитектуру и создавать масштабируемые отказоустойчивые приложения! Первые шаги можно сделать уже на открытых уроках онлайн-курсов «Microservice Architecture» и «Highload Architect» в OTUS.
🗓 29 мая в 20:00 — открытый урок «Эффективное использование clickhouse в высоких нагрузках»
Рассмотрим SQL диалект, физическое хранение данных, индексацию данных, включая разреженные индексы, а также различные движки, такие как MergeTree и Log.
👉 Записаться на урок — https://otus.pw/wEgd/
🗓 30 мая в 20:00 — открытый урок «Метрики и Prometheus»
Обсудим, зачем нужны метрики и какие они виды бывают, поговорим про устройство Prometheus, как его развернуть в Kubernetes и интегрировать с вашими приложениями, а также сделаем приложение на Spring Boot с метриками для Prometheus.
👉 Записаться на урок — https://otus.pw/lGWz/
Такие вебинары — это шанс увидеть своими глазами, как проходят занятия на онлайн-курсах в OTUS. После пробного урока вы сможете приобрести курсы любым удобным для вас способом.
Реклама. Информация о рекламодателе на сайте www.otus.ru
Стать сотрудником Яндекса быстрее и проще, чем кажется. Участвуйте в днях быстрого найма: решите тестовое, пройдите несколько секций собеседования и получите офер за несколько дней.
Теперь дни быстрого найма будут проходить регулярно. Чтобы вам было удобно следить за расписанием, собрали его на отдельной странице.
Ближайшие мероприятия:
• 29 мая – 2 июня — Fast Track для технических менеджеров и Crowd Solutions Architect, офер за 5 дней в команду Яндекс Crowd
• 3-4 июня — Fast Track для С++ разработчиков, либо тех, кто готов на него перейти, офер за 2 дня в команду Яндекс Маркета
Зарегистрироваться
Создание автоматизированной торговой системы с Binance Futures API
Binance Futures - это платформа для торговли фьючерсами на криптовалюты. Она предлагает высокую плечевую торговлю, что делает ее привлекательной для трейдеров. Создание автоматизированной торговой системы для Binance Futures - это сложная задача, требующая знаний не только Java, но и финансовых рынков.
1. Что такое Binance Futures API?
Binance Futures предоставляет REST и WebSocket API, которые позволяют создать автоматизированную систему торговли. Эти API дают доступ к данным о рынке, позволяют выставлять заявки и управлять аккаунтом.
2.Создание автоматизированной торговой системы с Binance Futures API
Автоматизированная торговая система обычно включает в себя два основных компонента:
- модуль для получения и анализа данных о рынке
- модуль для выставления и управления заявками.
GitHub ресурс для изучения:
Создание автоматизированной торговой системы для Binance Futures - это сложная задача, которая требует знаний как Java, так и финансовых рынков. Однако, это умение может быть очень ценным в сфере FinTech и криптовалют. Важно понимать, что такие системы подвержены высокому финансовому риску, и их использование должно осуществляться с осторожностью.
Интеграция Java приложения с Binance API
Binance - это ведущая криптовалютная биржа, предоставляющая API для взаимодействия с их платформой. Для разработчика Java, понимание, как работать с этим API, может быть весьма ценным навыком, особенно при разработке приложений для торговли криптовалютами или в области FinTech.
Что такое Binance API? Binance предоставляет REST и WebSocket API для доступа к их платформе. Он позволяет получить информацию о рынках, выполнять торговые операции, управлять аккаунтами и многое другое.
Использование Binance API с Java: Для взаимодействия с Binance API, можно использовать HTTP-клиенты, такие как HttpClient в Java. Однако, есть и библиотеки, которые упрощают эту задачу, например, binance-java-api.
GitHub ресурс для изучения
Работа с API Binance требует знания HTTP-клиентов и работы с веб-сокетами в Java. Это может быть сложной задачей, но умение эффективно интегрироваться с такими платформами как Binance - это важный навык для любого Java-разработчика, работающего в области FinTech или криптовалют.
Оптимизация производительности с использованием профилировщика Java
Производительность - это критически важный аспект любого приложения. Как Senior Java Developer, важно знать, как оптимизировать производительность вашего приложения, и для этого требуется хороший инструментарий.
Профилировщик Java - это инструмент, который помогает анализировать производительность вашего приложения, идентифицировать узкие места и помогает в диагностике проблем производительности.
Профилировщики Java, такие как JProfiler, YourKit или VisualVM, предоставляют детальную информацию о времени выполнения кода, потреблении памяти, использовании процессора и многом другом. Они помогают вам выявить и устранить проблемы с производительностью.
Используя профилировщик, мы можем увидеть, что этот код приводит к значительному использованию памяти из-за большого количества строк, добавленных в список.
Профилирование - это важный аспект оптимизации производительности в Java. Благодаря профилировщикам Java, разработчики могут идентифицировать и устранять проблемы производительности, улучшая таким образом производительность своих приложений.
🔥 Тест на знание языка Kotlin 🔥
Ответьте на 10 вопросов и проверьте, насколько хорошо знаете язык и готовы освоить Kotlin.
Сможете пройти успешно тест — пройдете на продвинутый онлайн-курс "Kotlin Backend Developer" от OTUS со скидкой.
👉 ПРОЙТИ ТЕСТ — https://otus.pw/QTwn/
Курс доступен в рассрочку!
🎁Для успешно прошедших тест, откроется доступ к открытым урокам курса!
Нативная интеграция. Информация о продукте www.otus.ru
Концепция Event Sourcing с использованием Axon Framework и Java
Event Sourcing - это мощная концепция в мире разработки программного обеспечения, которая ставит события в центр вашего приложения. Вместо того, чтобы просто хранить текущее состояние вашего домена, вы храните все события, которые привели к этому состоянию. Это дает вам возможность не только восстановить состояние вашего домена в любой момент времени, но и с легкостью реализовывать такие вещи, как CQRS (Command Query Responsibility Segregation), Event-Driven Architecture, и другие.
Axon Framework - это Java-фреймворк, который позволяет вам создавать приложения с использованием CQRS и Event Sourcing, и делает это на удивление простым.
В примере OrderAggregate обрабатывает команду CreateOrderCommand и применяет событие OrderCreatedEvent. Затем событие обрабатывается методом on, который обновляет состояние агрегата.
Более подробно об Axon Framework, его концепциях и примерах использования вы можете узнать в Github репозитории.
Event Sourcing и CQRS - это сложные концепции, но они предлагают мощные возможности для проектирования и реализации распределенных и легко масштабируемых приложений. Axon Framework значительно упрощает использование этих концепций в Java-приложениях. Разработчик на уровне Senior должен быть знаком с такими концепциями и знать, как и когда их использовать.
Работа с Azure Cosmos DB с использованием Java SDK
Azure Cosmos DB - это глобально распределенная база данных от Microsoft с множественными моделями. Она предлагает прозрачность распределения данных, горизонтальное масштабирование и мгновенные обновления по всему миру.
Создание клиента Cosmos DB и базы данных
Тут мы создаем клиента для работы с нашей базой данных в Azure Cosmos DB, а затем создаем саму базу данных.
Мы создаем контейнер в базе данных и записываем документ в этот контейнер. Каждый документ имеет уникальный идентификатор и ключ раздела.
Взаимодействие с Azure Cosmos DB может быть сложным ввиду его многообразия и глобальной распределенности, но с помощью SDK на Java это становится достаточно простым.
Подводя итог, Azure Cosmos DB - это мощный инструмент для управления данными в глобально распределенных приложениях. Освоив его, вы откроете для себя новые горизонты в мире разработки облачных приложений.
#вопросы_с_собеседований
Мультиязычность в микросервисной архитектуре на примере Spring Boot
Допустим, вы работаете над крупным международным проектом, и вашему приложению необходимо поддерживать множество языков. Как бы вы реализовали поддержку мультиязычности в контексте микросервисной архитектуры на Java с использованием Spring Boot?
Давайте посмотрим на этот вопрос на примере репозитория Spring Boot Internationalization Example.
(Рисунок 1)Сначала необходимо добавить в вашу pom.xml зависимость для поддержки интернационализации.
(Рисунок 2)Затем необходимо настроить LocaleResolver и LocaleChangeInterceptor в вашем классе конфигурации.
Ваше приложение теперь будет использовать выбранный язык в зависимости от параметра запроса lang.
Вы можете создать messages.properties файлы для каждого поддерживаемого языка, где вы определяете строки для каждого языка. Spring автоматически выберет правильный файл.
Итак, поддержка мультиязычности – это часто встречающаяся проблема в крупных международных проектах. Использование Spring Boot позволяет нам эффективно решить эту задачу с минимальными усилиями.
Больше деталей вы можете найти в указанном репозитории на GitHub. Делайте ваши приложения глобальными и доступными для пользователей со всего мира