Skip to content

Instantly share code, notes, and snippets.

@fxsjy
Created August 3, 2012 05:16
Show Gist options
  • Star 12 You must be signed in to star a gist
  • Fork 4 You must be signed in to fork a gist
  • Save fxsjy/3244607 to your computer and use it in GitHub Desktop.
Save fxsjy/3244607 to your computer and use it in GitHub Desktop.
Simple Memcached server in Javascript with 100 lines of code
/* Simple Memcached in Javascript
* @Author: Sun, Junyi
* @Email: ccnusjy@gmail.com
* @Date: 2012-8-3
*/
var net = require('net');
var store = {}
function handle_header(header,crlf_len){
var tup = header.split(" ")
var expect_body_len = 0
switch(tup[0]){
case 'get':
case 'delete':
expect_body_len = 0
break
case 'set':
expect_body_len = parseInt(tup[4]) + crlf_len
break
}
return expect_body_len
}
function handle_body(socket,header,body,call_back){
var response=""
var tup = header.split(" ")
switch(tup[0]){
case 'get':
var key = tup[1]
var obj = store[key]
if(obj){
response = "VALUE "+ obj.key+" " + obj.flag+" " + obj.data.length + "\r\n"
response += obj.data + "\r\n"
response += "END\r\n"
}
else
response = "NOT_FOUND\r\n"
break;
case 'delete':
var key = tup[1]
delete store[key]
response = "DELETED\r\n"
break;
case 'set':
var obj = {key: tup[1], flag: tup[2], data: body}
store[obj.key] = obj
response = "STORED\r\n"
break;
default:
response = "ERROR\r\n"
break;
}
socket.write(response,"binary",call_back)
}
var server = net.createServer(function (socket) {
console.log("client: ",socket.remoteAddress)
var user_state = 'reading_header'
var buf = ""
var header =""
var body = ""
var expect_body_len = 0
var CRLF_LEN = 2
socket.setEncoding("binary")
socket.on('data',function(data){
buf += data
socket.emit('user_event')
})
socket.on('user_event',function(){
switch(user_state){
case "reading_header":
var pos =-1
if((pos=buf.indexOf('\r\n'))!=-1){
header = buf.slice(0,pos)
buf = buf.slice(pos+2)
CRLF_LEN =2
}
else if((pos=buf.indexOf('\n'))!=-1){
header = buf.slice(0,pos)
buf = buf.slice(pos+1)
CRLF_LEN =1
}
if(pos!=-1){
user_state = 'reading_body'
expect_body_len = handle_header(header,CRLF_LEN)
socket.emit("user_event")
}
break
case "reading_body":
if(expect_body_len <= buf.length){
body = buf.slice(0,expect_body_len-CRLF_LEN)
buf = buf.slice(expect_body_len)
handle_body(socket,header,body,
function(){
user_state = 'reading_header'
if(buf.length>0)
socket.emit("user_event")
}
)
}
break
}
})
});
var port = 11211
console.log("listening at "+ port)
server.listen(port, '0.0.0.0')
@fxsjy
Copy link
Author

fxsjy commented Aug 3, 2012

Hi guys,

I am studying node.js. It is a wonderful utility to write network-based application.

Now, I have written a memcached server using node.js. You can have a look at https://gist.github.com/3244607

I tested the program, and found it could reach 12000/s throughput. However, during the test, I found sometimes the speed suddenly decreased due to the GC pause from my mind.

Is there a way to improve my code ?

Thanks

@bmeck
Copy link

bmeck commented Aug 3, 2012

Don't reemit to 'user_event', use recursion and an iife in the 'data' event. Avoid putting string concatenation in separate statements in handle_body (just wrap after / before a '+'). Avoid coercing buffers to strings, iterate using charCodes and a 'for' loop (this is probably the biggest possible speed gain). The bottlenecks on something with this high of IO is probably String<->Buffer coercions and syscalls from my experience, so you can think about those.

@sidorares
Copy link

big performance problem is probably in "buf += data". Try to keep list of incoming chunks ( buffers.push(data) ) instead. You can do this manually or re-use @substack's 'bufferlist' and/or 'binary' modules

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment