设为首页 收藏本站
查看: 662|回复: 0

[经验分享] Redis源码剖析——客户端和服务器

[复制链接]

尚未签到

发表于 2018-11-2 10:48:13 | 显示全部楼层 |阅读模式
  Redis服务器是典型的一对多服务器程序:一个服务器可以与多个客户端建立网络连接。这篇文章将通过源码看看客户端和服务器的底层数据结构和工作过程
  在Redis这种一对多的服务模式下,每个客户端可以向服务器发送命令请求,而服务器则接收并处理客户端发送的命令请求,并向客户端返回命令回复。通过使用由I/O多路复用技术实现的文件事件处理器,Redis服务器使用单线程单进程的方式来处理命令请求,并与多个客户端进行网络通信。
  客户端
  客户端数据结构
  客户端底层的数据结构如下:
  typedef struct redisClient {
  uint64_t> Client incremental unique>/
  // 套接字描述符
  int fd;
  redisDb db;
  int dictid;
  // 客户端名字
  robj name;             / As set by CLIENT SETNAME /
  // 输入缓冲区,保存客户端发送的命令请求
  sds querybuf;
  size_t querybuf_peak;   / Recent (100ms or more) peak of querybuf>/
  // 命令和命令参数
  int argc;
  robj *argv;
  // 命令实现函数字典
  struct redisCommand cmd, lastcmd;
  int reqtype;
  int multibulklen;       / number of multi bulk arguments left to read /
  long bulklen;           / length of bulk argument in multi bulk request /
  list reply;
  unsigned long reply_bytes; / Tot bytes of objects in reply list /
  int sentlen;            / Amount of bytes already sent in the current
  buffer or object being sent. /
  // 创建客户端时间
  time_t ctime;           / Client creation time /
  // 客户端和服务器最后一次进行互动的时间
  time_t lastinteraction; / time of the last interaction, used for timeout /
  time_t obuf_soft_limit_reached_time;
  // 标志,记录客户端的角色
  int flags;              / REDIS_SLAVE | REDIS_MONITOR | REDIS_MULTI ... /
  // 标志是否通过身份验证
  int authenticated;      / when requirepass is non-NULL /
  ... // 其他相关属性
  

/* Response buffer */  
// 回应缓冲区
  
int bufpos;
  
char buf[REDIS_REPLY_CHUNK_BYTES];
  

  } redisClient;
  在客户端的各个属性中:
  fd表示套接字描述符,伪客户端的fd属性的值为-1:伪客户端处理的命令请求来源于AOF文件或者Lua脚本,而不是网络,所以这种客户端不需要套接字连接;普通客户端的fd属性的值为大于-1的整数
  命令和命令参数是对输入缓冲的命令进行解析以后获得命令和参数。
  cmd 是命令的实现函数的数组,命令实现函数的结构如下:
  struct redisCommand {
  // 命令名称
  char name;
  // 命令执行函数
  redisCommandProc proc;
  // 参数个数
  int arity;
  // 字符串表示flag
  char sflags; / Flags as string representation, one char per flag. /
  // 实际flag
  int flags;    / The actual flags, obtained from the 'sflags' field. */
  

...  

  
// 指定哪些参数是key
  
int firstkey; /* The first argument that's a key (0 = no keys) */
  
int lastkey;  /* The last argument that's a key */
  
int keystep;  /* The step between first and last key */
  
// 统计信息
  
long long microseconds, calls;
  

  };
  客户端的创建和关闭
  当客户端向服务器发出connect请求的时候,服务器的事件处理器就会对这个事件进行处理,创建相应的客户端状态,并将这个新的客户端状态添加到服务器状态结构clients链表的末尾
  /*


  •   创建一个新客户端
      /
      redisClient createClient(int fd){
      // 分配空间
      redisClient *c = zmalloc(sizeof(redisClient));
      // 当 fd 不为 -1 时,创建带网络连接的客户端
      // 如果 fd 为 -1 ,那么创建无网络连接的伪客户端
      // 因为 Redis 的命令必须在客户端的上下文中使用,所以在执行 Lua 环境中的命令时
      // 需要用到这种伪终端
      if (fd != -1) {
      // 非阻塞
      anetNonBlock(NULL,fd);
      // 禁用 Nagle 算法
      anetEnableTcpNoDelay(NULL,fd);
      // 设置 keep alive
      if (server.tcpkeepalive)
      anetKeepAlive(NULL,fd,server.tcpkeepalive);
      // 绑定读事件到事件 loop (开始接收命令请求)
      if (aeCreateFileEvent(server.el,fd,AE_READABLE,
      readQueryFromClient, c) == AE_ERR)
      {
      close(fd);
      zfree(c);
      return NULL;
      }
      }
      // 初始化各个属性
      // 默认数据库
      selectDb(c,0);
      // 套接字
      c->fd = fd;
      ...
      listSetFreeMethod(c->pubsub_patterns,decrRefCountVoid);
      listSetMatchMethod(c->pubsub_patterns,listMatchObjects);
      // 如果不是伪客户端,那么添加到服务器的客户端链表中
      if (fd != -1) listAddNodeTail(server.clients,c);
      // 初始化客户端的事务状态
      initClientMultiState(c);
      // 返回客户端
      return c;
      }
      对于客户端的启动程序,其大致的逻辑是:读取本地配置,连接服务器获取服务器的配置,获取本地输入的命令并发送到服务器

  一个普通客户端可以因为多种原因而被关闭:
  如果客户端进程退出或者被杀死,那么客户端与服务器之间的网络连接将被关闭,从而造成客户端被关闭。
  如果客户端向服务器发送了带有不符合协议格式的命令请求,那么这个客户端也会被服务器关闭。
  如果客户端成为了CLIENT KLLL命令的目标,那么它也会被关闭。
  关闭客户端的底层实现:
  /*


  •   释放客户端
      /
      void freeClient(redisClient c){
      listNode *ln;
      ...
      / Free the query buffer /
      sdsfree(c->querybuf);
      c->querybuf = NULL;
      / Deallocate structures used to block on blocking ops. /
      if (c->flags & REDIS_BLOCKED) unblockClient(c);
      dictRelease(c->bpop.keys);
      / UNWATCH all the keys /
      // 清空 WATCH 信息
      unwatchAllKeys(c);
      listRelease(c->watched_keys);
      / Unsubscribe from all the pubsub channels /
      // 退订所有频道和模式
      pubsubUnsubscribeAllChannels(c,0);
      pubsubUnsubscribeAllPatterns(c,0);
      dictRelease(c->pubsub_channels);
      listRelease(c->pubsub_patterns);
      /* Close socket, unregister events, and remove list of replies and

    • accumulated arguments. */  // 关闭套接字,并从事件处理器中删除该套接字的事件
        if (c->fd != -1) {
        aeDeleteFileEvent(server.el,c->fd,AE_READABLE);
        aeDeleteFileEvent(server.el,c->fd,AE_WRITABLE);
        close(c->fd);
        }

      // 清空回复缓冲区
      listRelease(c->reply);
      // 清空命令参数
      freeClientArgv(c);
      / Remove from the list of clients /
      // 从服务器的客户端链表中删除自身
      if (c->fd != -1) {
      ln = listSearchKey(server.clients,c);
      redisAssert(ln != NULL);
      listDelNode(server.clients,ln);
      }
      // 删除客户端的阻塞信息
      if (c->flags & REDIS_UNBLOCKED) {
      ln = listSearchKey(server.unblocked_clients,c);
      redisAssert(ln != NULL);
      listDelNode(server.unblocked_clients,ln);
      }
      ...
      if (c->name) decrRefCount(c->name);
      // 清除参数空间
      zfree(c->argv);
      // 清除事务状态信息
      freeClientMultiState(c);
      sdsfree(c->peerid);
      // 释放客户端 redisClient 结构本身
      zfree(c);
      }``





运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-629733-1-1.html 上篇帖子: Redis的安装与配置 下篇帖子: Redis基础命令
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表