Современный COBOL: руководство по реализации микросервиса


Предварительные условия

Вы знаете основные принципы, методы и стандарты COBOL. В этом руководстве мы используем GnuCOBOL — бесплатный компилятор COBOL, реализующий значительную часть стандартов COBOL 85, COBOL 2002, COBOL 2014 и X/Open COBOL, а также многие расширения, включённые в другие компиляторы COBOL.

Вы знакомы с протоколом HTTP  —  форматами запросов и ответов.

У вас установлены: 

  • Docker  —  инструмент виртуализации, работающий из командной строки.
  • NPM  —  менеджер пакетов для JavaScript.
  •  Git  —  клиент с открытым исходным кодом для управления версиями.

У вас есть аккаунт на GitHub для публикации микросервисов.

Вы можете использовать любой удобный текстовый редактор, но я рекомендую Visual Studio Code (или его версию с открытым исходным кодом VSCodium) с установленным расширением синтаксиса COBOL bitlang.cobol.

TLDR

Полный исходный код этого руководства на GitHub.

Спецификации

Одна из сильных сторон COBOL — это десятичные вычисления. В этом руководстве мы создадим высокоточный микросервис обмена валют, обрабатывающий HTTP API и возвращающий сумму в евро в формате JSON.

Скажем, микросервис ожидает HTTP запрос GET /<currency>/<amount> по порту 8000 и отвечает JSON {"amount": <amount>}, где:

  • <currency>  —  это трёхбуквенный ISO код валюты, например, USD;
  • <amount>  —  это числовое значение, разделённое точкой, например, 999.999

Любые несоответствующие запросы, неподдерживаемые валюты и ошибки вычисления приведут к ответу 404 Not Found.

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

Структура

Нам нужно 3 каталога — src для основной программы, tests для тестовой и resources для статических файлов. Пожалуйста, скачайте CSV (.zip) с ECB и разархивируйте в каталог resources. Файл содержит обменные курсы евро к 32 валютам. Как указано в спецификации, курсы остаются неизменными.

$ ls resources src tests $ ls resources eurofxref.csv

Наконец, создадим пустые файлы microservice.cbl и microservice-test.cbl в каталогах src и tests соответственно. Они понадобятся нам позже.

Зависимости

Наш микросервис зависит от: 

  • HTTP-сервера для обработки запросов; 
  • парсера ECB для CSV;
  • среды тестирования GCBLUnit. 

Все эти компоненты доступны в реестре пакетов COBOL — cobolget.com. Мы с лёгкостью можем интегрировать эти зависимости с помощью инструмента управления пакетами COBOL с открытым исходным кодом cobolget. Вот полный листинг:

$ npm install -g cobolget $ cobolget init Manifest modules.json created. $ cobolget add core-network Dependency 'core-network' has been added to the manifest. $ cobolget add core-string Dependency 'core-string' has been added to the manifest. $ cobolget add --debug gcblunit Debug dependency 'gcblunit' has been added to the manifest. $ cobolget update Lockfile modules-lock.json updated. $ cobolget -t bca12d6c4efed0627c87f2e576b72bdb5ab88e34 install

В последней команде используется Team Token, поскольку core-network является закрытым пакетом, принадлежащим Cobolget,но свободно распространяемым в сообществе. Вы увидите длинный процесс загрузки, заканчивающийся строкой:

Copybook modules.cpy updated

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

Программа

По сути наша программа должна:

  • читать CSV-файл;
  • преобразовывать CSV-текст в список пар валюта-курс;
  • запускать локальный TCP/IP сервер на 8000 порту с помощью реализации обратного вызова, который обрабатывает HTTP запросы.
identification division. program-id. microservice. ... procedure division. *> чтение CSV-файла в csv-содержимое open input file-csv. if not file-exists display "Error reading file" upon syserr stop run end-if. perform until exit read file-csv at end exit perform end-read end-perform. close file-csv. *> преобразование csv-содержимого в список пар ключ-значение move csv-ecb-rates(csv-content) to dataset. *> запуск HTTP сервера с обратным вызовов http-обработчика call "receive-tcp" using "localhost", 8000, 0, address of entry "http-handler". end program microservice. identification division. program-id. http-handler. ... procedure division using l-buffer, l-length returning omitted. *> инициализация обменного курса set address of exchange-rates to dataset-ptr. *> парсинг запроса как "GET /<currency>/<amount>" unstring l-buffer(1:l-length) delimited by all SPACES into request-method, request-path. if not http-get perform response-NOK end-if. *> поиск валюты и расчёт суммы в евро perform varying idx from 1 by 1 until idx > 64 if rate-currency(idx) = get-currency compute eur-amount = numval(get-amount) / rate-value(idx) on size error perform response-NOK end-compute perform response-OK end-if end-perform. *> или ничего perform response-NOK. response-OK section. move HTTP-OK to response-status. move byte-length(response-content) to response-content-length. perform response-any. response-NOK section. move HTTP-NOT-FOUND to response-status. move 0 to response-content-length. perform response-any. response-any section. move 1 to l-length. string response delimited by size into l-buffer with pointer l-length. subtract 1 from l-length. goback. end program http-handler. copy "modules/modules.cpy".

