// 自动分割日志文件 wbt_status wbt_log_rotate() { // 重命名旧的日志文件 char log_file[256]; snprintf( log_file, sizeof(log_file), "%.*s/bmq.log", wbt_conf.logs.len, wbt_conf.logs.str ); char new_log_file[256]; snprintf( new_log_file, sizeof(new_log_file), "%.*s/bmq_%lu.log", wbt_conf.logs.len, wbt_conf.logs.str, wbt_cur_mtime ); #ifdef WIN32 if(wbt_close_file(wbt_log_file_fd) < 0) { return WBT_ERROR; } wbt_log_file_fd = 0; if(MoveFileEx(log_file, new_log_file, MOVEFILE_REPLACE_EXISTING | MOVEFILE_WRITE_THROUGH) == 0) { // 尝试重新打开日志文件 wbt_log_file_fd = wbt_open_logfile(log_file); if( (int)wbt_log_file_fd <=0 ) { wbt_log_file_fd = 0; wbt_log_debug("can't open log file, errno: %d\n", wbt_errno); } return WBT_ERROR; } #else if( rename(log_file, new_log_file) < 0 ) { // Bugfix: 如果 rename 执行失败,继续调用 wbt_lod_add 会导致死循环 wbt_log_debug("Could not rename log file. " "rename: %d\n", wbt_errno); return WBT_ERROR; } #endif // 创建新的日志文件 wbt_log_file_fd = wbt_open_logfile(log_file); if( (int)wbt_log_file_fd <=0 ) { wbt_log_file_fd = 0; wbt_log_debug("can't open log file, errno: %d\n", wbt_errno); return WBT_ERROR; } // 可选:压缩旧的日志文件 // 可选:清理旧的日志文件 return WBT_OK; }
void wbt_rbtree_print(wbt_rbtree_node_t *node) { if(node != wbt_rbtree_node_nil) { wbt_rbtree_print(node->left); wbt_log_debug("%.*s", node->key.len, (char *)node->key.ptr); wbt_rbtree_print(node->right); } }
wbt_status wbt_log_init() { char log_file[256]; snprintf( log_file, sizeof(log_file), "%.*s/bmq.log", wbt_conf.logs.len, wbt_conf.logs.str ); /* O_CLOEXEC 需要 Linux 内核版本大于等于 2.6.23 */ /* TODO 每个 worker 进程需要单独的日志文件,否则并发写会丢失一部分数据 */ wbt_log_file_fd = wbt_open_logfile(log_file); if( (int)wbt_log_file_fd <=0 ) { wbt_log_debug("can't open log file, errno: %d\n", wbt_errno); return WBT_ERROR; } // 获取日志文件大小 wbt_log_file_size = wbt_get_file_size( wbt_log_file_fd ); if( wbt_log_file_size < 0 ) { wbt_log_debug("can't get log file size, errno: %d\n", wbt_errno); return WBT_ERROR; } return WBT_OK; }
void wbt_file_cleanup_recursive(wbt_rb_node_t *node) { /* 从叶节点递归处理至根节点 */ if(node) { wbt_file_cleanup_recursive(node->left); wbt_file_cleanup_recursive(node->right); wbt_file_t * tmp_file = (wbt_file_t *)node->value.str; if( tmp_file->refer == 0 && wbt_cur_mtime - tmp_file->last_use_mtime > 10000 ) { wbt_log_debug("closed fd:%d %.*s\n", tmp_file->fd, node->key.len, node->key.str.s); wbt_close_file(tmp_file->fd); wbt_free(tmp_file->ptr); wbt_free(tmp_file->gzip_ptr); wbt_rb_delete(&wbt_file_rbtree, node); } } }
// 在红黑树 rbt 中删除结点 node void wbt_rbtree_delete(wbt_rbtree_t * rbt, wbt_rbtree_node_t * node) { wbt_rbtree_node_t * y; // 将要被删除的结点 wbt_rbtree_node_t * x; // 将要被删除的结点的唯一儿子 if( node->left == wbt_rbtree_node_nil || node->right == wbt_rbtree_node_nil ) { // 如果 node 有一个子树为空的话,那么将直接删除 node,即 y 指向 node y = node; } else { // 如果 node 的左右子树皆不为空的话,则寻找 node 的中序后继 y, // 用其值代替 node 的值,然后将 y 删除 ( 注意: y肯定是没有左子树的 ) y = wbt_rbtree_successor(rbt, node); } if( y->left != wbt_rbtree_node_nil ) { // 如果y的左子树不为空,则 x 指向 y 的左子树 x = y->left; } else { x = y->right; } // 将原来 y 的父母设为 x 的父母,y 即将被删除 x->parent = y->parent; if( y->parent == wbt_rbtree_node_nil ) { rbt->root=x; } else { if( y == y->parent->left ) { y->parent->left = x; } else { y->parent->right = x; } } if( y != node ) { // 如果被删除的结点 y 不是原来将要删除的结点 node, // 即只是用 y 的值来代替 node 的值,然后变相删除 y 以达到删除 node 的效果 wbt_free( &node->value ); node->value = y->value; wbt_free( &node->key ); node->key = y->key; y->key.ptr = NULL; y->key.len = 0; } if( y->color == WBT_RBT_COLOR_BLACK ) { // 如果被删除的结点 y 的颜色为黑色,那么可能会导致树违背性质4,导致某条路径上少了一个黑色 wbt_rbtree_delete_fixup(rbt, x); } /* 删除 y */ wbt_mem_t tmp; tmp.ptr = y; tmp.len = sizeof(wbt_rbtree_node_t); wbt_free( &y->key ); wbt_free( &tmp ); rbt->size --; wbt_rbtree_print(rbt->root); wbt_log_debug("rbtree delete"); }
wbt_rbtree_node_t * wbt_rbtree_insert(wbt_rbtree_t *rbt, wbt_str_t *key) { wbt_rbtree_node_t *tmp_node, *tail_node; wbt_mem_t buf; wbt_str_t tmp_str; int ret; /* 创建一个新的节点 */ wbt_malloc(&buf, sizeof(wbt_rbtree_node_t)); wbt_memset(&buf, 0); tmp_node = (wbt_rbtree_node_t *)buf.ptr; wbt_malloc(&tmp_node->key, key->len+1); wbt_memcpy(&tmp_node->key, (wbt_mem_t *)key, key->len); *((char *)tmp_node->key.ptr+key->len) = '\0'; tmp_node->left = tmp_node->right = wbt_rbtree_node_nil; tmp_node->parent = wbt_rbtree_node_nil; tmp_node->color = WBT_RBT_COLOR_RED; if( rbt->root == wbt_rbtree_node_nil ) { rbt->root = tmp_node; } else { tail_node = rbt->root; while(1) { tmp_str.len = tail_node->key.len-1; tmp_str.str = tail_node->key.ptr; ret = wbt_strcmp2(key, &tmp_str); if( ret == 0 ) { /* 键值已经存在 */ return NULL; } else if( ret > 0 ) { if( tail_node->right != wbt_rbtree_node_nil ) { tail_node = tail_node->right; } else { /* 找到了插入位置 */ tail_node->right = tmp_node; tmp_node->parent = tail_node; break; } } else { if( tail_node->left != wbt_rbtree_node_nil ) { tail_node = tail_node->left; } else { /* 找到了插入位置 */ tail_node->left = tmp_node; tmp_node->parent = tail_node; break; } } } } /* 插入节点后需要调整 */ wbt_rbtree_insert_fixup( rbt, tmp_node ); rbt->size ++; wbt_log_debug("rbtree insert"); return tmp_node; }
wbt_file_t * wbt_file_open( wbt_str_t * file_path ) { //wbt_log_debug("try to open: %.*s\n", file_path->len, file_path->str); /* TODO 不存在的文件也可以考虑放入文件树内 * 如果发生大量的 404 请求,可以提高性能 */ if( file_path->len >= 512 ) { /* 路径过长则拒绝打开 */ /* 由于接收数据长度存在限制,这里无需担心溢出 */ wbt_log_debug("file path too long\n"); tmp.fd = (wbt_fd_t)-3; return &tmp; } wbt_rb_node_t *file = wbt_rb_get(&wbt_file_rbtree, file_path); if( file == NULL ) { #ifndef WIN32 struct stat statbuff; if(stat(file_path->str, &statbuff) < 0){ /* 文件访问失败 */ tmp.size = 0; if( errno == ENOENT ) { tmp.fd = -1; /* 文件不存在 */ } else { tmp.fd = -2; /* 权限不足或者其他错误 */ } //wbt_log_debug("stat: %s\n", strerror(errno)); }else{ if( S_ISDIR(statbuff.st_mode) ) { /* 尝试打开的是目录 */ tmp.size = 0; tmp.fd = -2; } else { /* 尝试打开的是文件 */ tmp.size = statbuff.st_size; tmp.fd = open(file_path->str, O_RDONLY); tmp.last_modified = statbuff.st_mtime; if( tmp.fd > 0 ) { file = wbt_rb_insert(&wbt_file_rbtree, file_path); file->value.str = wbt_calloc(sizeof(wbt_file_t)); if( file->value.str == NULL ) { // TODO 如果这里失败了,该文件将永远不会被关闭 wbt_rb_delete(&wbt_file_rbtree, file); } else { wbt_file_t * tmp_file = (wbt_file_t *)file->value.str; tmp_file->fd = tmp.fd; tmp_file->refer = 1; tmp_file->size = tmp.size; tmp_file->last_modified = tmp.last_modified; tmp_file->mime_type = wbt_file_mime_type(file_path); wbt_log_debug("open file: %d " JL_SIZE_T_SPECIFIER "\n", tmp_file->fd, tmp_file->size); return tmp_file; } } else { tmp.size = 0; if( errno == EACCES ) { tmp.fd = -2; } else { tmp.fd = -1; } } } } #else tmp.fd = wbt_open_file(file_path->str); if (tmp.fd == INVALID_HANDLE_VALUE) { tmp.fd = (wbt_fd_t)-1; } else { file = wbt_rb_insert(&wbt_file_rbtree, file_path); file->value.str = wbt_calloc(sizeof(wbt_file_t)); if (file->value.str == NULL) { // TODO 如果这里失败了,该文件将永远不会被关闭 wbt_rb_delete(&wbt_file_rbtree, file); } else { wbt_file_t * tmp_file = (wbt_file_t *)file->value.str; tmp_file->fd = tmp.fd; tmp_file->refer = 1; tmp_file->size = wbt_get_file_size(tmp.fd); tmp_file->last_modified = wbt_get_file_last_write_time(tmp.fd); tmp_file->mime_type = wbt_file_mime_type(file_path); wbt_log_debug( "open file: %d " JL_SIZE_T_SPECIFIER "\n", tmp_file->fd, tmp_file->size ); return tmp_file; } } #endif } else { wbt_file_t * tmp_file = (wbt_file_t *)file->value.str; tmp_file->refer ++; return tmp_file; } return &tmp; }
wbt_status wbt_mq_persist_recovery(wbt_timer_t *timer) { wbt_mq_persist_aof_lock(); wbt_msg_block_t *block; wbt_msg_t *msg; uint32_t crc32; ssize_t nread; char *data; block = wbt_malloc(sizeof(wbt_msg_block_t)); if(!block) { goto error; } int wait_time = 50; int n = 1000; while( !timer || n ) { if(wbt_is_oom()) { // 内存不足,等待一会儿再尝试恢复 wait_time = 1000; n = 0; break; } nread = wbt_read_file(wbt_persist_aof_fd, block, sizeof(wbt_msg_block_t), wbt_mq_persist_recovery_offset); if( nread == 0 ) { break; } else if( nread != sizeof(wbt_msg_block_t) ) { goto error; } else { wbt_mq_persist_recovery_offset += sizeof(wbt_msg_block_t); } if( wbt_conf.aof_crc ) { if (wbt_read_file(wbt_persist_aof_fd, &crc32, sizeof(uint32_t), wbt_mq_persist_recovery_offset) != sizeof(uint32_t)) { goto error; } else { wbt_mq_persist_recovery_offset += sizeof(uint32_t); } if( crc32 != wbt_crc32( (unsigned char *)block, sizeof(wbt_msg_block_t) ) ) { // 校验失败 goto error; } } /* 注意,这里可能出现 malloc(0) 的情况 * 目前看来,已知的 malloc 实现都会在 mallc(0) 时返回一个可用指针而不是 NULL * 但是,C 标准规定 malloc(0) 可以返回 NULL。如果发生这种情况,可以考虑故意多 * 申请一个字节的空间。 */ // data = wbt_malloc(block->data_len+1); data = wbt_malloc(block->data_len); if(!data) { goto error; } if (wbt_read_file(wbt_persist_aof_fd, data, block->data_len, wbt_mq_persist_recovery_offset) != block->data_len) { wbt_free( data ); goto error; } else { wbt_mq_persist_recovery_offset += block->data_len; } if( wbt_conf.aof_crc ) { if (wbt_read_file(wbt_persist_aof_fd, &crc32, sizeof(uint32_t), wbt_mq_persist_recovery_offset) != sizeof(uint32_t)) { wbt_free( data ); goto error; } else { wbt_mq_persist_recovery_offset += sizeof(uint32_t); } if( crc32 != wbt_crc32( (unsigned char *)data, block->data_len ) ) { // 校验失败 wbt_free( data ); goto error; } } wbt_log_debug( "msg %lld recovered\n", block->msg_id ); // 由于内存和文件中保存的结构并不一致,这里我们不得不进行内存拷贝 // TODO 修改消息相关的内存操作,尝试直接使用读入的内存 // TODO 使用 mdb 文件加速载入 // 已过期的消息应当被忽略 if( block->type == MSG_ACK ) { // 未过期但是已经收到 ACK 的负载均衡消息可能会在 fast_boot 期间被重复处理 wbt_msg_t *msg = wbt_mq_msg_get(block->consumer_id); if(msg) { wbt_mq_msg_event_expire( &msg->timer ); } wbt_free(data); } else if( block->expire > wbt_cur_mtime ) { msg = wbt_mq_msg_create(block->msg_id); if( msg == NULL ) { // * ID 冲突,忽略该条消息,并将该消息从 aof 文件中移除 // * 注意,只能通过重写 aof 文件来完成移除操作,如果在重写操作完 // 成之前程序再次重启,aof 文件中将存在一部分 msg_id 冲突的消息 // * 不使用 fast_boot 可以避免该问题 wbt_free(data); } else { wbt_memcpy(msg, block, sizeof(wbt_msg_block_t)); msg->data = data; wbt_mq_msg_delivery(msg); } } else { wbt_free(data); } n --; } wbt_log_add( "%d msg recovered\n", 1000-n ); //success: if(timer && ( n == 0 ) && !wbt_wating_to_exit) { /* 重新注册定时事件 */ timer->timeout += wait_time; if(wbt_timer_mod(timer) != WBT_OK) { goto error; } } else { wbt_free(timer); wbt_mq_persist_aof_unlock(); } wbt_free(block); return WBT_OK; error: wbt_free( block ); wbt_free( timer ); wbt_mq_persist_aof_unlock(); return WBT_ERROR; }