golang文件存储纠删码实现

简介: // Verify(shards [][]byte) (bool, error)。每个分片都是[]byte类型,分片集合就是[][]byte类型,传入所有分片,如果有任意的分片数据错误,就返回false。

一般我们存储简单处理就是写三副本,但是三副本的成本太大了,采用纠删码可以比较好的降低存储空间的成本,具体看下golang中的代码实现。

// 纠删码测试
// 将一个文件拆分成10分,删除其中的任意三分,尝试还原文件
// 这边需要注意的一点是文件的拆分后的顺序和还原的顺序是相关的,顺序错误是无法还原的

// 其中Encoder接口有以下几个关键的函数。
// Verify(shards [][]byte) (bool, error)。每个分片都是[]byte类型,分片集合就是[][]byte类型,传入所有分片,如果有任意的分片数据错误,就返回false。
// Split(data []byte) ([][]byte, error)。将原始数据按照规定的分片数进行切分。注意:数据没有经过拷贝,所以修改分片也就是修改原数据。
// Reconstruct(shards [][]byte) error。  这个函数会根据shards中完整的分片,重建其他损坏的分片。
// Join(dst io.Writer, shards [][]byte, outSize int) error。将shards合并成完整的原始数据并写入dst这个Writer中。


package main

import (
    "flag"
    "fmt"
    "io/ioutil"
    "os"
    "strings"

    "github.com/klauspost/reedsolomon"
    "github.com/qtj/gosdk/file"
)

定义和初始化验证一些参数

var (
    srcFile      string // 原始文件
    dstDir       string // 目标目录
    recoverName  string // 还原后的文件名称
    dataShards   int    // 数据分片
    parityShards int    // 校验分片
    oper         string // 操作动作
)

func init() {
    flag.StringVar(&srcFile, "srcFile", "qtjErasureCode.exe", "原始文件名称")
    flag.StringVar(&dstDir, "dstDir", "dstDir", "目标目录")
    flag.StringVar(&recoverName, "recoverName", "recoverName", "还原后的文件名称")
    flag.StringVar(&oper, "oper", "", "split和recover二选一,split会将一个文件拆分成类似10个数据文件和3和校验文件,recover的时候可以删除目标目录下的三个文件做还原即可")
    flag.IntVar(&dataShards, "dataShards", 10, "数据分片个数")
    flag.IntVar(&parityShards, "parityShards", 3, "校验分片个数")
    flag.Parse()
}

主调用

// qtjErasureCode.exe -oper=split先将文件拆分
// 删除当前目录下的子文件夹dstDir里面的任意三个文件
// qtjErasureCode.exe -oper=recover -recoverName="recover.exe" 将文件还原
// 最后比对md5发现是一致的
func main() {
    if !strings.Contains(dstDir, "/") && !strings.Contains(dstDir, "\\") {
        dstDir = file.GetCurDir() + dstDir + "/"
    }
    if oper == "split" {
        file.RemoveDirTree(dstDir)
        file.CreateDirTree(dstDir)
        if err := splitFile(); err != nil {
            fmt.Println(err)
            return
        }
    } else if oper == "recover" {
        if err := recoverFile(); err != nil {
            fmt.Println(err)
            return
        }
    } else {
        fmt.Println("错误的操作动作参数,oper必须为split或者recover")
        return
    }
}

还原文件验证

func recoverFile() error {
    if recoverName == "" {
        return fmt.Errorf("还原后的文件名称%s不能为空", recoverName)
    }

    // 数据分10片和校验3片
    enc, err := reedsolomon.New(dataShards, parityShards)
    if err != nil {
        return fmt.Errorf("创建数据分片和校验分片失败,%s", err.Error())
    }

    shards := make([][]byte, dataShards+parityShards)
    for i := range shards {
        splitName := fmt.Sprintf("%ssplit%010d", dstDir, i)
        // 不管文件是否存在,需要保留原先的顺序
        if shards[i], err = ioutil.ReadFile(splitName); err != nil {
            fmt.Printf("读取文件[%s]失败,%s\n", splitName, err.Error())
        }
        fmt.Println(splitName)
    }

    ok, err := enc.Verify(shards)
    if ok {
        fmt.Println("非常好,数据块和校验块都完整")
    } else {
        if err = enc.Reconstruct(shards); err != nil {
            return fmt.Errorf("重建其他损坏的分片失败,%s", err.Error())
        }

        if ok, err = enc.Verify(shards); err != nil {
            return fmt.Errorf("数据块校验失败2,%s", err.Error())
        }
        if !ok {
            return fmt.Errorf("重建其他损坏的分片后数据还是不完整,文件损坏")
        }

    }
    f, err := os.Create(recoverName)
    if err != nil {
        return fmt.Errorf("创建还原文件[%s]失败,%s", recoverName, err.Error())
    }
    // 这部分的大小决定了还原后的大小和原先的是不是一致的,不然使用md5比对或者大小都是不一样的
    // 实际生产需要一开始就拆分文件时候就记录总的大小
    //if err = enc.Join(f, shards, len(shards[0])*dataShards); err != nil {
    _, ln, err := file.GetFileLenAndMd5(srcFile)
    if err != nil {
        return fmt.Errorf("计算原始文件[%s]大小失败,%s", srcFile, err.Error())
    }
    if err = enc.Join(f, shards, int(ln)); err != nil {
        return fmt.Errorf("写还原文件[%s]失败,%s", recoverFile(), err.Error())
    }
    return nil
}

