Статьи

Высокопроизводительные API для микросервисов с Baratine.io

Построение микросервисов требует проб и ошибок, но Baratine предлагает быстрый, основанный на POJO подход к итерации ваших API. Baratine будет с открытым исходным кодом с выпуском 1.0 GA! Надеюсь, ты повеселишься!

Baratine + Lucene: представление Lucene в качестве веб-службы

Аннотация: Мы представляем Baratine как асинхронный фасад, который можно разместить перед существующей библиотекой без каких-либо изменений в коде библиотеки. Таким образом, решаются две задачи: (1) представление библиотеки как веб-службы, доступной для любого языка, и (2) упрощение требований к неблокируемой, масштабируемой (shardable + возможность разделяться) веб-службе.

Мы сделали это, чтобы проиллюстрировать, как платформа POJO Baratine обеспечивает API-ориентированный подход к созданию высокопроизводительных микросервисов.

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

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

  1. Внедрите сервисную часть (SOA), затем
  2. Реализовать клиентскую библиотеку для общения.

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

В этой статье мы рассмотрим пример использования Baratine для демонстрации Apache Lucene, популярной библиотеки поиска Java с открытым исходным кодом, в качестве высокопроизводительного веб-сервиса.

Apache Foundation описывает Lucene как: «высокопроизводительную, полнофункциональную библиотеку механизма текстового поиска, полностью написанную на Java. Это технология, подходящая практически для любого приложения, требующего полнотекстового поиска, особенно кроссплатформенного ».

Наш пример послужит планом для подключения существующих приложений или библиотек к сети в качестве функционирующих веб-сервисов. Он превращает библиотеку Lucene в высокоэффективный поисковый веб-сервис со следующими характеристиками:

  • Действует как асинхронный сервис обмена сообщениями.
  • Предоставляет общедоступный API WebSocket для Lucene.
  • Развертывается на автономном управляемом сервере.

Выполнение примера

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

  1. Установите плагин Lucene: https://github.com/baratine/lucene-plugin
  2. Выполнить ./baratine-run.sh
  3. Откройте последний браузер и перейдите по адресу http: // localhost: 8085

Содержание и обзор проекта

Пример включает в себя Java-сервисы для Lucene, а также клиент JavaScript для браузеров или для node.js. В проекте также используется среда Angularjs для подключения внутренних сервисов. Это означает, что с помощью только Baratine и внешнего интерфейса можно легко создать высокопроизводительный веб-сервис. Что мы сделали, это опубликовали Lucene HTTP / WebSocket API, используя Baratine Services.

Это было выполнено в двух основных задачах:

  1. Публикация HTTP / WebSocket Client API.
  2. Соединение синхронной библиотеки Lucene с асинхронными службами Baratine.

Большая часть работы выполняется в следующих файлах Javascript и Java, все они написаны специально для нашего примера:

Клиенты:

  • Baratine Lucene Client  lucene-client.js  : функциональность метода Lucene от клиента, используемая для результатов браузера.
  • Клиент Baratine Javascript  baratine-js.js : библиотека протоколов, распространяемая вместе с Baratine, которая обменивается данными со службой, используя HTTP или WebSockets и протокол JAMP Baratine.

Обслуживание:

  • Служба Lucene Reader  LuceneReaderImpl.java  и служба записи LuceneWriterImpl.java : Отвечает за чтение и запись поисковых индексов, соединение между синхронным / блокирующим API Lucene и асинхронным / обменом сообщениями Баратины.
  • Служба общедоступного API  LuceneFacadeImpl.java : отвечает за передачу запросов метода в библиотеку Lucene.

Задача 1.  Баратин Сервис: LuceneFacade

LuceneFacade — это API для опубликованного HTTP / WebSocket, выполняющий первую задачу. Его реализация — LuceneFacadeImpl. Опубликованный API-интерфейс является асинхронным и использует результат Баратина в качестве держателя обратного вызова для результата. Реализация является однопоточной и неблокирующей, что исключает необходимость синхронизации.

Задача 2А. Баратин Сервис: LuceneReaderImpl

