ApsaraCache 源码 diff 分析

阿里最近开源了新项目,叫 ApsaraCache,号称对 redis 做了一些优化,但开源分步骤(明显是你们没开发完吧)逐渐放出。这也进一步体现了大公司技术栈的混乱,相信在阿里内部这种类 redis 的玩艺儿绝对也不少。不过不扯这个。在 ApsaraCache 开源一期的优化中,主要有两个功能点:

  1. 在 redis 4.0 的基础上增加了 memcached 协议的支持
  2. 对短连接的情况进行了优化

把 ApsaraCache 的代码 down 下来用 Beyond Compare 进行了简单地 diff,发现修改点其实并不多。如图:

除了几个专门处理 memcached 协议的文件之外,其它文件都是小改动,毕竟 memcached 协议支持的命令也不多,所以看起来支持起来也不复杂。

另外就是阿里声称的这个短连接的优化了,刚开始还是想通过 Beyond Compare 来看看他们到底改了哪些文件,不过找起来还是感觉有点麻烦。后来想想自己太蠢,直接去 github 看 commit 就好了:

https://github.com/alibaba/ApsaraCache/commit/8340591bd7fef121d6285efed0007a124ff288d7

改动也不多,其实就是增加了一个函数:

listNode *listAddNodeTailReturnNode(list *list, void *value)
{
    listNode *node;

    if ((node = zmalloc(sizeof(*node))) == NULL)
        return NULL;
    node->value = value;
    if (list->len == 0) {
        list->head = list->tail = node;
        node->prev = node->next = NULL;
    } else {
        node->prev = list->tail;
        node->next = NULL;
        list->tail->next = node;
        list->tail = node;
    }
    list->len++;
    return node; //===== 区别就这一行
}

来取代原本的

list *listAddNodeTail(list *list, void *value)
{
    listNode *node;

    if ((node = zmalloc(sizeof(*node))) == NULL)
        return NULL;
    node->value = value;
    if (list->len == 0) {
        list->head = list->tail = node;
        node->prev = node->next = NULL;
    } else {
        node->prev = list->tail;
        node->next = NULL;
        list->tail->next = node;
        list->tail = node;
    }
    list->len++;
    return list; //===== 区别就这里一行
}

两个函数顾名思义,实际上新函数就是在往 server.clients 这个链表里塞当前的 client 的时候,把位置指针也返回回来,并在当前的 client struct 里增加了新字段,用于存储这个返回的,在 server.clients 中的位置。

https://github.com/alibaba/ApsaraCache/commit/8340591bd7fef121d6285efed0007a124ff288d7#diff-72ff16d0b2fcbffdab8b8bde3ca82590R769

代码如下:

struct client {
....
listNode *client_list_node; /* list node in client list */
....
}

其实逻辑很简单,因为我删除 client 的时候是用 server.clients 链表删除该 client,那我 client struct 里只要存储了他在 clients 链表中的位置那就不用去做这个链表遍历啦。这样在 unlinkClient 的时候,少了一步 listSearchKey(server.clients,c) 的操作。redis 原来的 listSearchKey 也没什么神奇的,就是链表的线性查找。

listNode *listSearchKey(list *list, void *key)
{
    listIter iter;
    listNode *node;

    listRewind(list, &iter);
    while((node = listNext(&iter)) != NULL) {
        if (list->match) {
            if (list->match(node->value, key)) {
                return node;
            }
        } else {
            if (key == node->value) {
                return node;
            }
        }
    }
    return NULL;
}

短连接的时候 server.clients 应该会是比较长的链表(猜测)。

再回想一下当初在渣雷的短暂时光。。这种互相存储指针引用的做法在 c 里还蛮常见的。

之后有时间的话看到官方更新了 ApsaraCache 也会再跟进一下修改内容吧,大概。

Xargin

Xargin

If you don't keep moving, you'll quickly fall behind
Beijing