Redis源码解析:集群手动故障转移、从节点迁移详解

一:手动故障转移

Redis集群支持手动故障转移。也就是向从节点发送”CLUSTER  FAILOVER”命令,使其在主节点未下线的情况下,发起故障转移流程,升级为新的主节点,而原来的主节点降级为从节点。

为了不丢失数据,向从节点发送”CLUSTER  FAILOVER”命令后,流程如下:

a:从节点收到命令后,向主节点发送CLUSTERMSG_TYPE_MFSTART包;
         b:主节点收到该包后,会将其所有客户端置于阻塞状态,也就是在10s的时间内,不再处理客户端发来的命令;并且在其发送的心跳包中,会带有CLUSTERMSG_FLAG0_PAUSED标记;
         c:从节点收到主节点发来的,带CLUSTERMSG_FLAG0_PAUSED标记的心跳包后,从中获取主节点当前的复制偏移量。从节点等到自己的复制偏移量达到该值后,才会开始执行故障转移流程:发起选举、统计选票、赢得选举、升级为主节点并更新配置;

”CLUSTER  FAILOVER”命令支持两个选项:FORCE和TAKEOVER。使用这两个选项,可以改变上述的流程。

如果有FORCE选项,则从节点不会与主节点进行交互,主节点也不会阻塞其客户端,而是从节点立即开始故障转移流程:发起选举、统计选票、赢得选举、升级为主节点并更新配置。

如果有TAKEOVER选项,则更加简单粗暴:从节点不再发起选举,而是直接将自己升级为主节点,接手原主节点的槽位,增加自己的configEpoch后更新配置。

因此,使用FORCE和TAKEOVER选项,主节点可以已经下线;而不使用任何选项,只发送”CLUSTER  FAILOVER”命令的话,主节点必须在线。

clusterCommand函数中,处理”CLUSTER  FAILOVER”命令的部分代码如下:

else if (!strcasecmp(c->argv[1]->ptr,"failover") &&
      (c->argc == 2 || c->argc == 3))
{
  /* CLUSTER FAILOVER [FORCE|TAKEOVER] */
  int force = 0, takeover = 0; 

  if (c->argc == 3) {
    if (!strcasecmp(c->argv[2]->ptr,"force")) {
      force = 1;
    } else if (!strcasecmp(c->argv[2]->ptr,"takeover")) {
      takeover = 1;
      force = 1; /* Takeover also implies force. */
    } else {
      addReply(c,shared.syntaxerr);
      return;
    }
  } 

  /* Check preconditions. */
  if (nodeIsMaster(myself)) {
    addReplyError(c,"You should send CLUSTER FAILOVER to a slave");
    return;
  } else if (myself->slaveof == NULL) {
    addReplyError(c,"I'm a slave but my master is unknown to me");
    return;
  } else if (!force &&
        (nodeFailed(myself->slaveof) ||
        myself->slaveof->link == NULL))
  {
    addReplyError(c,"Master is down or failed, "
            "please use CLUSTER FAILOVER FORCE");
    return;
  }
  resetManualFailover();
  server.cluster->mf_end = mstime() + REDIS_CLUSTER_MF_TIMEOUT; 

  if (takeover) {
    /* A takeover does not perform any initial check. It just
     * generates a new configuration epoch for this node without
     * consensus, claims the master's slots, and broadcast the new
     * configuration. */
    redisLog(REDIS_WARNING,"Taking over the master (user request).");
    clusterBumpConfigEpochWithoutConsensus();
    clusterFailoverReplaceYourMaster();
  } else if (force) {
    /* If this is a forced failover, we don't need to talk with our
     * master to agree about the offset. We just failover taking over
     * it without coordination. */
    redisLog(REDIS_WARNING,"Forced failover user request accepted.");
    server.cluster->mf_can_start = 1;
  } else {
    redisLog(REDIS_WARNING,"Manual failover user request accepted.");
    clusterSendMFStart(myself->slaveof);
  }
  addReply(c,shared.ok);
} 

首先检查命令的最后一个参数是否是FORCETAKEOVER

