volume.go
package volume
import (
"os"
"path/filepath"
"strconv"
"sync"
"time"
"encoding/binary"
"errors"
)
var (
TruncateSize uint64 = 1 << 30 //1GB
MaxVolumeSize uint64 = 512 * TruncateSize //512GB
)
type Volume struct {
Id uint64
WriteAble bool
index Index
status *Status
dataFile *os.File
mutex sync.Mutex
}
//创建一个卷 或者磁盘空间
func NewVolume(dir string, vid uint64) (v *Volume, err error) {
path := filepath.Join(dir, strconv.FormatUint(vid, 10) + ".data")
v = new(Volume)
v.Id = vid
v.dataFile, err = os.OpenFile(path, os.O_CREATE | os.O_RDWR, 0666)
if err != nil {
if os.IsPermission(err) {//判断是不是权限错误
v.dataFile, err = os.OpenFile(path, os.O_RDONLY, 0)
if err != nil {
return nil, err
}
v.WriteAble = false
} else {
return nil, err
}
}
v.WriteAble = true
v.index, err = NewLevelDBIndex(dir, vid)
if err != nil {
return nil, err
}
v.status, err = NewStatus(dir, vid)
if err != nil {
return nil, err
}
return v, nil
}
//实现index接口
//
func (v *Volume)Get(fid uint64) (*File, error) {
fi, err := v.index.Get(fid)
if err == nil {
return &File{DataFile: v.dataFile, Info: fi}, nil
} else {
return nil, err
}
}
//删除文件
func (v *Volume)Delete(fid uint64, fileName string) error {
v.mutex.Lock()
defer v.mutex.Unlock()
file, err := v.Get(fid)
if err != nil {
return err
} else if file.Info.FileName != fileName {
return errors.New("filename is wrong")
}
//因为文件内容前后都写入fid(8 byte) 要一起释放
err = v.status.freeSpace(file.Info.Offset - 8, file.Info.Size + 16)
if err != nil {
return err
}
err = v.index.Delete(fid)
return err
}
//创建文件
func (v *Volume)NewFile(fid uint64, fileName string, size uint64) (f *File, err error) {
v.mutex.Lock()
defer v.mutex.Unlock()
if v.index.Has(fid) {
return nil, errors.New("fid is existed")
}
offset, err := v.newSpace(size + 16)
if err != nil {
return nil, err
}
defer func() {
if err != nil {
if e := v.status.freeSpace(offset, size + 16); e != nil {
panic(e)
}
}
}()
//在文件内容前后都写入fid
b := make([]byte, 8)
binary.BigEndian.PutUint64(b, fid)
_, err = v.dataFile.WriteAt(b, int64(offset))
if err != nil {
return nil, err
}
_, err = v.dataFile.WriteAt(b, int64(offset + 8 + size))
if err != nil {
return nil, err
}
fileInfo := &FileInfo{
Fid: fid,
Offset: offset + 8,
Size: size,
Ctime: time.Now(),
Mtime: time.Now(),
Atime: time.Now(),
FileName: fileName,
}
err = v.index.Set(fileInfo)
if err != nil {
return nil, err
} else {
return &File{DataFile: v.dataFile, Info: fileInfo}, nil
}
}
//清空文件空间
func (v *Volume)truncate() {
currentDatafileSize := v.GetDatafileSize()
if currentDatafileSize >= MaxVolumeSize {
return
}
//清空指定偏移量位置的数据
err := v.dataFile.Truncate(int64(currentDatafileSize) + int64(TruncateSize))
if err != nil {
panic(err)
}
//释放文件空间
err = v.status.freeSpace(currentDatafileSize, TruncateSize)
if err != nil {
panic(err)
}
}
//分配新的文件空间
func (v *Volume)newSpace(size uint64) (uint64, error) {
offset, err := v.status.newSpace(size)
if err == nil {
return offset, err
}
v.truncate()
return v.status.newSpace(size)
}
//资源释放
func (v *Volume)Close() {
v.mutex.Lock()
//v.status.spaceMutex.Lock()
//因为要退出,所以不解锁,禁止读写
//defer v.rwMutex.Unlock()
//将所有资源释放
v.dataFile.Close()
v.dataFile = nil
v.status.db.Close()
v.status = nil
v.index.Close()
v.index = nil
}
//获取数据文件大小
func (v *Volume)GetDatafileSize() uint64 {
fi, err := v.dataFile.Stat()
if err != nil {
panic(err)
}
return uint64(fi.Size())
}
//获取最大释放文件空间
func (v *Volume)GetMaxFreeSpace() uint64 {
currentDatafileSize := v.GetDatafileSize()
//以块方式存储,所以自由空间无法直接相加
maxFreeSpace := v.status.getMaxFreeSpace()
if currentDatafileSize < MaxVolumeSize {
freeSpace := MaxVolumeSize - currentDatafileSize
if freeSpace > maxFreeSpace {
maxFreeSpace = freeSpace
}
}
//fid前后各占8个字节
if maxFreeSpace > 16 {
return maxFreeSpace - 16
} else {
return 0
}
}
volume.go的更多相关文章
- Java中实现SAX解析xml文件到MySQL数据库
大致步骤: 1.Java bean 2.DBHelper.java 3.重写DefaultHandler中的方法:MyHander.java 4.循环写数据库:SAXParserDemo.java ① ...
- CSharpGL(8)使用3D纹理渲染体数据 (Volume Rendering) 初探
CSharpGL(8)使用3D纹理渲染体数据 (Volume Rendering) 初探 2016-08-13 由于CSharpGL一直在更新,现在这个教程已经不适用最新的代码了.CSharpGL源码 ...
- 理解Docker(8):Docker 存储之卷(Volume)
(1)Docker 安装及基本用法 (2)Docker 镜像 (3)Docker 容器的隔离性 - 使用 Linux namespace 隔离容器的运行环境 (4)Docker 容器的隔离性 - 使用 ...
- Docker Volume 之权限管理(转)
Volume数据卷是Docker的一个重要概念.数据卷是可供一个或多个容器使用的特殊目录,可以为容器应用存储提供有价值的特性: 持久化数据与容器的生命周期解耦:在容器删除之后数据卷中的内容可以保持.D ...
- NFS Volume Provider(Part III) - 每天5分钟玩转 OpenStack(64)
今天我们将前一小节创建的 NFS volume “nfs-vol-1” attach 到 instance “c2”上. 这里我们重点关注 nova-compute 如何将“nfs-vol-1” at ...
- NFS Volume Provider(Part II) - 每天5分钟玩转 OpenStack(63)
上一节我们将 NFS volume provider 配置就绪,本节将创建 volume. 创建 volume 创建 NFS volume 操作方法与 LVM volume 一样,唯一区别是在 vol ...
- NFS Volume Provider(Part I) - 每天5分钟玩转 OpenStack(62)
cinder-volume 支持多种 volume provider,前面我们一直使用的是默认的 LVM,本节我们将增加 NFS volume provider. 虽然 NFS 更多地应用在实验或小规 ...
- Boot from Volume - 每天5分钟玩转 OpenStack(61)
Volume 除了可以用作 instance 的数据盘,也可以作为启动盘(Bootable Volume),那么如何使 volume 成为 bootable 呢? 现在我们打开 instance 的 ...
- Restore Volume 操作 - 每天5分钟玩转 OpenStack(60)
前面我们 backup 了 voluem,今天我们将讨论如何 restore volume. restore 的过程其实很简单,两步走: 在存储节点上创建一个空白 volume. 将 backup 的 ...
- Backup Volume 操作 - 每天5分钟玩转 OpenStack(59)
本节我们讨论 volume 的 Backup 操作. Backup 是将 volume 备份到别的地方(备份设备),将来可以通过 restore 操作恢复. Backup VS Snapshot 初看 ...
随机推荐
- JVM学习--(一)基本原理
前言 JVM一直是java知识里面进阶阶段的重要部分,如果希望在java领域研究的更深入,则JVM则是如论如何也避开不了的话题,本系列试图通过简洁易读的方式,讲解JVM必要的知识点. 运行流程 我们都 ...
- javascript随机一个1-9的数字
window.onload=function(){ var oTxt=document.getElementById('txt'); for(i=1;i<=200;i ...
- partition List(划分链表)
Given a linked list and a value x, partition it such that all nodes less than x come before nodes gr ...
- JAVA程序员面试宝典
程序员面试之葵花宝典 面向对象的特征有哪些方面 1. 抽象:抽象就是忽略一个主题中与当前目标2. 无关的那些方面,3. 以便更充分地注意与当前目标4. 有关的方面.抽象并不5. 打算了解全部问题 ...
- 超强js博客值得学习!!!
再读ecmascript 摘要: 这几天,又花了点时间看了下ecmascript.以下是我摘录出来的一些理解.在此记录下.第一部分:关于变量对象的理解1) 什么是变量对象?数据的存取与读取机制,就是变 ...
- [Micropython]TPYBoard v10x MFRC522智能门禁系统
MF RC522 是应用于13.56MHz 非接触式通信中高集成度读写卡系列芯片中的一员.也就是射频卡. 经常忘带钥匙,最尴尬的上周竟然去开4楼的门,(家住五楼,无电梯),开了好一会没打开,事后对4楼 ...
- JSON-RPC远程调用协议
1. JSON-RPC简介 2. 请求 3. 响应 4. 错误 4.1. 错误对象 4.2. 错误码 5. 批量调用 6. 示例 6.1. 列表形式参数 6.2. key-value形式参数 6.3. ...
- Python中Json对象处理的jsonpath-rw
这两天在写一个爬虫,需要从网站返回的json数据提取一些有用的数据. 向url发起请求,返回的是response,在python3中,response.content是二进制bytes类型的,需要用d ...
- Python 装饰器(Decorator)
装饰器的语法为 @dec_name ,置于函数定义之前.如: import atexit @atexit.register def goodbye(): print('Goodbye!') print ...
- Java开源生鲜电商平台-支付模块的设计与架构(源码可下载)
Java开源生鲜电商平台-支付模块的设计与架构(源码可下载) 开源生鲜电商平台支付目前支持支付宝与微信.针对的是APP端(android or IOS) 1. 数据库表设计. 说明:无论是支付宝还 ...