August Lilleaas
Hva er poenget med Node.js?
Push-meldinger for iOS
Nettlesertesting
JavaScript: Ikke så viktig
Jeg lager ikke websider i Node.js
Skreddersydde servere
Løser multitrådet programmering for deg
Node.js har ikke tråder
Bare tøys
Din kode kjører alltid i samme tråd
API-er bruker tråder i bakgrunnen
fs.readFile("/path/to/a", function (err, data) {
console.log("a");
});
fs.readFile("/path/to/b", function (err, data) {
console.log("b");
});
console.log("test");
// => test, a, b
// => test, b, a
fs.readFile("/path", func)
childProcess.exec("pygments 'var myCode = {};'", func)
tcpSocket.on("data", func)
httpServer.on("request", func)
udpSocket.on("data", func)
Din kode kjører alltid i samme tråd
API-er bruker tråder i bakgrunnen
teddziuba.com/2011/10/node-js-is-cancer.html
Mangel på programmerer-tråder kan være et problem
uv_work_t* req = new uv_work_t();
req->data = ...;
uv_queue_work(uv_default_loop(), req, OperationFunc, AfterFunc);
Callback spaghetti is about the last pattern with which you'd ever want to write anything
HTTPS-server, ActiveMQ, TLS-sockets
var httpsServer = https.createServer({
key: fs.readFileSync("server.key"),
cert: fs.readFileSync("server.crt"),
requestCert: true,
ca: fs.readFileSync("ca.crt"),
rejectUnauthorized: true
});
httpsServer.on("request", function (req, res) {
var data = JSON.parse(req.body);
var peerCert = req.connection.getPeerCertificate();
var fingerprint = peerCert.fingerprint;
mysql.find({
table: "apps",
where: ["fingerprint = ?", fingerprint],
success: function (app) {
activeMQ.queue(app, data);
res.writeHead(200); res.end();
}
});
});
function onStompMessage(app, data) {
if (!(app.identifier in apnsSockets)) {
var socket = new ApnsSocket(app);
apnsSockets[app.identifier] = socket;
}
apnsSockets[app.identifier].write(data);
}
write: function (data) {
queue.push(data);
consumeQueue();
}
function consumeQueue() {
if (queue.length == 0) return;
if (connectInProgress) return;
if (!connected) connect(); return;
var message = queue.shift();
writeToSocket(message);
process.nextTick(function () { consumeQueue(); });
}
function connect() {
connectInProgress = true;
promise.all([
pfs.readFile(app.sslPath + "/apns.key"),
pfs.readFile(app.sslPath + "/apns.crt")
]).then(function (key, cert) {
var socket = tls.connect(2195, "gateway.push.apple.com", {
key: key, cert: cert, ca: CA_CERTS
}, function () {
if (socket.authorized) {
onSuccessfulConnection(socket);
}
});
});
}
function onSuccessfulConnection(socket) {
connectionInProgress = false;
connected = true;
socket.on("end", function () { connected = false; });
socket.on("close", onSocketClose);
socket.on("data", onApnsError);
connectedSocket = socket;
consumeQueue();
}
Vi har nå sett et eksempel på multitrådet kode hvor deadlocks og annet snacks er umulig.
var buffer = new Buffer(1 + 4 + 4 + 2 + deviceToken.length + 2 + payload.length);
var i = 0;
// Command
buffer[i++] = 1;
// Message identifier
buffer[i++] = msgId >> 24 & 0xFF;
buffer[i++] = msgId >> 16 & 0xFF;
buffer[i++] = msgId >> 8 & 0xFF;
buffer[i++] = msgId & 0xFF;
// Expiry in epoch seconds (a day from now)
var expiresAt = (now.getTime() / 1000) + 86400;
buffer[i++] = expiresAt >> 24 & 0xFF;
buffer[i++] = expiresAt >> 16 & 0xFF;
buffer[i++] = expiresAt >> 8 & 0xFF;
buffer[i++] = expiresAt & 0xFF;
// Device token
buffer[i++] = deviceToken.length >> 8 & 0xFF;
buffer[i++] = deviceToken.length & 0xFF;
deviceToken.copy(buffer, i, 0, deviceToken.length);
i += deviceToken.length;
// Payload
buffer[i++] = payload.length >> 8 & 0xFF;
buffer[i++] = payload.length & 0xFF;
payload.copy(buffer, i, 0, payload.length);
return buffer;
HTTP capture server, resource sets
var captureServer = busterCaptureServer.create();
var httpServer = http.createServer();
httpServer.listen(8080);
captureServer.attach(httpServer);
function respond(req, res) {
if (req.method == "GET" && req.path == capturePath) {
captureSlave(req, res); return true;
}
if (req.method == "GET" && req.path == "/sessions") {
listSessionsFromRequest(res); return true;
}
if (req.method == "GET" && url.pathname == "/sessions/current") {
showCurrentSession(res); return true;
}
if (req.method == "POST" && req.path == "/sessions") {
createSessionFromRequest(req, res); return true;
}
// ...
function captureSlave(req, res) {
var slave = bSlave.create(this, bayeuxServer, currentSession);
slave.on("end", function () { unloadSlave(slave); });
res.writeHead(302, {"Location": slave.url}); res.end();
bayeux.publish("/capture", slave.serialize());
slaves.push(slave);
}
function createSessionFromRequest(req, res) {
var requestBody = "";
req.setEncoding("utf8");
req.on("data", function (chunk) { requestBody += chunk; });
req.on("end", function () {
createSessionWithRequestBody(requestBody, res);
});
}
function createSessionWithRequestBody(requestBody, res) {
try {
var data = JSON.parse(requestBody);
} catch(e) {
res.writeHead(400); res.write("Invalid JSON"); res.end();
return;
}
var session = createSession(data);
res.writeHead(sessions.length > 1 ? 202 : 201);
res.write(JSON.stringify(session.serialize()));
res.end();
}
var resourceSet = busterResources.resourceSet.deserialize({
resources: [
{path: "/foo.js", content: "var a = 123;"},
{path: "/bar.js", content: "var b = 456;"}
],
loadPath: ["/foo.js"]
});
function respond(req, res) {
if (req.method == "GET" && req.path == capturePath) {
captureSlave(req, res); return true;
}
if (req.method == "GET" && req.path == "/sessions") {
listSessionsFromRequest(res); return true;
}
// ...
if (resourceMiddleware.respond(req, res)) return true;
var resourceMiddleware = bResources.resourceMiddleware.create();
// ...
resourceMiddleware.mount(
"/sessions/" + session.id,
session.resourceSet
);
// Klient
$~> buster test
var rs = bResourceSet.create(); rs.addResource(...);
rs.serialize();
// Server
var rs = bResourceSet.deserialize(reqBody);
middleware.mount("/sessions/" + id);
slave.load("/sessions/" + id)
Med gode abstraksjoner kan man lage fine høynivå webapper med Node.js
Node er ikke som poteten
(Poteten kan brukes til alt)
Takk for meg