Введение в Ratpack

Введение в Ratpack

1. обзор

Ratpack - это набор библиотек на основеJVM, созданных для современных высокопроизводительных приложений реального времени. Он построен на основе встроенного механизма управления событиямиNetty и полностью соответствует шаблону реактивного проектирования.

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

2. Почему Ratpack?

Основные преимущества Ratpack:

  • это очень легкий, быстрый и масштабируемый

  • он потребляет меньше памяти, чем другие фреймворки, такие как DropWizard; можно найти интересный результат сравнения тестовhere

  • поскольку он построен на основеNetty, Ratpack полностью событийно-управляемый и неблокирующий по своей природе

  • он поддерживает управление зависимостямиGuice

  • как иSpringBoot, Ratpack имеет собственные библиотеки тестирования для быстрой настройки тестовых случаев.

3. Создание приложения

Чтобы понять, как работает Ratpack, давайте начнем с создания с его помощью небольшого приложения.

3.1. Maven Зависимости

Во-первых, давайте добавим следующие зависимости в нашpom.xml:


    io.ratpack
    ratpack-core
    1.4.5


    io.ratpack
    ratpack-test
    1.4.5

Вы можете проверить последнюю версию наMavenCentral.

Обратите внимание, что хотя мы используем Maven в качестве нашей системы сборки, согласноRatpack recommendation, лучше использоватьGradle в качестве инструмента сборки, поскольку Ratpack имеет первоклассную поддержку Gradle, предоставляемую черезRatpack’s Gradle plugin.

Мы можем использовать следующий скрипт сборки Gradle:

buildscript {
    repositories {
      jcenter()
    }
    dependencies {
      classpath "io.ratpack:ratpack-gradle:1.4.5"
    }
}

apply plugin: "io.ratpack.ratpack-java"
repositories {
    jcenter()
}
dependencies {
    testCompile 'junit:junit:4.11'
    runtime "org.slf4j:slf4j-simple:1.7.21"
}
test {
    testLogging {
      events 'started', 'passed'
    }
}

3.2. Сборка приложения

После настройки управления сборкой нам нужно создать класс для запуска встроенного сервераNetty и построить простой контекст для обработки запросов по умолчанию:

public class Application {

    public static void main(String[] args) throws Exception {
        RatpackServer.start(server -> server.handlers(chain -> chain
          .get(ctx -> ctx.render("Welcome to example ratpack!!!"))));
    }
}

Как мы видим, используяRatpackServer, теперь мы можем запустить сервер (порт по умолчанию 5050). Методhandlers() принимает функцию, которая получает объектChain, который отображает все соответствующие входящие запросы. Этот «API цепочки обработчиков» используется для построения стратегии обработки ответов.

Если мы запустим этот фрагмент кода и нажмем в браузереhttp://localhost:5050, «Добро пожаловать в пример ratpack !!!» должен отображаться.

Точно так же мы можем отобразить HTTP-запрос POST.

3.3. Обработка параметров пути URL

В следующем примере нам нужно захватить некоторый параметр пути URL в нашем приложении. В Ratpack мы используемPathTokens для их захвата:

RatpackServer.start(server -> server
  .handlers(chain -> chain
  .get(":name", ctx -> ctx.render("Hello "
  + ctx.getPathTokens().get("name") + " !!!"))));

Здесь мы сопоставляем параметр URLname. Всякий раз, когда приходит запрос типаhttp://localhost:5050/John, ответ будет «Привет, Джон !!!».

3.4. Request/Response Header Modification With/Without Filter

Иногда нам нужно изменить заголовок встроенного HTTP-ответа в зависимости от наших потребностей. Ratpack имеетMutableHeaders для настройки исходящих ответов.

Например, нам нужно изменить следующие заголовки в ответе:Access-Control-Allow-Origin,Accept-Language иAccept-Charset:

RatpackServer.start(server -> server.handlers(chain -> chain.all(ctx -> {
    MutableHeaders headers = ctx.getResponse().getHeaders();
    headers.set("Access-Control-Allow-Origin", "*");
    headers.set("Accept-Language", "en-us");
    headers.set("Accept-Charset", "UTF-8");
    ctx.next();
}).get(":name", ctx -> ctx
    .render("Hello " + ctx.getPathTokens().get("name") + "!!!"))));

ИспользуяMutableHeaders, мы устанавливаем три заголовка и помещаем их вChain.

Таким же образом мы можем проверить заголовки входящих запросов:

ctx.getRequest().getHeaders().get("//TODO")

То же самое может быть достигнуто путем создания фильтра. Ratpack имеет интерфейсHandler,, который можно реализовать для создания фильтра. У него есть только один методhandle(),, который принимает текущийContext в качестве параметра:

