如何使用jredis模糊查询进行发布订阅

请求/响应协议和RTT

Rredis模糊查询是一种基于客户端-服务端模型以及请求/响应协议的TCP服务

这意味着通常情况下一个请求会遵循以下步骤:

  • 客户端向服务端发送一个查询请求,并監听Socket返回通常是以阻塞模式,等待服务端响应
  • 服务端处理命令,并将结果返回给客户端

因此,例如下面是4个命令序列执行情况:

客戶端和服务器通过网络进行连接这个连接可以很快(loopback接口)或很慢(建立了一个多次跳转的网络连接)。无论网络延如何延时数据包總是能从客户端到达服务器,并从服务器返回数据回复客户端

这个时间被称之为 RTT (Round Trip Time - 往返时间). 当客户端需要在一个批处理中执行多次请求时佷容易看到这是如何影响性能的(例如添加许多元素到同一个list,或者用很多Keys填充数据库)例如,如果RTT时间是250毫秒(在一个很慢的连接下)即使服务器每秒能处理100k的请求数,我们每秒最多也只能处理4个请求

如果采用loopback接口,RTT就短得多(比如我的主机ping 127.0.0.1只需要44毫秒)但它任嘫是一笔很多的开销在一次批量写入操作中。

幸运的是有一种方法可以改善这种情况

一次请求/响应服务器能实现处理新的请求即使旧的請求还未被响应。这样就可以将多个命令发送到服务器而不用等待回复,最后在一个步骤中读取该答复

这就是管道(pipelining),是一种几十姩来广泛使用的技术例如许多POP3协议已经实现支持这个功能,大大加快了从服务器下载新邮件的过程

Rredis模糊查询很早就支持管道(pipelining)技术,因此无论你运行的是什么版本你都可以使用管道(pipelining)操作Rredis模糊查询。

下面是一个使用的例子:

2用nc与服务端建立连接,并发送多个指囹

$3的意思是100这个宽度是3

重要说明: 使用管道发送命令时,服务器将被迫回复一个队列答复占用很多内存。所以如果你需要发送大量的命令,最好是把他们按照合理数量分批次的处理例如10K的命令,读回复然后再发送另一个10k的命令,等等这样速度几乎是相同的,但是茬回复这10k命令队列需要非常大量的内存用来组织返回数据内容

Rredis模糊查询 客户端可以订阅任意数量的频道。

当有新消息通过 PUBLISH 命令发送给频噵 channel1 时 这个消息就会被发送给订阅它的三个客户端:

以下实例演示了发布订阅是如何工作的,需要开启三个 rredis模糊查询-cli 客户端

2,再开启一個客户端用于发布消息:apple

3可以看到两个客户端都收到了另外一个客户端发布的消息:apple

  • 实时性的数据通过发布/订阅来实现
  • 三天之内的聊天記录放在rredis模糊查询的sorted_set有序列表里面,以时间作为分值来排序
  • 更久的数据肯定是要放到mysql数据库里面了

上面实例我们想一下,当我通过发布/訂阅收到一个客户端发送来的消息时我们要调一下sorted_set去存储,又要调一下数据库去存储那么我现在不想调用,有没有更好的方式呢当嘫有:

我们把sorted_set拆分出来一个rredis模糊查询,通过订阅实时rredis模糊查询来实现通知数据库微服务也要订阅当前rredis模糊查询来达到通知的效果就可以叻。

、 、 和 是 Rredis模糊查询 事务相关的命令事务可以一次执行多个命令, 并且带有以下两个重要的保证:

  • 事务是一个单独的隔离操作:事务Φ的所有命令都会序列化、按顺序地执行事务在执行的过程中,不会被其他客户端发送来的命令请求所打断
  • 事务是一个原子操作:事務中的命令要么全部被执行,要么全部都不执行

命令负责触发并执行事务中的所有命令:

  • 如果客户端在使用 开启了一个事务之后,却因為断线而没有成功执行 那么事务中的所有命令都不会被执行。
  • 另一方面如果客户端成功在开启事务之后执行 ,那么事务中的所有命令嘟会被执行

命令用于开启一个事务,它总是返回 OK 执行之后, 客户端可以继续向服务器发送任意多条命令 这些命令不会立即被执行, 洏是被放到一个队列中 当 命令被调用时, 所有队列中的命令才会被执行

另一方面, 通过调用 客户端可以清空事务队列, 并放弃执行倳务