如果当前节点是主节点;或者当前节点是从节点,但没有主节点;或者当前从节点的主节点已经下线或者断链,并且命令中没有FORCE或TAKEOVER参数,则直接回复客户端错误信息后返回;

然后调用resetManualFailover,重置手动强制故障转移的状态;

置mf_end为当前时间加5秒,该属性表示手动强制故障转移流程的超时时间,也用来表示当前是否正在进行手动强制故障转移;

如果命令最后一个参数为TAKEOVER,这表示收到命令的从节点无需经过选举的过程,直接接手其主节点的槽位,并成为新的主节点。因此首先调用函数clusterBumpConfigEpochWithoutConsensus,产生新的configEpoch,以便后续更新配置;然后调用clusterFailoverReplaceYourMaster函数,转变成为新的主节点,并将这种转变广播给集群中所有节点;

如果命令最后一个参数是FORCE,这表示收到命令的从节点可以直接开始选举过程,而无需达到主节点的复制偏移量之后才开始选举过程。因此置mf_can_start为1,这样在函数clusterHandleSlaveFailover中,即使在主节点未下线或者当前从节点的复制数据比较旧的情况下,也可以开始故障转移流程;

如果最后一个参数不是FORCE或TAKEOVER,这表示收到命令的从节点,首先需要向主节点发送CLUSTERMSG_TYPE_MFSTART包,因此调用clusterSendMFStart函数,向其主节点发送该包;

主节点收到CLUSTERMSG_TYPE_MFSTART包后,在clusterProcessPacket函数中,是这样处理的:

else if (type == CLUSTERMSG_TYPE_MFSTART) {
  /* This message is acceptable only if I'm a master and the sender
   * is one of my slaves. */
  if (!sender || sender->slaveof != myself) return 1;
  /* Manual failover requested from slaves. Initialize the state
   * accordingly. */
  resetManualFailover();
  server.cluster->mf_end = mstime() + REDIS_CLUSTER_MF_TIMEOUT;
  server.cluster->mf_slave = sender;
  pauseClients(mstime()+(REDIS_CLUSTER_MF_TIMEOUT*2));
  redisLog(REDIS_WARNING,"Manual failover requested by slave %.40s.",
    sender->name);
} 

如果字典中找不到发送节点,或者发送节点的主节点不是当前节点,则直接返回;

调用resetManualFailover,重置手动强制故障转移的状态;

然后置mf_end为当前时间加5秒,该属性表示手动强制故障转移流程的超时时间,也用来表示当前是否正在进行手动强制故障转移;

然后设置mf_slave为sender,该属性表示要进行手动强制故障转移的从节点;

然后调用pauseClients,使所有客户端在之后的10s内阻塞;

主节点在发送心跳包时,在构建包头时,如果发现当前正处于手动强制故障转移阶段,则会在包头中增加CLUSTERMSG_FLAG0_PAUSED标记:

void clusterBuildMessageHdr(clusterMsg *hdr, int type) {
  ...
  /* Set the message flags. */
  if (nodeIsMaster(myself) && server.cluster->mf_end)
    hdr->mflags[0] |= CLUSTERMSG_FLAG0_PAUSED;
  ...
}   

从节点在clusterProcessPacket函数中处理收到的包,一旦发现主节点发来的,带有CLUSTERMSG_FLAG0_PAUSED标记的包,就会将该主节点的复制偏移量记录到server.cluster->mf_master_offset中:

int clusterProcessPacket(clusterLink *link) {
  ...
  /* Check if the sender is a known node. */
  sender = clusterLookupNode(hdr->sender);
  if (sender && !nodeInHandshake(sender)) {
    ...
    /* Update the replication offset info for this node. */
    sender->repl_offset = ntohu64(hdr->offset);
    sender->repl_offset_time = mstime();
    /* If we are a slave performing a manual failover and our master
     * sent its offset while already paused, populate the MF state. */
    if (server.cluster->mf_end &&
      nodeIsSlave(myself) &&
      myself->slaveof == sender &&
      hdr->mflags[0] & CLUSTERMSG_FLAG0_PAUSED &&
      server.cluster->mf_master_offset == 0)
    {
      server.cluster->mf_master_offset = sender->repl_offset;
      redisLog(REDIS_WARNING,
        "Received replication offset for paused "
        "master manual failover: %lld",
        server.cluster->mf_master_offset);
    }
  }
}   

