Статьи

Проект без сервера, Java и FN, первые шаги

Без сервера не новость, но справедливо сказать, что по-прежнему много шумихи по поводу того, как это все изменит, и как в будущем все будет без серверов. Помимо безсерверных / функций, предоставляемых облачными провайдерами, на нашем пути появляется все больше и больше безсерверных проектов, цель которых — избавить нас от привязки к поставщикам и позволить нам работать без серверов даже в помещении. Давайте посмотрим на один такой проект FN Project.

Что такое FN Project

Если мы перейдем на официальный сайт проекта FN http://fnproject.io/, мы можем прочитать что-то вроде этого:

«Проект Fn представляет собой серверную платформу с открытым исходным кодом, которую вы можете запускать где угодно — в любом облаке или локально. Он прост в использовании, поддерживает все языки программирования, является расширяемым и производительным ».

FN Project — это проект с открытым исходным кодом, поддерживаемый Oracle, основанный на контейнерах. Таким образом, теоретически все, что может стать контейнером и может читать и записывать из / в stdin / stdout, может стать функцией в проекте FN. Это очень хорошая функция, поскольку она означает, что теоретически она может поддерживать любой язык программирования, в отличие от безсерверных / функций, предоставляемых облачными провайдерами, где, если ваш предпочитаемый язык не поддерживается, вы не сможете использовать его с безсерверным.

Еще одна приятная особенность проекта FN заключается в том, что он может работать локально, либо в облаке, либо в нескольких облаках, либо в сочетании всего перечисленного.

Начальная настройка

Единственным предварительным условием для проекта FN является Docker 17.10.0-ce или новее.

Для настройки проекта FN нам нужно только скачать бинарный файл FN

и добавьте его к пути. После этого мы готовы начать играть с FN.

Начальная функция в проекте FN

Первое, что нам нужно сделать, это запустить сервер FN. Для этого нам нужно всего лишь набрать это в терминале / консоли

1
$ fn start

Чтобы проверить, что все работает хорошо, мы можем запустить эту команду

1
$ fn version

Это распечатает версию сервера fn и клиента fn, работающего на машине. В случае моего ноутбука я получаю эти значения

1
2
3
$ fn version
  Client version: 0.5.15
  Server version: 0.3.595

Как только мы проверили, что все хорошо, мы можем начать создавать нашу первую функцию.

Первая функция в проекте FN

Как уже упоминалось, проект FN «независим от языка» , теоретически он может поддерживать любой язык, но это не значит, что он поддерживает все языки в настоящий момент. Чтобы увидеть, какие языки поддерживаются в имеющейся у нас версии, мы можем запустить следующую команду:

1
$ fn init --help

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

1
$ fn init --runtime java --trigger http function1

function1 — это имя функции, и здесь мы помещаем имя, которое хотим использовать. Опция –trigger http означает, что мы хотим создать HTTP-триггер для нашей функции, который позволит нам вызывать его по HTTP, например, через curl. После выполнения этой команды fn сгенерирует начальную функцию для нас и поместит ее в каталог, названный так, как мы назвали нашу функцию, в моем случае function1 .

Давайте посмотрим на то, что генерируется

1
2
3
4
5
6
7
$ cd function1
$ find .
 
./src/main/java/com/example/fn/HelloFunction.java
./src/test/java/com/example/fn/HelloFunctionTest.java
./pom.xml
./func.yaml

Если мы откроем файл pom.xml, он будет выглядеть как любой другой файл pom.xml. Только зависимости там для проекта FN будут зависимостями для тестируемой части, нет никаких зависимостей для сборки или запуска нашей java-функции fn.

Если мы откроем HelloFunction.java , мы снова увидим, что это простой Java-класс с ZERO-зависимостями.

01
02
03
04
05
06
07
08
09
10
11
package com.example.fn;
 
    public class HelloFunction {
 
    public String handleRequest(String input) {
        String name = (input == null || input.isEmpty()) ?
                                             "world" : input;
 
        return "Hello, " + name + "!";
    }
}

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

«Магия» проекта FN

Так как же тогда работает FN? Как он знает, как запустить нашу функцию?

Вся магия в файле func.yaml . Или, если быть более точным, все настройки, необходимые для создания функции в проекте FN. Давайте посмотрим на это поближе.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
$ cat func.yaml
 
schema_version: 20180708
name: function1
version: 0.0.1
runtime: java
build_image: fnproject/fn-java-fdk-build:jdk9-1.0.75
run_image: fnproject/fn-java-fdk:jdk9-1.0.75
cmd: com.example.fn.HelloFunction::handleRequest
format: http-stream
triggers:
- name: function1-trigger
type: http
source: /function1-trigger