分隔文件处理

func splitFile() error {
    // 数据分10片和校验3片
    enc, err := reedsolomon.New(dataShards, parityShards)
    if err != nil {
        return fmt.Errorf("创建数据分片和校验分片失败,%s", err.Error())
    }

    bigfile, err := ioutil.ReadFile(srcFile)
    if err != nil {
        return fmt.Errorf("读取原始文件[%s]失败,%s", srcFile, err.Error())
    }

    // 将原始数据按照规定的分片数进行切分
    shards, err := enc.Split(bigfile)
    if err != nil {
        return fmt.Errorf("针对原始文件[%s]拆分成数据[%d]块,校验[%d]块失败,%s", srcFile, dataShards, parityShards, err.Error())
    }

    // 编码校验块
    if err = enc.Encode(shards); err != nil {
        return fmt.Errorf("编码校验块失败,%s", err.Error())
    }
    for i := range shards {
        splitName := fmt.Sprintf("%ssplit%010d", dstDir, i)
        fmt.Println(splitName)
        if err = file.SaveFile(shards[i], splitName); err != nil {
            return fmt.Errorf("原始文件[%s]拆分文件[%s]写失败,%s", srcFile, splitName, err.Error())
        }
    }

    return nil
}
目录
相关文章
golang操作文件
1、读取文件信息: /* 读取文件信息 */ func readFile(path string) string { fi, err := os.Open(path) if err != nil { panic(err) } defer fi.
1381 0
|
4天前
|
安全 Go 调度
Go语言中的并发编程
Go语言自带了强大的并发编程能力,它的协程机制可以让程序轻松地实现高并发。本文将从并发编程的基础概念出发,介绍Go语言中的协程机制、通道和锁等相关知识点,帮助读者更好地理解并发编程在Go语言中的实践应用。
|
1天前
|
数据采集 Web App开发 Go
Go语言与chromedp结合:实现Instagram视频抓取的完整流程
使用Go语言和chromedp库,本文展示了如何抓取Instagram的视频文件,同时通过代理IP保障爬虫稳定和隐私。步骤包括安装chromedp、配置代理(如亿牛云),创建Chrome会话,导航至Instagram,提取视频URL,然后下载视频。关键操作有设置代理服务器、启动Chrome会话、抓取和下载视频。提供的代码示例详细解释了实现过程,有助于开发者学习Instagram数据采集。
Go语言与chromedp结合:实现Instagram视频抓取的完整流程
|
2天前
|
缓存 Go 调度
浅谈在go语言中的锁
【5月更文挑战第11天】本文评估了Go标准库`sync`中的`Mutex`和`RWMutex`性能。`Mutex`包含状态`state`和信号量`sema`,不应复制已使用的实例。`Mutex`适用于保护数据,而`RWMutex`在高并发读取场景下更优。测试显示,小并发时`Mutex`性能较好,但随着并发增加,其性能下降;`RWMutex`的读性能稳定,写性能在高并发时低于`Mutex`。
132 0
浅谈在go语言中的锁
|
3天前
|
存储 安全 编译器
go语言中进行不安全的类型操作
【5月更文挑战第10天】Go语言中的`unsafe`包提供了一种不安全但强大的方式来处理类型转换和底层内存操作。包含两个文档用途的类型和八个函数,本文也比较了不同变量和结构体的大小与对齐系数,强调了字段顺序对内存分配的影响。
69 8
go语言中进行不安全的类型操作
|
3天前
|
Go
配置go语言下载包 - 蓝易云
这个命令会将包下载到你的GOPATH目录下,并自动安装它。
59 1
|
5天前
|
Ubuntu Unix Linux
【GO基础】1. Go语言环境搭建
【GO基础】1. Go语言环境搭建
|
6天前
|
JSON 前端开发 Go
lucky - go 语言实现的快速开发平台
go 语言实现的快速开发平台,自动生成crud代码,前端页面通过json配置,无需编写前端代码。
12 0
|
7天前
|
存储 Java Go
Go 语言切片如何扩容?(全面解析原理和过程)
Go 语言切片如何扩容?(全面解析原理和过程)
18 2