Files
skynet/service/redis-cli.lua
2012-08-31 21:18:19 +08:00

171 lines
3.5 KiB
Lua

local skynet = require "skynet"
local socket = require "socket"
local int64 = require "int64"
local string = string
local table = table
local tonumber = tonumber
local ipairs = ipairs
local unpack = unpack
local redis_server, redis_db = ...
local function compose_message(msg)
local lines = { "*" .. #msg }
for _,v in ipairs(msg) do
local t = type(v)
if t == "number" then
v = tostring(v)
elseif t == "userdata" then
v = int64.tostring(int64.new(v),10)
end
table.insert(lines,"$"..#v)
table.insert(lines,v)
end
table.insert(lines,"")
local cmd = table.concat(lines,"\r\n")
return cmd
end
local function select_db(id)
local result , ok = skynet.call(skynet.self(), "lua", "SELECT", tostring(id))
assert(result and ok == "OK")
end
local request_queue = { head = 1, tail = 1 }
local function push_request_queue(reply)
request_queue[request_queue.tail] = reply
request_queue.tail = request_queue.tail + 1
end
local function pop_request_queue()
assert(request_queue.head < request_queue.tail)
local reply = request_queue[request_queue.head]
request_queue[request_queue.head] = nil
request_queue.head = request_queue.head + 1
return reply
end
local function response(...)
local reply = pop_request_queue()
skynet.redirect(reply.address,0, "response", reply.session, skynet.pack(...))
end
local function readline(sep)
while true do
local line = socket.readline(sep)
if line then
return line
end
coroutine.yield()
end
end
local function readbytes(bytes)
while true do
local block = socket.read(bytes)
if block then
return block
end
coroutine.yield()
end
end
local redcmd = {}
redcmd[42] = function(data) -- '*'
local n = tonumber(data)
if n < 0 then
response(true, nil)
return
end
local bulk = {}
for i = 1,n do
local line = readline "\r\n"
local bytes = tonumber(string.sub(line,2) + 2)
local data = readbytes(bytes)
table.insert(bulk, string.sub(data,1,-3))
end
response(true, bulk)
end
redcmd[36] = function(data) -- '$'
local bytes = tonumber(data)
if bytes < 0 then
response(true,nil)
return
end
local firstline = readbytes(bytes+2)
response(true,string.sub(firstline,1,-3))
end
redcmd[43] = function(data) -- '+'
response(true,data)
end
redcmd[45] = function(data) -- '-'
response(false,data)
end
redcmd[58] = function(data) -- ':'
-- todo: return string later
response(true, tonumber(data))
end
local function split_package()
while true do
local result = readline "\r\n"
local firstchar = string.byte(result)
local data = string.sub(result,2)
local f = redcmd[firstchar]
assert(f)
f(data)
end
end
local function init()
while socket.connect(redis_server) do
skynet.sleep(1000)
end
if redis_db then
select_db(redis_db)
end
end
local split_co = coroutine.create(split_package)
local function reconnect()
init()
for i = request_queue.head, request_queue.tail-1 do
local request = request_queue[i]
socket.write(request[3])
end
split_co = coroutine.create(split_package)
end
skynet.register_protocol {
name = "client",
id = 3,
pack = function(...) return ... end,
unpack = function(msg,sz)
if msg == nil then
skynet.timeout(0, reconnect)
return
end
socket.push(msg,sz)
assert(coroutine.resume(split_co))
end,
dispatch = function () end
}
skynet.start(function()
skynet.dispatch("lua", function(session, address, ...)
local message = { ... }
local cmd = compose_message(message)
socket.write(cmd)
push_request_queue { session = session , address = address, cmd = cmd }
end)
init()
end)