Здесь есть несколько полей:

  • schema_version указывает, какая версия Fn использовалась для создания этого файла
  • имя это имя нашей функции
  • версия — это текущая версия нашей функции, и при ее развертывании она будет автоматически инкрементирована
  • язык выполнения, который мы выбрали для написания нашей функции
  • Образ докера build_image, используемый для построения нашей функции, зависит, конечно, от выбранного языка
  • Образ докера run_image, используемый для запуска нашей функции
  • cmd точка входа в нашу функцию, что нужно вызвать для выполнения нашей бизнес-логики
  • триггеры здесь определены триггеры для вызова нашей функции, в нашем случае у нас есть триггер HTTP

Модульные испытания в проекте FN

Возможно, вы заметили, что один из сгенерированных файлов — HelloFunctionTest.java , этот файл действительно является файлом модульного теста для нашей функции, который также автоматически сгенерирован для нас, и содержит простой пример модульного теста. Давайте посмотрим на этот файл.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
public class HelloFunctionTest {
 
    @Rule
    public final FnTestingRule testing =  
                                FnTestingRule.createDefault();
 
    @Test
    public void shouldReturnGreeting() {
        testing.givenEvent().enqueue();
        testing.thenRun(HelloFunction.class, "handleRequest");
 
        FnResult result = testing.getOnlyResult();
        assertEquals("Hello, world!",
                     result.getBodyAsString());
    }
}

За исключением некоторых зависимостей fn и части с @Rule , все остальное похоже на любой другой тест JUnit в java. Этот модульный тест просто вызовет нашу функцию без передачи каких-либо параметров и проверит, является ли результат «Hello world!». Самое замечательное в этом тесте состоит в том, что мы можем запустить его как любой другой модульный тест, мы можем вызвать его из maven или IDE любым стандартным способом.

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

1
2
3
4
5
6
7
8
9
@Test
    public void shouldReturnGreetingwithBodyValue() {
        testing.givenEvent().withBody("Java").enqueue();
        testing.thenRun(HelloFunction.class, "handleRequest");
 
        FnResult result = testing.getOnlyResult();
        assertEquals("Hello, Java!",
                     result.getBodyAsString());
}

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

Развертывание и вызов функции FN

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

1
$ fn deploy --app myapp1 --local

Здесь мы говорим fn развернуть нашу функцию в приложении myapp1 и развернуть ее только локально, указав параметр –local . Как только мы успешно развернули нашу функцию, мы можем вызвать ее. Чтобы вызвать его, мы можем запустить следующую команду

1
$ fn invoke myapp1 function1

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

1
$ echo "Java is great" | fn invoke myapp1 function1

Если вы помните, мы также создали триггер HTTP, поэтому давайте использовать его для вызова нашей функции.

1
$ curl http://localhost:8080/t/myapp1/function1-trigger

Функция FN с JSON

Мы уже можем сделать много хороших вещей с этим, но давайте перейдем к следующему уровню, где мы будем использовать JSON в качестве ввода и вывода наших функций FN. Во-первых, нам нужно создать простой класс POJO, что-то вроде этого

01
02
03
04
05
06
07
08
09
10
11
12
public class Hello {
 
    private String message;
 
    public String getMessage() {
        return message;
    }
 
    public void setMessage(String message) {
        this.message = message;
    }
}

Теперь мы можем изменить нашу функцию, чтобы она воспринимала этот класс как ввод и вывод, чтобы функция выглядела так:

01
02
03
04
05
06
07
08
09
10
public Hello handleRequest(Hello input) {
    String name = (input == null ||
                     input.getMessage().isEmpty()) ? "world" :
                                           input.getMessage();
 
    Hello hello = new Hello();
    hello.setMessage(message + ", " + name + "!")
 
    return hello;
}

после того, как мы развернем функцию, мы можем вызвать ее так

1
2
$ curl -d '{"message":"JSON Input"}' \
              http://localhost:8080/t/myapp1/function1-trigger

Ссылки и будущие чтения

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

То, что мы увидели здесь, является лишь частью возможностей проекта FN, для получения дополнительной информации о FN в целом и дополнительной информации о возможностях, которые я бы предложил посмотреть на сайтах, перечисленных ниже

Опубликовано на Java Code Geeks с разрешения Владимира Деяновича, партнера нашей программы JCG . Смотрите оригинальную статью здесь: Serverless, Java и FN Project, первые шаги

Мнения, высказанные участниками Java Code Geeks, являются их собственными.