FastDFS不同步怎么破

一、背景说明

FastDFS是一款开源的分布式文件系统,具体介绍就不说了,有兴趣的可以自行百度下。

 

以下是官方的架构图:

 

 

一次完整的写交互过程如下:

1、Client向Tracker查询可用的Storage;

2、Tracker随机返回一个Storage;

3、Client向Storage发起写请求;

 

一次完整的读交互:

1、Client向Tracker查询可用的Storage;

2、Tracker随机返回一个Storage;

3、Client向Storage发起读请求;

 

可以看到每个Storage都是对等的,即每个Storage上存储的文件都是全量的。

 

最近一朋友线上FastDFS服务器老是报文件不存在的错误,版本为5.11:

  •  
[2020-08-12 23:16:37] WARNING - file: storage_service.c, line: 6899, client ip: xx.xx.xxx.xxx, logic file: 06/75/xxxx.jpg not exist

架构如下:

2台Tracker,2台Storage。

 

每台机器上都有上述报错。

 

二、FastDFS同步机制分析

 

我们先分析FastDFS如何实现文件在不同服务器的同步的,FastDFS是以binglog的格式同步各自上传/修改的文件的,具体位置在安装目录的data/sync目录下,文件一般叫binlog.000这样,以下为我开发机的截图:

 

 

具体内容如下:

1589182799 C M00/00/00/rBVrTV65AU-ACKi2AAARqXyG2io334.jpg1589182885 C M00/00/00/rBVrTV65AaWAAYwKAAARqXyG2io765.jpg1589427410 C M00/00/00/rBVrTV68vNKAbceuAAARqXyG2io657.jpg

 

第1列是时间戳,第2列是修改内容,示例中大部分是创建文件,所以是C,其它参考文件 storage/storage_sync.h:

 

#define STORAGE_OP_TYPE_SOURCE_CREATE_FILE  'C'  //upload file#define STORAGE_OP_TYPE_SOURCE_APPEND_FILE  'A'  //append file#define STORAGE_OP_TYPE_SOURCE_DELETE_FILE  'D'  //delete file#define STORAGE_OP_TYPE_SOURCE_UPDATE_FILE  'U'  //for whole file update such as metadata file#define STORAGE_OP_TYPE_SOURCE_MODIFY_FILE  'M'  //for part modify#define STORAGE_OP_TYPE_SOURCE_TRUNCATE_FILE  'T'  //truncate file#define STORAGE_OP_TYPE_SOURCE_CREATE_LINK  'L'  //create symbol link

 

有了binglog只是保证不同服务器可以同步数据了,真正实现还有很多东西要考虑:

1、每次是全量还是增量同步,如果是增量,如何记录最后同步的位置,同步的位置做持久化吗;

2、binlog如何保证可靠性,即FastDFS实现的时候是binlog刷磁盘即fsync后才返回给客户端吗;

 

关于第1点,FastDFS是实现增量同步的,最后位置保存在安装目录的data/sync目录下,扩展名是mark的文件,具体格式是这样的:

172.21.107.236_23000.mark

 

即 IP_端口.mark。

 

如果集群中有两个Storage,172.21.104.36, 172.21.104.35,则在36上有1个mark文件:172.21.104.35_23000.mark,而在35上mark文件也只有1个:

172.21.104.36_23000.mark。

 

mark文件具体内容如下:​​​​​​​

binlog_index=0binlog_offset=3422need_sync_old=1sync_old_done=1until_timestamp=1596511256scan_row_count=118sync_row_count=62

 

关键参数是binlog_offset,即binlog中最后同步成功的偏移量,每同步一个文件后,都会将偏移量更新。

 

那binlog是异步还是同步将binlog同步给其它的Storage呢,答案是异步,具体可以参考函数:storage_sync_thread_entrance,这个函数是线程的入口,FastDFS在启动时会启动这个线程用来同步:

​​​​​​​

