Статьи

Одиночная зависимая док-станция Java Endpoint HTTP

В этой статье мы создадим конечную точку HTTP на основе Java, сделаем из нее исполняемый файл jar, упакуем его в Docker и быстро запустим локально.

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

Подавляющее большинство примеров, описывающих Java-приложения в среде Dockerized, включают использование тяжелых сред, таких как Spring Boot. Здесь мы хотим показать, что вам не нужно много работать для запуска конечной точки с Java в Docker.

Фактически, мы будем использовать только одну библиотеку в качестве зависимости: ядро HttpMate . В этом примере мы будем использовать конструктор LowLevel HttpMate с одним обработчиком HTTP.

Среда, используемая для этого примера

  • Java 11+
  • Maven 3.5+
  • Java-дружественная IDE
  • Докер версия 18+
  • Базовое понимание HTTP / Bash / Java

Окончательный результат доступен в этом git-репо .

Организация проекта

Давайте создадим нашу первоначальную структуру проекта:

1
mkdir -p simple-java-http-docker/src/main/java/com/envimate/examples/http

Давайте начнем с файла pom проекта в корневом каталоге, который мы назвали здесь simple-java-http-docker :

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
    <modelVersion>4.0.0</modelVersion>
 
    <groupId>com.envimate.examples</groupId>
    <artifactId>simple-java-http-docker</artifactId>
    <version>0.0.1</version>
 
    <dependencies>
        <dependency>
            <groupId>com.envimate.httpmate</groupId>
            <artifactId>core</artifactId>
            <version>1.0.21</version>
        </dependency>
    </dependencies>
</project>

Здесь мы имеем:

  • Стандартное определение groupId / artifactId / version для нашего проекта
  • Одиночная зависимость от базовой библиотеки HttpMate

Этого достаточно, чтобы начать разработку нашей конечной точки в выбранной IDE. Большинство из них поддерживают Java-проекты на основе Maven.

Точка входа

Чтобы запустить наш маленький сервер, мы будем использовать простой метод main. Давайте создадим запись для нашего приложения в виде файла Application.java в каталоге src/main/java/com/envimate/examples/http , который пока просто выводит время на консоль.

01
02
03
04
05
06
07
08
09
10
11
12
package com.envimate.examples.http;
 
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
 
public final class Application {
    public static void main(String[] args) {
        final LocalDateTime time = LocalDateTime.now();
        final String dateFormatted = time.format(DateTimeFormatter.ISO_TIME);
        System.out.println("current time is " + dateFormatted);
    }
}

Попробуйте запустить этот класс, и вы увидите текущее время.

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

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
package com.envimate.examples.http;
 
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.function.Supplier;
 
public final class Application {
    public static void main(String[] args) {
        Supplier handler = () -> {
            final LocalDateTime time = LocalDateTime.now();
            final String dateFormatted = time.format(DateTimeFormatter.ISO_TIME);
            return "current time is " + dateFormatted;
        };
 
        System.out.println(handler.get());
    }
}

Удобный интерфейс, предоставляемый низкоуровневым HttpMate, не выглядит сильно отличающимся, за исключением того, что вместо возврата String в качестве ответа указывается String , а также указывается, что все прошло хорошо (код ответа 200).

1
2
3
4
5
6
7
final HttpHandler httpHandler = (request, response) -> {
    final LocalDateTime time = LocalDateTime.now();
    final String dateFormatted = time.format(DateTimeFormatter.ISO_TIME);
 
    response.setStatus(200);
    response.setBody("current time is " + dateFormatted);
};

HttpMate также предоставляет простую оболочку Java HttpServer — PureJavaEndpoint — которая позволит вам запускать конечную точку без каких-либо дополнительных зависимостей.

Все, что нам нужно сделать, это дать ему экземпляр HttpMate:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
package com.envimate.examples.http;
 
import com.envimate.httpmate.HttpMate;
import com.envimate.httpmate.convenience.endpoints.PureJavaEndpoint;
import com.envimate.httpmate.convenience.handler.HttpHandler;
 
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
 
import static com.envimate.httpmate.HttpMate.anHttpMateConfiguredAs;
import static com.envimate.httpmate.LowLevelBuilder.LOW_LEVEL;
 
public final class Application {
    public static void main(String[] args) {
        final HttpHandler httpHandler = (request, response) -> {
            final LocalDateTime time = LocalDateTime.now();
            final String dateFormatted = time.format(DateTimeFormatter.ISO_TIME);
 
            response.setStatus(200);
            response.setBody("current time is " + dateFormatted);
        };
 
        final HttpMate httpMate = anHttpMateConfiguredAs(LOW_LEVEL)
                .get("/time", httpHandler)
                .build();
        PureJavaEndpoint.pureJavaEndpointFor(httpMate).listeningOnThePort(1337);
    }
}

Обратите внимание, что мы настроили наш httpHandler для обслуживания пути /time при вызове методом GET.

Пришло время запустить наше приложение и сделать несколько запросов:

1
2
curl http://localhost:1337/time
current time is 15:09:34.458756

Прежде чем мы поместим все это в Dockerfile, нам нужно упаковать его как старый добрый jar.

Строить банку

