FastDFS分布式文件系统点滴记录5 -- upload上传机制剖析3

来源:互联网 发布:mac 休眠后断网 编辑:程序博客网 时间:2024/05/21 05:36
有了tracker 分析的基础,我们直接进入storage 的任务处理函数 int storage_deal_task(struct fast_task_info *pTask);
storage_service.c 6473行:

  1. case STORAGE_PROTO_CMD_UPLOAD_FILE:
  2.             result = storage_upload_file(pTask, false);
  3.             break;
如果命令是上传文件,会调用storage_upload_file函数处理,我们进入这个函数。

storage_service.c 3735行,是storage_upload_file 的入口,

storage_service.c 3771行 :
  1. = pTask->data + sizeof(TrackerHeader);
  2.     store_path_index = *p++;
  3.     if (store_path_index < 0 || store_path_index >= g_fdfs_path_count)
  4.     {
  5.         logError("file: "__FILE__", line: %d, " \
  6.             "client ip: %s, store_path_index: %d " \
  7.             "is invalid", __LINE__, \
  8.             pTask->client_ip, store_path_index);
  9.         pClientInfo->total_length = sizeof(TrackerHeader);
  10.         return EINVAL;
  11.     }

  12.     file_bytes = buff2long(p);
  13.     p += FDFS_PROTO_PKG_LEN_SIZE;
  14.     if (file_bytes < 0 || file_bytes != nInPackLen - \
  15.             (+ FDFS_PROTO_PKG_LEN_SIZE + \
  16.              FDFS_FILE_EXT_NAME_MAX_LEN))
  17.     {
  18.         logError("file: "__FILE__", line: %d, " \
  19.             "client ip: %s, pkg length is not correct, " \
  20.             "invalid file bytes: "INT64_PRINTF_FORMAT \
  21.             ", total body length: "INT64_PRINTF_FORMAT, \
  22.             __LINE__, pTask->client_ip, file_bytes, nInPackLen);
  23.         pClientInfo->total_length = sizeof(TrackerHeader);
  24.         return EINVAL;
  25.     }

  26.     memcpy(file_ext_name, p, FDFS_FILE_EXT_NAME_MAX_LEN);
  27.     *(file_ext_name + FDFS_FILE_EXT_NAME_MAX_LEN) = '\0';
  28.     p += FDFS_FILE_EXT_NAME_MAX_LEN;

  29.     pFileContext->calc_crc32 = true;
  30.     pFileContext->calc_file_hash = g_check_file_duplicate;
  31.     pFileContext->extra_info.upload.start_time = time(NULL);
代码分析:
1. 首先解析出store_path_index;
2. 解析出file_bytes;
3. 解析出file_ext_name;
4. 将相关的属性赋值给pFileContext


storage_service.c 3810行 : 
  1.     pFileContext->extra_info.upload.file_type = _FILE_TYPE_REGULAR;
  2.     pFileContext->sync_flag = STORAGE_OP_TYPE_SOURCE_CREATE_FILE;
  3.     pFileContext->timestamp2log = pFileContext->extra_info.upload.start_time;
  4.     pFileContext->op = FDFS_STORAGE_FILE_OP_WRITE;
  5.     if (bAppenderFile)
  6.     {
  7.         pFileContext->extra_info.upload.file_type |= \
  8.                     _FILE_TYPE_APPENDER;
  9.     }
  10.     else
  11.     {
  12.         if (g_if_use_trunk_file && trunk_check_size( \
  13.             TRUNK_CALC_SIZE(file_bytes)))
  14.         {
  15.             pFileContext->extra_info.upload.file_type |= \
  16.                         _FILE_TYPE_TRUNK;
  17.         }
  18.     }
注意,这里把pFileContext->op  置为 FDFS_STORAGE_FILE_OP_WRITE;说明要执行的是写操作。
为了简化分析,我们暂时不考虑trunk 这种方式,以后会单独详细分析。

接着往下执行,storage_service.c 3893行 : 

  1.     return storage_write_to_file(pTask, file_offset, file_bytes, \
  2.             p - pTask->data, dio_write_file, \
  3.             storage_upload_file_done_callback, \
  4.             clean_func, store_path_index);
开始真正的写文件操作了。

