Volajte program v Pythone, ktorý stále beží, cez PYPELINE()
PYPELINE(name, [options]) spustí pypelines/name.py pomocou python3 a udržiava s ním spojenie, takže aplikácia môže používať knižnice Pythonu (dáta, modely, spracovanie obrázkov) bez spustenia nového procesu pre každú požiadavku. Vráti objekt s send(data) na odoslanie správy, on('data', fn) na prijímanie odpovedí a close() na zastavenie skriptu. Názov, ktorý začína ~, je namiesto toho cesta k súboru (~/opt/job.py) a text, ktorý je kódom (má zalomenia riadkov alebo vyzerá ako Python), sa spustí ako inline program cez python3 -c.
- Protokol. Skript sa spustí ako
python3 -u script.py <socket>: posledný argument je cesta k Unix socketu, na ktorom aplikácia počúva, a skript sa k nemu pripojí. Každá správa v oboch smeroch je rámec: 4 bajty s dĺžkou (big endian) nasledované telom, ktoré je predvolene JSON. - Voľby:
type('json'predvolene,'text'alebo čokoľvek iné pre surovéBuffery),autorestart(false, ak sa skript po skončení nemá reštartovať; predvolene sa spustí znova),threadsasocket: false(bez socketu, iba spustiť program). - Správy odoslané predtým, než sa skript pripojil, čakajú a doručia sa, keď sa pripojí.
- Udalosti:
open(skript sa pripojil),data(správa),restartaclose. - Cesta k socketu má limit. Socket je súbor v priečinku
tmpaplikácie a systém obmedzuje celú cestu (asi 104 znakov na macOS a 108 na Linuxe). V projekte, ktorý je hlboko na disku, server zlyhá slisten EINVAL, skript sa nikdy nepripojí a správy čakajú navždy. Cesta je vlastnosťpypeline.socket: nastavte ju hneď poPYPELINE()(súbor sa kontroluje a server sa spúšťa až potom) na krátku, ako to robí príklad. - Program v Pythone je bežný program, ktorý beží s právami aplikácie: je len tak bezpečný ako kód súboru a musí byť nainštalovaný Python 3 (
python3vPATH).
import json
import socket
import struct
import sys
# The last argument is the socket that the application listens on
sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
sock.connect(sys.argv[-1])
def read(count):
data = b''
while len(data) < count:
chunk = sock.recv(count - len(data))
if not chunk:
raise SystemExit(0)
data += chunk
return data
while True:
size = struct.unpack('>I', read(4))[0]
message = json.loads(read(size).decode('utf-8'))
answer = json.dumps({ 'id': message['id'], 'upper': message['text'].upper(), 'words': len(message['text'].split()) }).encode('utf-8')
sock.sendall(struct.pack('>I', len(answer)) + answer)exports.install = function() {
ROUTE('POST /shout/ @json', shout);
};
const pypeline = PYPELINE('shout');
// A short path for the socket: the folder of a project can be too deep for a Unix socket
pypeline.socket = require('os').tmpdir() + '/forge-shout-' + process.pid + '.socket';
const pending = {};
let counter = 0;
// Every answer says which message it is for
pypeline.on('data', function(answer) {
const callback = pending[answer.id];
if (callback) {
delete pending[answer.id];
callback(answer);
}
});
function shout($) {
const id = ++counter;
const timer = setTimeout(() => $.invalid('The script did not answer'), 5000);
pending[id] = function(answer) {
clearTimeout(timer);
$.json(answer);
};
pypeline.send({ id: id, text: $.body.text });
}