Программа receive-tcp  —  это сервер, который принимает входящие соединения, считывает содержимое запроса в буфер и делится буфером с программой обратного вызова. Обратный вызов парсит содержимое и заменяет буфер ответом. Сервер отправляет ответ обратно клиенту. Полный листинг программы на GitHub.

Давайте установим среду выполнения GnuCOBOL Docker:

$ docker run -d -i --name gnucobol olegkunitsyn/gnucobol:2.2 $ docker exec -i gnucobol cobc -V cobc (GnuCOBOL) 2.2.0 Copyright (C) 2017 Free Software Foundation, Inc. License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html> This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. Written by Keisuke Nishida, Roger While, Ron Norman, Simon Sobisch, Edward Hart Built Jul 26 2020 07:44:23 Packaged Sep 06 2017 18:45:29 UTC C version "9.3.0"

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

Тест

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

Давайте создадим Dockerfile микросервиса:

FROM olegkunitsyn/gnucobol:2.2 RUN mkdir /microservice WORKDIR /microservice COPY . . EXPOSE 8000 RUN cobc -x -debug modules/gcblunit/gcblunit.cbl tests/* --job='microservice-test'

Мы открываем 8000 порт и выполняем работу microservice-test при каждой сборке образа. Последний элемент всей картины — это тест-файл microservice-test.cbl:

>>SOURCE FORMAT FREE identification division. program-id. microservice-test. environment division. configuration section. repository. function csv-ecb-rates function substr-pos function all intrinsic. data division. working-storage section. 01 dataset external. 05 dataset-ptr usage pointer. 01 buffer pic x(1024) value "GET /USD/1 HTTP1.1". procedure division. move csv-ecb-rates(concatenate("Date, USD, " x"0a" "17 July 2020, 1.1428, ")) to dataset. call "http-handler" using buffer, byte-length(buffer). perform http-handler-test. goback. http-handler-test section. call "assert-notequals" using 0, substr-pos(buffer, "HTTP/1.1 200 OK"). call "assert-notequals" using 0, substr-pos(buffer, "Content-Type: application/json"). call "assert-notequals" using 0, substr-pos(buffer, "Content-Length: 44"). call "assert-equals" using 104, substr-pos(buffer, "0.8750437521876093"). end program microservice-test. copy "src/microservice.cbl".

Для целей тестирования я подготовил минимальное CSV содержимое только с одной валютой — USD. Как видно из определения буфера, тест запрашивает конвертацию 1 USD. Мы ожидаем ненулевые HTTP-заголовки, а также обменную сумму с высокой точностью 0.8750437521876093. Последняя строка включает в себя основную тестируемую программу.

Контейнер

Создадим образ Docker:

$ docker build --tag microservice . ... OK Tests: 0000000001, Skipped: 0000000000 Assertions: 0000000004, Failures: 0000000000, Exceptions: 0000000000 ...

Прекрасно! Наш образ Docker успешно прошёл тест, рассчитав 4 выражения, и готов к запуску:

$ docker run -d -i --name microservice -p 8000:8000 microservice $ docker exec -i microservice cobc -j -x src/microservice.cbl TCP server started on localhost:08000. Hit Ctrl+C to stop.

Откроем http://localhost:8000/USD/99.99 и http://localhost:8000/ABC/1 в браузере и посмотрим, что произойдёт. Чтобы остановить и удалить контейнер, запустим:

$ docker rm --force microservice

GitHub

Наконец, опубликуем микросервис, применяя GitHub Actions, где каждый запрос на включение внесённых изменений или пуш в репозиторий запускают выполнение microservice-test. Всё, что вам нужно,  —  это файл docker-image.yml в каталоге .github/workflows:

name: Docker Image CI on: push: branches: [ master ] pull_request: branches: [ master ] jobs: build: runs-on: ubuntu-latest steps: - uses: actions/[email protected] - name: Build the Docker image run: docker build . --file Dockerfile --tag my-image-name:$(date +%s)

Заключение

Мы реализовали микросервис с помощью библиотеки Git, менеджера пакетов, модульного тестирования и виртуализации в рамках подхода непрерывной интеграции. COBOL, которому уже 60 лет, подходит для современной разработки программного обеспечения!


Перевод статьи Oleg Kunitsyn: Modern COBOL: Microservice Tutorial


Поделиться статьей:


Вернуться к статьям

Комментарии

    Ничего не найдено.