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号