Вызываем код на Java, C, NodeJS, C#, Python из InterSystems IRIS

от автора

Введение

Одно из ключевых направлений развития платформы данных InterSystems IRIS — открытость. Открытость во взаимодействии с языками программирования, технологиями и протоколами. Поддержка языков программирования двусторонняя — возможен как вызов кода из InterSystems IRIS, так и предоставляется API для работы с InterSystems IRIS извне. В этой статье речь пойдёт о первом варианте — вызове кода из InterSystems IRIS. Целью этого небольшого повествования является демонстрация того, как просто и удобно можно это сделать. Я не буду сравнивать различные языки программирования (хотя в конце есть таблица по скорости работы различных имплементаций), всё зависит от решаемых вами задач и требований, предъявляемых к результату разработки. В этой статье я продемонстрирую несколько различных подходов к вызовам сторонних библиотек, а реализовывать мы будем одну и ту же функциональность — вызов функции DELFATE из библиотеки zlib.

NodeJS

Недавно я прочитал вот эту статью. Речь там идет как раз о вызове функции DELFATE на 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:

Метод C ObjectScript Python Java NodeJS
Время 76,3541 76,499 283,84 147,2436 953,7311
Скорость (Kb/s) 13097 13072 3440 6791 1049
Разница, % -/- 0,19 371 92,84 1149,09%

Для запуска тестов загрузите код и вызовите: do ##class(isc.zlib.Test).test(textLength, iterations)

Заключение

С платформой InterSystems IRIS вы легко можете использовать существующий код на ряде популярных языков. Однако выбор реализации не всегда прост: необходимо учитывать несколько метрик, таких как скорость разработки, производительность, кроссплатформенность и простота сопровождения. Ответы на эти вопросы помогут вам определиться с оптимальным планом разработки.

Ссылки

ссылка на оригинал статьи https://habr.com/ru/company/intersystems/blog/536940/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *