天下之事,必先处之难,而后易之。

互联网解决方案-文件存储方案:seafile真实案例

目录

seafile可靠性保证

事件驱动

seafile.log

events.log

事件驱动好处

本地联思文件同步云联思真实案例

本地联思文件同步云联思架构

云联思客户端检查文件API

本地联思访问客户端封装

本地联思队列消费检查

实践过程中的弯路

文件目录处理 move = copy & delete

seafile文件进程占用问题

联思文件生产者-消费者

生产者

消费者


Seafile 是一款开源的企业云盘,注重可靠性和性能,支持全平台客户端。Seafile 内置协同文档 SeaDoc ,让协作撰写、管理和发布文档更便捷。它支持私有化部署,能够满足诸多文件协同的应用场景。


​官网:https://www.seafile.com/home/
国内:https://bbs.seafile.com/
国外:http://forum.seafile.com/

seafile可靠性保证

事件驱动

通过分析seafile的日志发现它是通过事件驱动的。可以通过目录和日志去查看:

seafile.log

[08/05/24 09:36:32] Repo 'mesDir' sync state transition from 'committing' to 'synchronized'.
[08/05/24 09:37:03] Repo 'mesDir' sync state transition from 'initializing' to 'downloading'.
[08/05/24 09:37:03] Transfer repo '9319d653': ('normal', 'init') --> ('normal', 'check')
[08/05/24 09:37:03] Transfer repo '9319d653': ('normal', 'check') --> ('normal', 'commit')
[08/05/24 09:37:03] Transfer repo '9319d653': ('normal', 'commit') --> ('normal', 'fs')
[08/05/24 09:37:03] Transfer repo '9319d653': ('normal', 'fs') --> ('normal', 'data')
[08/05/24 09:37:03] Transfer repo '9319d653': ('normal', 'data') --> ('finished', 'finished')
[08/05/24 09:37:03] Repo 'mesDir' sync state transition from 'downloading' to 'synchronized'.
[08/05/24 09:37:05] Repo 'mesDir' sync state transition from 'synchronized' to 'committing'.
[08/05/24 09:37:05] All events are processed for repo 9319d653-ff60-4cf3-8f59-906697cf0c2d.
[08/05/24 09:37:05] Repo 'mesDir' sync state transition from 'committing' to 'synchronized'.
[08/05/24 09:37:31] Repo 'mesDir' sync state transition from 'synchronized' to 'committing'.
[08/05/24 09:37:31] All events are processed for repo 9319d653-ff60-4cf3-8f59-906697cf0c2d.
[08/05/24 09:37:31] Repo 'mesDir' sync state transition from 'committing' to 'synchronized'.
[08/05/24 09:38:02] [08/05/24 09:38:02] Repo 'mesDir' sync state transition from 'initializing' to 'downloading'.
Transfer repo '9319d653': ('normal', 'init') --> ('normal', 'check')
[08/05/24 09:38:02] Transfer repo '9319d653': ('normal', 'check') --> ('normal', 'commit')
[08/05/24 09:38:02] Transfer repo '9319d653': ('normal', 'commit') --> ('normal', 'fs')
[08/05/24 09:38:02] Transfer repo '9319d653': ('normal', 'fs') --> ('normal', 'data')
[08/05/24 09:38:04] Transfer repo '9319d653': ('normal', 'data') --> ('finished', 'finished')
[08/05/24 09:38:04] Repo 'mesDir' sync state transition from 'downloading' to 'synchronized'.
[08/05/24 09:38:06] Repo 'mesDir' sync state transition from 'synchronized' to 'committing'.
[08/05/24 09:38:06] All events are processed for repo 9319d653-ff60-4cf3-8f59-906697cf0c2d.

events.log

[08/05/24 09:33:05] Starting record seafile events.
[08/05/24 09:42:32] mesDir 9319d653-ff60-4cf3-8f59-906697cf0c2d 98ee680edca112626d1a4db7e961eef5a08457f5
[event 1] delete, attachment/订单Json/2024/8/2024-08-05/e669a2ec-5c06-4f44-a789-ba00f618822e/YYCYJJ240803001YG-13-03.json 
[event 2] delete, attachment/IMOS炸单DWG文件/2024/8/2024-08-05/40a8bc7e-3a5b-48c2-b64c-247c059f9930/YYCYJJ240803001YG-13-03.DWG 
[event 3] delete, attachment/IMOS炸单DWG文件/2024/8/2024-08-05/b5ef8d18-4660-49b9-8907-7235682b5ea0/AJ0SDS022407008YG-10-02.DWG 
[event 4] delete, mp4/AJ0SCP012407006YG-08-03/AJ0SCP012407006YG-08-031001.mpr 
[event 5] delete, mp4/AJ0SCP012407006YG-08-04/AJ0SCP012407006YG-08-041001.mpr 
[event 6] delete, mp4/AJ0SCP012407006YG-08-03/AJ0SCP012407006YG-08-031002.mpr 
[event 7] delete, mp4/AJ0SCP012407006YG-08-04/AJ0SCP012407006YG-08-041002.mpr 
[event 8] delete, mp4/AJ0SCP012407006YG-08-03/AJ0SCP012407006YG-08-031003.mpr 
[event 9] delete, mp4/AJ0SCP012407006YG-08-04/AJ0SCP012407006YG-08-041003.mpr 
[event 10] delete, mp4/AJ0SCP012407006YG-08-03/AJ0SCP012407006YG-08-031004.mpr 
[event 11] delete, mp4/AJ0SCP012407006YG-08-04/AJ0SCP012407006YG-08-041004.mpr 
[event 12] delete, mp4/AJ0SCP012407006YG-08-04/AJ0SCP012407006YG-08-041005.mpr 
[event 13] delete, mp4/AJ0SCP012407006YG-08-03/AJ0SCP012407006YG-08-031005.mpr 
[event 14] delete, mp4/AJ0SCP012407006YG-08-04/AJ0SCP012407006YG-08-041006.mpr 
[event 15] delete, mp4/AJ0SCP012407006YG-08-03/AJ0SCP012407006YG-08-031006.mpr 
[event 16] delete, mp4/AJ0SCP012407006YG-08-04/AJ0SCP012407006YG-08-041007.mpr 
[event 17] delete, mp4/AJ0SCP012407006YG-08-03/AJ0SCP012407006YG-08-031007.mpr 
[event 18] delete, mp4/AJ0SCP012407006YG-08-03/AJ0SCP012407006YG-08-031008.mpr 
[event 19] delete, mp4/AJ0SCP012407006YG-08-04/AJ0SCP012407006YG-08-041010.mpr 
[event 20] delete, mp4/AJ0SCP012407006YG-08-03/AJ0SCP012407006YG-08-031009.mpr 
[event 21] delete, mp4/AJ0SCP012407006YG-08-04/AJ0SCP012407006YG-08-041011.mpr 
[event 22] delete, mp4/AJ0SCP012407006YG-08-03/AJ0SCP012407006YG-08-031010.mpr 
[event 23] delete, mp4/AJ0SCP012407006YG-08-04/AJ0SCP012407006YG-08-041012.mpr 
[event 24] delete, mp4/AJ0SCP012407006YG-08-03/AJ0SCP012407006YG-08-031011.mpr 

事件驱动好处

逻辑解耦: 不需要关心太多的业务复杂度,只需要在需要的时候发起对应的业务逻辑调用。

时序性保证:通过事件发出就像将处理放入队列一样,能够保证时序性。

可靠性保证:由于事件的时序性很强,从而使数据一致性和可靠性得到保证。

本地联思文件同步云联思真实案例

先介绍下业务背景:由于工业软件的特殊性需要将文件进行不同磁盘目录的合并,通过seafile的同步中转能力实现文件的归档,使得在对外开放的服务器上能够正常访问到文件。

本地联思文件同步云联思架构

架构设计图如下:

 由于seafile上传虽然宣称是可靠的,但是在实际操作中发现并不是百分之百的文件都上去了,还是有掉文件的情况。于是本地联思需要负责检查云联思seafile和云联思目录是否真实存在相应的文件。通过云联思部署客户端对外访问的API接口判定云联思seafile或MES目录是否真实存在上传文件,任意一个目录存在就表示同步完成,当然文件还要校验md5,不是名称一样就表示存在。

云联思客户端检查文件API

云联思客户端提供REST HTTP接口访问,提供心跳接口和相关的校验接口:

@RestController
@RequestMapping("/localMes/productionFiles")
@RequiredArgsConstructor
public class ProductionFileController extends BaseController {

    private final SyncFileMappingConfig syncFileConfig;

    /**
     * 远端请求心跳测试
     *
     * @return
     */
    @GetMapping("/heartbeat")
    public Response<String> heartbeat(HttpServletRequest request) {
        String ipAddress = request.getRemoteAddr();
        String message = String.format("请求心跳:%s %s", ipAddress, DateUtil.formatDate(new Date().getTime(), "yyyy-MM-dd HH:mm:ss.SSS"));
        logger.info(message);
        return onSuccess(message);
    }

    /**
     * 验证路径attachment文件是否存在
     *
     * @param syncFileModel
     * @param bindingResult
     * @return
     */
    @RequestMapping(value = "/attachment/check/exists", method = RequestMethod.POST)
    public Response<SyncFileModel> checkAttachmentPath(@Valid @RequestBody SyncFileModel syncFileModel, BindingResult bindingResult) {
        if (bindingResult.hasErrors()) {
            return onBindingInvalidInput(bindingResult);
        }
        markFile(syncFileModel);
        return onSuccess(syncFileModel);
    }

    /**
     * 删除不存在的attachment文件
     *
     * @param syncFileModel
     * @param bindingResult
     * @return
     */
    @RequestMapping(value = "/attachment/delete/notExists", method = RequestMethod.POST)
    public Response<SyncFileModel> deleteAttachmentPath(@Valid @RequestBody SyncFileModel syncFileModel, BindingResult bindingResult) {
        if (bindingResult.hasErrors()) {
            return onBindingInvalidInput(bindingResult);
        }
        deleteFile(syncFileModel);
        return onSuccess(syncFileModel);
    }

    /**
     * 验证路径mpr文件是否存在
     *
     * @param syncFileModelList
     * @param bindingResult
     * @return
     */
    @RequestMapping(value = "/mpr/check/exists", method = RequestMethod.POST)
    public Response<SyncFileModelList> checkMprPath(@Valid @RequestBody SyncFileModelList syncFileModelList, BindingResult bindingResult) {
        if (bindingResult.hasErrors()) {
            return onBindingInvalidInput(bindingResult);
        }
        for (SyncFileModel syncFileModel : syncFileModelList.getSyncFileModelList()) {
            markFile(syncFileModel);
        }

        // 删除云联思独有的mpr文件
        deleteOnlyOnTargetMprFiles(syncFileModelList);

        return onSuccess(syncFileModelList);
    }

    /**
     * 删除不存在的mpr文件
     *
     * @param syncFileModelList
     * @param bindingResult
     * @return
     */
    @RequestMapping(value = "/mpr/delete/notExists", method = RequestMethod.POST)
    public Response<SyncFileModelList> deleteMprPath(@Valid @RequestBody SyncFileModelList syncFileModelList, BindingResult bindingResult) {
        if (bindingResult.hasErrors()) {
            return onBindingInvalidInput(bindingResult);
        }
        if (CollectionUtils.isEmpty(syncFileModelList.getSyncFileModelList())) {
            File file = new File(syncFileConfig.getMappingMp4(), syncFileModelList.getOrderNo());
            if (file.exists()) {
                FileUtil.del(file);
            }
        } el
posted @ 2024-08-05 10:50  boonya  阅读(53)  评论(0)    收藏  举报  来源
我有佳人隔窗而居,今有伊人明月之畔。
轻歌柔情冰壶之浣,涓涓清流梦入云端。
美人如娇温雅悠婉,目遇赏阅适而自欣。
百草层叠疏而有致,此情此思怀彼佳人。
念所思之唯心叩之,踽踽彳亍寤寐思之。
行云如风逝而复归,佳人一去莫知可回?
深闺冷瘦独自徘徊,处处明灯影还如只。
推窗见月疑是归人,阑珊灯火托手思忖。
庐居闲客而好品茗,斟茶徐徐漫漫生烟。

我有佳人在水之畔,瓮载渔舟浣纱归还。
明月相照月色还低,浅近芦苇深深如钿。
庐山秋月如美人衣,画堂春阁香气靡靡。
秋意幽笃残粉摇曳,轻轻如诉画中蝴蝶。
泾水潺潺取尔浇园,暮色黄昏如沐佳人。
青丝撩弄长裙翩翩,彩蝶飞舞执子手腕。
香带丝缕缓缓在肩,柔美体肤寸寸爱怜。
如水之殇美玉成欢,我有佳人清新如兰。
伊人在水我在一边,远远相望不可亵玩。