当前位置:   article > 正文

redis源码分析(3)——请求处理_redis-cli --pipe too big inline request

redis-cli --pipe too big inline request

前两篇介绍了redis的初始化过程,以及事件循环。本篇来看一下客户端的连接建立与请求处理。

(1)连接建立

在初始化一篇中提到过,redis在将监听socket初始化完毕之后,会将他们添加到事件循环中:
  1. for (j = 0; j < server.ipfd_count; j++) {
  2. if (aeCreateFileEvent(server.el, server.ipfd[j], AE_READABLE,
  3. acceptTcpHandler,NULL) == AE_ERR)
  4. {
  5. redisPanic(
  6. "Unrecoverable error creating server.ipfd file event.");
  7. }
  8. }
监听socket的读事件就是有客户端连接请求过来,对应的事件处理函数是acceptTcpHandler,这个函数就是用于处理客户端连接建立。函数如下:
  1. void acceptTcpHandler(aeEventLoop *el, int fd, void *privdata, int mask) {
  2. int cport, cfd, max = MAX_ACCEPTS_PER_CALL;
  3. char cip[REDIS_IP_STR_LEN];
  4. REDIS_NOTUSED(el);
  5. REDIS_NOTUSED(mask);
  6. REDIS_NOTUSED(privdata);
  7. while(max--) {
  8. cfd = anetTcpAccept(server.neterr, fd, cip, sizeof(cip), &cport);
  9. if (cfd == ANET_ERR) {
  10. if (errno != EWOULDBLOCK)
  11. redisLog(REDIS_WARNING,
  12. "Accepting client connection: %s", server.neterr);
  13. return;
  14. }
  15. redisLog(REDIS_VERBOSE,"Accepted %s:%d", cip, cport);
  16. acceptCommonHandler(cfd,0);
  17. }
  18. }
当这个函数被调用时(对应的监听socket的读事件发生),已经有客户端完成三次握手建立连接。函数anetTcpAccept用于accept客户端的连接,其返回值是客户端对应的socket。然后,会调用acceptCommonHandler对连接以及客户端进行初始化。这部分逻辑是在一个while循环中,最多迭代MAX_ACCEPTS_PER_CALL(1000)次,也就是说每次事件循环最多可以处理1000个客户端的连接。
下面,看一下accetpCommonHandler函数
  1. static void acceptCommonHandler(int fd, int flags) {
  2. redisClient *c;
  3. if ((c = createClient(fd)) == NULL) {
  4. redisLog(REDIS_WARNING,
  5. "Error registering fd event for the new client: %s (fd=%d)",
  6. strerror(errno),fd);
  7. close(fd); /* May be already closed, just ignore errors */
  8. return;
  9. }
  10. /* If maxclient directive is set and this is one client more... close the
  11. * connection. Note that we create the client instead to check before
  12. * for this condition, since now the socket is already set in non-blocking
  13. * mode and we can send an error for free using the Kernel I/O */
  14. if (listLength(server.clients) > server.maxclients) {
  15. char *err = "-ERR max number of clients reached\r\n";
  16. /* That's a best effort error message, don't check write errors */
  17. if (write(c->fd,err,strlen(err)) == -1) {
  18. /* Nothing to do, Just to avoid the warning... */
  19. }
  20. server.stat_rejected_conn++;
  21. freeClient(c);
  22. return;
  23. }
  24. server.stat_numconnections++;
  25. c->flags |= flags;
  26. }
这个函数主要调用createClient初始化客户端相关数据结构以及对应的socket,初始化后会判断当前连接的客户端是否超过最大值,如果超过的话,会拒绝这次连接。否则,更新客户端连接数的计数。
      数据结构redisClient用于表示一个客户端的连接,包括一个客户多次请求的状态,createClient函数主要是初始化这个数据结构。在createClient函数中,首先是创建redisClient,然后是设置socket的属性,然后添加该socket的读事件。
  1. if (fd != -1) {
  2. anetNonBlock(NULL,fd);
  3. // <MM>
  4. // 关闭Nagle算法,提升响应速度
  5. // </MM>
  6. anetEnableTcpNoDelay(NULL,fd);
  7. if (server.tcpkeepalive)
  8. anetKeepAlive(NULL,fd,server.tcpkeepalive);
  9. if (aeCreateFileEvent(server.el,fd,AE_READABLE,
  10. readQueryFromClient, c) == AE_ERR)
  11. {
  12. close(fd);
  13. zfree(c);
  14. return NULL;
  15. }
  16. }
将socket设置为非阻塞的并且no delay,关闭Nagle算法,提升响应速度。最后会注册socket的读事件,事件处理函数是readQueryFromClient,这个函数便是客户端请求的起点,之后会详细介绍。
      createClient函数的最后部分,就是对redisClient的属性初始化,代码不再列出。
      当从acceptTcpHandler返回后,客户端的连接就建立完毕,接下来就是等待客户端的请求。

(2)请求处理

可以把redis请求处理的过程分成3个步骤:
          (1)读取请求buffer
          (2)解析请求
          (3)处理请求
      先简单概括一下这个流程。redis的请求分为两种类型:
          (1)inline:简单字符串格式,比如:ping命令
          (2)multi bulk:字符串数组格式,比如:set,get等等大部分命令
      在请求处理时,会根据不同类型,分别处理。redisClient->reqtype存储请求类型。
1)readQueryFromClient函数
前文介绍到,readQueryFromClient是请求处理的起点,这个函数就是用于读取请求buffer的,下面详解一下这个函数。首先,设置当前服务的client,然后是设置这次从socket读取的数据的默认大小(REDIS_IOBUF_LEN为16KB)。
  1. server.current_client = c;
  2. readlen = REDIS_IOBUF_LEN;
这段代码重新设置读取数据的大小,避免频繁拷贝数据。如果当前请求是一个multi bulk类型的,并且要处理的bulk的大小大于REDIS_MBULK_BIG_ARG(32KB),则将读取数据大小设置为该bulk剩余数据的大小。
  1. /* If this is a multi bulk request, and we are processing a bulk reply
  2. * that is large enough, try to maximize the probability that the query
  3. * buffer contains exactly the SDS string representing the object, even
  4. * at the risk of requiring more read(2) calls. This way the function
  5. * processMultiB
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/喵喵爱编程/article/detail/978350
推荐阅读
相关标签
  

闽ICP备14008679号