LuceneReaderImpl реализует службу поисковых запросов, которая соединяет синхронную библиотеку Lucene с асинхронными API Baratine. Поскольку поиск Lucene является многопоточным и блокирующим, LuceneReaderImpl использует поддержку нескольких рабочих Baratine (@Workers). Поддержка нескольких рабочих аналогична пулу соединений с базой данных. Пользователи LuceneReader видят асинхронный сервис без необходимости знать, что он реализован как многопоточный сервис.

Задача 2B. Баратин Сервис: LuceneWriterImpl

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

Это не только иллюстрирует, как просто инкапсулировать вызов метода в качестве собственной автономной службы, но также предотвращает блокировку вызовов этих методов другими клиентскими запросами. Lucene не предоставляет асинхронный API, однако, поскольку службы Baratine являются асинхронными, мы разрешаем обработку, пока эти методы вызываются непрерывно. Общий обзор новой архитектуры можно представить следующим образом:

Высокий уровень

HTTP / WebSocket Client API

Клиентский API — это Java-интерфейс службы, в данном случае LuceneFacade.java. Каждый сервис имеет свой собственный адрес, используя синтаксис URL. Методы в сервисе вызываются как обычные вызовы методов. Javascript библиотека Баратина управляет деталями, предоставляя интерфейс метода для протокола, который использует вызовы методов JSON для HTTP или WebSockets.

Методы Lucene API:

void indexFile(String collection, String path, Result<Boolean> result) throws LuceneException;

void indexText(String collection, String id, String text,Result<Boolean> result) throws LuceneException;

void indexMap(String collection, String id, Map<String,Object> map, Result<Boolean> result) throws LuceneException;

void search(String collection, String query, int limit, Result<List<LuceneEntry>> result) throws LuceneException;

void delete(String collection, String id, Result<Boolean> result) throws LuceneException;

void clear(String collection, Result<Void> result) throws LuceneException;

Затем клиенты обращаются напрямую к этим методам.

Как сделать клиентов

Мы знаем, что служба находится по URL-адресу, поэтому как нам создать клиента для общения с этой службой?

Создание клиента:

  1. Проход в сервисный URL к Баратине
  2. this.client = new Jamp.BaratineClient(url);
  3. Вызовите поиск на ServiceRef
  4. Сохранить возвращенный прокси из поиска
  5. Вызов методов на прокси

Долгоживущие клиенты, такие как сервер приложений Java или служба Node.js, могут совместно использовать одно поточно-ориентированное соединение, поскольку протокол является асинхронным и использует сообщения. Быстрые запросы, такие как поиск в кэше памяти, могут завершать работу не по порядку, прежде чем ранее медленные запросы, такие как поиск Lucene, который вызывает базу данных mysql. Использование одного соединения может даже повысить эффективность, поскольку можно объединить несколько вызовов, что повышает производительность TCP.

Клиент JavaScript

Для примера Lucene API включает методы поиска текстового документа и добавления новых документов в поисковую систему.

Baratine реализует интерфейсы как классы, позволяющие нам вызывать методы непосредственно на интерфейсе, который мы выбираем для предоставления. Результат вызова — обратный вызов, который вернет результат поиска или уведомит о завершении индекса. Поскольку Baratine является асинхронным, вызов search или indexText не блокируется.

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

Клиент JavaScript для Lucene следует API Java. Следующее извлечено из файла lucene.js, чтобы показать, как вызовы Baratine транслируются в Javascript.

Сначала создается новое соединение с новым Jamp.BaratineClient по HTTP-адресу сервера, который называется «http: // localhost: 8085 / s / lucene». Как правило, клиент будет долгоживущим, используется для многих запросов.

Search
      {
        this.client.query("/service", 
        "search", [coll, query, limit], onResult);     
      }

Поиск прост, передает запросы непосредственно к бэкэнду.

Index
{
      this.client.send("/service",
  "indexText", [coll, extId, text]);
    }

Индексирование текста реализовано как неблокирующий метод.

Услуги предоставляются посредством Baratine: создание Боба, которым управляет Baratine.

Java-клиент

Служба также может быть вызвана из клиента Java, такого как веб-приложение, использующее Lucene. Как и в случае с клиентом Javascript, клиент Java обычно является долгоживущим соединением. Поскольку сам клиент является поточно-ориентированным, его можно эффективно использовать из многопоточного приложения.

