Καλέστε ένα πρόγραμμα Python που συνεχίζει να τρέχει με το PYPELINE()
Το PYPELINE(name, [options]) ξεκινά το pypelines/name.py με python3 και κρατά μια σύνδεση μαζί του, ώστε η εφαρμογή να μπορεί να χρησιμοποιεί βιβλιοθήκες Python (δεδομένα, μοντέλα, επεξεργασία εικόνων) χωρίς να ξεκινά νέα διεργασία για κάθε αίτημα. Επιστρέφει ένα αντικείμενο με send(data) για να στείλει ένα μήνυμα, on('data', fn) για να λάβει τις απαντήσεις και close() για να σταματήσει το σενάριο. Ένα όνομα που ξεκινά με ~ είναι αντίθετα η διαδρομή ενός αρχείου (~/opt/job.py) και ένα κείμενο που είναι κώδικας (έχει αλλαγές γραμμής ή μοιάζει με Python) εκτελείται ως ενσωματωμένο πρόγραμμα με python3 -c.
- Το πρωτόκολλο. Το σενάριο ξεκινά ως
python3 -u script.py <socket>: το τελευταίο όρισμα είναι η διαδρομή ενός Unix socket στο οποίο ακούει η εφαρμογή και το σενάριο συνδέεται σε αυτό. Κάθε μήνυμα, και προς τις δύο κατευθύνσεις, είναι ένα πλαίσιο: 4 byte με το μήκος (big endian) που ακολουθούνται από το σώμα, το οποίο είναι JSON από προεπιλογή. - Επιλογές:
type('json'προεπιλογή,'text'ή οτιδήποτε άλλο για ακατέργασταBuffer),autorestart(falseγια να μην επανεκκινείται το σενάριο όταν τελειώνει· από προεπιλογή ξεκινά ξανά),threadsκαιsocket: false(χωρίς socket, μόνο εκκίνηση του προγράμματος). - Τα μηνύματα που στέλνονται πριν συνδεθεί το σενάριο περιμένουν και παραδίδονται όταν συνδεθεί.
- Συμβάντα:
open(το σενάριο συνδέθηκε),data(ένα μήνυμα),restartκαιclose. - Η διαδρομή του socket έχει όριο. Το socket είναι ένα αρχείο στον φάκελο
tmpτης εφαρμογής και το σύστημα περιορίζει ολόκληρη τη διαδρομή (περίπου 104 χαρακτήρες σε macOS και 108 σε Linux). Σε ένα έργο που βρίσκεται βαθιά στον δίσκο ο διακομιστής αποτυγχάνει μεlisten EINVAL, το σενάριο δεν συνδέεται ποτέ και τα μηνύματα περιμένουν για πάντα. Η διαδρομή είναι η ιδιότηταpypeline.socket: ορίστε την αμέσως μετά τοPYPELINE()(το αρχείο ελέγχεται και ο διακομιστής ξεκινά αργότερα) σε μια σύντομη, όπως κάνει το παράδειγμα. - Το πρόγραμμα Python είναι ένα κανονικό πρόγραμμα που τρέχει με τα δικαιώματα της εφαρμογής: είναι τόσο ασφαλές όσο ο κώδικας του αρχείου και πρέπει να είναι εγκατεστημένο το Python 3 (
python3στοPATH).
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 });
}