从节点在集群定时器函数clusterCron中,会调用clusterHandleManualFailover函数,判断一旦当前从节点的复制偏移量达到了server.cluster->mf_master_offset,就会置server.cluster->mf_can_start为1。这样在接下来要调用的clusterHandleSlaveFailover函数中,就会立即开始故障转移流程了。

clusterHandleManualFailover函数的代码如下:

void clusterHandleManualFailover(void) {
  /* Return ASAP if no manual failover is in progress. */
  if (server.cluster->mf_end == 0) return;
  /* If mf_can_start is non-zero, the failover was already triggered so the
   * next steps are performed by clusterHandleSlaveFailover(). */
  if (server.cluster->mf_can_start) return;
  if (server.cluster->mf_master_offset == 0) return; /* Wait for offset... */
  if (server.cluster->mf_master_offset == replicationGetSlaveOffset()) {
    /* Our replication offset matches the master replication offset
     * announced after clients were paused. We can start the failover. */
    server.cluster->mf_can_start = 1;
    redisLog(REDIS_WARNING,
      "All master replication stream processed, "
      "manual failover can start.");
  }
} 

不管是从节点,还是主节点,在集群定时器函数clusterCron中,都会调用manualFailoverCheckTimeout函数,一旦发现手动故障转移的超时时间已到,就会重置手动故障转移的状态,表示终止该过程。manualFailoverCheckTimeout函数代码如下:

/* If a manual failover timed out, abort it. */
void manualFailoverCheckTimeout(void) {
  if (server.cluster->mf_end && server.cluster->mf_end < mstime()) {
    redisLog(REDIS_WARNING,"Manual failover timed out.");
    resetManualFailover();
  }
} 

二:从节点迁移

在Redis集群中,为了增强集群的可用性,一般情况下需要为每个主节点配置若干从节点。但是这种主从关系如果是固定不变的,则经过一段时间之后,就有可能出现孤立主节点的情况,也就是一个主节点再也没有可用于故障转移的从节点了,一旦这样的主节点下线,整个集群也就不可用了。

因此,在Redis集群中,增加了从节点迁移的功能。简单描述如下:一旦发现集群中出现了孤立主节点,则某个从节点A就会自动变成该孤立主节点的从节点。该从节点A满足这样的条件:A的主节点具有最多的附属从节点;A在这些附属从节点中,节点ID是最小的(The acting slave is the slave among the masterswith the maximum number of attached slaves, that is not in FAIL state and hasthe smallest node ID)。

该功能是在集群定时器函数clusterCron中实现的。这部分的代码如下:

void clusterCron(void) {
  ...
  orphaned_masters = 0;
  max_slaves = 0;
  this_slaves = 0;
  di = dictGetSafeIterator(server.cluster->nodes);
  while((de = dictNext(di)) != NULL) {
    clusterNode *node = dictGetVal(de);
    now = mstime(); /* Use an updated time at every iteration. */
    mstime_t delay; 

    if (node->flags &
      (REDIS_NODE_MYSELF|REDIS_NODE_NOADDR|REDIS_NODE_HANDSHAKE))
        continue; 

    /* Orphaned master check, useful only if the current instance
     * is a slave that may migrate to another master. */
    if (nodeIsSlave(myself) && nodeIsMaster(node) && !nodeFailed(node)) {
      int okslaves = clusterCountNonFailingSlaves(node); 

      /* A master is orphaned if it is serving a non-zero number of
       * slots, have no working slaves, but used to have at least one
       * slave. */
      if (okslaves == 0 && node->numslots > 0 && node->numslaves)
        orphaned_masters++;
      if (okslaves > max_slaves) max_slaves = okslaves;
      if (nodeIsSlave(myself) && myself->slaveof == node)
        this_slaves = okslaves;
    }
    ...
  }
  ...
  if (nodeIsSlave(myself)) {
    ...
    /* If there are orphaned slaves, and we are a slave among the masters
     * with the max number of non-failing slaves, consider migrating to
     * the orphaned masters. Note that it does not make sense to try
     * a migration if there is no master with at least *two* working
     * slaves. */
    if (orphaned_masters && max_slaves >= 2 && this_slaves == max_slaves)
      clusterHandleSlaveMigration(max_slaves);
  }
  ...
}  

轮训字典server.cluster->nodes,只要其中的节点不是当前节点,没有处于REDIS_NODE_NOADDR或者握手状态,就对该node节点做相应的处理:

如果当前节点是从节点,并且node节点是主节点,并且node未被标记为下线,则首先调用函数clusterCountNonFailingSlaves,计算node节点未下线的从节点个数okslaves,如果node主节点的okslaves为0,并且该主节点负责的插槽数不为0,说明该node主节点是孤立主节点,因此增加orphaned_masters的值;如果该node主节点的okslaves大于max_slaves,则将max_slaves改为okslaves,因此,max_slaves记录了所有主节点中,拥有最多未下线从节点的那个主节点的未下线从节点个数;如果当前节点正好是node主节点的从节点之一,则将okslaves记录到this_slaves中,以上都是为后续做从节点迁移做的准备;

轮训完所有节点之后,如果存在孤立主节点,并且max_slaves大于等于2,并且当前节点刚好是那个拥有最多未下线从节点的主节点的众多从节点之一,则调用函数clusterHandleSlaveMigration,满足条件的情况下,进行从节点迁移,也就是将当前从节点置为某孤立主节点的从节点。

clusterHandleSlaveMigration函数的代码如下:

void clusterHandleSlaveMigration(int max_slaves) {
  int j, okslaves = 0;
  clusterNode *mymaster = myself->slaveof, *target = NULL, *candidate = NULL;
  dictIterator *di;
  dictEntry *de;
  /* Step 1: Don't migrate if the cluster state is not ok. */
  if (server.cluster->state != REDIS_CLUSTER_OK) return;
  /* Step 2: Don't migrate if my master will not be left with at least
   *     'migration-barrier' slaves after my migration. */
  if (mymaster == NULL) return;
  for (j = 0; j < mymaster->numslaves; j++)
    if (!nodeFailed(mymaster->slaves[j]) &&
      !nodeTimedOut(mymaster->slaves[j])) okslaves++;
  if (okslaves <= server.cluster_migration_barrier) return;
  /* Step 3: Idenitfy a candidate for migration, and check if among the
   * masters with the greatest number of ok slaves, I'm the one with the
   * smaller node ID.
   *
   * Note that this means that eventually a replica migration will occurr
   * since slaves that are reachable again always have their FAIL flag
   * cleared. At the same time this does not mean that there are no
   * race conditions possible (two slaves migrating at the same time), but
   * this is extremely unlikely to happen, and harmless. */
  candidate = myself;
  di = dictGetSafeIterator(server.cluster->nodes);
  while((de = dictNext(di)) != NULL) {
    clusterNode *node = dictGetVal(de);
    int okslaves;
    /* Only iterate over working masters. */
    if (nodeIsSlave(node) || nodeFailed(node)) continue;
    /* If this master never had slaves so far, don't migrate. We want
     * to migrate to a master that remained orphaned, not masters that
     * were never configured to have slaves. */
    if (node->numslaves == 0) continue;
    okslaves = clusterCountNonFailingSlaves(node);
    if (okslaves == 0 && target == NULL && node->numslots > 0)
      target = node;
    if (okslaves == max_slaves) {
      for (j = 0; j < node->numslaves; j++) {
        if (memcmp(node->slaves[j]->name,
              candidate->name,
              REDIS_CLUSTER_NAMELEN) < 0)
        {
          candidate = node->slaves[j];
        }
      }
    }
  }
  dictReleaseIterator(di);
  /* Step 4: perform the migration if there is a target, and if I'm the
   * candidate. */
  if (target && candidate == myself) {
    redisLog(REDIS_WARNING,"Migrating to orphaned master %.40s",
      target->name);
    clusterSetMaster(target);
  }
} 

如果当前集群状态不是REDIS_CLUSTER_OK,则直接返回;如果当前从节点没有主节点,则直接返回;

