Введение
Одно из ключевых направлений развития платформы данных InterSystems IRIS – открытость. Открытость во взаимодействии с языками программирования, технологиями и протоколами. Поддержка языков программирования двусторонняя – возможен как вызов кода из InterSystems IRIS, так и предоставляется API для работы с InterSystems IRIS извне. В этой статье речь пойдёт о первом варианте – вызове кода из InterSystems IRIS. Целью этого небольшого повествования является демонстрация того, как просто и удобно можно это сделать. Я не буду сравнивать различные языки программирования (хотя в конце есть таблица по скорости работы различных имплементаций), всё зависит от решаемых вами задач и требований, предъявляемых к результату разработки. В этой статье я продемонстрирую несколько различных подходов к вызовам сторонних библиотек, а реализовывать мы будем одну и ту же функциональность – вызов функции DEFLATE из библиотеки zlib.
NodeJS
Недавно я прочитал вот эту статью. Речь там идет как раз о вызове функции DEFLATE на NodeJS из платформы InterSystems IRIS, и эта статья натолкнула меня на мысль описать вызов кода и на других языках.
Итак, начнем с NodeJS. Я беру код почти целиком из статьи Бернда, за исключением того, что в нем не используются файлы, а прямое http-соединение для передачи данных. В целом лучше передавать данные в теле запроса, и кодировать как запрос, так и ответ в виде base64. Тем не менее, вот код:
//zlibserver.js const express = require('express'); const zlib = require('zlib'); var app = express(); app.get('/zlibapi/:text', function(req, res) { res.type('application/json'); var text=req.params.text; try { zlib.deflate(text, (err, buffer) => { if (!err) { res.status(200).send(buffer.toString('binary')); } else { res.status(500).json( { "error" : err.message}); // handle error } }); } catch(err) { res.status(500).json({ "error" : err.message}); return; } }); app.listen(3000, function(){ console.log("zlibserver started"); });
Чтобы запустить сервер, выполните в терминале ОС (должны быть установлены node
и npm
):
cd <repo>\node npm install node ./zlibserver.js
Что здесь происходит? Слушаем порт 3000
, сжимаем DEFLATE тело запроса и возвращаем сжатые данные в ответ. На стороне InterSystems IRIS используется http запрос для взаимодействия с данным API:
/// NodeJS implementation /// do ##class(isc.zlib.Test).node() ClassMethod node(text As %String = "Hello World", Output response As %String) As %Status { kill response set req = ##class(%Net.HttpRequest).%New() set req.Server = "localhost" set req.Port = 3000 set req.Location = "/zlibapi/" _ text set sc = req.Get(,,$$$NO) quit:$$$ISERR(sc) sc set response = req.HttpResponse.Data.Read($$$MaxStringLength) quit sc }
Обратите внимание, что я устанавливаю третий аргумент set sc = req.Get(,,$$$NO)
– reset
равным нулю. Если вы пишете интерфейс для внешнего http(s) сервера, то лучше всего повторно использовать один объект запроса и просто модифицировать его по мере необходимости для выполнения новых запросов.
Java
Java Gateway позволяет вызывать произвольный Java-код. Недавно я писал про него более подробно. Стандартная библиотека Java включает класс Deflater
, который делает именно то, что нам нужно:
package isc.zlib; import java.util.Arrays; import java.util.zip.Deflater; public abstract class Java { public static byte[] compress(String inputString) { byte[] output = new byte[inputString.length()*3]; try { // Encode a String into bytes byte[] input = inputString.getBytes("UTF-8"); // Compress the bytes Deflater compresser = new Deflater(); compresser.setInput(input); compresser.finish(); int compressedDataLength = compresser.deflate(output); compresser.end(); output = Arrays.copyOfRange(output, 0, compressedDataLength); } catch (java.io.UnsupportedEncodingException ex) { // handle } return output; } }
Особенность этой имплементации заключается в том, что она возвращает массив byte[]
, который становится потоком на стороне InterSystems IRIS. Я пытался вернуть строку, но не смог найти, как сформировать бинарную строку из byte[]
. Если у вас есть какие-то идеи, пожалуйста, оставьте комментарий.
Чтобы запустить код, поместите jar из релизов в папку <instance>/bin
, загрузите код в свой инстанс InterSystems IRIS и выполните:
write $System.Status.GetErrorText(##class(isc.zlib.Utils).createGateway()) write $System.Status.GetErrorText(##class(isc.zlib.Utils).updateJar())
Проверьте метод createGateway
перед запуском. Второй аргумент javaHome
предполагает что переменная окружения JAVA_HOME
установлена. Если это не так, вручную передайте путь до Java 1.8 JRE. Для сжатия строки text
выполните этот код:
set gateway = ##class(isc.zlib.Utils).connect() set response = ##class(isc.zlib.Java).compress(gateway, text)
C
Библиотека InterSystems Callout это динамическая библиотека, предоставляющая функции, которые вы можете вызвать из InterSystems IRIS.
Вот наша библиотека:
#define ZF_DLL // Ugly Windows hack #ifndef ulong typedef unsigned long ulong; #endif #include "string.h" #include "stdio.h" #include "stdlib.h" #include "zlib.h" #include <cdzf.h> #int Compress(char* istream, CACHE_EXSTRP retval) { ulong srcLen = strlen(istream)+1; // +1 for the trailing `\0` ulong destLen = compressBound(srcLen); // estimate size needed for the buffer char* ostream = malloc(destLen); int res = compress(ostream, &destLen, istream, srcLen); CACHEEXSTRKILL(retval); if (!CACHEEXSTRNEW(retval,destLen)) {return ZF_FAILURE;} memcpy(retval->str.ch,ostream,destLen); // copy to retval->str.ch return ZF_SUCCESS; } ZFBEGIN ZFENTRY("Compress","cJ",Compress) ZFEND
Для запуска загрузите dll
или so
со страницы релизов в папку <instance>/bin
. В репозитории есть также скрипты для сборки вашей собственной версии библиотеки.
Перед сборкой установите:
- Linux:
apt install build-essential zlib1g zlib1g-devel
- Windows: WinBuilds
Для работы с Callout библиотекой выполните:
set path = ##class(isc.zlib.Test).getLibPath() //get path to library file set response = $ZF(-3, path, "Compress", text) // execute function do $ZF(-3, "") //unload library
Python
Используя Python Gateway вызовем данный код:
import zlib zlib.compress(b'text')
Из InterSystems IRIS:
set out = ##class(isc.py.Callout).SimpleString("import zlib" _ $$$NL _ "x = zlib.compress(b'" _ text _ "')", "x")
C
Net Gateway позволяет вызывать произвольный код на языке C#. Недавно я писал про него более подробно.
Реализация на C# также возвращает поток, а не строку:
using System; using System.IO; using System.IO.Compression; namespace isc.zlib { public class Net { public static byte[] compress(String str) { using (MemoryStream output = new MemoryStream()) { using (DeflateStream gzip = new DeflateStream(output, CompressionMode.Compress)) { using (StreamWriter writer = new StreamWriter(gzip, System.Text.Encoding.UTF8)) { writer.Write(str); } } return output.ToArray(); } } } }
Для запуска загрузите zlibnet.dll
со страницы релизов в папку <instance>/bin
.
write $System.Status.GetErrorText(##class(isc.zlib.Utils).createNetGateway()) write $System.Status.GetErrorText(##class(isc.zlib.Utils).updateNet())
Для сжатия строки text
выполните этот код:
set gateway = ##class(isc.zlib.Utils).connect() set response = ##class(isc.zlib.Net).compress(gateway, text)
InterSystems ObjectScript
Несколько неожиданно в статье о механизмах вызова кода на других языках, но в ObjectScript также есть встроенная функция Compress (и парная функция Decompress). Вызывается так:
set response = $extract($SYSTEM.Util.Compress(text), 2, *-1)
Помните, что поиск в документации или вопрос на Developers Community может сэкономить вам некоторое время.
Сравнение
Я запустил простой тест (1Kb text, 1 000 000 итераций) на Linux (VPS) и Windows (Ноутбук) и получил следующие результаты.
Windows:
Метод | С | ObjectScript | Python | Java | NodeJS | С# |
---|---|---|---|---|---|---|
Время | 22,77 | 33,41 | 91,52 | 152,73 | 622,51 | 216,43 |
Скорость (Kb/s) | 43912 | 29927 | 10670 | 6547 | 1606 | 4512 |
Разница, % | -/- | 46,73 | 401,93 | 570,75 | 2633,90 | 950,5 |
Linux:
Метод | С | ObjectScript | Python | Java | NodeJS |
---|---|---|---|---|---|
Время | 76,3541 | 76,499 | 283,84 | 147,2436 | 953,7311 |
Скорость (Kb/s) | 13097 | 13072 | 3440 | 6791 | 1049 |
Разница, % | -/- | 0,19 | 92,84 | 92,84 | 1149,09% |
Для запуска тестов загрузите код и вызовите:
do ##class(isc.zlib.Test).test(textLength, iterations)
Заключение
С платформой InterSystems IRIS вы легко можете использовать существующий код на ряде популярных языков. Однако выбор реализации не всегда прост: необходимо учитывать несколько метрик, таких как скорость разработки, производительность, кроссплатформенность и простота сопровождения. Ответы на эти вопросы помогут вам определиться с оптимальным планом разработки.