6.7 KiB
PLBA
PLBA (Platform Runtime for Business Applications) - runtime для бизнес-приложений.
Платформа берет на себя инфраструктурные обязанности:
- lifecycle приложения
- запуск и остановку воркеров
- health/status
- tracing
- logging
- control plane
- загрузку конфигурации
Бизнес-приложение на базе plba собирается вокруг трех уровней:
ApplicationModuleсобирает приложение и регистрирует воркерыWorkerуправляет исполнением и lifecycleRoutineреализует бизнес-функцию
Routine не является контрактом plba. Это рекомендуемый архитектурный паттерн для прикладного кода.
Правила построения приложений на платформе собраны отдельно в application_guidelines.md.
Installation
Установка plba через pip из git-репозитория:
pip install "plba @ git+https://git.lesha.spb.ru/alex/plba.git"
Runtime model
- приложение объявляет
ApplicationModule - модуль регистрирует один или несколько
Worker RuntimeManagerзапускает все воркеры- каждый
Workerзапускает свою бизнес-активность - runtime агрегирует health и status
- runtime останавливает воркеры graceful или forcefully
Main contracts
ApplicationModule
Описывает, из чего состоит приложение.
Ответственность:
- дать имя модуля
- зарегистрировать воркеры
- зарегистрировать health contributors при необходимости
- собрать прикладные зависимости
Worker
Главный runtime-контракт платформы.
Контракт:
namecriticalstart()stop(force=False)health()status()
Worker отвечает только за runtime-поведение:
- как запускается бизнес-активность
- в одном потоке или нескольких
- single-run или loop
- graceful shutdown
- интерпретацию ошибок в
health/status
Routine
Рекомендуемый application-level паттерн.
Routine описывает бизнес-функцию:
- что читать
- какие сервисы вызывать
- какие бизнес-решения принимать
- что сохранять или отправлять наружу
Обычно воркер получает одну routine через конструктор и вызывает ее в start() или во внутренних helper-методах.
Minimal example
from threading import Event, Lock, Thread
from time import sleep
from plba import (
ApplicationModule,
Worker,
WorkerHealth,
WorkerStatus,
create_runtime,
)
class OrdersRoutine:
def __init__(self, service) -> None:
self._service = service
def run(self) -> None:
self._service.process_new_orders()
class OrdersWorker(Worker):
def __init__(self, routine: OrdersRoutine, interval: float = 1.0) -> None:
self._routine = routine
self._interval = interval
self._thread: Thread | None = None
self._stop_requested = Event()
self._lock = Lock()
self._in_flight = 0
self._failures = 0
@property
def name(self) -> str:
return "orders-worker"
@property
def critical(self) -> bool:
return True
def start(self) -> None:
if self._thread and self._thread.is_alive():
return
self._stop_requested.clear()
self._thread = Thread(target=self._run_loop, daemon=True)
self._thread.start()
def stop(self, force: bool = False) -> None:
del force
self._stop_requested.set()
def health(self) -> WorkerHealth:
if self._failures > 0:
return WorkerHealth(self.name, "degraded", self.critical)
return WorkerHealth(self.name, "ok", self.critical)
def status(self) -> WorkerStatus:
alive = self._thread is not None and self._thread.is_alive()
state = "busy" if self._in_flight else "idle"
if not alive:
state = "stopped"
elif self._stop_requested.is_set():
state = "stopping"
return WorkerStatus(name=self.name, state=state, in_flight=self._in_flight)
def _run_loop(self) -> None:
while not self._stop_requested.is_set():
with self._lock:
self._in_flight += 1
try:
self._routine.run()
except Exception:
self._failures += 1
finally:
with self._lock:
self._in_flight -= 1
sleep(self._interval)
class OrdersModule(ApplicationModule):
@property
def name(self) -> str:
return "orders"
def register(self, registry) -> None:
service = OrderService()
routine = OrdersRoutine(service)
registry.add_worker(OrdersWorker(routine))
runtime = create_runtime(OrdersModule(), config_path="config.yml")
runtime.start()
Health and status
Практика такая:
Routineвыполняет бизнес-работуWorkerловит ее ошибкиWorkerинтерпретирует outcome вhealth()иstatus()
То есть routine не выставляет health напрямую.
In-memory queue
InMemoryTaskQueue остается в платформе как простой in-memory utility.
Это не базовый платформенный контракт и не обязательный паттерн архитектуры. Ее можно использовать в прикладном коде как локальный буфер между компонентами, если это действительно помогает.
from plba import InMemoryTaskQueue
queue = InMemoryTaskQueue[str]()
queue.put("payload")
item = queue.get(timeout=0.1)
Public API
Основные публичные сущности:
ApplicationModuleWorkerWorkerHealthWorkerStatusRuntimeManagerWorkerSupervisorTraceServiceHealthRegistryInMemoryTaskQueuecreate_runtime(...)