接下来计算,当前从节点的主节点,具有未下线从节点的个数okslaves;如果okslaves小于等于迁移阈值server.cluster_migration_barrier,则直接返回;

接下来,开始轮训字典server.cluster->nodes,针对其中的每一个节点node:

如果node节点是从节点,或者处于下线状态,则直接处理下一个节点;如果node节点没有配置从节点,则直接处理下一个节点;

调用clusterCountNonFailingSlaves函数,计算该node节点的未下线主节点数okslaves;如果okslaves为0,并且该node节点的numslots大于0,说明该主节点之前有从节点,但是都下线了,因此找到了一个孤立主节点target;

如果okslaves等于参数max_slaves,说明该node节点就是具有最多未下线从节点的主节点,因此将当前节点的节点ID,与其所有从节点的节点ID进行比较,如果当前节点的名字更大,则将candidate置为具有更小名字的那个从节点;(其实从这里就可以直接退出返回了)

轮训完所有节点后,如果找到了孤立节点,并且当前节点拥有最小的节点ID,则调用clusterSetMaster,将target置为当前节点的主节点,并开始主从复制流程。

三:configEpoch冲突问题

在集群中,负责不同槽位的主节点,具有相同的configEpoch其实是没有问题的,但是有可能因为人为介入的原因或者BUG的问题,导致具有相同configEpoch的主节点都宣称负责相同的槽位,这在分布式系统中是致命的问题;因此,Redis规定集群中的所有节点,必须具有不同的configEpoch。

当某个从节点升级为新主节点时,它会得到一个大于当前所有节点的configEpoch的新configEpoch,所以不会导致具有重复configEpoch的从节点(因为一次选举中,不会有两个从节点同时胜出)。但是在管理员发起的重新分片过程的最后,迁入槽位的节点会自己更新自己的configEpoch,而无需其他节点的同意;或者手动强制故障转移过程,也会导致从节点在无需其他节点同意的情况下更新configEpoch,以上的情况都可能导致出现多个主节点具有相同configEpoch的情况。

因此,就需要一种算法,保证集群中所有节点的configEpoch都不相同。这种算法是这样实现的:当某个主节点收到其他主节点发来的心跳包后,发现包中的configEpoch与自己的configEpoch相同,就会调用clusterHandleConfigEpochCollision函数,解决这种configEpoch冲突的问题。

clusterHandleConfigEpochCollision函数的代码如下:

void clusterHandleConfigEpochCollision(clusterNode *sender) {
  /* Prerequisites: nodes have the same configEpoch and are both masters. */
  if (sender->configEpoch != myself->configEpoch ||
    !nodeIsMaster(sender) || !nodeIsMaster(myself)) return;
  /* Don't act if the colliding node has a smaller Node ID. */
  if (memcmp(sender->name,myself->name,REDIS_CLUSTER_NAMELEN) <= 0) return;
  /* Get the next ID available at the best of this node knowledge. */
  server.cluster->currentEpoch++;
  myself->configEpoch = server.cluster->currentEpoch;
  clusterSaveConfigOrDie(1);
  redisLog(REDIS_VERBOSE,
    "WARNING: configEpoch collision with node %.40s."
    " configEpoch set to %llu",
    sender->name,
    (unsigned long long) myself->configEpoch);
} 

如果发送节点的configEpoch不等于当前节点的configEpoch,或者发送节点不是主节点,或者当前节点不是主节点,则直接返回;

如果相比于当前节点的节点ID,发送节点的节点ID更小,则直接返回;

因此,较小名字的节点能获得更大的configEpoch,接下来首先增加自己的currentEpoch,然后将configEpoch赋值为currentEpoch。

这样,即使有多个节点具有相同的configEpoch,最终,只有具有最大节点ID的节点的configEpoch保持不变,其他节点都会增加自己的configEpoch,而且增加的值会不同,具有最小NODE ID的节点,最终具有最大的configEpoch。

总结

以上就是本文关于Redis源码解析:集群手动故障转移、从节点迁移详解的全部内容,感兴趣的朋友可以参阅:详细分析Redis集群故障、简述Redis和MySQL的区别、Spring AOP实现Redis缓存数据库查询源码等,有不足之处,请留言指出,感谢朋友们对本站的支持!

