SYSTEM_BLOG
TIME: 00:00:00
STATUS: ONLINE
~/blog/distributed-print-system-architecture
$ cat distributed-print-system-architecture.md _
| 2025-11-25 | 15 min

Building a Distributed Print System with Node.js and WebSockets

# Building a Distributed Print System with Node.js and WebSockets

Enterprise label printing demands reliability at scale. This architecture handles thousands of print jobs across multiple locations with automatic printer discovery and intelligent failover.

## System Overview

The print system consists of three layers:

  1. API Gateway - Receives print requests
  2. Job Orchestrator - Routes and manages jobs
  3. Print Workers - Execute actual printing
TEXT
┌─────────────┐     ┌───────────────┐     ┌──────────────┐
│   Client    │────▶│   Gateway     │────▶│  Orchestrator│
└─────────────┘     └───────────────┘     └──────────────┘
                                                  │
                    ┌─────────────────────────────┼─────────────────────────────┐
                    │                             │                             │
                    ▼                             ▼                             ▼
            ┌──────────────┐             ┌──────────────┐             ┌──────────────┐
            │   Worker 1   │             │   Worker 2   │             │   Worker N   │
            │  (Warehouse) │             │  (Shipping)  │             │   (Office)   │
            └──────────────┘             └──────────────┘             └──────────────┘

## Auto-Discovery Protocol

Workers announce themselves on startup via UDP broadcast:

JAVASCRIPT
const dgram = require('dgram');
const socket = dgram.createSocket('udp4');

function announceWorker(workerId, capabilities) {
const message = Buffer.from(JSON.stringify({
type: 'WORKERANNOUNCE',
workerId,
capabilities,
timestamp: Date.now()
}));

socket.setBroadcast(true);
socket.send(message, 0, message.length, 5000, '255.255.255.255');
}

### Discovery Response

The orchestrator listens for announcements and maintains a registry:

JAVASCRIPT
const workers = new Map();

socket.on('message', (msg, rinfo) => {
const data = JSON.parse(msg.toString());

if (data.type === 'WORKERANNOUNCE') {
workers.set(data.workerId, {
...data,
address: rinfo.address,
lastSeen: Date.now()
});
}
});

## WebSocket Communication

Real-time job dispatch uses WebSockets for bidirectional communication:

JAVASCRIPT
const WebSocket = require('ws');

class PrintWorker {
constructor(orchestratorUrl, workerId) {
this.ws = new WebSocket(orchestratorUrl);
this.workerId = workerId;
this.activeJobs = new Map();

this.ws.on('message', this.handleMessage.bind(this));
}

handleMessage(data) {
const message = JSON.parse(data);

switch (message.type) {
case 'JOBASSIGN':
this.processJob(message.job);
break;
case 'JOB
CANCEL':
this.cancelJob(message.jobId);
break;
case 'STATUS_REQUEST':
this.reportStatus();
break;
}
}
}

## Smart Retry Logic

Print failures are inevitable. The retry system handles them gracefully:

Failure TypeRetry StrategyMax Attempts
Network TimeoutExponential backoff5
Paper JamImmediate retry3
Offline PrinterRoute to backup1
Invalid DataNo retry (fail fast)0
JAVASCRIPT
async function executeWithRetry(job, options = {}) {
  const { maxAttempts = 3, backoffMs = 1000 } = options;

for (let attempt = 1; attempt <= maxAttempts; attempt++) {
try {
return await executePrintJob(job);
} catch (error) {
if (!isRetryable(error) || attempt === maxAttempts) {
throw error;
}

const delay = backoffMs * Math.pow(2, attempt - 1);
await sleep(delay);
}
}
}

## Brother Raster Protocol Integration

Label printing uses the Brother raster protocol for thermal printers:

JAVASCRIPT
const net = require('net');

function sendLabelToPrinter(printerIp, labelData) {
return new Promise((resolve, reject) => {
const socket = new net.Socket();

socket.connect(9100, printerIp, () => {
// Initialize printer
socket.write(Buffer.from([0x1B, 0x40])); // ESC @

// Set media type
socket.write(Buffer.from([0x1B, 0x69, 0x7A, ...]));

// Send raster data
socket.write(labelData);

// End job
socket.write(Buffer.from([0x1A]));
socket.end();
});

socket.on('close', resolve);
socket.on('error', reject);
});
}

## Monitoring and Metrics

Every component emits metrics for observability:

## Conclusion

Distributed printing requires careful orchestration of discovery, routing, and failure handling. This event-driven architecture scales horizontally while maintaining reliability through smart retry logic and real-time communication.