Ext

Небольшая библиотека для организации межпроцессного взаимодействия (Inter-Process Communication, IPC) между программными компонентами, реализованными на различных языках программирования.

Идея, которая родилась из другого моего проекта, Guiserver, заключается в разделении функциональности приложения на несколько исполняемых файлов/процессов.


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

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

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

Взаимодействие может осуществляться как в синхронном, так и в асинхронном режимею

Для лучшего понимания концепции Ext вспомним одно из близких по духу решений — UCI (Universal Chess Interface), протокол взаимодействия шахматных GUI с шахматными движками. Здесь

Эта концепция может быть полезна в следующих случаях:

Примеры использования

Простейшие клиентские программы:

на Harbour

FUNCTION Main

   LOCAL hExt
   // LOCAL cExe := "python test_srv_1.py"  /* если серверный процесс сделан на Python */
   LOCAL cExe := "test_srv_1.exe"

   // Запускаем серверный процесс
   hExt := ecli_run( cExe )
   IF Empty( hExt )
      ? "Can't launch " + cExe
      RETURN Nil
   ENDIF

   /* Посылаем команду серверному процессу выполнить функцию "fu1" с переданными параметрами
        и выводим в консоль результат */
   ? ecli_RunFunc( hExt, "fu1",{"test","10"} )
   ?

   RETURN Nil
  

на Python

import sys
sys.path.append('../source/python')
from extcli import ecli_Run, ecli_RunFunc, ecli_Close

def main():

    hExt = None
    exe_name = "test_srv_1.exe"

    # Запускаем серверный процесс
    hExt = ecli_Run(exe_name)
    if hExt == None:
        print(f"Can't launch {exe_name}")
        input("Press any key to continue...")
        return None

    # Посылаем команду серверному процессу выполнить функцию "fu1" с переданными параметрами
    #    и выводим в консоль результат
    print( ecli_RunFunc(hExt, "fu1", ("test","10")) )

    return None

if __name__ == "__main__":
    main()
  

на Go

package main

import (
	"fmt"
	ecli "tests/go/extcli"
)

func main() {
	var exeName = "test_srv_1.exe"

	// Запускаем серверный процесс
	hExt := ecli.Init(exeName, 1, "", "")
	if hExt == nil {
		fmt.Println( "Can't launch program" )
		return
	}

	// Посылаем команду серверному процессу выполнить функцию "fu1" с переданными параметрами
	//    и выводим в консоль результат
	fmt.Println( ecli.RunFunc( hExt, "fu1", []string{"test","10"}, false ) )

	ecli.Close(hExt)
}
  

Соответствующие серверные программы:

на Harbour

FUNCTION Main( ... )

   LOCAL hExt

   // Сначала выполняем esrv_Init() для инициализации соединения с вызывающим (клиентским) процессом
   hExt := esrv_Init( hb_arrayToParams( hb_aParams() ) )
   IF Empty( hExt )
      RETURN Nil
   ENDIF

   // И ждем команды от вызывающего (клиентского) процесса
   esrv_Wait( hExt )

   RETURN Nil

// Функция, которую может запросить вызывающий (клиентский) процесс
FUNCTION FU1( aParams )
   RETURN "fu1: " + Time() + " " + aParams[1] + " " + aParams[2]
  

на Python

import sys
sys.path.append('../source/python')
from extsrv import esrv_Init, esrv_Wait

# Функция, которую может запросить вызывающий (клиентский) процесс
def fu1( aparams ):
    return "fu1: " + aparams[0] + " " + aparams[1]

def main( aparams ):

    # Сначала выполняем esrv_Init() для инициализации соединения с вызывающим (клиентским) процессом
    hExt = esrv_Init(aparams)
    if hExt == None:
        return None

    # И ждем команды от вызывающего (клиентского) процесса
    esrv_Wait(hExt)

    return None

if __name__ == "__main__":
    main( sys.argv[1:] )
  

на Go

package main

import "os"
import esrv "tests/go/extsrv"

func main() {

	var sParam = ""

	// Регистрация функций, которые могут быть вызваны клиентским процессом
	esrv.RegFunc("fu1", fu1)
	
	// Формируем строку аргументов для передачи в esrv.Init()
	for i := 1; i < len(os.Args); i++ {
		sParam = sParam + "\n" + os.Args[i]
	}
	// Выполняем esrv_Init() для инициализации соединения с вызывающим (клиентским) процессом
	if esrv.Init(sParam) != 0 {
		esrv.WriteLog( "Init failed" )
		return
	}

	// И ждем команды от вызывающего (клиентского) процесса
	esrv.Wait()
	
	// Закрываем соединение
	esrv.Exit()
}

// Функция, которую может запросить вызывающий (клиентский) процесс
func fu1(p []string) string {
	return "fu1 is launched " + p[0] + " " + p[1]
}
  

Документация

Подробные инструкции и примеры кода доступны в репозитории проекта.

Обратите также внимание на статью в заметках.



Комментариев:       ()       пред.    след.       Добавить комментарий
Длина комментария - не больше 4000 символов.
Ваше имя:

Адрес электронной почты:
(не предназначено к показу)
 
Введите текст с картинки: