Redis运行流程源码解析--转载

http://blog.nosqlfan.com/html/4007.html

http://www.searchdatabase.com.cn/showcontent_62166.htm

:本文分析源码基于Redis 2.4.7 stable版本,对Redis运行流程,命令处理的内部实现进行了深入讲解。

    

  概述

  Redis通过定义一个 struct redisServer 类型的全局变量server 来保存服务器的相关信息(比如:配置信息,统计信息,服务器状态等等)。启动时通过读取配置文件里边的信息对server进行初始化(如果没有指定配置文 件,将使用默认值对sever进行初始化),初始化的内容有:起监听端口,绑定有新连接时的回调函数,绑定服务器的定时函数,虚拟内存初始化,log初始 化等等。

  启动

  初始化服务器配置

  先来看看redis 的main函数的入口

  Redis.c:1694

 2)) {         usage();     } else {         ...     }     if (server.daemonize) daemonize();     initServer();     ...</tr></table>

  • initServerConfig初始化全局变量 server 的属性为默认值。
  • 如果命令行指定了配置文件, resetServerSaveParams重置对落地备份的配置(即重置为默认值)并读取配置文件的内容对全局变量 server 再进行初始化 ,没有在配置文件中配置的将使用默认值。
  • 如果服务器配置成后台执行,则对服务器进行 daemonize。
  • initServer初始化服务器,主要是设置信号处理函数,初始化事件轮询,起监听端口,绑定有新连接时的回调函数,绑定服务器的定时函数,初始化虚拟内存和log等等。
  • 创建服务器监听端口。

  Redis.c:923

</tr></table>

  • anetTcpServer创建一个socket并进行监听,然后把返回的socket fd赋值给server.ipfd。

  事件轮询结构体定义

  先看看事件轮询的结构体定义

  Ae.h:88

</tr></table>

  • maxfd是最大的文件描述符,主要用来判断是否有文件事件需要处理(ae.c:293)和当使用select 来处理网络IO时作为select的参数(ae_select.c:50)。
  • timeEventNextId 是下一个定时事件的ID。
  • events[AE_SETSIZE]用于保存通过aeCreateFileEvent函数创建的文件事件,在sendReplyToClient函数和freeClient函数中通过调用aeDeleteFileEvent函数删除已经处理完的事件。
  • fired[AE_SETSIZE] 用于保存已经触发的文件事件,在对应的网络I/O函数中进行赋值(epoll,select,kqueue),不会对fired进行删除操作,只会一直覆 盖原来的值。然后在aeProcessEvents函数中对已经触发的事件进行处理。
  • timeEventHead 是定时事件链表的头,定时事件的存储用链表实现。
  • Stop 用于停止事件轮询处理。
  • apidata 用于保存轮询api需要的数据,即aeApiState结构体,对于epoll来说,aeApiState结构体的定义如下:
</tr></table>

  • beforesleep 是每次进入处理事件时执行的函数。

  创建事件轮询

  Redis.c:920

timeEventHead = NULL;     eventLoop->timeEventNextId = 0;     eventLoop->stop = 0;     eventLoop->maxfd = -1;     eventLoop->beforesleep = NULL;     if (aeApiCreate(eventLoop) == -1) {         zfree(eventLoop);         return NULL;     } /* Events with mask == AE_NONE are not set. So let's initialize  * the vector with it. */     for (i = 0; i < AE_SETSIZE; i++)         eventLoop->events[i].mask = AE_NONE;     return eventLoop; }</tr></table>

  绑定定时函数和有新连接时的回调函数

  redis.c:973

 0 &&     aeCreateFileEvent(server.el,server.ipfd,AE_READABLE,acceptTcpHandler,NULL) == AE_ERR) oom("creating file event");</tr></table>

  • aeCreateTimeEvent 创建定时事件并绑定回调函数serverCron,这个定时事件第一次是超过1毫秒就有权限执行,如果其他事件的处理时间比较长,可能会出现超过一定时间 都没执行情况。这里的1毫秒只是超过后有可执行的权限,并不是一定会执行。第一次执行后,如果还要执行,是由定时函数的返回值确定的,在 processTimeEvents(ae.c:219)中,当调用定时回调函数后,获取定时回调函数的返回值,如果返回值不等于-1,则设置定时回调函 数的下一次触发时间为当前时间加上定时回调函数的返回值,即调用间隔时间。serverCron的返回值是100ms,表明从二次开始,每超过100ms 就有权限执行。(定时回调函数serverCron用于更新lru时钟,更新服务器的状态,打印一些服务器信息,符合条件的情况下对hash表进行重哈 希,启动后端写AOF或者检查后端写AOF或者备份是否完成,检查过期的KEY等等)
  • aeCreateFileEvent创建监听端口的socket fd的文件读事件(即注册网络io事件)并绑定回调函数acceptTcpHandler。

  进入事件轮询

  初始化后将进入事件轮询

  Redis.c:1733

</tr></table>

  • 设置每次进入事件处理前会执行的函数beforeSleep。
  • 进入事件轮询aeMain。
  • 退出事件轮询后删除事件轮询,释放事件轮询占用内存aeDeleteEventLoop(不过没在代码中发现有执行到这一步的可能,服务器接到shutdown命令时通过一些处理后直接就通过exit退出了,可能是我看错了,待验证)。

  事件轮询函数aeMain

  看看aeMain的内容

  Ae.c:382

stop = 0;     while (!eventLoop->stop) {         if (eventLoop->beforesleep != NULL)             eventLoop->beforesleep(eventLoop);         aeProcessEvents(eventLoop, AE_ALL_EVENTS);     } }</tr></table>

  • 每次进入事件处理前,都会调用设置的beforesleep,beforeSleep函数主要是处理被阻塞的命令和根据配置写AOF。
  • aeProcessEvents处理定时事件和网络io事件。

  启动完毕,等待客户端请求

  到进入事件轮询函数后,redis的启动工作就做完了,接下来就是等待客户端的请求了。

  接收请求

  新连接到来时的回调函数

  在绑定定时函数和有新连接时的回调函数中说到了绑定有新连接来时的回调函数acceptTcpHandler,现在来看看这个函数的具体内容

  Networking.c:427

</tr></table>

  • anetTcpAccept 函数 accept新连接,返回的cfd是新连接的socket fd。
  • acceptCommonHandler 函数是对新建立的连接进行处理,这个函数在使用 unix socket 时也会被用到。

  接收客户端的新连接

  接下来看看anetTcpAccept函数的具体内容

</tr></table>

  再进去anetGenericAccept 看看

  Anet.c:313

</tr></table>

  • anetTcpAccept 函数中调用anetGenericAccept 函数进行接收新连接,anetGenericAccept函数在 unix socket 的新连接处理中也会用到。
  • anetTcpAccept 函数接收新连接后,获取客户端得ip,port 并返回。

  创建redisClient进行接收处理

  anetTcpAccept 运行完后,返回新连接的socket fd,然后返回到调用函数acceptTcpHandler中,继续执行acceptCommonHandler 函数

  Networking.c:403

 server.maxclients) {         char *err = "-ERR max number of clients reached\r\n";         /* That's a best effort error message, don't check write errors */         if (write(c->fd,err,strlen(err)) == -1) {             /* Nothing to do, Just to avoid the warning... */         }         freeClient(c);         return;     }     server.stat_numconnections++; }</tr></table>

  • 创建一个 redisClient 来处理新连接,每个连接都会创建一个 redisClient 来处理。
  • 如果配置了最大并发客户端,则对现有的连接数进行检查和处理。
  • 最后统计连接数。

  绑定有数据可读时的回调函数

  Networking.c:15

bufpos = 0;     anetNonBlock(NULL,fd);     anetTcpNoDelay(NULL,fd);     if (aeCreateFileEvent(server.el,fd,        readQueryFromClient, c) == AE_ERR)     {         close(fd);         zfree(c);         return NULL;     }     selectDb(c,0);     c->fd = fd;     c->querybuf = sdsempty(); c->reqtype = 0; ... }</tr></table>

  • 创建新连接的socket fd对应的文件读事件,绑定回调函数readQueryFromClient。
  • 如果创建成功,则对 redisClient 进行一系列的初始化,因为 redisClient 是通用的,即不管是什么命令的请求,都是通过创建一个 redisClient 来处理的,所以会有比较多的字段需要初始化。

  createClient 函数执行完后返回到调用处acceptCommonHandler函数,然后从acceptCommonHandler函数再返回到acceptTcpHandler函数。

  接收请求完毕,准备接收客户端得数据

   到此为止,新连接到来时的回调函数acceptTcpHandler执行完毕,在这个回调函数中创建了一个redisClient来处理这个客户端接下 来的请求,并绑定了接收的新连接的读文件事件。当有数据可读时,网络i/o轮询(比如epoll)会有事件触发,此时绑定的回调函数 readQueryFromClient将会调用来处理客户端发送过来的数据。

  读取客户端请求的数据

  在绑定有数据可读时的回调函数中的createClient函数中绑定了一个有数据可读时的回调函数readQueryFromClient函数,现在看看这个函数的具体内容

  Networking.c:874

querybuf = sdscatlen(c->querybuf,buf,nread);         c->lastinteraction = time(NULL);     } else {         server.current_client = NULL;         return;     }     if (sdslen(c->querybuf) > server.client_max_querybuf_len) {         sds ci = getClientInfoString(c), bytes = sdsempty();         bytes = sdscatrepr(bytes,c->querybuf,64);         redisLog(REDIS_WARNING,"Closing client that reached max query buffer length: %s (qbuf initial bytes: %s)", ci, bytes);         sdsfree(ci);         sdsfree(bytes);         freeClient(c);         return;     }     processInputBuffer(c);     server.current_client = NULL; }</tr></table>

  • 调用系统函数read来读取客户端传送过来的数据,调用read后对读取过程中被系统中断的情况(nread == -1 && errno == EAGAIN),客户端关闭的情况(nread == 0)进行了判断处理。
  • 如果读取的数据超过限制(1GB)则报错。
  • 读取完后进入processInputBuffer进行协议解析。

  请求协议

  从readQueryFromClient函数读取客户端传过来的数据,进入processInputBuffer函数进行协议解析,可以把processInputBuffer函数看作是输入数据的协议解析器

  Networking.c:835

querybuf)) {         /* Immediately abort if the client is in the middle of something. */         if (c->flags & REDIS_BLOCKED || c->flags & REDIS_IO_WAIT) return;         /* REDIS_CLOSE_AFTER_REPLY closes the connection once the reply is          * written to the client. Make sure to not let the reply grow after          * this flag has been set (i.e. don't process more commands). */         if (c->flags & REDIS_CLOSE_AFTER_REPLY) return;         /* Determine request type when unknown. */         if (!c->reqtype) {             if (c->querybuf[0] == '*') {                 c->reqtype = REDIS_REQ_MULTIBULK;             } else {                 c->reqtype = REDIS_REQ_INLINE;             }         }         if (c->reqtype == REDIS_REQ_INLINE) {             if (processInlineBuffer(c) != REDIS_OK) break;         } else if (c->reqtype == REDIS_REQ_MULTIBULK) {             if (processMultibulkBuffer(c) != REDIS_OK) break;         } else {             redisPanic("Unknown request type");         }         /* Multibulk processing could see a <= 0 length. */         if (c->argc == 0) {             resetClient(c);         } else {             /* Only reset the client when the command was executed. */             if (processCommand(c) == REDIS_OK)                 resetClient(c);         }     } }</tr></table>

  • Redis支持两种协议,一种是inline,一种是multibulk。inline协议是老协议,现在一般只在命令行下的redis客户端使用,其他情况一般是使用multibulk协议。
  • 如 果客户端传送的数据的第一个字符时‘*’,那么传送数据将被当做multibulk协议处理,否则将被当做inline协议处理。Inline协议的具体 解析函数是processInlineBuffer,multibulk协议的具体解析函数是processMultibulkBuffer。
  • 当协议解析完毕,即客户端传送的数据已经解析出命令字段和参数字段,接下来进行命令处理,命令处理函数是processCommand。

  Inline请求协议

  Networking.c:679

</tr></table>

  • 根据空格分割客户端传送过来的数据,把传送过来的命令和参数保存在argv数组中,把参数个数保存在argc中,argc的值包括了命令参数本身。即set key value命令,argc的值为3。详细解析见协议详解

  Multibulk请求协议

   Multibulk协议比inline协议复杂,它是二进制安全的,即传送数据可以包含不安全字符。Inline协议不是二进制安全的,比如,如果 set key value命令中的key或value包含空白字符,那么inline协议解析时将会失败,因为解析出来的参数个数与命令需要的的参数个数会不一致。

  协议格式

 CR LF $ CR LF  CR LF ... $ CR LF  CR LF</tr></table>

  协议举例

</tr></table>

  具体解析代码位于

  Networking.c:731

