Reactor (Реактор)
Паттерн проектирования Reactor
Описание Reactor
Реактор (англ. Reactor) — предназначен для синхронной передачи запросов сервису от одного или нескольких источников.
Шаблон проектирования реактора представляет собой шаблон обработки событий для обработки запросов на обслуживание, передаваемых одновременно обработчику услуг одним или несколькими входами. Обработчик сервиса затем демультиплексирует входящие запросы и отправляет их синхронно связанным обработчикам запросов.
Структура:
- Ресурсы Любой ресурс, который может обеспечить ввод или потребление выходных данных из системы.
- Синхронный демультиплексор событий Использует цикл событий для блокировки всех ресурсов. Демультиплексор отправляет ресурс диспетчеру, когда можно запустить синхронную операцию на ресурсе без блокировки (пример: синхронный вызов read () будет блокироваться, если нет данных для чтения. Демультиплексор использует select () на ресурс, который блокируется до тех пор, пока ресурс не будет доступен для чтения. В этом случае синхронный вызов read () не будет блокироваться, а демультиплексор может отправить ресурс диспетчеру.)
- Диспетчер Обрабатывает регистрацию и отмена регистрации обработчиков запросов. Отправляет ресурсы из демультиплексора в соответствующий обработчик запросов.
- Обработчик запросов Обработчик обработанного приложения и связанный с ним ресурс.
Плюсы:
Модель реактора полностью отделяет конкретный код приложения от реализации реактора, что означает, что компоненты приложения можно разделить на модульные, повторно используемые детали. Кроме того, из-за синхронного вызова обработчиков запросов шаблон реактора допускает простой параллельный анализ, не добавляя сложность нескольких потоков в систему.
Минусы
Модель реактора может быть сложнее отлаживать, чем процедурный шаблон из-за перевернутого потока управления. Кроме того, только синхронно обрабатывая обработчики запросов, шаблон реактора ограничивает максимальный параллелизм, особенно на симметричном многопроцессорном оборудовании. Масштабируемость шаблона реактора ограничена не только вызовом обработчиков запросов синхронно, но и демультиплексором.
Паттерн описан Андреем Болониным.
Примеры реализации
// Reactor Pattern in JavaScript
class EventHandler {
constructor(name) {
this.name = name;
}
handle(event) {
console.log(`${this.name} handling event:`, event);
return `Processed by ${this.name}`;
}
}
class Reactor {
constructor() {
this.handlers = new Map();
this.isRunning = false;
}
registerHandler(eventType, handler) {
if (!this.handlers.has(eventType)) {
this.handlers.set(eventType, []);
}
this.handlers.get(eventType).push(handler);
console.log(`Registered handler for event type: ${eventType}`);
}
unregisterHandler(eventType, handler) {
if (this.handlers.has(eventType)) {
const handlers = this.handlers.get(eventType);
const index = handlers.indexOf(handler);
if (index > -1) {
handlers.splice(index, 1);
console.log(`Unregistered handler for event type: ${eventType}`);
}
}
}
dispatch(event) {
const eventType = event.type;
if (this.handlers.has(eventType)) {
const handlers = this.handlers.get(eventType);
console.log(`Dispatching event ${eventType} to ${handlers.length} handlers`);
handlers.forEach(handler => {
try {
handler.handle(event);
} catch (error) {
console.error(`Error in handler for ${eventType}:`, error);
}
});
} else {
console.log(`No handlers for event type: ${eventType}`);
}
}
start() {
this.isRunning = true;
console.log('Reactor started');
}
stop() {
this.isRunning = false;
console.log('Reactor stopped');
}
}
// Usage
const reactor = new Reactor();
// Create handlers
const userHandler = new EventHandler('UserHandler');
const emailHandler = new EventHandler('EmailHandler');
const logHandler = new EventHandler('LogHandler');
// Register handlers
reactor.registerHandler('user.created', userHandler);
reactor.registerHandler('user.created', emailHandler);
reactor.registerHandler('user.created', logHandler);
reactor.registerHandler('user.updated', userHandler);
reactor.registerHandler('user.updated', logHandler);
// Start reactor
reactor.start();
// Dispatch events
reactor.dispatch({ type: 'user.created', data: { id: 1, name: 'John Doe' } });
reactor.dispatch({ type: 'user.updated', data: { id: 1, name: 'John Smith' } });
reactor.dispatch({ type: 'user.deleted', data: { id: 1 } });
<?php
// Reactor Pattern in PHP
class EventHandler {
private $name;
public function __construct($name) {
$this->name = $name;
}
public function handle($event) {
echo "{$this->name} handling event: " . json_encode($event) . "\n";
return "Processed by {$this->name}";
}
}
class Reactor {
private $handlers = [];
private $isRunning = false;
public function registerHandler($eventType, $handler) {
if (!isset($this->handlers[$eventType])) {
$this->handlers[$eventType] = [];
}
$this->handlers[$eventType][] = $handler;
echo "Registered handler for event type: $eventType\n";
}
public function unregisterHandler($eventType, $handler) {
if (isset($this->handlers[$eventType])) {
$index = array_search($handler, $this->handlers[$eventType], true);
if ($index !== false) {
unset($this->handlers[$eventType][$index]);
$this->handlers[$eventType] = array_values($this->handlers[$eventType]);
echo "Unregistered handler for event type: $eventType\n";
}
}
}
public function dispatch($event) {
$eventType = $event['type'];
if (isset($this->handlers[$eventType])) {
$handlers = $this->handlers[$eventType];
echo "Dispatching event $eventType to " . count($handlers) . " handlers\n";
foreach ($handlers as $handler) {
try {
$handler->handle($event);
} catch (Exception $e) {
echo "Error in handler for $eventType: " . $e->getMessage() . "\n";
}
}
} else {
echo "No handlers for event type: $eventType\n";
}
}
public function start() {
$this->isRunning = true;
echo "Reactor started\n";
}
public function stop() {
$this->isRunning = false;
echo "Reactor stopped\n";
}
}
// Usage
$reactor = new Reactor();
// Create handlers
$userHandler = new EventHandler('UserHandler');
$emailHandler = new EventHandler('EmailHandler');
$logHandler = new EventHandler('LogHandler');
// Register handlers
$reactor->registerHandler('user.created', $userHandler);
$reactor->registerHandler('user.created', $emailHandler);
$reactor->registerHandler('user.created', $logHandler);
$reactor->registerHandler('user.updated', $userHandler);
$reactor->registerHandler('user.updated', $logHandler);
// Start reactor
$reactor->start();
// Dispatch events
$reactor->dispatch(['type' => 'user.created', 'data' => ['id' => 1, 'name' => 'John Doe']]);
$reactor->dispatch(['type' => 'user.updated', 'data' => ['id' => 1, 'name' => 'John Smith']]);
$reactor->dispatch(['type' => 'user.deleted', 'data' => ['id' => 1]]);
?>
// Reactor Pattern in Go
package main
import (
"fmt"
"sync"
)
type Event struct {
Type string
Data interface{}
}
type EventHandler interface {
Handle(event Event) string
}
type BaseEventHandler struct {
Name string
}
func (h BaseEventHandler) Handle(event Event) string {
fmt.Printf("%s handling event: %+v\n", h.Name, event)
return fmt.Sprintf("Processed by %s", h.Name)
}
type Reactor struct {
handlers map[string][]EventHandler
mutex sync.RWMutex
isRunning bool
}
func NewReactor() *Reactor {
return &Reactor{
handlers: make(map[string][]EventHandler),
}
}
func (r *Reactor) RegisterHandler(eventType string, handler EventHandler) {
r.mutex.Lock()
defer r.mutex.Unlock()
r.handlers[eventType] = append(r.handlers[eventType], handler)
fmt.Printf("Registered handler for event type: %s\n", eventType)
}
func (r *Reactor) UnregisterHandler(eventType string, handler EventHandler) {
r.mutex.Lock()
defer r.mutex.Unlock()
if handlers, exists := r.handlers[eventType]; exists {
for i, h := range handlers {
if h == handler {
r.handlers[eventType] = append(handlers[:i], handlers[i+1:]...)
fmt.Printf("Unregistered handler for event type: %s\n", eventType)
break
}
}
}
}
func (r *Reactor) Dispatch(event Event) {
r.mutex.RLock()
defer r.mutex.RUnlock()
if handlers, exists := r.handlers[event.Type]; exists {
fmt.Printf("Dispatching event %s to %d handlers\n", event.Type, len(handlers))
for _, handler := range handlers {
go func(h EventHandler) {
defer func() {
if r := recover(); r != nil {
fmt.Printf("Error in handler for %s: %v\n", event.Type, r)
}
}()
h.Handle(event)
}(handler)
}
} else {
fmt.Printf("No handlers for event type: %s\n", event.Type)
}
}
func (r *Reactor) Start() {
r.mutex.Lock()
defer r.mutex.Unlock()
r.isRunning = true
fmt.Println("Reactor started")
}
func (r *Reactor) Stop() {
r.mutex.Lock()
defer r.mutex.Unlock()
r.isRunning = false
fmt.Println("Reactor stopped")
}
// Usage
func main() {
reactor := NewReactor()
// Create handlers
userHandler := BaseEventHandler{Name: "UserHandler"}
emailHandler := BaseEventHandler{Name: "EmailHandler"}
logHandler := BaseEventHandler{Name: "LogHandler"}
// Register handlers
reactor.RegisterHandler("user.created", userHandler)
reactor.RegisterHandler("user.created", emailHandler)
reactor.RegisterHandler("user.created", logHandler)
reactor.RegisterHandler("user.updated", userHandler)
reactor.RegisterHandler("user.updated", logHandler)
// Start reactor
reactor.Start()
// Dispatch events
reactor.Dispatch(Event{Type: "user.created", Data: map[string]interface{}{"id": 1, "name": "John Doe"}})
reactor.Dispatch(Event{Type: "user.updated", Data: map[string]interface{}{"id": 1, "name": "John Smith"}})
reactor.Dispatch(Event{Type: "user.deleted", Data: map[string]interface{}{"id": 1}})
}