Перейти к основному содержимому

Введение в Ratpack

· 6 мин. чтения

1. Обзор

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

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

2. Почему Ratpack?

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

  • это очень легкий, быстрый и масштабируемый
  • он потребляет меньше памяти, чем другие фреймворки, такие как DropWizard; интересный результат сравнительного теста можно найти здесь
  • поскольку он построен на основе Netty , Ratpack полностью управляем событиями и неблокирует по своей природе.
  • он поддерживает управление зависимостями Guice
  • как и Spring Boot , Ratpack имеет собственные библиотеки тестирования для быстрой настройки тестовых случаев.

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

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

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

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

<dependency>
<groupId>io.ratpack</groupId>
<artifactId>ratpack-core</artifactId>
<version>1.4.5</version>
</dependency>
<dependency>
<groupId>io.ratpack</groupId>
<artifactId>ratpack-test</artifactId>
<version>1.4.5</version>
</dependency>

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

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

Мы можем использовать следующий скрипт сборки 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 ForEach ratpack!!!"))));
}
}

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

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

Точно так же мы можем сопоставить запрос 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") + " !!!"))));

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

3.4. Модификация заголовка запроса/ответа с/без фильтра

Иногда нам нужно изменить встроенный заголовок ответа 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 , мы устанавливаем три заголовка и помещаем их в цепочку .

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

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

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

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 foreach ratpack!!!"))));
}

3.5. Парсер JSON

Ratpack внутри использует более быстрый джексон для парсинга JSON. Мы можем использовать модуль Джексона для анализа любого объекта в 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<Employee> employees = new ArrayList<Employee>();
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 с помощью модуля Джексона . Как только будет достигнут URL-адрес /data/employees , будет возвращен объект JSON.

Здесь следует отметить, что мы вообще не используем ObjectMapper , поскольку модуль Ratpack Jackson сделает все необходимое на лету.

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

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

<dependency>
<groupId>io.ratpack</groupId>
<artifactId>ratpack-hikari</artifactId>
<version>1.4.5</version>
</dependency>

Если мы используем 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:foreach;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 foreach ratpack!!!",
appUnderTest.getHttpClient().getText("/"));
}

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

@Test
public void givenUrl_getListOfEmployee()
throws JsonProcessingException {

List<Employee> employees = new ArrayList<Employee>();
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.

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