導讀:隨著5G的普及,物聯(lián)網(wǎng)安全顯得特別重要,himqtt是首款完整源碼的高性能MQTT物聯(lián)網(wǎng)防火墻 - MQTT Application FireWall,C語言編寫,采用epoll模式支持IoT數(shù)十萬的高并發(fā)連接,并且兼容ModSecurity部分規(guī)則。 代碼非常優(yōu)秀,非常值得收藏和學習,今天筆者就從結(jié)合himqtt的源碼來進行ringbuffer數(shù)據(jù)結(jié)構(gòu)分析,主要特點是速度快。
隨著5G的普及,物聯(lián)網(wǎng)安全顯得特別重要,himqtt是首款完整源碼的高性能MQTT物聯(lián)網(wǎng)防火墻 - MQTT Application FireWall,C語言編寫,采用epoll模式支持IoT數(shù)十萬的高并發(fā)連接,并且兼容ModSecurity部分規(guī)則。 代碼非常優(yōu)秀,非常值得收藏和學習,今天筆者就從結(jié)合himqtt的源碼來進行ringbuffer數(shù)據(jù)結(jié)構(gòu)分析,主要特點是速度快。
ringBuffer 稱作環(huán)形緩沖,也有叫 circleBuffer 的,Linux內(nèi)核也大量使用了這種數(shù)據(jù)結(jié)構(gòu)。就是取內(nèi)存中一塊連續(xù)的區(qū)域用作環(huán)形緩沖區(qū)的數(shù)據(jù)存儲區(qū)。這塊連續(xù)的存儲會被反復使用,向 ringBuffer 寫入數(shù)據(jù)總是從寫指針的位置開始,如寫到實際存儲區(qū)的末尾還沒有寫完,則將剩余的數(shù)據(jù)從存儲區(qū)的頭開始寫;從該 ringBuffer 讀出數(shù)據(jù)也是從讀指針的位置開始,如讀到實際存儲區(qū)的末尾還沒有讀完,則從存儲區(qū)的頭開始讀剩下的數(shù)據(jù)。
1、Ring Buffer 比鏈表要快,因為它是數(shù)組,而且有一個容易預測的訪問模式。這很不錯,對 CPU 高速緩存友好 (CPU-cache-friendly)-數(shù)據(jù)可以在硬件層面預加載到高速緩存,因此 CPU 不需要經(jīng)?;氐街鲀?nèi)存 RAM 里去尋找 Ring Buffer 的下一條數(shù)據(jù)。
2、Ring Buffer 是一個數(shù)組,你可以預先分配內(nèi)存,并保持數(shù)組元素永遠有效。這意味著內(nèi)存垃圾收集(GC)在這種情況下幾乎什么也不用做。此外,也不像鏈表那樣每增加一條數(shù)據(jù)都要創(chuàng)建對象-當這些數(shù)據(jù)從鏈表里刪除時,這些對象都要被清理掉。
總之就是內(nèi)存處理速度特別快,特別適合高并發(fā)、大量的請求。
首先在github上下載himqtt的源碼:https://github.com/qq4108863/himqtt
找到src目錄下的ringbuffer.c和ringbuffer.h文件。
1、ringBuffer 的結(jié)構(gòu)體
typedef struct bufent {
char *data;
char *ptr;
size_t left;
struct bufent *next;
} bufent;
typedef struct ringbuffer {
bufent *slots;
bufent *head; // reads from the head
bufent *tail; // writes to the tail
char *buf;
int used;
int num_slots;
int data_len;
size_t bytes_written;
} ringbuffer;
2、創(chuàng)建 ringBuffer 函數(shù)
void
ringbuffer_init(ringbuffer *rb, int num_slots, int data_len)
{
rb->num_slots = num_slots ?: DEF_RING_SLOTS;
rb->data_len = data_len ?: DEF_RING_DATA_LEN;
rb->slots = malloc(rb->num_slots * sizeof(rb->slots[0]));
AN(rb->slots);
rb->head = &rb->slots[0];
rb->tail = &rb->slots[0];
int x;
for (x=0; x < rb->num_slots; x++) {
rb->slots[x].next = &(rb->slots[(x + 1) % rb->num_slots]);
rb->slots[x].data = malloc(rb->data_len);
AN(rb->slots[x].data);
}
rb->used = 0;
rb->bytes_written = 0;
}
3、清空 ringBuffer 函數(shù)
void
ringbuffer_cleanup(ringbuffer *rb)
{
int x;
for (x=0; x < rb->num_slots; x++) {
free(rb->slots[x].data);
}
free(rb->slots);
}
4、讀數(shù)據(jù)函數(shù)
char *
ringbuffer_read_next(ringbuffer *rb, int * length)
{
assert(rb->used);
*length = rb->head->left;
return rb->head->ptr;
}
5、寫數(shù)據(jù)函數(shù)
void
ringbuffer_write_append(ringbuffer *rb, int length)
{
assert(rb->used < rb->num_slots);
rb->used++;
rb->tail->ptr = rb->tail->data;
rb->tail->left = length;
rb->tail = rb->tail->next;
}
如果僅僅有一個讀用戶和一個寫用戶,那么不需要添加互斥保護機制就可以保證數(shù)據(jù)的正確性。如果有多個讀寫用戶訪問環(huán)形緩沖區(qū),那么必須添加互斥保護機制來確保多個用戶互斥訪問環(huán)形緩沖區(qū)。
Ringbuffer特別適合一個讀,一個寫的高速場景。5G 時代就是大量的設(shè)備會聯(lián)網(wǎng),并且持續(xù)通信,對himqtt這類物聯(lián)網(wǎng)防火墻就提出了很高的要求,所以算法和數(shù)據(jù)結(jié)構(gòu)設(shè)計就非常重要。