(0)

相关推荐

  • 在redhat6.4安装redis集群【教程】

    参考: http://redis.io/topics/cluster-tutorial(主要是Creating a Redis Cluster using the create-cluster script部分) https://ruby.taobao.org/ 安装一款不熟悉的软件前先看INSTALL,README,这是习惯,生产上要建立普通用户并调节适当参数,下面是以root身份安装运行. 下载解压并安装redis make test提示需要更高版本的tcl,跳到安装过程可能遇到的问题 wg

  • CentOS 7下安装 redis 3.0.6并配置集群的过程详解

    安装依赖 [root@centos7-1 ~]# yum -y install gcc openssl-devel libyaml-devel libffi-devel readline-devel zlib-devel gdbm-devel ncurses-devel gcc-c++ automake autoconf 安装 redis [root@centos7-1 ~]# wget http://download.redis.io/releases/redis-3.0.6.tar.gz [

  • Redis 集群搭建和简单使用教程

    前言 Redis集群搭建的目的其实也就是集群搭建的目的,所有的集群主要都是为了解决一个问题,横向扩展. 在集群的概念出现之前,我们使用的硬件资源都是纵向扩展的,但是纵向扩展很快就会达到一个极限,单台机器的Cpu的处理速度,内存大小,硬盘大小没办法一直满足需求,而且机器纵向扩展的成本是相当高的.集群的出现就是能够让多台机器像一台机器一样工作,实现了资源的横向扩展. Redis是内存型数据库,当我们要存储的数据达到一定程度时,单台机器的内存满足不了我们的需求,搭建集群则是一种很好的解决方案. 介绍安

  • 简单注解实现集群同步锁(spring+redis+注解)

    互联网面试的时候,是不是面试官常问一个问题如何保证集群环境下数据操作并发问题,常用的synchronized肯定是无法满足了,或许你可以借助for update对数据加锁.本文的最终解决方式你只要在方法上加一个@P4jSyn注解就能保证集群环境下同synchronized的效果,且锁的key可以任意指定.本注解还支持了锁的超时机制. 本文需要对Redis.spring和spring-data-redis有一定的了解.当然你可以借助本文的思路对通过注解对方法返回数据进行缓存,类似com.googl

  • Windows环境部署Redis集群

    一.准备文件 1.下载Redis for windows 的最新版本 下载地址:https://github.com/MSOpenTech/redis/releases 安装到 c:\Redis 目录下(Redis-x64-3.2.100.msi <Windows服务版>) 2.下载 RubyInstaller 下载地址:http://rubyinstaller.org/downloads/ 安装时,勾选:(所使用版本rubyinstaller-2.3.1-x64.exe) Install T

  • Redis源码解析:集群手动故障转移、从节点迁移详解

    一:手动故障转移 Redis集群支持手动故障转移.也就是向从节点发送"CLUSTER  FAILOVER"命令,使其在主节点未下线的情况下,发起故障转移流程,升级为新的主节点,而原来的主节点降级为从节点. 为了不丢失数据,向从节点发送"CLUSTER  FAILOVER"命令后,流程如下: a:从节点收到命令后,向主节点发送CLUSTERMSG_TYPE_MFSTART包:          b:主节点收到该包后,会将其所有客户端置于阻塞状态,也就是在10s的时间内

  • Quartz集群原理以及配置应用的方法详解

    1.Quartz任务调度的基本实现原理 Quartz是OpenSymphony开源组织在任务调度领域的一个开源项目,完全基于Java实现.作为一个优秀的开源调度框架,Quartz具有以下特点: (1)强大的调度功能,例如支持丰富多样的调度方法,可以满足各种常规及特殊需求: (2)灵活的应用方式,例如支持任务和调度的多种组合方式,支持调度数据的多种存储方式: (3)分布式和集群能力,Terracotta收购后在原来功能基础上作了进一步提升.本文将对该部分相加阐述. 1.1 Quartz 核心元素

  • mongodb 集群重构和释放磁盘空间实例详解

    MongoDB集群重构,释放磁盘空间 由于mongodb删除了一部分数据后,不会回收相应的磁盘空间,所以这里通过重建数据目录的方式释放磁盘空间. 一 实验环境 配置了一个副本集,该副本集由以下三个节点组成: 10.192.203.201:27017 PRIMARY 10.192.203.202:27017 SECONDARY 10.192.203.202:10001  ARBITER 二 实验步骤 2.1 模拟环境 use dba; for(var i=0;i<1000000;i++)db.c.

  • php源码之将图片转化为data/base64数据流实例详解

    php源码之将图片转化为data/base64数据流 这里我们分享一个将图片转换为base64编码格式的方法: <?php $img = 'test.jpg'; $base64_img = base64EncodeImage($img); echo '<img src="' . $base64_img . '" />'; /* 作者:http://www.manongjc.com */ function base64EncodeImage ($image_file)

  • 从Linux源码看Socket(TCP)Client端的Connect的示例详解

    前言 笔者一直觉得如果能知道从应用到框架再到操作系统的每一处代码,是一件Exciting的事情. 今天笔者就来从Linux源码的角度看下Client端的Socket在进行Connect的时候到底做了哪些事情.由于篇幅原因,关于Server端的Accept源码讲解留给下一篇博客. (基于Linux 3.10内核) 一个最简单的Connect例子 int clientSocket; if((clientSocket = socket(AF_INET, SOCK_STREAM, 0)) < 0) {

  • 详解Redis 缓存删除机制(源码解析)

    删除的范围 过期的 key 在内存满了的情况下,如果继续执行 set 等命令,且所有 key 都没有过期,那么会按照缓存淘汰策略选中的 key 过期删除 redis 中设置了过期时间的 key 会单独存储一份 typedef struct redisDb { dict *dict; // 所有的键值对 dict *expires; //设置了过期时间的键值对 // ... } redisDb; 设置有效期 Redis 中有 4 个命令可以给 key 设置过期时间,分别是 expire pexpi

  • Redis ziplist 压缩列表的源码解析

    目录 前言 源码解读 ziplist 布局 entry 节点 prelen encoding 编码 总结 前言 相信对使用过 Redis 的人来说,数据类型 List 是不会陌生的吧.大多数人需要实现一个队列时候,首选的就是 List 了.但是其实 Redis 的 List 类型有多种实现方式.这篇文章就是介绍其中一种实现 ziplist - 压缩列表. 源码解读 一如既往,关于 ziplist 的定义和实现还是放在一对文件中,分别是 ziplist.h 和 ziplist.c.在 ziplis

  • JetCache 缓存框架的使用及源码解析(推荐)

    目录 一.简介 为什么使用缓存? 使用场景 使用规范 二.如何使用 引入maven依赖 添加配置 配置说明 注解说明 @EnableCreateCacheAnnotation @EnableMethodCache @CacheInvalidate @CacheUpdate @CacheRefresh @CachePenetrationProtect @CreateCache 三.源码解析 项目的各个子模块 常用注解与变量 缓存API Cache接口 AbstractCache抽象类 Abstra

  • 源码解析gtoken替换jwt实现sso登录

    目录 jwt的问题 jwt的请求流程图 gtoken的优势 注意问题 演示demo 分析源码 刷新token GfToken结构体 思考题 总结 jwt的问题 首先说明一个jwt存在的问题,也就是要替换jwt的原因: jwt无法在服务端主动退出的问题 jwt无法作废已颁布的令牌,只能等到令牌过期问题 jwt携带大量用户扩展信息导致降低传输效率问题 jwt的请求流程图 gtoken的优势 gtoken的请求流程和jwt的基本一致. gtoken的优势就是能帮助我们解决jwt的问题,另外还提供好用的

  • Laravel框架源码解析之入口文件原理分析

    本文实例讲述了Laravel框架源码解析之入口文件原理.分享给大家供大家参考,具体如下: 前言 提升能力的方法并非使用更多工具,而是解刨自己所使用的工具.今天我们从Laravel启动的第一步开始讲起. 入口文件 laravel是单入口框架,所有请求必将经过index.php define('LARAVEL_START', microtime(true)); // 获取启动时间 使用composer是现代PHP的标志 require __DIR__.'/../vendor/autoload.php

随机推荐