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

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

从这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);//初始化connHashListif (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) {  inthash;  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) {  inthash;  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->keepTimerpNode->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的又是双链表,双链表本身也是一种循环的数据结构,这本身也代表着一种禅意吧。

源文地址:https://www.guoxiongfei.cn/csdn/7665.html