Асинхронный Java-клиент

@Inject
@Lookup("public://lucene/service")
LuceneFacade _lucene;

Когда многопоточное синхронное Java-приложение использует службу Baratine, такую ​​как служба Lucene, оно обычно будет использовать синхронный блокирующий вызов синхронной версии API службы. Клиентский прокси Baratine действует как мост от синхронного клиента к асинхронной службе Baratine. Синхронная версия фасада Lucene выглядит следующим образом:

LuceneFacadeSync

public interface LuceneFacadeSync extends LuceneFacade
{
    List<LuceneEntry> search (String collection, String query, int limit);
}

Вызов клиента выглядит как простой вызов метода Java, как в коде ниже. Поскольку ServiceClient является поточно-ориентированным и может использоваться для нескольких служб Baratine, его можно использовать как одиночный. Фактически, из-за внутренней пакетной обработки и обмена сообщениями Baratine более эффективно использовать один клиент, совместно используемый потоками.

Синхронизировать Java-клиент

ServiceClient client = ServiceClient.newClient(url).build();
LuceneFacadeSync lucene;
lucene = client.lookup("remote:///service").as(LuceneFacadeSync.class);
List<LuceneEntry> result = lucene.search(collection, query, limit);

Как видите, создание клиента простое, поскольку гибкая архитектура Baratine позволяет эффективно проектировать протокол API.

Внедрение Lucene Server

У сервера Lucene есть две задачи: публиковать клиентский API и выступать в роли моста от реализации многопоточной блокировки Lucene к асинхронной архитектуре Baratine. В примере используются три службы Baratine для реализации службы:

  • Клиент API Фасад Сервис
  • Читатель Сервис для поиска
  • Writer Сервис для обновления поискового индекса

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

LuceneFacadeImpl.java

Клиентский API реализован сервисом LuceneFacade Baratine. Он в первую очередь отправляет запросы в службы чтения и записи. Фасад будет играть большую роль, когда мы будем использовать несколько сервисов для разделения сервера Lucene в следующей статье. В этом примере полезно сосредоточить внимание на клиентском API на простоте, чтобы убедиться, что сервер работает для упрощения работы с клиентами. LuceneIndexBean.java

Поскольку библиотека Lucene написана как единичный экземпляр, службы для читателей и писателей используют один и тот же LuceneIndexBean. Этот дизайн основан на собственном дизайне Lucene; мы используем Baratine для работы с существующей архитектурой, вместо того, чтобы заставлять Lucene следовать Baratine. Если бы мы создавали сервис Baratine с нуля, вместо того, чтобы адаптировать существующую библиотеку, мы, вероятно, выбрали бы другую архитектуру. Общий синглтон LuceneIndexBean вводится в службы чтения и записи при инициализации.

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

LuceneWriterImpl.java

Служба Writer принимает запросы с фасада и обновляет индексы Lucene. Он имеет один рабочий поток, который записывает обновления в Lucene для столько запросов, сколько имеет его входной почтовый ящик. Когда его входной почтовый ящик пуст, он вызывает метод commit Lucene для завершения записи. При большой нагрузке в папку «Входящие» поступает больше запросов, что приводит к увеличению объема пакета и повышению производительности.

LuceneReaderImpl.java

Поскольку поиски Lucene могут блокировать ожидание медленной базы данных, а Lucene является многопоточным, реализация читателя использует аннотацию @Worker для запроса нескольких потоков для службы. Аннотируя эту службу с помощью @Workers (20), мы предоставляем исполнителя пула потоков, который может непрерывно отправлять потоки в службу чтения. Поскольку потоки отправляются только тогда, когда это необходимо, несколько рабочих мест обходятся недорого. Несколько сотрудников позволяют нашему сервису Lucene обслуживать несколько запросов одновременно.

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

Баратинская архитектура

Что мы сделали, так это внедрили Lucene API в виде набора Baratine Services. В Baratine каждый сервис живет по своему уникальному URL и работает по однопоточному, единоличному / единоличному договору. (Служба мостов с несколькими рабочими, используемая в считывателях Lucene, является исключением; она используется для переноса внешних библиотек в Baratine.) Запросы к конкретному URL-адресу службы помещаются в очередь входящих сообщений этой службы, что гарантирует упорядочение запросов по мере их обработки. ,