int storage_sync_thread_start(const FDFSStorageBrief *pStorage){  int result;  pthread_attr_t pattr;  pthread_t tid;
 //省略非关键代码
  /*  //printf("start storage ip_addr: %s, g_storage_sync_thread_count=%d\n",       pStorage->ip_addr, g_storage_sync_thread_count);  */
  if ((result=pthread_create(&tid, &pattr, storage_sync_thread_entrance, \    (void *)pStorage)) != 0)  {    logError("file: "__FILE__", line: %d, " \      "create thread failed, errno: %d, " \      "error info: %s", \      __LINE__, result, STRERROR(result));
    pthread_attr_destroy(&pattr);    return result;  }

 

在这个线程中,会周期地读取binlog,然后同步给其它的Storage:​​​​​​​

  while (g_continue_flag && (!g_sync_part_time || \      (current_time >= start_time && \      current_time <= end_time)) && \      (pStorage->status == FDFS_STORAGE_STATUS_ACTIVE || \      pStorage->status == FDFS_STORAGE_STATUS_SYNCING))    {      //读取binlog      read_result = storage_binlog_read(&reader, \          &record, &record_len);      //省略非关键代码      }
      if (read_result != 0)      {        //省略非关键代码      }      else if ((sync_result=storage_sync_data(&reader, \        &storage_server, &record)) != 0)      {        //上面就是就binlog同步到其它Storage        logDebug("file: "__FILE__", line: %d, " \          "binlog index: %d, current record " \          "offset: %"PRId64", next " \          "record offset: %"PRId64, \          __LINE__, reader.binlog_index, \          reader.binlog_offset, \          reader.binlog_offset + record_len);        if (rewind_to_prev_rec_end(&reader) != 0)        {          logCrit("file: "__FILE__", line: %d, " \            "rewind_to_prev_rec_end fail, "\            "program exit!", __LINE__);          g_continue_flag = false;        }
        break;      }          if (reader.last_scan_rows != reader.scan_row_count)    {      //定稿mark文件      if (storage_write_to_mark_file(&reader) != 0)      {        logCrit("file: "__FILE__", line: %d, " \          "storage_write_to_mark_file fail, " \          "program exit!", __LINE__);        g_continue_flag = false;        break;      }    }

 

可以看到,这个线程周期性地调用storage_binlog_read 读取binlog,然后调用storage_sync_data同步给其它Storage,然后调用storage_write_to_mark_file 将mark文件写入到磁盘持久化。

 

通过上面的分析,可以判断FastDFS在异步情况下是会丢数据的,因为同步binlog给其它Storage是异步的,所以还没同步之前这台机器挂了并且起不来,数据是会丢失的;

 

 

另外binlog不是每1次都刷磁盘的,有参数设置,单位为秒:

sync_binlog_buff_interval

 

即保证多久将将mark文件刷新到磁盘中,果设置大于0,也是会容易丢失数据的。

 

 

三、解决方案

回到问题本身,为什么出现数据不同步呢,是因为在搭建 FastDFS的时候,运维的同学直接从其它服务器上拷过来的,包括整个data目录,也包括data下面的sync目录,这样就容易出现mark文件的偏移量不准的问题。

 

如何解决呢,手动修改mark文件,将binlog_offset设为0,这样FastDFS就会从头同步文件,碰到已经存在的文件,系统会略过的,这是我开发机上的日志:

[2020-08-11 20:27:36] DEBUG - file: storage_sync.c, line: 143, sync data file, logic file: M00/00/00/rBVrTV8yZl6ATOQyAAAJTMk6Vgo7337.md on dest server xx.xx.xx.xx:23000 already exists, and same as mine, ignore it

 

当然前提是日志级别开到DEBUG级别。

 

PS:

源代码中同步文件成功是没有日志的,写mark文件成功也是没有日志的,为了调试方便,我们都加上相关的调试日志了。

 

保存mark文件加日志可以在函数storage_write_to_mark_file中加入一条info日志。

  if ((result=storage_write_to_fd(pReader->mark_fd, \    get_mark_filename_by_reader, pReader, buff, len)) == 0)  {    pReader->last_scan_rows = pReader->scan_row_count;    pReader->last_sync_rows = pReader->sync_row_count;
    logInfo("file: "__FILE__", line: %d, " \              "write server:%s mark file success, offset:%d", \              __LINE__, pReader->storage_id, pReader->binlog_offset);          }

 

 

往期精彩文章:

redis-port支持前缀迁移

Nginx配置不当险酿S0

分布式文件系统FastDFS

 

 

码字不易,如果觉得这篇文章有帮助,请关注我的个人公众号:

<think>好的,我现在要解决用户在IAR编译器中遇到的错误:“incomplete type is not allowed”。首先,我需要理解这个错误的含义和常见原因。根据我的知识库,这个错误通常发生在代码中使用了未完全定义的类型,比如结构体或联合体声明了但未定义,或者前向声明了某个类型但没有提供完整的定义。 首先,我应该检查用户代码中是否有结构体或联合体的声明但没有定义的情况。比如,如果有一个结构体声明为`struct MyStruct;`,但没有后续的完整定义,那么在需要该结构体大小或成员的地方,编译器就会报这个错误。这时候,需要用户补全结构体的定义,包括成员变量。 其次,检查是否在头文件中只声明了类型而没有包含定义。例如,如果在头文件中使用前向声明,但在使用该类型的源文件中没有包含实际的定义头文件,这也会导致错误。此时,需要确保在使用类型的地方包含了正确的头文件,或者将前向声明替换为完整的定义。 另外,还要考虑typedef的问题。如果用户使用了typedef创建别名,但原始类型未完全定义,同样会引发错误。例如,`typedef struct MyStruct MyType;`之后,如果尝试使用MyType但MyStruct尚未定义,就会出错。需要确保在使用typedef之前,原始类型已经被完整定义。 此外,数组声明中的类型完整也是一个常见原因。比如声明数组时,如果元素类型未完全定义,编译器无法确定数组的大小,导致错误。需要检查数组声明中的类型是否已经完整定义。 可能还有其他情况,比如在C和C++混合编程时,如果没有正确使用extern "C",或者在头文件中遗漏了必要的包含,也可能导致类型完整。需要用户检查这些方面。 接下来,我需要将这些可能的原因整理成清晰的解决骤,并给出具体的代码示例,帮助用户理解如何修改。同时,根据用户提供的引用内容,虽然引用[1]和引用[2]是关于其他错误的,但用户可能希望看到类似的结构,即分点说明解决方案,并引用相关资源。 最后,生成相关问题,帮助用户进一了解可能相关的编译错误,比如类型未定义、前向声明问题等。这样用户可以有更全面的认识,预防其他类似错误的发生。</think>### IAR编译器错误'incomplete type is not allowed'解决方案 该错误表示代码中使用了**未完全定义的类型**,编译器无法确定其内存布局或成员信息。以下是具体原因及解决方法: #### 1. 结构体/联合体未定义完整 若声明了结构体但未定义成员,在实例化时会报错: ```c struct MyStruct; // 前向声明完整 struct MyStruct var; // 错误:使用未完成类型 ``` **解决方案**:补全类型定义 ```c struct MyStruct { int id; char name[20]; }; ``` #### 2. 头文件包含缺失 当跨文件使用类型时,需确保定义可见: ```c // file.h struct Data; // 前向声明 // file.c struct Data { // 实际定义 int value; }; ``` **解决方案**:在使用该类型的文件中包含定义头文件 ```c #include "file.c" // 包含实际定义 ``` #### 3. typedef别名问题 使用typedef时原始类型必须完整: ```c typedef struct Node NodeT; // 前向声明 NodeT* ptr; // 允许指针声明 NodeT instance; // 错误:完整类型 ``` **解决方案**:先完成类型定义再typedef ```c struct Node { int data; struct Node* next; }; typedef struct Node NodeT; ``` #### 4. 数组声明完整 数组元素类型必须完全定义: ```c struct Element; struct Element arr[10]; // 错误:元素类型未定义 ``` **解决方案**: ```c struct Element { int type; float value; }; struct Element arr[10]; // 合法 ``` #### 调试建议 1. 在IAR工程中搜索错误行号定位问题代码 2. 使用Go to Definition功能追踪类型定义 3. 检查所有头文件包含链 4. 确认没有循环依赖的头文件 编译器需要知道类型的完整信息才能: - 计算sizeof大小 - 分配内存空间 - 访问成员变量 - 进行类型对齐 [^1]: 类似类型转换错误可参考浮点转整型的类型适配问题。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值