- Notifications
You must be signed in to change notification settings - Fork2
A Redis client for multi-threaded servers.
License
guzba/ready
Folders and files
Name | Name | Last commit message | Last commit date | |
---|---|---|---|---|
Repository files navigation
nimble install ready
Ready is a Redis client that is built to work well in multi-threaded programs. A great use-case for Ready is in a multi-threaded HTTP server likeMummy.
Check out theexamples/ folder for more sample code using Ready.
First you'll need to open a Redis connection. By default Ready connects to the default Redis server at localhost:6379. You can easily specify a different address and port innewRedisConn
when needed.
import readylet redis=newRedisConn()# Defaults to localhost:6379
After opening a connection you can start sending commands. You can send any of Redis's vast set of commands.
import ready, std/optionslet redis=newRedisConn()# Defaults to localhost:6379let value= redis.command("GET","key").to(Option[string])
We useOption[string]
above since the reply may be nil if the key is not present. Alternatively, if you know the key exists, you could just usestring
.
You can also easily work with replies to more complex commands:
import readylet redis=newRedisConn()# Defaults to localhost:6379let values= redis.command("MGET","key1","key2","key3").to(seq[string])
Here we are usingMGET
to request multiple keys in one command. Since we expect multiple reply entries, we can useto
to convert the reply to aseq[string]
.
A call tocommand
orreceive
will return aRedisReply
object. You'll want to convert that into the types you expect. Ready makes that easy by providing theto
proc.
# Basic conversions:echo reply.to(int)echo reply.to(string)echo reply.to(Option[string])# If the reply can be nil# Convert array replies to seq:echo reply.to(seq[int])echo reply.to(seq[string])echo reply.to(seq[Option[string]])# Convert array replies to tuples:echo reply.to((int,string))echo reply.to((int,Option[string]))echo reply.to((string,Option[string],int))# Mix and match:echo reply.to((string,Option[string],seq[int]))# Index access, if you know the reply is an array you can access its elementsecho reply[0].to(string)
A call toreply.to
for a type Ready does not know how to convert to will fail at compile time.
If Ready is unable to convert the reply from Redis to your requested type, aRedisError
is raised.
Ready includes a built-in connection pool when compiled with--threads:on
:
import readylet redisPool=newRedisPool(3)# Defaults to localhost:6379# This automatically removes a connection from the pool, runs the command# and then returns it back to the poolredisPool.command("PING")
Or, if you want to run more than one command with the same connection:
import readylet redisPool=newRedisPool(3)# Defaults to localhost:6379redisPool.withConnection conn:# `conn` is automatically recycled back into the pool after this blockdiscard conn.command("PING")
Reusing Redis connections is much faster and more efficient than opening new connections for every command.
Ready also includes separatesend
andreceive
calls as an alternative to thecommand
call. These commands make pipelining commands easy:
import readylet redis=newRedisConn()redis.send("MULTI")redis.send("INCR","mycount")redis.send("SET","mykey","myvalue")redis.send("EXEC")## OR:# redis.send([# ("MULTI", @[]),# ("INCR", @["mycount"]),# ("SET", @["mykey", "myvalue"]),# ("EXEC", @[])#])# Match the number of `receive` calls to the number of commands sentdiscard redis.receive()# OKdiscard redis.receive()# QUEUEDdiscard redis.receive()# QUEUEDlet (num, _)= redis.receive().to((int,string))
Pipelining as an advanced technique when using Redis that can drastically increase performance when possible.
Important! Remember to match the number ofreceive
calls to the number of commands sent.
Ready makes it easy to use Redis'sPubSub functionality.
Here we dedicate a thread to receiving messages on a PubSub connection while our other thread is free to send commands likeSUBSCRIBE
andUNSUBSCRIBE
to manage the PubSub connection.
let pubsub=newRedisConn()# Defaults to localhost:6379procreceiveThreadProc()=try:whiletrue:let reply= pubsub.receive()echo"Event:", reply[0].to(string)echo"Channel:", reply[1].to(string)echo"Raw:", replyexceptRedisErroras e:echo e.msgvar receiveThread:Thread[void]createThread(receiveThread, receiveThreadProc)pubsub.send("SUBSCRIBE","mychannel")
Note that using PubSub with Ready requires threads.
You can use Ready in two ways, either by callingcommand
or by callingsend
andreceive
. Callingcommand
is the equivalent of callingsend
and then callingreceive
immediately.
Whenever acommand
orreceive
call gets an error reply from Redis aRedisError
is raised. This means discarding the reply indiscard redis.command("PING")
is perfectly ok. If the reply was an error an exception would have been raised.
If you open a short-lived Redis connection, remember to callclose
when you no longer need it. The connections are not garbage collected. (For HTTP servers this is unlikely, see#1 for a brief discussion.)
First, where possible, it is more efficient to pipeline many Redis commands. This is easy to do with Ready, just callsend
multiple times (or ideally callsend
with a seq of commands).
Second, you may want to have a separate thread be sending vs receiving. A common use of this isPubSub, where one thread is dedicated to receiving messages and the sending thread manages what channels are subscribed to. Seethis example.
About
A Redis client for multi-threaded servers.