mirror of
https://github.com/cloudwu/skynet.git
synced 2026-07-24 20:23:06 +00:00
new socket service and socket lib instead of old connection, and rewrite redis lib
This commit is contained in:
179
lualib/redis.lua
179
lualib/redis.lua
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
Reference in New Issue
Block a user