我们进入storage_write_to_file,storage_service.c 5567行 : 

  1. static int storage_write_to_file(struct fast_task_info *pTask, \
  2.         const int64_t file_offset, const int64_t upload_bytes, \
  3.         const int buff_offset, TaskDealFunc deal_func, \
  4.         FileDealDoneCallback done_callback, \
  5.         DisconnectCleanFunc clean_func, const int store_path_index)
  6. {
  7.     StorageClientInfo *pClientInfo;
  8.     StorageFileContext *pFileContext;
  9.     int result;

  10.     pClientInfo = (StorageClientInfo *)pTask->arg;
  11.     pFileContext = &(pClientInfo->file_context);

  12.     pClientInfo->deal_func = deal_func;
  13.     pClientInfo->clean_func = clean_func;

  14.     pFileContext->fd = -1;
  15.     pFileContext->buff_offset = buff_offset;
  16.     pFileContext->offset = file_offset;
  17.     pFileContext->start = file_offset;
  18.     pFileContext->end = file_offset + upload_bytes;
  19.     pFileContext->dio_thread_index = storage_dio_get_thread_index( \
  20.         pTask, store_path_index, pFileContext->op);
  21.     pFileContext->done_callback = done_callback;

  22.     if (pFileContext->calc_crc32)
  23.     {
  24.         pFileContext->crc32 = CRC32_XINIT;
  25.     }

  26.     if (pFileContext->calc_file_hash)
  27.     {
  28.         INIT_HASH_CODES4(pFileContext->file_hash_codes)
  29.     }

  30.     if ((result=storage_dio_queue_push(pTask)) != 0)
  31.     {
  32.         pClientInfo->total_length = sizeof(TrackerHeader);
  33.         return result;
  34.     }

  35.     return STORAGE_STATUE_DEAL_FILE;
  36. }
代码分析:
1. 注意参数的回调函数,这里含义是写动作执行完成后,会主动调用;
2.storage_dio_queue_push 函数,把pTask push 到dio 队列中;
3. 前面在分析storage 入口函数的时候,在main函数init 时,已经完成了对dio 线程的初始化动作;
4. 这里是往dio队列中push pTask;
5. storage_dio_queue_push 内部 使用的task_queue_push,同时,pthread_cond_signal 通知队列另端有数据到来。
6.特别需要注意 pClientInfo->deal_func = deal_func; 下面会用到。

既然已经放入队列,那么,另端就应该从队列中取出任务执行。这里,我们看一下dio线程执行函数。

storage_dio.c 646行:
  1. static void *dio_thread_entrance(void* arg) 
  2. {
  3.     int result;
  4.     struct storage_dio_context *pContext; 
  5.     struct fast_task_info *pTask;

  6.     pContext = (struct storage_dio_context *)arg; 

  7.     pthread_mutex_lock(&(pContext->lock));
  8.     while (g_continue_flag)
  9.     {
  10.         if ((result=pthread_cond_wait(&(pContext->cond), \
  11.             &(pContext->lock))) != 0)
  12.         {
  13.         logError("file: "__FILE__", line: %d, " \
  14.             "call pthread_cond_wait fail, " \
  15.             "errno: %d, error info: %s", \
  16.             __LINE__, result, STRERROR(result));
  17.         }

  18.         while ((pTask=task_queue_pop(&(pContext->queue))) != NULL)
  19.         {
  20.             ((StorageClientInfo *)pTask->arg)->deal_func(pTask);
  21.         }
  22.     }
  23.     pthread_mutex_unlock(&(pContext->lock));

  24.     if ((result=pthread_mutex_lock(&g_dio_thread_lock)) != 0)
  25.     {
  26.         logError("file: "__FILE__", line: %d, " \
  27.             "call pthread_mutex_lock fail, " \
  28.             "errno: %d, error info: %s", \
  29.             __LINE__, result, STRERROR(result));
  30.     }
  31.     g_dio_thread_count--;
  32.     if ((result=pthread_mutex_unlock(&g_dio_thread_lock)) != 0)
  33.     {
  34.         logError("file: "__FILE__", line: %d, " \
  35.             "call pthread_mutex_lock fail, " \
  36.             "errno: %d, error info: %s", \
  37.             __LINE__, result, STRERROR(result));
  38.     }

  39.     logDebug("file: "__FILE__", line: %d, " \
  40.         "dio thread exited, thread count: %d", \
  41.         __LINE__, g_dio_thread_count);

  42.     return NULL;
  43. }
代码分析:
1. 当队列有数据,取出数据,执行之;
2. ((StorageClientInfo *)pTask->arg)->deal_func(pTask); 这里通过回调函数完成任务的处理。
结合前面的分析,deal_func 函数指针,其实是函数 dio_write_file;
我们接着分析 dio_write_file,这个函数在 storage_dio.c 421行。

