cegh&&OSS
相关部署:https://blog.csdn.net/m0_58833554/article/details/134604853
使用golang实现cegh上传
data, _ := ioutil.ReadAll(newFile)
cephPath := "/ceph/" + fileMeta.FileSha1
_ = ceph.PutObject("userfile", cephPath, data)
fileMeta.Location = cephPath
相关的cegh操作:
func GetCephConnection() *s3.S3 {
if cephConn != nil {
return cephConn
}
// 1. 初始化ceph的一些信息
auth := aws.Auth{
AccessKey: cfg.CephAccessKey,
SecretKey: cfg.CephSecretKey,
}
curRegion := aws.Region{
Name: "default",
EC2Endpoint: cfg.CephGWEndpoint,
S3Endpoint: cfg.CephGWEndpoint,
S3BucketEndpoint: "",
S3LocationConstraint: false,
S3LowercaseBucket: false,
Sign: aws.SignV2,
}
// 2. 创建S3类型的连接
return s3.New(auth, curRegion)
}
// GetCephBucket : 获取指定的bucket对象
func GetCephBucket(bucket string) *s3.Bucket {
conn := GetCephConnection()
return conn.Bucket(bucket)
}
// PutObject : 上传文件到ceph集群
func PutObject(bucket string, path string, data []byte) error {
return GetCephBucket(bucket).Put(path, data, "octet-stream", s3.PublicRead)
}
OSS
设置相关参数:
package config
const (
// OSSBucket : oss bucket名
OSSBucket = "buckettest-filestore2"
// OSSEndpoint : oss endpoint
OSSEndpoint = "oss-cn-shenzhen.aliyuncs.com"
// OSSAccesskeyID : oss访问key
OSSAccesskeyID = "<你的AccesskeyId>"
// OSSAccessKeySecret : oss访问key secret
OSSAccessKeySecret = "<你的AccessKeySecret>"
)
相关操作:
var ossCli *oss.Client
// Client : 创建oss client对象
func Client() *oss.Client {
if ossCli != nil {
return ossCli
}
ossCli, err := oss.New(cfg.OSSEndpoint,
cfg.OSSAccesskeyID, cfg.OSSAccessKeySecret)
if err != nil {
fmt.Println(err.Error())
return nil
}
return ossCli
}
// Bucket : 获取bucket存储空间
func Bucket() *oss.Bucket {
cli := Client()
if cli != nil {
bucket, err := cli.Bucket(cfg.OSSBucket)
if err != nil {
fmt.Println(err.Error())
return nil
}
return bucket
}
return nil
}
// DownloadURL : 临时授权下载url
func DownloadURL(objName string) string {
signedURL, err := Bucket().SignURL(objName, oss.HTTPGet, 3600)
if err != nil {
fmt.Println(err.Error())
return ""
}
return signedURL
}
将文件上传到OSS中
else if cfg.CurrentStoreType == cmn.StoreOSS {
// 文件写入OSS存储
ossPath := "oss/" + fileMeta.FileSha1
// 判断写入OSS为同步还是异步
if !cfg.AsyncTransferEnable {
err = oss.Bucket().PutObject(ossPath, newFile)
if err != nil {
fmt.Println(err.Error())
w.Write([]byte("Upload failed!"))
return
}
fileMeta.Location = ossPath
}
对下载进行改造
目标:生成文件的下载地址,根据文件的存储位置(本地、Ceph、OSS)生成相应的下载链接,并返回给客户端。代码的工作流程如下:
代码流程解析:
-
获取文件的哈希值:
- 使用
r.Form.Get("filehash")从请求中获取文件的哈希值(filehash),这个值唯一标识文件,方便在存储系统中查找文件。
- 使用
-
从数据库查找文件信息:
- 通过
dblayer.GetFileMeta(filehash)从文件元数据表中查找与该哈希值对应的文件记录。记录中包含文件的存储路径等信息。
- 通过
-
判断文件的存储位置:
- 代码通过
row.FileAddr.String的值来判断文件存储在何处。依据文件存储路径的前缀,分别处理:- 本地存储:如果文件路径以
"/tmp"开头,则表示文件存储在本地服务器。 - Ceph 存储:如果路径以
"/ceph"开头,表示文件存储在分布式存储系统 Ceph 中。 - OSS 存储:如果路径以
"oss/"开头,表示文件存储在对象存储服务 OSS(如阿里云OSS、Amazon S3)中。
- 本地存储:如果文件路径以
- 代码通过
-
生成下载URL:
- 本地文件:如果文件存储在本地,使用
r.Host、filehash、username和token生成一个临时的下载链接,返回给客户端。客户端通过这个链接可以直接从服务器下载文件。 - OSS文件:如果文件存储在 OSS,代码调用
oss.DownloadURL()方法生成一个带签名的下载链接(signedURL)。签名下载URL是一种安全的访问方式,允许用户在规定的时间内下载文件。 - Ceph文件:Ceph部分的逻辑暂未实现,但类似于 OSS,通常也需要生成特定的下载URL供客户端使用。
- 本地文件:如果文件存储在本地,使用
为什么要这样做:
-
不同存储位置的不同处理方式:
- 文件可以存储在多个位置(本地、Ceph、OSS)。根据文件的存储位置生成不同的下载链接,确保客户端可以从正确的存储源获取文件。不同的存储方案有各自的下载方式,故需要分别处理。
-
避免不必要的数据传输:
- 对于存储在OSS中的文件,不需要将文件传回上传服务器再返回给客户端。直接生成OSS的签名URL并返回给客户端,可以大大减少服务器的负载和带宽消耗。OSS等云存储通常具有独立的CDN或下载能力,客户端直接从OSS下载比通过服务器中转要更高效。
-
提高系统的扩展性和性能:
- 当文件规模变大、访问频率增加时,服务器负载会随之上升。通过将下载过程直接交给OSS或其他存储系统,可以减少对主服务器的压力,提高系统的整体性能和扩展性。
具体实现
func DownloadURLHandler(w http.ResponseWriter, r *http.Request) {
filehash := r.Form.Get("filehash")
// 从文件表查找记录
row, _ := dblayer.GetFileMeta(filehash)
// TODO: 判断文件存在OSS,还是Ceph,还是在本地
if strings.HasPrefix(row.FileAddr.String, "/tmp") {
username := r.Form.Get("username")
token := r.Form.Get("token")
tmpUrl := fmt.Sprintf("http://%s/file/download?filehash=%s&username=%s&token=%s",
r.Host, filehash, username, token)
w.Write([]byte(tmpUrl))
} else if strings.HasPrefix(row.FileAddr.String, "/ceph") {
// TODO: ceph下载url
} else if strings.HasPrefix(row.FileAddr.String, "oss/") {
// oss下载url
signedURL := oss.DownloadURL(row.FileAddr.String)
w.Write([]byte(signedURL))
}
}
这段代码的主要作用是为指定的OSS(对象存储服务)存储桶设置生命周期规则(Lifecycle Rule)。它通过定义规则,自动管理文件(对象)的存储周期,如自动删除或归档过期文件,从而优化存储成本和提高管理效率。
OSS对象声明周期管理
对象声明周期存储的必要性:
-
自动化文件管理:
- 对象存储系统中,文件可能随着时间积累而增加。手动管理这些文件的存储周期(如定期删除不再使用的文件)费时费力,且容易出错。通过设置生命周期规则,系统可以自动执行这些任务,简化文件管理流程。
-
优化存储成本:
- 随着数据量增长,存储成本也会增加,尤其是对于那些不再频繁访问的文件。通过设置文件自动过期和删除规则,企业可以有效减少存储空间占用,进而降低存储成本。OSS等云存储服务通常根据使用的存储空间收费,因此及时删除不需要的文件可以显著节约费用。
-
提升系统效率:
- 自动清理过期文件不仅节省了存储空间,也有助于提高存储系统的性能。减少无效或过期文件的占用,有助于加快数据查找和处理速度,提升整体响应效率。
-
减少人为错误:
- 手动清理数据可能会带来误删文件的风险,而通过明确的生命周期规则,可以确保数据在合理的时间点被自动清理,避免人为操作失误。
代码流程解析:
-
定义生命周期规则:
ruleTest1 := oss.BuildLifecycleRuleByDays("rule1", "test/", true, 30):- 调用
oss.BuildLifecycleRuleByDays方法创建一个生命周期规则ruleTest1,其中包含:- 规则的唯一标识符
"rule1"。 - 对象的前缀
"test/",表示该规则只对路径以test/开头的文件生效。 true表示规则开启(启用状态)。30表示生命周期的时长,即文件最后修改时间30天后将被删除。
- 规则的唯一标识符
- 调用
- 该规则的作用是:任何存储在存储桶中并且路径以
test/开头的文件,在距最后修改时间30天后会自动过期和删除。
-
规则列表:
rules := []oss.LifecycleRule{ruleTest1}:- 将创建的生命周期规则
ruleTest1放入一个规则列表rules中。虽然此处只定义了一个规则,但OSS允许对一个存储桶应用多个规则,方便用户根据不同的前缀或文件类型进行分类管理。
- 将创建的生命周期规则
-
设置生命周期规则到存储桶:
Client().SetBucketLifecycle(bucketName, rules):- 调用OSS客户端的
SetBucketLifecycle方法,将前面创建的规则列表rules应用于指定的bucketName(存储桶)。此操作会在OSS后台生效,自动管理符合规则的对象文件。
- 调用OSS客户端的
具体实现
func BuildLifecycleRule(bucketName string) {
// 表示前缀为test的对象(文件)距最后修改时间30天后过期。
ruleTest1 := oss.BuildLifecycleRuleByDays("rule1", "test/", true, 30)
rules := []oss.LifecycleRule{ruleTest1}
Client().SetBucketLifecycle(bucketName, rules)
}
优化客户端上传文件流程

传统架构中的问题(左图)
在左图所示的传统文件上传架构中,文件上传由一个“上传服务(调度)”进行统筹调度,整个流程大致如下:
-
客户端上传文件到上传服务:
- 客户端将文件上传到位于中间层的上传服务。上传服务负责处理客户端请求,并根据系统中的存储规则将文件转发至对应的存储系统,如 Ceph 或 OSS。
-
上传服务调用存储 SDK:
- 上传服务根据不同的存储系统选择合适的 SDK 来进行文件上传,
ceph_sdk用于上传到 Ceph,oss_sdk用于上传到 OSS。
- 上传服务根据不同的存储系统选择合适的 SDK 来进行文件上传,
-
上传服务的存储调度:
- 上传服务在这个流程中不仅需要接收文件,还要负责传输和调度整个文件上传的过程。虽然这样的设计能统一管理文件上传的行为,但存在以下几个问题:
- 性能瓶颈:上传服务充当了文件上传的中间人,导致其成为整个上传流程的瓶颈。
- 冗余操作:客户端实际上可以直接与存储系统交互,但中间层的存在增加了额外的延迟。
- 复杂性高:上传服务既要处理调度,还要负责传输,增加了系统复杂性和维护成本。
- 上传服务在这个流程中不仅需要接收文件,还要负责传输和调度整个文件上传的过程。虽然这样的设计能统一管理文件上传的行为,但存在以下几个问题:
优化架构中的提升(右图)
为了解决上述问题,右图展示了一个经过优化的文件上传流程。通过对流程的简化和优化设计,客户端上传文件的效率显著提升。优化后的流程如下:
-
获取授权,直接上传:
- 客户端通过上传服务获取到目标存储系统(如 OSS)的上传授权。这一步骤不再由上传服务直接处理上传,而是授权给客户端进行操作。授权可以通过获取短期访问凭证(如 STS token)实现,确保客户端有权访问存储系统。
-
客户端直接上传文件到存储系统:
- 获取授权后,客户端可以直接将文件上传到 OSS。这种方式跳过了上传服务的中转过程,客户端与存储系统直接交互,不仅减少了上传延迟,还降低了上传服务的负载。
-
上传完成后通知服务:
- 文件上传完成后,客户端会通知上传服务,告知上传已成功完成。上传服务负责记录此次上传的状态或进一步处理文件的元数据。这个步骤可以帮助系统实现上传结果的追踪和后续业务逻辑的执行。
优化带来的核心好处
-
提高上传效率:
- 通过去除上传服务作为中转层的设计,客户端直接与存储系统交互,减少了不必要的数据传输步骤,大大提升了上传效率,尤其是在大文件上传的场景下。
-
减轻上传服务负担:
- 传统架构中的上传服务不仅要调度,还要处理数据的转发,增加了负载。而在优化后的架构中,上传服务仅负责权限管理和调度,实际的文件上传由客户端直接完成,从而减轻了上传服务的负担,提升了系统的可扩展性。
-
增强系统灵活性:
- 优化后的架构更加模块化,客户端的上传操作与存储系统的交互更加直接。如果未来需要更换存储系统或增加新的存储选项,客户端只需更新获取授权的逻辑,而不需要修改上传服务的核心逻辑。
-
上传完成通知机制提升稳定性:
- 上传完成通知的设计使得上传服务可以更好地追踪每次上传的状态,从而为后续的文件处理、审核或其他业务操作提供保障。这种机制有助于在大规模并发上传时保持系统的稳定性和一致性。
实现建议
在实现过程中,可以参考以下几点来进行部署和优化:
- 分布式授权管理:使用类似 STS 的授权机制来管理不同存储系统的访问权限,客户端可以根据需要动态获取访问凭证。
- 上传进度控制:在客户端实现上传进度的管理和重试机制,以应对网络抖动或中断等问题。
- 智能调度:上传服务可以基于存储系统的负载、文件大小、网络状态等多因素动态调度上传策略。
- 缓存与断点续传:对于大文件或不稳定网络环境,可以在客户端实现缓存和断点续传机制,进一步提升用户体验。

浙公网安备 33010602011771号