又要加班,最近加班。在做一个本地和云端数据双向同步的功能。逻辑比较复杂,问题比较多。
一个线程监听目录下的操作,主要监听重命名。另外的一个线程,按时间间隔,对比差异。
初始时,同步过后,两端的文件目录是完全一致的。在此基础xml,分别拉取云端差异,同步到本地,如果与此同时,本地也有同名的冲突,按时间值选一端。首先,先作业云端这端需要同步到本地的,只作业需要云端望本地同步的。如果,本地需要往云端同步。
待把云端同步到本地完成后。进行,本地往云端的同步,一个同步周期期间,可能在本地做删除,重命名和新建文件的操作。也可能新建了一个文件,再重命名多次。
监听重命名,是为了本地同步到云端作业时,不是删除重建,而只是改名字,最好做好这方面的工作。这部分逻辑非常复杂。因为对比本地xml和旧的xml差异时,如果出现父级或父父极等祖先极目录的重命名,那么会需要保存它的重命名影响到哪些子文件或子目录,但是也要考虑目录下面的文件或目录也可能重命名,而且这两个动作先后顺序可能不同。一个同步周期检查期间,同一个文件可能被连续重命名多次。最终是要得到一个谁重命名成谁的列表。比如a到b,b到c,最终应该是拿到a到c,原文件的id要从旧xml中取。
两种有区别:一个新建的文件,重命名多次,这种只需要上传就好了;一个原本存在的文件,重命名多次,这种就要找到原文件重命名。
监听文件重命名是一直在监听。
假如同步周期是10秒钟,在开始作业时,对比本地xml差异的时候,如果是新建的文件,拿到的名字可能是默认名字,也可能是中间某个状态。这个时候要把当前的列表保存一个当前状态,拿到这个节点的数据,作为作业时候的参考。至于后面新监听到的重命名列表,要放到下一轮同步时候作业。这样的话,当真正开始作业的时候,比如新建的文件,中间重命名为1.txt后要上传了。
1重命名为7了,会拿到1的id和父id值,进行作业,但真正要作业时,又发现,7找不到了,因为刚好这期间,本地操作重命名了,那么,就从新的重命名列表中,再去找7最终重命名成谁了。
一个监听线程可能会改变重命名列表,另外一个作业线程也可能会改变它,因为上传或者重命名成功后,要把相关的列表数据清除,所以需要加锁,保证原子操作。但是监听会一直存在,不能等在那里,可能还得需要一个中转的存储列表。
考虑这种全局变量加锁是不是不合适,golang是协程安全的,不要通过共享内存来通信,而要通过通信来共享内存,前者是传统的加锁,后者就是channel。即使我编的这两个函数,监听函数和同步函数,最终是由C++分别启线程调用的,他们之间的通信,用通道应该也是没问题的。因为通信的通道内存是在golang中。
那如果是用通道的话,用缓存的通道,监听的线程往通道不断地放,作业的线程从通道中取。通道是同步的,也就是同一时间只有放或者取,建的通道是双向的,可读可写。
放数据的时机是,不断有重命名操作时,放入,取的时机是,多久后要开始作业了。能不能通过通道来控制监听线程和作业线程的同步。比如,虽然10秒钟后要同步,但因为通道中仍有数据。
其实作业线程是否执行,应该是由,通道中是否存待操作项决定的,如果存在待操作项或者存在重命名项。作业不仅包含本地到云端,也有云端到本地。这些是顺序作业的。而且云端同步到本地后,需要更新xml,再以此为基础更新本地,本地不仅要对比本地旧的xml,还要对比这个更新后的。能否把作业线程中的作业和检查差异分离开,是否耦合的太紧,无法分开。检查差异前需要取一次重命名列表的通道,取了之后,要产生一个新的列表,放新的数据。检查差异仅检查,检查完毕,往通道丢,如果丢进去了以后,还有变化。作业的时候,出现找不到文件之类的,就要从重命名通道再取。
检查是按照周期检查。往通道丢,是有数据,才作业。还有监听线程。这三个如何很好的同步呢?
一个文件被多次重命名,问题是真正上传或者重命名的时候,要找到最新的那个名字。才能作业。对比xml时候可能是一个名字,到云盘作业时已经变成另外一个名字了。
没有单独的作业线程。检查所有变化后,才开始作业。这期间仍然可能有变化。所以,检查变化时需要参考重命名列表,作业时,如果当前找不到这文件,仍然要参考重命名列表,是否是,又被改名了。作业成功后,仍要把重命名列表相关的项清掉。
如果一次同步后,重命名列表中仍有值,要继续下一轮作业。
检查变化,是为了知道哪些新上传,哪些删除,哪些重命名。之后的重命名操作仍然要。移除文件或目录,也要更新这个重命名列表。但是如果移除再撤销呢?这时候不考虑。
逻辑越来越复杂,能覆盖所有可能的情况吗?总觉得,还是没有够严谨。实在是太复杂了。有没有折中的办法。也没有可以参考的。纯想的逻辑,会不会有问题。
能不能在进行本地往云端同步期间,锁定所有文件目录不可操作,同步完成后,再解除锁定。这样保证,一次同步期间,不会有任何操作变化,直到完成后。
网友评论