Programovanie neblokujúcich I/O pomocou greenletov a v node.js
V dnešnom blogu sa pozrieme na dva rôzne spôsoby zápisu neblokujúcich I/O. Jeden bude použitím callback funkcií používaných v node.js a druhý pomocou greenletov v pyhtone.
Príklad
Náš ukážkový program bude veľmi jednoduchý. Náš program bude zobrazovať výrok dňa, ktorý bude uložený v memcache (kvôli jednoduchosti). Kontaktovať memcache budeme vždy 2x - prvým prístupom zistíme kľúč v ktorom je výrok uložený a druhým prístupom získame samotný výrok.
Node.js
// server.js
var http = require('http');
var Memcached = require('memcached');
var memcached = new Memcached('127.0.0.1:11211'); // pripojenie na memcache
// Nastavenie premenných, toto by mal riešiť externý skript
memcached.set("msg_var", "vyrok_2013-07-12")
memcached.set("vyrok_2013-07-12", "vyrok pre dnesok")
http.createServer(function (req, res) {
// IO operácia - načítanie msg var
memcached.get('msg_var', function(err, msg_var) {
// callback po načítaní msg_var
if (err) {
sys.puts('Error!');
return;
}
// IO operácia - načítanie konkrétneho výroku
memcached.get(msg_var, function(err, msg) {
if (err) {
sys.puts('Error!');
return;
}
res.writeHead(200, {'Content-Type': 'text/plain'});
res.end(msg); // výpis výroku
});
});
}).listen(8888, '127.0.0.1');
Príklad sa spúšťa príkazom node server.js. Pred spustením samozrejme musíme nainštalovať a spustiť memcached a nainštalovať npm modul príkazom npm install memcached.
gevent
# server.py
from gevent.wsgi import WSGIServer
import umemcache as memcache
mc = memcache.Client('127.0.0.1:11211')
mc.connect()
mc.set("msg_var", "vyrok_2013-07-12")
mc.set("vyrok_2013-07-12", "vyrok pre dnesok")
def application(environ, start_response):
start_response("200 OK", [('Content-Type', 'text/plain')])
msg_var = mc.get("msg_var")[0]
msg = mc.get(msg_var)[0]
yield msg
WSGIServer(('127.0.0.1', 8888), application, log=None).serve_forever()
Server sa spustí príkazom python server.py. Závislosti ako gevent a umemcache musia byť samozrejme pred spustením nainštalované.
Výsledky
Oba servery boli testované príkazom ab2 -n 10000 -c 100 127.0.0.1:8888.
Node:
Concurrency Level: 100
Time taken for tests: 16.881 seconds
Complete requests: 10000
Failed requests: 0
Write errors: 0
Total transferred: 1180000 bytes
HTML transferred: 170000 bytes
Requests per second: 592.38 [#/sec] (mean)
Time per request: 168.811 [ms] (mean)
Time per request: 1.688 [ms] (mean, across all concurrent requests)
Transfer rate: 68.26 [Kbytes/sec] received
Connection Times (ms)
min mean[+/-sd] median max
Connect: 0 0 3.1 0 46
Processing: 20 168 65.8 179 413
Waiting: 19 168 65.7 179 412
Total: 65 168 65.9 179 413
Percentage of the requests served within a certain time (ms)
50% 179
66% 204
75% 216
80% 220
90% 254
95% 281
98% 303
99% 389
100% 413 (longest request)
Gevent:
Concurrency Level: 100
Time taken for tests: 19.746 seconds
Complete requests: 10000
Failed requests: 0
Write errors: 0
Total transferred: 1180000 bytes
HTML transferred: 170000 bytes
Requests per second: 506.42 [#/sec] (mean)
Time per request: 197.464 [ms] (mean)
Time per request: 1.975 [ms] (mean, across all concurrent requests)
Transfer rate: 58.36 [Kbytes/sec] received
Connection Times (ms)
min mean[+/-sd] median max
Connect: 0 0 1.6 0 24
Processing: 16 196 13.9 197 219
Waiting: 15 196 13.9 197 219
Total: 32 196 12.7 198 219
Percentage of the requests served within a certain time (ms)
50% 198
66% 201
75% 203
80% 204
90% 207
95% 209
98% 211
99% 212
100% 219 (longest request)
Výsledok
Žiaden zázrak v podobe zdrvujúcej prehry geventu sa nekonal. Dobre implementovaný kooperatívny multitasking je prakticky ekvivalentný s eventloopom v node.js. Rozdiel je prakticky len v zápise. Kvôli katastrofálnemu zápisu príkazov v node.js vznikli knižnice ako async, v tomto prípade sú však neaplikovateľné. Node.js má samozrejme svoje výhody v podobe knižníc pripravených na async I/O, ale rovnaká funkcionalita sa dá dosiahnuť aj inými spôsobmi.

Pre pridávanie komentárov sa musíte prihlásiť.