new socket service and socket lib instead of old connection, and rewrite redis lib

This commit is contained in:
云风
2013-06-21 13:02:51 +08:00
parent 0ffd9b8b8e
commit 277b6957cb
16 changed files with 1182 additions and 1248 deletions

View File

@@ -1,65 +1,168 @@
local skynet = require "skynet"
local string = string
local table = table
local unpack = unpack
local assert = assert
local socket = require "socket"
local config = require "config"
local redis_conf = skynet.getenv "redis"
local name = config (redis_conf)
local redis_manager
local batch = false
local readline = socket.readline
local readbytes = socket.read
local table = table
local string = string
local redis = {}
local command = {}
local meta = {
__index = command,
__gc = function(self)
socket.close(self.__handle)
end,
}
function redis.connect(dbname)
local fd = socket.open(name[dbname])
return setmetatable( { __handle = fd, __mode = false }, meta )
end
function command:disconnect()
socket.close(self.__handle)
setmetatable(self, nil)
end
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 redcmd = {}
redcmd[42] = function(fd, data) -- '*'
local n = tonumber(data)
if n < 0 then
return true, nil
end
local bulk = {}
for i = 1,n do
local line = readline(fd,"\r\n")
local bytes = tonumber(string.sub(line,2))
if bytes < 0 then
table.insert(bulk, nil)
else
local data = readbytes(fd, bytes + 2)
table.insert(bulk, string.sub(data,1,-3))
end
end
return true, bulk
end
redcmd[36] = function(fd, data) -- '$'
local bytes = tonumber(data)
if bytes < 0 then
return true,nil
end
local firstline = readbytes(fd, bytes+2)
return true,string.sub(firstline,1,-3)
end
redcmd[43] = function(fd, data) -- '+'
return true,data
end
redcmd[45] = function(fd, data) -- '-'
return false,data
end
redcmd[58] = function(fd, data) -- ':'
-- todo: return string later
return true, tonumber(data)
end
local function read_response(fd)
local result = readline(fd, "\r\n")
local firstchar = string.byte(result)
local data = string.sub(result,2)
return redcmd[firstchar](fd,data)
end
setmetatable(command, { __index = function(t,k)
local cmd = string.upper(k)
local f = function(self, ...)
if batch then
skynet.send( self.__handle, "lua", cmd , ...)
local f = function (self, ...)
local fd = self.__handle
if self.__mode then
socket.write(fd, compose_message { cmd, ... })
self.__batch = self.__batch + 1
else
local err, result = skynet.call( self.__handle, "lua", cmd, ...)
assert(err, result)
return result
socket.lock(fd)
socket.write(fd, compose_message { cmd, ... })
local ok, ret = read_response(fd)
socket.unlock(fd)
return ok, ret
end
end
t[k] = f
t[k] = f
return f
end})
function command:exists(key)
assert(not batch, "exists can't used in batch mode")
local result , exists = skynet.call( self.__handle, "lua" , "EXISTS", key)
assert(result, exists)
assert(not self.__mode, "exists can't used in batch mode")
local fd = self.__handle
socket.lock(fd)
socket.write(fd, compose_message { "EXISTS", key })
local ok, exists = read_response(fd)
socket.unlock(fd)
assert(ok, exists)
exists = exists ~= 0
return exists
end
function command:batch(mode)
if mode == "end" then
assert(batch, "Open batch mode first")
batch = false
local err, result = skynet.unpack(skynet.rawcall( self.__handle, "text", mode))
assert(err, result)
return result
local fd = self.__handle
if self.__mode == "read" then
local allok = true
local allret = {}
for i = 1, self.__batch do
local ok, ret = read_response(fd)
allok = allok and ok
allret[i] = ret
end
assert(allok, "batch read failed")
socket.unlock(self.__handle)
self.__mode = false
return allret
else
local allok = true
local allret = true
for i = 1, self.__batch do
local ok, ret = read_response(fd)
allok = allok and ok
if ret~="OK" then
allret = false
end
end
socket.unlock(self.__handle)
self.__mode = false
assert(allret, "reading in batch write")
return allok
end
else
assert(mode == "read" or mode == "write")
batch = mode
skynet.send( self.__handle, "text", mode)
self.__mode = mode
self.__batch = 0
socket.lock(self.__handle)
end
end
local meta = {
__index = command
}
function redis.connect(dbname)
local handle = skynet.call(redis_manager, "lua", dbname)
assert(handle ~= nil)
return setmetatable({ __handle = handle } , meta)
end
skynet.init(function()
redis_manager = skynet.uniqueservice("redis-mgr")
end, "redis")
return redis

View File

