28 January 2015

前言

我终于找到时间研究Redis了,前一段时间居然去搞前端去了,感觉有点不自在。果然,server端才是自己的前进的方向,Rails,Redis,Mongodb之类的。

redis-rb

redis-rb是[Redis][http://redis.io]的ruby客户端。redis-rb客户端尝试一一匹配Redis的API,并提供地道 的接口。其具备线程安全、客户端分片、管道以及不错的性能。

从2.x升级到3.0, 不过,网站上使用的已经是最新的redis版本了。

起步

2.0的客户端匹配2.0以上的Redis版本。如果想要使用2.0之前的redis实例,可以使用老的客户端(不再开发维护)。

连接Redis需要实例化Redis类。

require "redis"

redis = Redis.new

这里假设Redis以默认配置启动,即监听localhost和6379端口。如果需要连接到远程服务器的不同端口,可以 这么做:

redis = Redis.new(:host => "10.0.1.1", :port => 6380, :db => 15)

当然,也可以通过URL来指定连接选项:

redis = Redis.new(:url => "redis://:p4ssw0rd@10.0.1.1:6380/15")

默认情况下,客户端尝试读取REDIS_URL环境变量,并使用其中的值作为链接的URL。设置环境变量并 以不带参的调用Redis.new,上述的语句功效是相同的。

可以通过Unix套接字连接Redis的设置,其代码如下:

redis = Redis.new(:path => "/tmp/redis.sock")

连接带密码保护的Redis实例,可以使用:

redis = Redis.new(:password => "mysecret")

Redis类提供的方法与所执行的命令名一致。这些方法接受的参数与命令接受的参数也一致,命令参数 的详细描述参考Redis website

redis.set("mykey", "hello world")
# => "OK"

redis.get("mykey")
# => "hello world"

所有的命令、参数及其返回值都是被文档化的,并且可在 rdoc.info 中查看。

哨兵支持(Sentinel support)

通过使用Redis哨兵,客户端可以自动支持故障转义。

建立哨兵链接的命令如下:

SENTINELS = [{:host => "127.0.0.1", :port => 26380},
             {:host => "127.0.0.1", :port => 26381}]

redis = Redis.new(:url => "redis://mymaster", :sentinels => SENTINELS, :role => :master)
  • master名指定了主从模型的一组Redis实例(例子中为mymaster)
  • 可指定角色选项,其值可为masterslave。当角色为slave时,客户端将尝试连接到由特定master指定的随机slave中。 如果没有指定角色,客户端将链接到master上。
  • 使用哨兵支持需要指定链接的哨兵列表。列表中不需要枚举出所有的哨兵实例,但是必须确保,一个挂了后还能找到另一个替代 客户端能记住上一个可正确应答的哨兵,并在下次请求时使用该哨兵。

备注: 哨兵支持的redis支持集群的一种形式。Redis 3.0 发布了新的集群支持方案。

对象保存(Storing objects)

Redis只能将字符串存储为值,如果想要保存对象,必须是使用某种序列化的机制,比如JSON:

require "json"

redis.set "foo", [1, 2, 3].to_json
# => OK

JSON.parse(redis.get("foo"))
# => [1, 2, 3]

Ruby中存在一个封送的功能支持。

管道(Pipelining)

当多个命令顺序执行,且彼此依赖时,这叫管道。 这意味着,在发送下一条命令之前,客户端不需要等待 第一条命令的回复。管道的优势是,多条命令一次发送,整体执行更快。

通过调用#pipelined方法,客户端可以执行管道命令。在代码块执行后,客户端将所有的命令发送给Redis, 然后收集其响应。响应由#pipelined方法返回。

redis.pipelined do
  redis.set "foo", "bar"
  redis.incr "baz"
end
# => ["OK", 1]

以原子的方式执行命令

You can use MULTI/EXEC to run a number of commands in an atomic fashion. This is similar to executing a pipeline, but the commands are preceded by a call to MULTI, and followed by a call to EXEC. Like the regular pipeline, the replies to the commands are returned by the #multi method.

可以MULTI/EXEC命令以原子的方式,运行一组命令。 这和执行管道类似,但是,必须以MULTI作为前置命令, 以EXEC作为后置命令。与常规的管道,相应命令的功能由#multi方法提供。

redis.multi do
  redis.set "foo", "bar"
  redis.incr "baz"
end
# => ["OK", 1]

Futures(未来?)

管道中命令的响应可以通过未来对象来访问(redis-rb 3.0之后)。在管道块中的所有调用都会返回一个 未来对象, 其可调用#value方法。但管道成功执行时,所有复制的未来对象都可以使用。

注: 我觉得这里就是闭包功能的体现,所以,未来对象的命名感觉很新奇。

redis.pipelined do
  @set = redis.set "foo", "bar"
  @incr = redis.incr "baz"
end

@set.value
# => "OK"

@incr.value
# => 1

错误处理(Error Handling)

如果,有些事情挂了,你就会得到一个异常。如果不能连接到服务器,就会抛出Redis::CannotConnectError

begin
  redis.ping
rescue Exception => e
  e.inspect
# => #<Redis::CannotConnectError: Timed out connecting to Redis on 10.0.1.1:6380>

  e.message
# => Timed out connecting to Redis on 10.0.1.1:6380
end

在发生异常时,参考lib/redis/errors.rb,寻找更多的信息。

ruby代码本身具有极强的可读性,其代码可当作文档来看的。

专家模式选项(Expert-Mode Options)

  • inherit_socket: true: disable safety check that prevents a forked child from sharing a socket with its parent; this is potentially useful in order to mitigate connection churn when:
  • many short-lived forked children of one process need to talk to redis, AND
  • your own code prevents the parent process from using the redis connection while a child is alive

inherit_socket使用不当将会导致错误和不正确的反应。

可选的驱动(Alternate drivers)

默认情况下,redis-rb使用Ruby的套接字库与Redis交互。当然在初始化客户端对象时,指定使用其他 可选的链接库。当前指南仅对redis-rb 3.0有效,想要在redis-rb 2.2使用可选的驱动,请参 考老的README文档

hiredis

hiredis驱动使用hiredis-rb的连接工厂。换而言之,hiredis-rb是官方hiredis客户端库的绑定。其优化了速度和 内存开销。由于其是C扩展,默认不支持JRuby。

It is best to use hiredis when you have large replies (for example: LRANGE, SMEMBERS, ZRANGE, etc.) and/or use big pipelines.

当回复数据巨大或使用大管道时,最好使用hiredis,例如在使用LRANGE, SMEMBERS, ZRANGE等命令。

在Gemfile中,按如下的方式包含hiredis:

gem "redis", "~> 3.0.1"
gem "hiredis", "~> 0.4.5"

然后在实例化客户端对象时,指定hiredis:

redis = Redis.new(:driver => :hiredis)

同步(synchrony)

同步驱动添加了对em-synchrony的支持。这使得 redis-rb可以与EventMachine的异步I/O协同工作,而无序改变现有的API。此时,也需要包含hiredis 的gem包,因为同步驱动使用hiredis解析Redis协议。

在Gemfile中,需要包含em-synchrony和hiredis:

gem "redis", "~> 3.0.1"
gem "hiredis", "~> 0.4.5"
gem "em-synchrony"

实例化客户端对象时,需要指定synchrony选项:

redis = Redis.new(:driver => :synchrony)

测试

redis-rb的测试使用的是[Travis],其测试包含了如下的解释器和驱动:

  • MRI 1.8.7 (drivers: ruby, hiredis)
  • MRI 1.9.2 (drivers: ruby, hiredis, synchrony)
  • MRI 1.9.3 (drivers: ruby, hiredis, synchrony)
  • MRI 2.0.0 (drivers: ruby, hiredis, synchrony)
  • JRuby 1.7 (1.8 mode) (drivers: ruby)
  • JRuby 1.7 (1.9 mode) (drivers: ruby)

后记

学到一个git命令: git shortlog -sn查看项目贡献者的排名。意外的发现,某个项目中,前辈没有提交一行代码,那就是NodeJS的 那个项目。

文档中介绍的两个驱动也是个学习点,以及项目中引入的redis-namespace(完全为了resque而引入的,为键值引入命名空间),不对,我发现依赖根源了,我需要研究的应该是resque,所以, 我应该去研究resque。




傲娇的使用Disqus