Для этого нам понадобятся два плагина maven: maven-compiler-plugin и maven-assembly-plugin для сборки исполняемого файла jar.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
<?xml version="1.0" encoding="UTF-8"?>
    <modelVersion>4.0.0</modelVersion>
 
    <groupId>com.envimate.examples</groupId>
    <artifactId>simple-java-http-docker</artifactId>
    <version>0.0.1</version>
 
    <dependencies>
        <dependency>
            <groupId>com.envimate.httpmate</groupId>
            <artifactId>core</artifactId>
            <version>1.0.21</version>
        </dependency>
    </dependencies>
 
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.1</version>
                <configuration>
                    <release>${java.version}</release>
                    <source>${java.version}</source>
                    <target>${java.version}</target>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-assembly-plugin</artifactId>
                <executions>
                    <execution>
                        <phase>package</phase>
                        <goals>
                            <goal>single</goal>
                        </goals>
                        <configuration>
                            <archive>
                                <manifest>
                                    <mainClass>
                                        com.envimate.examples.http.Application
                                    </mainClass>
                                </manifest>
                            </archive>
                            <descriptorRefs>
                                <descriptorRef>jar-with-dependencies</descriptorRef>
                            </descriptorRefs>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
</project>

Как только мы получим это, давайте создадим нашу банку:

1
mvn clean verify

И запустите получившуюся банку:

1
java -jar target/simple-java-http-docker-0.0.1-jar-with-dependencies.jar

Тот же локон:

1
2
curl http://localhost:1337/time
current time is 15:14:42.992563

Докеризация банки

Dockerfile выглядит довольно просто:

1
2
3
4
5
6
7
FROM openjdk:12
 
ADD target/simple-java-http-docker-0.0.1-jar-with-dependencies.jar /opt/application.jar
 
EXPOSE 1337
 
ENTRYPOINT exec java -jar /opt/application.jar

Это указывает

  • FROM : какое изображение использовать в качестве основы. Мы будем использовать изображение openjdk.
  • ADD : банку мы хотим в каталог, который мы хотим
  • EXPOSE : порт, на котором мы слушаем
  • ENTRYPOINT : к команде, которую мы хотим выполнить

Чтобы создать и пометить наш образ докера, мы запускаем следующую команду из корня каталога:

1
docker build --tag simple-java-http-docker .

Это создаст образ докера, который мы можем запустить:

1
docker run --publish 1337:1337 simple-java-http-docker

Обратите внимание, что мы --publish параметр --publish , который указывает, что открытый порт 1337 доступен через порт 1337 машины.

Тот же локон:

1
2
curl http://localhost:1337/time
current time is 15:23:04.275515

И это все: у нас есть простая док-станция для конечной точки HTTP!

Глазурь

Конечно, это упрощенный пример, и конечная точка, которую мы написали, не совсем полезна. Это демонстрирует, однако, что вам не нужны тонны библиотек только для того, чтобы иметь работающую конечную точку HTTP, насколько легко упаковать исполняемый jar, использовать docker с вашим java-приложением и базовое использование низкоуровневого HttpMate.

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

Обработчик HttpMate предоставляет доступ к объекту с именем MetaData который в значительной степени так и называется — метаданными вашего запроса, то есть всем доступным по вашему запросу.

Используя этот объект, мы можем напечатать все, что есть в запросе.

01
02
03
04
05
06
07
08
09
10
11
12
public final class FakeTwitter {
    public static void main(String[] args) {
        final HttpMate httpMate = HttpMate.aLowLevelHttpMate()
                .callingTheHandler(metaData -> {
                    System.out.println(metaData);
                })
                .forRequestPath("/*").andRequestMethods(GET, POST, PUT)
                .build();
 
        PureJavaEndpoint.pureJavaEndpointFor(httpMate).listeningOnThePort(1337);
    }
}

Путь /time запроса теперь заменяется шаблоном, охватывающим все пути, и мы можем добавить все интересующие нас методы HTTP.

Запуск нашего сервера FakeTwitter и выдача запроса:

1
curl -XGET http://localhost:1337/some/path/with?someParameter=someValue

Мы увидим следующий вывод в консоли (вывод, отформатированный для удобства чтения: это карта внизу, так что вы можете отформатировать ее, если хотите)

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
{
    PATH=Path(path=/some/path/with),
    BODY_STRING=,
    RAW_QUERY_PARAMETERS={someParameter=someValue},
    QUERY_PARAMETERS=QueryParameters(
        queryParameters={
            QueryParameterKey(key=someParameter)=QueryParameterValue(value=someValue)
        }
    ),
    RESPONSE_STATUS=200,
    RAW_HEADERS={Accept=*/*,
    Host=localhost:1337,
    User-agent=curl/7.61.0},
    RAW_METHOD=GET,
    IS_HTTP_REQUEST=true,
    PATH_PARAMETERS=PathParameters(pathParameters={}),
    BODY_STREAM=sun.net.httpserver.FixedLengthInputStream@6053cef4,
    RESPONSE_HEADERS={},
    HEADERS=Headers(headers={HeaderKey(value=user-agent)=HeaderValue(value=curl/7.61.0),
    HeaderKey(value=host)=HeaderValue(value=localhost:1337),
    HeaderKey(value=accept)=HeaderValue(value=*/*)}),
    CONTENT_TYPE=ContentType(value=null),
    RAW_PATH=/some/path/with,
    METHOD=GET,
    LOGGER=com.envimate.httpmate.logger.Loggers$$Lambda$17/0x000000080118f040@5106c12f,
    HANDLER=com.envimate.examples.http.FakeTwitter$$Lambda$18/0x000000080118f440@68157191
}

Заключительные слова

HttpMate сам по себе предлагает гораздо больше функциональности. Тем не менее, он молодой, еще не для производственного использования и нуждается в вашей поддержке! Если вам нравится то, что вы читаете, сообщите нам об этом, отправив нам электронное письмо по адресу [email protected] или попробуйте HttpMate и оставьте комментарий в проблеме с обратной связью .

См. Оригинальную статью здесь: Java Одиночная зависимость Dockerized HTTP Endpoint Мнения, выраженные разработчиками Java Code Geeks, являются их собственными.