storage_dio.c 454行: 

  1.    if (write(pFileContext->fd, pDataBuff, write_bytes) != write_bytes)
  2.     {
  3.         result = errno != 0 ? errno : EIO;
  4.         logError("file: "__FILE__", line: %d, " \
  5.             "write to file: %s fail, fd=%d, write_bytes=%d, " \
  6.             "errno: %d, error info: %s", \
  7.             __LINE__, pFileContext->filename, \
  8.             pFileContext->fd, write_bytes, \
  9.             result, STRERROR(result));
  10.     }
调用write 写数据了。

接着分析, storage_dio.c 478行:  

  1.     if (pFileContext->calc_crc32)
  2.     {
  3.         pFileContext->crc32 = CRC32_ex(pDataBuff, write_bytes, \
  4.                     pFileContext->crc32);
  5.     }
storage 通过crc32 保证数据的完整性。

接着分析, storage_dio.c 495行:   

  1.     pFileContext->offset += write_bytes;
  2.     if (pFileContext->offset < pFileContext->end)
  3.     {
  4.         pFileContext->buff_offset = 0;
  5.         storage_nio_notify(pTask); //notify nio to deal
  6.     }
  7.     else
  8.     {
  9.         if (pFileContext->calc_crc32)
  10.         {
  11.             pFileContext->crc32 = CRC32_FINAL( \
  12.                         pFileContext->crc32);
  13.         }

  14.         if (pFileContext->calc_file_hash)
  15.         {
  16.             FINISH_HASH_CODES4(pFileContext->file_hash_codes)
  17.         }

  18.         if (pFileContext->extra_info.upload.before_close_callback != NULL)
  19.         {
  20.             result = pFileContext->extra_info.upload. \
  21.                     before_close_callback(pTask);
  22.         }

  23.         /* file write done, close it */
  24.         close(pFileContext->fd);
  25.         pFileContext->fd = -1;

  26.         if (pFileContext->done_callback != NULL)
  27.         {
  28.             pFileContext->done_callback(pTask, result);
  29.         }
  30.     }
代码分析:
1. 如果文件数据长度没有接收完,会storage_nio_notify 继续从nio(网络io) 读取文件内容;
2. 写文件完成后,得到文件 crc32 的值;
3. 执行回调函数,pFileContext->done_callback(pTask, result);
4. 而这个done_callback 实际上是 storage_upload_file_done_callback。


 接着分析storage_upload_file_done_callback, storage_dio.c  1004行:   

  1. static void storage_upload_file_done_callback(struct fast_task_info *pTask, \
  2.             const int err_no)
 
接着分析, 先跳过trunk文件的处理,trunk 后面分析,storage_dio.c 1029行:  

  1.     if (result == 0)
  2.     {
  3.         result = storage_service_upload_file_done(pTask);
  4.         if (result == 0)
  5.         {
  6.         if (pFileContext->create_flag & STORAGE_CREATE_FLAG_FILE)
  7.         {
  8.             result = storage_binlog_write(\
  9.                 pFileContext->timestamp2log, \
  10.                 STORAGE_OP_TYPE_SOURCE_CREATE_FILE, \
  11.                 pFileContext->fname2log);
  12.         }
  13.         }
  14.     }
代码分析:
1. 调用storage_service_upload_file_done,完成接收文件后的操作。目前暂时先不展开分析,因为这里涉及到slave、link、fastdht 等机制,后面专门详细介绍。
2. 文件上传结束后,会调用 storage_binlog_write 写入binlog。

接着往下分析, storage_dio.c  1085行:  

  1.     pHeader = (TrackerHeader *)pTask->data;
  2.     pHeader->status = result;
  3.     pHeader->cmd = STORAGE_PROTO_CMD_RESP;
  4.     long2buff(pClientInfo->total_length - sizeof(TrackerHeader), \
  5.             pHeader->pkg_len);

  6.     storage_nio_notify(pTask);
构造应答的数据包,storage_nio_notify发送至客户端。

至此,我们简要分析了storage 接收文件的处理流程。大概的调用脉络上还是清晰的。像slave、trunk、link、fastdht 等机制,比较复杂,后面慢慢分析。本章节主要解释的就是storage对文件上传的大概处理流程。

欢迎感兴趣的朋友一起交流研究,提出意见。
FastDFS技术交流群:164684842
0 0
原创粉丝点击