1,开启client1设置k1的值bo,并开启事物然后输入查询命令get k1

2,开启client2开启事物,并删除k1并执行事物

发现此时key1已经被删除了!

注意:Rredis模糊查询是单进程,单线程单实例的,所以谁的exec先到就先执行谁的事物!

使用事务时可能会遇上以下两种错误:

  • 事务在执行 之前,入队的命令可能会出错比如说,命令可能会产生语法错误(参数数量错误参数名错误,等等)或者其他更严重的错误,比如内存不足(如果服务器使用 maxmemory 设置了最大内存限制的话)
  • 命令可能在 调用之后失败。举个例子事务中的命令可能处理了错误类型的键,比如将列表命囹用在了字符串键上面诸如此类。

对于发生在 执行之前的错误客户端以前的做法是检查命令入队所得的返回值:如果命令入队时返回 QUEUED ,那么入队成功;否则就是入队失败。如果有命令在入队时失败那么大部分客户端都会停止并取消这个事务。不过从 Rredis模糊查询 2.6.5 开始,服务器会对命令入队失败的情况进行记录并在客户端调用 命令时,拒绝执行并自动放弃这个事务

至于那些在 命令执行之后所产生的錯误, 并没有对它们进行特别处理: 即使事务中有某个/某些命令在执行时产生了错误 事务中的其他命令仍然会继续执行。

如果你有使用關系式数据库的经验 那么 “Rredis模糊查询 在事务失败时不进行回滚,而是继续执行余下的命令”这种做法可能会让你觉得有点奇怪

以下是這种做法的优点:

  • Rredis模糊查询 命令只会因为错误的语法而失败(并且这些问题不能在入队时发现),或是命令用在了错误类型的键上面:这吔就是说从实用性的角度来说,失败的命令是由编程错误造成的而这些错误应该在开发的过程中被发现,而不应该出现在生产环境中
  • 因为不需要对回滚进行支持,所以 Rredis模糊查询 的内部可以保持简单且快速

有种观点认为 Rredis模糊查询 处理事务的做法会产生 bug , 然而需要注意嘚是 在通常情况下, 回滚并不能解决编程错误带来的问题 举个例子, 如果你本来想通过 命令将键的值加上 1 却不小心加上了 2 , 又或者對错误类型的键执行了 回滚是没有办法处理这些情况的。

当执行 命令时 事务会被放弃, 事务队列会被清空 并且客户端会从事务状态Φ退出:

被 的键会被监视,并会发觉这些键是否被改动过了 如果有至少一个被监视的键在 执行之前被修改了, 那么整个事务都会被取消 返回来表示事务已经失败。

3client1 执行事物,查询一下k1的值

可见当client2执行事物时,由于被watch住的k1已经被client1加1所以导致client2在执行事物时获取k1的值时會返回(nil),证明不会执行此事物

通常Rredis模糊查询 keys创建时没有设置相关过期时间。他们会一直存在除非使用显示的命令移除,例如使用命囹。

EXPIRE一类命令能关联到一个有额外内存开销的key当key执行过期操作时,Rredis模糊查询会确保按照规定时间删除他们

key的过期时间和永久有效性可鉯通过EXPIRE和命令(或者其他相关命令)来进行更新或者删除过期时间。

  • rredis模糊查询的过期时间不会随着访问而延长
  • 发生写会剔除过期时间。

rredis模糊查询设置过期时间

rredis模糊查询清除过期时间

Rredis模糊查询 keys过期有两种方式:被动和主动方式

当一些客户端尝试访问它时,key会被发现并主动嘚过期

当然,这样是不够的因为有些过期的keys,永远不会访问他们 无论如何,这些keys应该过期所以定时随机测试设置keys的过期时间。所囿这些过期的keys将会从密钥空间删除

具体就是Rredis模糊查询每秒10次做的事情:

  1. 测试随机的20个keys进行相关过期检测。
  2. 删除所有已经过期的keys
  3. 如果有哆于25%的keys过期,重复步奏1.

这是一个平凡的概率算法基本上的假设是,我们的样本是这个密钥控件并且我们不断重复过期检测,直到过期嘚keys的百分百低于25%,这意味着在任何给定的时刻,最多会清除1/4的过期keys

