如题,redis网络库是基于单进程单线程的机制实现,简单高效。可用直接将它提取出来使用。Redis网络库是一个单线程EPOLL模型的网络库,和Memcached使用的libevent相比,它没有那么庞大,代码一共2000多行,因此比较容易分析。本篇拿出了Redis网络部分的代码,添加了应用层buffer,让它现在变成了一个可以实现tcp通信的程序。
Redis网络库是一个单线程EPOLL模型,也就是说接收连接和处理读写请求包括定时器任务都被这一个线程包揽,真的是又当爹又当妈,但是效率一定比多线程差吗?不见得。
单线程的好处有:
1:避免线程切换带来的上下文切换开销。
2:单线程避免了锁的争用。
3:对于一个内存型数据库,如果不考虑数据持久化,也就是读写物理磁盘,不会有阻塞操作,内存操作是非常快的。
#The network library from redis v4.0.9 -- https://github.com/antirez/redis
#把源码以下文件提取出来即可使用,无需作任何修改。
#/redis-unstable/src/ae.c
#/redis-unstable/src/ae.h
#/redis-unstable/src/ae_epoll.c
#/redis-unstable/src/ae_evport.c
#/redis-unstable/src/ae_kqueue.c
#/redis-unstable/src/ae_select.c
#/redis-unstable/src/anet.c
#/redis-unstable/src/anet.h
#/redis-unstable/src/atomicvar.h
#/redis-unstable/src/config.h
#/redis-unstable/src/fmacros.h
#/redis-unstable/src/zmalloc.c
#/redis-unstable/src/zmalloc.h
根据内存分配器的使用不同,区别为:
完整的工程源码下载(malloc使用原生的libc):https://download.csdn.net/download/libaineu2004/10468733
完整的工程源码下载(malloc使用jemalloc):https://download.csdn.net/download/libaineu2004/10468734
如果不清楚libc和jemalloc的概念,请看http://blog.csdn.net/libaineu2004/article/details/79400357
jemalloc的源码下载和编译:https://blog.csdn.net/libaineu2004/article/details/79402103
CMakeLists.txt(使用libc)
cmake_minimum_required(VERSION 2.8) #The network library from redis v4.0.9 -- https://github.com/antirez/redis #No changes were made. #/redis-unstable/src/ae.c #/redis-unstable/src/ae.h #/redis-unstable/src/ae_epoll.c #/redis-unstable/src/ae_evport.c #/redis-unstable/src/ae_kqueue.c #/redis-unstable/src/ae_select.c #/redis-unstable/src/anet.c #/redis-unstable/src/anet.h #/redis-unstable/src/atomicvar.h #/redis-unstable/src/config.h #/redis-unstable/src/fmacros.h #/redis-unstable/src/zmalloc.c #/redis-unstable/src/zmalloc.h project(myRedisNetDemo) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c99 -g") set(REDIS_SRC ./redis_network/ae.c ./redis_network/anet.c ./redis_network/zmalloc.c) include_directories(./redis_network) #https://github.com/antirez/redis add_executable(${PROJECT_NAME} example_firecat.c buffer.c ${REDIS_SRC}) CMakeLists.txt(使用jemalloc) cmake_minimum_required(VERSION 2.8) #The network library from redis v4.0.9 -- https://github.com/antirez/redis #No changes were made. #/redis-unstable/src/ae.c #/redis-unstable/src/ae.h #/redis-unstable/src/ae_epoll.c #/redis-unstable/src/ae_evport.c #/redis-unstable/src/ae_kqueue.c #/redis-unstable/src/ae_select.c #/redis-unstable/src/anet.c #/redis-unstable/src/anet.h #/redis-unstable/src/atomicvar.h #/redis-unstable/src/config.h #/redis-unstable/src/fmacros.h #/redis-unstable/src/zmalloc.c #/redis-unstable/src/zmalloc.h project(myRedisNetDemo) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c99 -g") set(REDIS_SRC ./redis_network/ae.c ./redis_network/anet.c ./redis_network/zmalloc.c) find_library(JEMALLOC_LIB libjemalloc.so /usr/local/lib) IF (NOT JEMALLOC_LIB) MESSAGE(FATAL_ERROR "libjemalloc not found") ENDIF(NOT JEMALLOC_LIB) SET(JEMALLOC_INCLUDE_DIR /usr/include/jemalloc) SET(JEMALLOC_LIB /usr/local/lib) INCLUDE_DIRECTORIES(./redis_network) #https://github.com/antirez/redis INCLUDE_DIRECTORIES(${JEMALLOC_INCLUDE_DIR}) LINK_DIRECTORIES(${JEMALLOC_LIB}) #ADD_EXECUTABLE必须在TARGET_LINK_LIBRARIES前面,否则会报错 ADD_EXECUTABLE(${PROJECT_NAME} example_firecat.c buffer.c ${REDIS_SRC}) TARGET_LINK_LIBRARIES(${PROJECT_NAME} jemalloc)
buffer.h,关注头文件包含:
#include "zmalloc.h"//libc要用这个 //#include "redis_network/zmalloc.h"//或者libc要用这个 //#include "myjemalloc.h"//jemalloc要用这个 #ifndef MYREDISNET_AEBUFFER_H #define MYREDISNET_AEBUFFER_H /// 《Linux多线程服务端编程:使用muduo C++网络库》陈硕著 7.4章节,P204 /// https://github.com/chenshuo/muduo /// muduo buf:A buffer class modeled after org.jboss.netty.buffer.ChannelBuffer /// /// @code /// +-------------------+------------------+------------------+ /// | prependable bytes | readable bytes | writable bytes | /// | | (CONTENT) | | /// +-------------------+------------------+------------------+ /// | | | | /// 0 <= readerIndex <= writerIndex <= size /// /// @endcode //#include "zmalloc.h"//不能直接包含这个头文件,编译会报错 basic_string.h:2423:7: error: ‘__str’ was not declared in this scope #include "myjemalloc.h"//要用这个 #include <sys/types.h> #define DEFAULT_BUFF_SIZE 1024 typedef struct { unsigned char *buff; size_t size; size_t read_idx; size_t write_idx; } buffer_t; buffer_t *alloc_buffer(); void free_buffer(buffer_t *buffer); void check_buffer_size(buffer_t *buffer, size_t avlid_size); size_t get_readable_size(buffer_t *buffer); size_t get_writeable_size(buffer_t *buffer); #endif //MYREDISNET_AEBUFFER_H
myjemalloc.h -- 本人自定义的头文件
#ifndef __MYJEMALLOC_H #define __MYJEMALLOC_H #include <jemalloc/jemalloc.h> #define zmalloc(size) je_malloc(size) #define zcalloc(count,size) je_calloc(count,size) #define zrealloc(ptr,size) je_realloc(ptr,size) #define zfree(ptr) je_free(ptr) #define zmallocx(s ize,flags) je_mallocx(size,flags) #define zdallocx(ptr,flags) je_dallocx(ptr,flags) #endif
buffer.c
#include "buffer.h" #include <stdio.h> #include <stdlib.h> #include <string.h> #include <assert.h> buffer_t *alloc_buffer() { buffer_t *buffer = zmalloc(sizeof(buffer_t)); if (buffer == NULL) { goto err; } buffer->buff = zmalloc(DEFAULT_BUFF_SIZE); buffer->size = DEFAULT_BUFF_SIZE; buffer->read_idx = 0; buffer->write_idx = 0; return buffer; err: if (buffer) { zfree(buffer->buff); buffer->buff = NULL; zfree(buffer); buffer = NULL; } return NULL; } void free_buffer(buffer_t *buffer) { if (buffer) { zfree(buffer->buff); buffer->buff = NULL; zfree(buffer); buffer = NULL; } } void check_buffer_size(buffer_t *buffer, size_t avlid_size) { if (buffer->read_idx > DEFAULT_BUFF_SIZE) { size_t data_len = get_readable_size(buffer); memmove(buffer->buff, buffer->buff + buffer->read_idx, data_len); buffer->read_idx = 0; buffer->write_idx = data_len; } if (get_writeable_size(buffer) < avlid_size) { size_t new_size = buffer->size + avlid_size; buffer->buff = zrealloc(buffer->buff, new_size); buffer->size = new_size; } } size_t get_readable_size(buffer_t *buffer) { assert(buffer->size >= buffer->write_idx); assert(buffer->read_idx <= buffer->write_idx); return buffer->write_idx - buffer->read_idx; } size_t get_writeable_size(buffer_t *buffer) { assert(buffer->size >= buffer->write_idx); assert(buffer->read_idx <= buffer->write_idx); return buffer->size - buffer->write_idx; }