public class RequestValidatorFilter implements Handler {

    @Override
    public void handle(Context ctx) throws Exception {
        MutableHeaders headers = ctx.getResponse().getHeaders();
        headers.set("Access-Control-Allow-Origin", "*");
        ctx.next();
    }
}

Мы можем использовать этот фильтр следующим образом:

RatpackServer.start(
    server -> server.handlers(chain -> chain
      .all(new RequestValidatorFilter())
      .get(ctx -> ctx.render("Welcome to example ratpack!!!"))));
}

3.5. JSON Parser

Ratpack внутренне используетfaster-jackson для разбора JSON. Мы можем использовать модульJackson для синтаксического анализа любого объекта в JSON.

Давайте создадим простой класс POJO, который будет использоваться для анализа:

public class Employee {

    private Long id;
    private String title;
    private String name;

    // getters and setters

}

Здесь мы создали один простой класс POJO с именемEmployee, который имеет три параметра:id, title иname. Теперь мы будем использовать этот объектEmployee для преобразования в JSON и возвращать то же самое при попадании определенного URL:

List employees = new ArrayList();
employees.add(new Employee(1L, "Mr", "John Doe"));
employees.add(new Employee(2L, "Mr", "White Snow"));

RatpackServer.start(
    server -> server.handlers(chain -> chain
      .get("data/employees",
      ctx -> ctx.render(Jackson.json(employees)))));

Как мы видим, мы вручную добавляем два объектаEmployee в список и анализируем их как JSON с помощью модуляJackson. Как только URL/data/employees будет достигнут, объект JSON будет возвращен.

Здесь следует отметить, чтоwe are not using ObjectMapper at all, поскольку модуль Jackson от Ratpack сделает все необходимое на лету.

3.6. База данных в памяти

Ratpack имеет поддержку первого класса для баз данных в памяти. Он используетHikariCP для пула соединений JDBC. Чтобы использовать его, нам нужно добавитьHikariCP module dependency Ratpack вpom.xml:


    io.ratpack
    ratpack-hikari
    1.4.5

Если мы используемGradle, то же самое нужно добавить в файл сборки Gradle:

compile ratpack.dependency('hikari')

Теперь нам нужно создать файл SQL с операторами таблицы DDL, чтобы таблицы создавались, как только сервер запущен и работает. Мы создадим файлDDL.sql в каталогеsrc/main/resources и добавим в него несколько операторов DDL.

Поскольку мы используем базу данных H2, мы должны добавить и для нее зависимости.

Теперь, используя HikariModule, мы можем инициализировать базу данных во время выполнения:

RatpackServer.start(
    server -> server.registry(Guice.registry(bindings ->
      bindings.module(HikariModule.class, config -> {
          config.setDataSourceClassName("org.h2.jdbcx.JdbcDataSource");
          config.addDataSourceProperty("URL",
          "jdbc:h2:mem:example;INIT=RUNSCRIPT FROM 'classpath:/DDL.sql'");
      }))).handlers(...));

4. тестирование

Как упоминалось ранее, Ratpack имеет первоклассную поддержку для тестовых случаев jUnit. ИспользуяMainClassApplicationUnderTest, мы можем легко создавать тестовые примеры и тестировать конечные точки:

@RunWith(JUnit4.class)
public class ApplicationTest {

    MainClassApplicationUnderTest appUnderTest
      = new MainClassApplicationUnderTest(Application.class);

    @Test
    public void givenDefaultUrl_getStaticText() {
        assertEquals("Welcome to example ratpack!!!",
          appUnderTest.getHttpClient().getText("/"));
    }

    @Test
    public void givenDynamicUrl_getDynamicText() {
        assertEquals("Hello dummybot!!!",
          appUnderTest.getHttpClient().getText("/dummybot"));
    }

    @Test
    public void givenUrl_getListOfEmployee()
      throws JsonProcessingException {

        List employees = new ArrayList();
        ObjectMapper mapper = new ObjectMapper();
        employees.add(new Employee(1L, "Mr", "John Doe"));
        employees.add(new Employee(2L, "Mr", "White Snow"));

        assertEquals(mapper.writeValueAsString(employees),
          appUnderTest.getHttpClient().getText("/data/employees"));
    }

    @After
    public void shutdown() {
        appUnderTest.close();
    }

}

Обратите внимание, что нам нужно вручную завершить работающий экземплярMainClassApplicationUnderTest, вызвав методclose(), поскольку он может излишне блокировать ресурсы JVM. Вот почему мы использовали аннотацию@After для принудительного завершения экземпляра после выполнения тестового примера.

5. Заключение

В этой статье мы увидели простоту использования Ratpack.

Как всегда, доступен полный исходный кодover on GitHub.