从这50行缓存实现的代码中,我读出了禅意

      这几周,笔者写了好几篇有关Tdengine开源代码解读的博客,其实按照代码质量来说https://blog.csdn.net/BEYONDMA/article/details/98473143这篇有关定时器的解读是水平最高的,不过这篇似乎没引起什么讨论。究其原因可能还是这个定时器的逻辑理解起来难度比较高,从这篇的唯一评论也能看出来。

    

    难度较高也就限制了嘴炮式程序员的发挥空间,之前在解读有关consumer-producer的文章(https://blog.csdn.net/BEYONDMA/article/details/96578186)后,引发的各方所谓大神的评论,比如以下这种:

    所以能落个清静,笔者也是比较开心。凭我自身的感觉这段时间通过阅读TDengine的代码,尤其是在陶老师亲自的指点下进行解读,感觉C语言的编程的水平提高非常快,在这里也感谢一下陶老可以开源这么优秀的代码,供大家学习。鉴于难度较高的代码接受度不高,所以这次为大家带来一个相对简单一些的缓存实现代码。

    为什么要开源

     前几天看陶老师的朋友圈,赫然写着这么一句话“如果你的产品真的牛,那就一定要开源”如果水平就那样就千万别开源了,因为没开源就可以对外宣称自主可控,不过一旦开源可就不是自己说的算了“,笔者看到后有一种醍醐灌顶的感觉,代码是没有二义性的,开源就意味着坦荡的面对世界,自身不保留秘密,这是开宗立派的气势。

    无论是创立禅宗的达摩,还是儒家圣人孔子、兵家始终孙武,这些人物在布道时的最大特点都是毫无保留。而需要保留的情况往往是自身水平尚不过硬,需要留有一定的神秘感才能生存。

  所以将开源是加速产品发展的手段,如果质量过硬就能迅速积累口碑打开市场,如果质量不行也能马上知道自身的成色,尽快转型。

    TDCache的基本原理

废话不多说了,直接上代码,本次解读的源码位置在https://github.com/taosdata/TDengine/blob/master/src/client/src/tscCache.c,其基本的工作原理如下:

1.缓存初始化(taosOpenConnCache):首先初始化缓存对象SConnCache,再初始化哈希表connHashList,并调用taosTmrReset,重置timer(这也就是咱们上次解读的timer)

2.链接加入缓存(taosAddConnIntoCache):首先通过ip、port、username计算其哈希值(hash),然后将此链接(connInfo)加入connHashList[hash]对应的pNode节点,pNode本身又是一个双链表,也会根据添加时间将哈希值相同的connInfo排序,放入pNode双链表中。注意这里pNode是哈希表connHashList的一个节点,而其自身也是一个链表。

3.将链接由缓存中取出(taosGetConnFromCache):根据ip、port、username计算其哈希值(hash),取出connHashList[hash]对应的pNode节点,再从pNode当中取出ip、port与需求相同的元素。

其工作示意图如下:

    TDCache的代码

   1.taosOpenConnCache

void *taosOpenConnCache(int maxSessions, void (*cleanFp)(void *), void *tmrCtrl, int64_t keepTimer) {
  SConnHash **connHashList;
  mpool_h     connHashMemPool;
  SConnCache *pObj;

  connHashMemPool = taosMemPoolInit(maxSessions, sizeof(SConnHash));//初始化SConnHash
  if (connHashMemPool == 0) return NULL;

  connHashList = calloc(sizeof(SConnHash *), maxSessions);//初始化connHashList 
  if (connHashList == 0) {
    taosMemPoolCleanUp(connHashMemPool);
    return NULL;
  }

  pObj = malloc(sizeof(SConnCache));
  if (pObj == NULL) {
    taosMemPoolCleanUp(connHashMemPool);
    free(connHashList);
    return NULL;
  }
  memset(pObj, 0, sizeof(SConnCache));

  pObj->count = calloc(sizeof(int), maxSessions);
  pObj->total = 0;
  pObj->keepTimer = keepTimer;
  pObj->maxSessions = maxSessions;
  pObj->connHashMemPool = connHashMemPool;
  pObj->connHashList = connHashList;
  pObj->cleanFp = cleanFp;
  pObj->tmrCtrl = tmrCtrl;
  taosTmrReset(taosCleanConnCache, pObj->keepTimer * 2, pObj, pObj->tmrCtrl, &pObj->pTimer);//这是咱们上次解读过的timer,到期进行缓存的清理

  pthread_mutex_init(&pObj->mutex, NULL);

  return pObj;
}

 taosAddConnIntoCache的代码:

void *taosAddConnIntoCache(void *handle, void *data, uint32_t ip, short port, char *user) {
  int         hash;
  SConnHash * pNode;
  SConnCache *pObj;

  uint64_t time = taosGetTimestampMs();

  pObj = (SConnCache *)handle;
  if (pObj == NULL || pObj->maxSessions == 0) return NULL;
  if (data == NULL) {
    tscTrace("data:%p ip:%p:%d not valid, not added in cache", data, ip, port);
    return NULL;
  }

  hash = taosHashConn(pObj, ip, port, user);//通过ip port user计算哈希值
  pNode = (SConnHash *)taosMemPoolMalloc(pObj->connHashMemPool);
  pNode->ip = ip;
  pNode->port = port;
  pNode->data = data;
  pNode->prev = NULL;
  pNode->time = time;

  pthread_mutex_lock(&pObj->mutex);
  //以下是将链接信息加入pNode的链表
  pNode->next = pObj->connHashList[hash];
  if (pObj->connHashList[hash] != NULL) (pObj->connHashList[hash])->prev = pNode;
  pObj->connHashList[hash] = pNode;

  pObj->total++;
  pObj->count[hash]++;
  taosRemoveExpiredNodes(pObj, pNode->next, hash, time);

  pthread_mutex_unlock(&pObj->mutex);

  tscTrace("%p ip:0x%x:%d:%d:%p added, connections in cache:%d", data, ip, port, hash, pNode, pObj->count[hash]);

  return pObj;
}
void *taosGetConnFromCache(void *handle, uint32_t ip, short port, char *user) {
  int         hash;
  SConnHash * pNode;
  SConnCache *pObj;
  void *      pData = NULL;

  pObj = (SConnCache *)handle;
  if (pObj == NULL || pObj->maxSessions == 0) return NULL;

  uint64_t time = taosGetTimestampMs();

  hash = taosHashConn(pObj, ip, port, user);//计算哈希值 
  pthread_mutex_lock(&pObj->mutex);

  pNode = pObj->connHashList[hash];//取出pNode,并找到与ip port 与需求相同的链接
  while (pNode) {
    if (time >= pObj->keepTimer + pNode->time) {
      taosRemoveExpiredNodes(pObj, pNode, hash, time);
      pNode = NULL;
      break;
    }

    if (pNode->ip == ip && pNode->port == port) break;

    pNode = pNode->next;
  }

  if (pNode) {
    taosRemoveExpiredNodes(pObj, pNode->next, hash, time);

    if (pNode->prev) {
      pNode->prev->next = pNode->next;
    } else {
      pObj->connHashList[hash] = pNode->next;
    }

    if (pNode->next) {
      pNode->next->prev = pNode->prev;
    }

    pData = pNode->data;
    taosMemPoolFree(pObj->connHashMemPool, (char *)pNode);
    pObj->total--;
    pObj->count[hash]--;
  }

  pthread_mutex_unlock(&pObj->mutex);

  if (pData) {
    tscTrace("%p ip:0x%x:%d:%d:%p retrieved, connections in cache:%d", pData, ip, port, hash, pNode, pObj->count[hash]);
  }

  return pData;
}

我们看到这次pNode的又是双链表,双链表本身也是一种循环的数据结构,这本身也代表着一种禅意吧。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值