</tr></table>

  详细解析见协议详解

  处理命令

  当协议解析完毕,则表示客户端的命令输入已经全部读取并已经解析成功,接下来就是执行客户端命令前的准备和执行客户端传送过来的命令

  Redis.c:1062

cmd = c->lastcmd = lookupCommand(c->argv[0]->ptr); ... call(c); ... }</tr></table>

  • lookupCommand先根据客户端传送过来的数据查找该命令并找到命令的对应处理函数。
  • Call函数调用该命令函数来处理命令,命令与对应处理函数的绑定位于。

  Redi.c:72

</tr></table>

  回复请求

  回复请求位于对应的命令中,以get命令为例

  T_string.c:67

</tr></table>

  T_string.c:52

argv[1],shared.nullbulk)) == NULL)         return REDIS_OK;     if (o->type != REDIS_STRING) {         addReply(c,shared.wrongtypeerr);         return REDIS_ERR;     } else {         addReplyBulk(c,o);         return REDIS_OK;     } }</tr></table>

  • getGenericCommand在getset 命令中也会用到。
  • lookupKeyReadOrReply是以读数据为目的查询key函数,并且如果该key不存在,则在该函数中做不存在的回包处理。
  • 如果该key存在,则返回该key对应的数据,addReply函数以及以addReply函数开头的都是回包函数。

  绑定写数据的回调函数

  接下来看看addReply函数里的内容

  Networking.c:190

</tr></table>

  Networking.c:64

fd <= 0) return REDIS_ERR;     if (c->bufpos == 0 && listLength(c->reply) == 0 &&         (c->replstate == REDIS_REPL_NONE ||          c->replstate == REDIS_REPL_ONLINE) &&         aeCreateFileEvent(server.el, c->fd, AE_WRITABLE,        sendReplyToClient, c) == AE_ERR) return REDIS_ERR;     return REDIS_OK; }</tr></table>

  • addReply函数一进来就先调用绑定写数据的回调函数installWriteEvent。
  • installWriteEvent函数中创建了一个文件写事件和绑定写事件的回调函数为sendReplyToClient。

  准备写的数据内容

    addReply函数一进来后就绑定写数据的回调函数,接下来就是准备写的数据内容

  Networking.c:190

storage == REDIS_VM_MEMORY);     /* This is an important place where we can avoid copy-on-write      * when there is a saving child running, avoiding touching the      * refcount field of the object if it's not needed.      *      * If the encoding is RAW and there is room in the static buffer      * we'll be able to send the object to the client without      * messing with its page. */     if (obj->encoding == REDIS_ENCODING_RAW) {         if (_addReplyToBuffer(c,obj->ptr,sdslen(obj->ptr)) != REDIS_OK)             _addReplyObjectToList(c,obj);     } else {         /* FIXME: convert the long into string and use _addReplyToBuffer()          * instead of calling getDecodedObject. As this place in the          * code is too performance critical. */         obj = getDecodedObject(obj);         if (_addReplyToBuffer(c,obj);         decrRefCount(obj);     } }</tr></table>

  • 先尝试把要返回的内容添加到发送数据缓冲区中(redisClient->buf),如果该缓冲区的大小已经放不下这次想放进去的数据,或者已经有数据在排队(redisClient->reply 链表不为空),则把数据添加到发送链表的尾部。

  给客户端答复数据

  在绑定写数据的回调函数中看到绑定了回调函数sendReplyToClient,现在来看看这个函数的主要内容

  Networking.c:566

bufpos > 0 || listLength(c->reply)) {     ...     if(c->bufpos > 0){         ...             nwritten=write(fd,...,c->bufpos-c->sentlen);             ...         } else {             o = listNodeValue(listFirst(c->reply));             ...             nwritten=write(fd,objlen-c->sentlen);             ...         }     } }</tr></table>

  • 通过调用系统函数write给客户端发送数据,如果缓冲区有数据就把缓冲区的数据发送给客户端,缓冲区的数据发送完了,如果有排队数据,则继续发送。

  退出

  Redis 服务器的退出是通过shutdown命令来退出的,退出前会做一系列的清理工作

  Db.c:347

</tr></table>

  总结

  框架从启动,接收请求,读取客户端数据,请求协议解析,处理命令,回复请求,退出对redis运行的整个流程做了一个梳理。对整个redis的运作和框架有了一个初步的了解。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


文章浏览阅读1.3k次。在 Redis 中,键(Keys)是非常重要的概念,它们代表了存储在数据库中的数据的标识符。对键的有效管理和操作是使用 Redis 数据库的关键一环,它直接影响到数据的存取效率、系统的稳定性和开发的便利性。本文将深入探讨 Redis 中键的管理和操作,包括键的命名规范、常用的键操作命令以及一些最佳实践。我们将详细介绍如何合理命名键、如何使用键的过期和持久化特性、如何批量删除键等技巧,旨在帮助读者更好地理解并灵活运用 Redis 中的键,从而提高数据管理和操作的效率和可靠性。
文章浏览阅读3.3k次,点赞44次,收藏88次。本篇是对单节点的应用,但从中我们也能推断出一些关于集群的应用,不过大多数公司能搞个主从就已经是不错了,所以你能学会这个已经算是很有用了,关于ES,博主前面也讲过一些基础应用,创建一个工具类利用ES的数据模型进行存储就可以达到一个canal同时对Redis和ES的同步,如果担心出问题,可以把Canal搞成集群的形式,这个后续有时间博主再给大家做讲解。今天就到这里了,觉得不错就支持一下吧。_canal redis
文章浏览阅读8.4k次,点赞8次,收藏18次。Spring Boot 整合Redis实现消息队列,RedisMessageListenerContainer的使用,Pub/Sub模式的优缺点_springboot redis 消息队列
文章浏览阅读978次,点赞25次,收藏21次。在Centos上安装Redis5.0保姆级教程!_centos7 安装redis5.0服务器
文章浏览阅读1.2k次,点赞21次,收藏22次。Docker-Compose部署Redis(v7.2)主从模式首先需要有一个redis主从集群,才能接着做redis哨兵模式。_warning: sentinel was not able to save the new configuration on disk!!!: dev
文章浏览阅读2.2k次,点赞59次,收藏38次。合理的JedisPool资源池参数设置能为业务使用Redis保驾护航,本文将对JedisPool的使用、资源池的参数进行详细说明,最后给出“最合理”配置。_jedispool资源池优化
文章浏览阅读1.9k次。批量删除指定前缀的Key有两中方法,一种是借助 redis-cli,另一种是通过 SCAN命令来遍历所有匹配前缀的 key,并使用 DEL命令逐个删除它们。_redis删除前缀的key
文章浏览阅读890次,点赞18次,收藏20次。1. Redis时一个key-cakye的数据库,key一般是String类型,不过value类型有很多。eg.String Hash List Set SortedSet (基本) | GEO BitMap HyperLog (特殊)2.Redis为了方便学习,将操作不同类型的命令做了分组,在官网可以进行查询。
文章浏览阅读1.1k次,点赞19次,收藏26次。若不使用Redisson,而是用synchronized(this),此时会造成对服务器的加锁,若开始大量查询ID为1的商品,每台机器都会先跑一遍加个锁,然后在查询ID为2的数据,此时需要等待ID为1的锁释放,所以需要将this对象调整为全局商品ID。若在执行bgsave命令时,还有其他redis命令被执行(主线程数据修改),此时会对数据做个副本,然后bgsave命令执行这个副本数据写入rdb文件,此时主线程还可以继续修改数据。在当前redis目录下会生成aof文件,对redis修改数据的命令进行备份。
文章浏览阅读1.5k次,点赞39次,收藏24次。本文全面剖析Redis集群在分布式环境下的数据一致性问题,从基础原理到高级特性,涵盖主从复制、哨兵模式、持久化策略等关键点,同时也分享了关于监控、故障模拟与自适应写一致性策略的实践经验。_redis集群一致性
文章浏览阅读1k次。RDB因为是二进制文件,在保存的时候体积也是比较小的,它恢复的比较快,但是它有可能会丢数据,我们通常在项目中也会使用AOF来恢复数据,虽然AOF恢复的速度慢一些,但是它丢数据的风险要小很多,在AOF文件中可以设置刷盘策略,我们当时设置的就是每秒批量写入一次命令。AOF的含义是追加文件,当redis操作写命令的时候,都会存储这个文件中,当redis实例宕机恢复数据的时候,会从这个文件中再次执行一遍命令来恢复数据。:在Redis中提供了两种数据持久化的方式:1、RDB 2、AOF。
文章浏览阅读1k次,点赞24次,收藏21次。NoSQL(No only SQL)数据库,泛指非关系型数据库,实现对于传统数据库而言的。NoSQL 不依赖业务逻辑方式进行存储,而以简单的 key-value 模式存储。因此大大增加了数据库的扩展能力。不遵循SQL标准不支持ACID远超于SQL的性能Redis是当前比较热门的NOSQL系统之一,它是一个开源的使用ANSI c语言编写的key-value存储系统(区别于MySQL的二维表格的形式存储。
文章浏览阅读988次,点赞17次,收藏19次。在上面的步骤中,我们已经开启了 MySQL 的远程访问功能,但是,如果使用 MySQL 管理工具 navicat 连接 MySQL 服务端时,还是可能会出现连接失败的情况。在实际工作中,如果我们需要从其他地方访问和管理 MySQL 数据库,就需要开启 MySQL 的远程访问功能并设置相应的权限。这对于我们的工作效率和数据安全都有很大的帮助。通过查看 MySQL 用户表,我们可以看到’host’为’%’,说明 root 用户登录 MySQL 的时候,可以允许任意的 IP 地址访问 MySQL 服务端。
文章浏览阅读956次。Redis Desktop Manager(RDM)是一款用于管理和操作Redis数据库的图形化界面工具。提供了简单易用的界面,使用户能够方便地执行各种Redis数据库操作,并且支持多个Redis服务器的连接_redisdesktopmanager安装包
文章浏览阅读1.9k次,点赞52次,收藏27次。缓存击穿指的是数据库有数据,缓存本应该也有数据,但是缓存过期了,Redis 这层流量防护屏障被击穿了,请求直奔数据库。缓存穿透指的是数据库本就没有这个数据,请求直奔数据库,缓存系统形同虚设。缓存雪崩指的是大量的热点数据无法在 Redis 缓存中处理(大面积热点数据缓存失效、Redis 宕机),流量全部打到数据库,导致数据库极大压力。
文章浏览阅读1.2k次。一次命令时间(borrow|return resource + Jedis执行命令(含网络) )的平均耗时约为1ms,一个连接的QPS大约是1000,业务期望的QPS是50000,那么理论上需要的资源池大小是50000 / 1000 = 50个,实际maxTotal可以根据理论值合理进行微调。JedisPool默认的maxTotal=8,下面的代码从JedisPool中借了8次Jedis,但是没有归还,当第9次(jedisPool.getResource().ping())3、发生异常可能的情况。_redis.clients.jedis.exceptions.jedisconnectionexception: could not get a res
文章浏览阅读1k次,点赞27次,收藏18次。在这篇文章中,你将了解到如何在 CentOS 系统上安装 Redis 服务,并且掌握通过自定义域名来访问 Redis 服务的技巧。通过使用自定义域名,你可以方便地管理和访问你的 Redis 数据库,提高工作效率。无论你是开发者、系统管理员还是对 Redis 感兴趣的读者,这篇文章都会为你提供清晰的指导和实用的技巧。阅读本文,轻松搭建自己的 Redis 服务,并体验自定义域名带来的便捷!_redis怎么自定义域名
文章浏览阅读1.1k次,点赞15次,收藏18次。我们post请求,拦截器要预先读取HtppServletRequest里面的body的数据,是通过io的方式,都知道io读取完毕之后,之前的数据是变为null的,但是,当我么后面的接口来委派的时候,也是通过io读取body。我们要考虑一个事情,就是我们要验证数据的重复提交: 首先第一次提交的数据肯定是要被存储的,当而第二次往后,每次提交数据都会与之前的数据产生比对从而验证数据重复提交,我们要具体判断数据是否重复提交的子类。发现数据是成功存入的,剩余7s过期,在10s之内,也就是数据没过期之前,在发送一次。_json.parseobject(str, clazz, auto_type_filter);
文章浏览阅读3.9k次,点赞3次,收藏7次。PHP使用Redis实战实录系列:我们首先检查$redis->connect()方法的返回值来确定是否成功连接到Redis服务器。如果连接失败,我们可以输出相应的错误信息。如果连接成功,我们再执行一些操作,如$redis->set()、$redis->get()等,并检查每个操作的返回结果来判断是否发生了异常。_php redis
文章浏览阅读1.5w次,点赞23次,收藏51次。Redis(Remote Dictionary Server ),即远程字典服务,是一个开源的使用ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API。Redis 是一个高性能的key-value数据库。redis的出现,很大程度补偿了memcached这类key/value存储的不足,在部 分场合可以对关系数据库起到很好的补充作用。_redisdesktopmanager下载