目的是稍微牺牲了一些内存,但是保证了rredis模糊查询高性能!

  • 在之前的文章中介绍了使用Rredis模糊查询列表这种数据类型来实现一个轻量级的消息队列不过使用列表实现的消息队列存在一个缺陷就是由于是基于列表实现,所以消息出隊列之后则不再存在所以只能被一个消费者消费一次,不支持多个不同的消费者各消费一次即不支持消息广播。
  • 为了实现消息队列常見的消息发布订阅PubSub模式在Rredis模糊查询中提供了消息的发布与订阅实现,即消息生产者客户端可以往某个指定的频道channel或者模式pattern发布一个消息然后将这个消息广播给多个订阅了这个频道channel的客户端或者广播给订阅了该消息匹配的某个模式pattern的客户端。
  • 所以Rredis模糊查询提供的是消息的發布与订阅不是传统的消息队列实现,发布的消息并不会被存储如Rredis模糊查询基于列表实现的消息队列会在消费之前存放在列表的链表數据结构里。Rredis模糊查询提供的消息订阅发布是实时的消息发布和订阅接收如果消息所发往的频道channel或者模式pattern没有订阅者,则该条消息不会傳给任何其他客户端直接过掉或者说丢弃掉。
  • Rredis模糊查询所提供的消息订阅发布可以理解为是一个轻量级的消息订阅发布实现,所谓轻量级是相对于RabbitMQ和Kafka这种专业的消息队列所提供的消息订阅发布而言的即RabbitMQ和Kafka需要在服务器单独配置和启动服务端Broker进程来接收客户端的消息写叺和消息读取消费,而Rredis模糊查询的消息订阅发布由于是Rredis模糊查询内置的由于在项目中通常会使用Rredis模糊查询作为分布式缓存实现,所以不需要进行其他额外的配置和部署
  • 所以如果项目中刚开始没有使用RabbitMQ这种专业的队列,而项目后期又需要对项目进行解耦需要用到消息的訂阅发布功能,同时不想额外在生产服务器申请资源来部署RabbitMQ或者Kafka则可以直接使用作为缓存的Rredis模糊查询所提供的消息发布订阅功能。
  • 其中消息的发布订阅模式的一个应用场景为为了性能方面的考虑一个服务的多个部署实例使用本地缓存来缓存数据(不常更新),由于客户端可能连接任意一个部署实例并对这个缓存进行更新,所以为了实现不同部署实例间的本地缓存数据同步则这多个部署可以订阅同一个channel,當某个实例接收到客户端的更新请求时更新后发布到该channel,从而通知到订阅了这个channel的其他部署实例
  • 在使用层面,主要包括对频道channel的精确訂阅和对模式pattern的模糊匹配订阅

基于Rredis模糊查询命令行使用

  • 以下基于Rredis模糊查询的命令行对消息的订阅与发布功能进行演示,其中包含订阅频噵channel和订阅模式pattern两种用法

1.如下首先在一个命令行订阅名为testChannel的频道,阻塞等待:

2.然后在另外一个命令行往testChannel频道发布一个消息:

3.在之前的订阅命令行接收到了这个发布的消息并且继续阻塞等待:

  • 订阅模式主要是模糊匹配,如模式test* 则匹配所有以test开头的模式和频道channel即如果某个客戶端订阅了模式 test*,则当另外一个客户端往testChannel这个频道发布了一条消息或者往test* 这个模式发布了一条消息该客户端会收到消息,如下订阅test* 这个模式pattern然后使用上面的命令往testChannel这个频道发布一条消息hello,则该客户端会收到消息如下:
  • 在Java编程中,可以基于Rredis模糊查询的Java客户端Jredis模糊查询来對消息订阅与发布功能进行使用如果项目使用了spring-data-rredis模糊查询包提供的封装类来使用。以下使用Jredis模糊查询进行一个简单演示:
  • 当发布消息到某个频道channel时由于模式pattern是模糊匹配,所以如果存在与这个频道channel匹配的pattern则订阅了这个pattern的客户端也会收到消息。由于Jredis模糊查询的频道接收subscribe和模式接收psubscribe都是阻塞方法所以只有一个会收到并在Jredis模糊查询PubSub方法的回调方法打印,所以在实际编程中由于是阻塞方法,通常在不同的线程进行分别接收

异步消息的方式有很多这篇博愙介绍如何使用rredis模糊查询进行发布订阅,

完成这个示例只需要三个文件

我要回帖

更多关于 redis模糊查询 的文章

 

随机推荐