Базовый строительный блок Baratine выглядит следующим образом:

Баратинская архитектура

Теперь мы можем суммировать услугу Baratine с предыдущей картинкой и следующими пунктами:

  1. Сервис Baratine живет по уникальному URL
  2. Служба Baratine имеет один собственный поток, отвечающий за манипулирование данными службы
  3. Запросы помещаются в почтовый ящик сервиса и обрабатываются партиями.

В Baratine нам не нужно добавлять синхронизацию к нашим звонкам. Поскольку на каждую службу отвечает только один поток, для другого потока невозможно повредить данные, которые обновляются. Это позволяет нашим классам быть объектами POJO. Производительность и сравнение с Apache Solr

Как могли заметить некоторые читатели, наш пример похож на Apache Solr, который предоставляет сервер для библиотеки Lucene. Solr — хорошее сравнение, потому что это знакомый пример и его можно сравнить напрямую.

Мы сравнили Apache Solr с несколькими клиентами. При тестировании только на чтение Баратин был лучше или в пределах 20% от производительности Solr. В смешанном тесте чтения / записи Baratine оказался в 3 раза быстрее, чем Solr.

Тестирование проводилось со следующими характеристиками:

Процессор Intel® Core ™ 2 Quad с процессором 
Q6700 @ 2,66 ГГц Память 4 Г Скорость: 667 МГц Жесткий диск: ST3250410AS

java версия «1.8.0_51» ОС Linux deb-0 3.16.0-4-amd64 # 1 SMP Debian 3.16.7-ckt11-1 + deb8u2 (2015-07-17) x86_64 GNU / Linux

Графики производительности следующие:

чтение производительности производительность в сочетании чтение / запись

Как показывают результаты, поиск Baratine (чтение) превосходит Apache Solr в сравнении друг с другом.

Смешанная загрузка запросов на чтение / запись, показ конкурентных чисел.

Резюме

То, что мы сделали для Lucene в этом примере, можно сделать с любой библиотекой или приложением. Оборачивая службу Baratine как фасад в библиотеку, мы можем преобразовать любую библиотеку (например, java.util) в асинхронную службу.

Многие из принципов, которые включает Баратин, отражены в реактивном манифесте  http://reactivemanifesto.org, Реактивные приложения являются гибкими, отзывчивыми, отказоустойчивыми и управляемыми сообщениями. Это то, что требуется Интернетом вещей, где десятки тысяч устройств подключаются к одному приложению. Эти принципы являются желанными для разработчиков, но их трудно реализовать на практике. Отличная абстракция уровня POJO от Baratine определяет уровень инкапсуляции данных и потоков, который снимает эту трудность. Таким образом, Baratine — это SOA-реализация реактивной платформы, которая позволяет разработчикам программировать объектно-ориентированным способом, к которому они привыкли. Мы считаем, что большинство новых веб-приложений будут включать эти принципы при необходимости интеграции с их текущими системами. Таким образом, как мы показали в этой статье, Baratine идеально подходит для построения обоих.

Значение Baratine увеличивается по мере добавления функциональности в приложение. Если, например, было принято решение запустить оперативную аналитику по выполняемым запросам, на узле Baratine можно развернуть простую статистику сбора классов POJO и передать эту информацию. Он может предоставлять эту информацию в виде значения, близкого к реальному времени, исходя из текущих обновлений BFS (файловой системы Baratine), или он может предварительно вычислять и группировать эти результаты для внешнего источника для потребления. Какой бы ни был выбор, унифицированная, но гибкая архитектура Baratine позволяет разрабатывать систему специально для поставленной задачи без ограничения будущего потенциала. Поскольку сервисы всегда приукрашивают качества необходимого веб-приложения, проверка концепций может перейти от API-интерфейса доски к развертыванию за считанные минуты.

Baratine в настоящее время находится в бета-версии (0.10), с запланированным планом выпуска готовой к выпуску версии в четвертом квартале 2015 года. В этом примере мы только коснулись возможностей Baratine, поддерживающих веб-сервис Lucene.

Следите за новостями второй части, так как мы решаем проблему шардинга и масштабирования этого веб-сервиса Lucene в Baratine!