@@ -1,79 +1,189 @@
local buffer = require "socketbuffer"
local skynet = require "skynet"
local c = require "socket.c"
local table = table
local next = next
local assert = assert
local coroutine = coroutine
local type = type
local READBUF = {} -- fd:buffer
local READREQUEST = {} -- fd:request_size
local READSESSION = {} -- fd:session
local READLOCK = {} -- fd:queue(session)
local READTHREAD= {} -- fd:thread
local selfaddr = skynet.self()
local function response(session)
skynet.redirect(selfaddr , 0, "response", session, "")
end
skynet.register_protocol {
name = "client",
id = 3, -- PTYPE_CLIENT
pack = buffer.pack,
unpack = buffer.unpack,
dispatch = function (_, _, fd, msg, sz)
local qsz = READREQUEST[fd]
local buf = READBUF[fd]
local bsz
if sz == 0 or buf == true then
buf,bsz = true, qsz
else
buf,bsz = buffer.push(buf, msg, sz)
end
READBUF[fd] = buf
if qsz == nil then
return
end
if type(qsz) == "number" then
if qsz > bsz then
return
end
else
-- request readline
if buffer.readline(buf, qsz, true) == nil then
return
end
end
response(READSESSION[fd])
end
}
skynet.register_protocol {
name = "system",
id = 4, -- PTYPE_SYSTEM
pack = skynet.pack,
unpack = function (...) return ... end,
dispatch = function (session, addr, msg, sz)
fd, t, sz = skynet.unpack(msg,sz)
assert(addr == selfaddr, "PTYPE_SYSTEM message must send by self")
if t == 0 then
-- lock fd
if READTHREAD[fd] == nil then
skynet.ret()
return
end
local q = READLOCK[fd]
if q == nil then
READLOCK[fd] = { session }
else
table.insert(q, session)
end
else
-- request bytes or readline
local buf = READBUF[fd]
if buf == true then
skynet.ret()
return
end
local _,bsz = buffer.push(buf)
if t == 1 then
-- sz is size
if bsz >= sz then
skynet.ret()
return
end
else
-- sz is sep
if buffer.readline(buf, sz, true) then -- don't real read
skynet.ret()
return
end
end
READSESSION[fd] = session
end
end
}
local socket = {}
local fd
local object
local data = {}
local function presend()
if next(data) then
local tmp = data
data = {}
for _,v in ipairs(tmp) do
socket.write(fd, v)
function socket.open(addr, port)
local cmd = "open" .. " " .. (port and (addr..":"..port) or addr)
local r = skynet.call(".socket", "text", cmd)
if r == "" then
error(cmd .. " failed")
end
return tonumber(r)
end
function socket.stdin()
local r = skynet.call(".socket", "text", "bind 1")
if r == "" then
error("stdin bind failed")
end
return tonumber(r)
end
function socket.close(fd)
socket.lock(fd)
skynet.call(".socket", "text", "close", fd)
READBUF[fd] = true
READLOCK[fd] = nil
end
function socket.read(fd, sz)
assert(coroutine.running() == READTHREAD[fd], "call socket.lock first")
local str = buffer.pop(READBUF[fd],sz)
if str then
return str
end
READREQUEST[fd] = sz
skynet.call(selfaddr, "system",fd,1,sz) -- singal size 1
READREQUEST[fd] = nil
str = buffer.pop(READBUF[fd],sz)
return str
end
function socket.readline(fd, sep)
assert(coroutine.running() == READTHREAD[fd], "call socket.lock first")
local str = buffer.readline(READBUF[fd],sep)
if str then
return str
end
READREQUEST[fd] = sep
skynet.call(selfaddr, "system",fd,2,sep) -- singal sep 2
READREQUEST[fd] = nil
str = buffer.readline(READBUF[fd],sep)
return str
end
function socket.write(fd, msg, sz)
assert(coroutine.running() == READTHREAD[fd], "call socket.lock first")
skynet.send(".socket", "client", fd, msg, sz)
end
function socket.lock(fd)
local lt = READTHREAD[fd]
local ct = coroutine.running()
if lt then
assert(lt ~= ct, "already lock")
skynet.call(selfaddr, "system",fd,0)
end
READTHREAD[fd] = ct
end
function socket.unlock(fd)
READTHREAD[fd] = nil
local q = READLOCK[fd]
if q then
if q[1] then
response(q[1])
end
table.remove(q,1)
if q[1] == nil then
READLOCK[fd] = nil
end
end
end
function socket.connect(addr)
local ip, port = string.match(addr,"([^:]+):(.+)")
port = tonumber(port)
socket.close()
fd = c.open(ip,port)
if fd == nil then
return true
end
skynet.send(".connection", "text", "ADD", fd , skynet.address(skynet.self()))
object = c.new()
presend()
end
function socket.stdin()
skynet.send(".connection", "text", "ADD", 1 , skynet.address(skynet.self()))
object = c.new()
end
function socket.push(msg,sz)
if msg then
c.push(object, msg, sz)
end
end
function socket.read(bytes)
return c.read(object, bytes)
end
function socket.readline(sep)
return c.readline(object, sep)
end
function socket.readblock(...)
return c.readblock(object,...)
end
function socket.write(...)
local str = c.write(fd, ...)
if str then
socket.close()
table.insert(data, str)
end
end
function socket.writeblock(...)
local str = c.writeblock(fd, ...)
if str then
socket.close()
table.insert(data, str)
end
end
function socket.close()
if fd then
skynet.send(".connection","text", "DEL", fd)
fd = nil
end
end
return socket