MinIO源码学习笔记

一、MinIOn对外提供http接口的入口点

调用路径为/cmd下server-main.go -> routers.go -> api-router.go

server-main.serverMain --> routers.configureServerHandler(globalEndpoints)
–> routers.configureServerHandler // 返回handler给http server
–> api-router.registerAPIRouter(router)

func registerAPIRouter(router *mux.Router) {…} 函数提供了兼容S3的主从API接口

在该函数里,有上传和下载

1.1、上传对象

上传对象:

// PutObject
bucket.Methods(http.MethodPut).Path("/{object:.+}").HandlerFunc(
			maxClients(collectAPIStats("putobject", httpTraceHdrs(api.PutObjectHandler))))

看object-handlers.go中的PutObjectHandler函数是如何实现的把一个对象上传到一个桶里的

// PutObjectHandler - PUT Object
// ----------
// This implementation of the PUT operation adds an object to a bucket.
// Notice: The S3 client can send secret keys in headers for encryption related jobs,
// the handler should ensure to remove these keys before sending them to the object layer.
// Currently these keys are:
//   - X-Amz-Server-Side-Encryption-Customer-Key
//   - X-Amz-Copy-Source-Server-Side-Encryption-Customer-Key
func (api objectAPIHandlers) PutObjectHandler(w http.ResponseWriter, r *http.Request) {
    
    ...}

1)先验证header里没有X-Amz-Copy-Source

// X-Amz-Copy-Source shouldn't be set for this call.
if _, ok := r.Header[xhttp.AmzCopySource]; ok {
    
    
	writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrInvalidCopySource), r.URL, guessIsBrowserReq(r))
	return
}

2)验证metadata是有效的

// Validate storage class metadata if present
if sc := r.Header.Get(xhttp.AmzStorageClass); sc != "" {
    
    
	if !storageclass.IsValid(sc) {
    
    
		writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrInvalidStorageClass), r.URL, guessIsBrowserReq(r))
		return
	}
}

3)检测客户端发送的HTTP HEADER中的Content-Md5是否存在,并获取该MD5(MD5是16进制数Base64Encode之后的结果)

// Get Content-Md5 sent by client and verify if valid
md5Bytes, err := checkValidMD5(r.Header)
if err != nil {
    
    
	writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrInvalidDigest), r.URL, guessIsBrowserReq(r))
	return
}

4)检验X-Amz-Decoded-Content-Length是否存在

5)从http header中获取所有meta信息

6)检测当前桶是否有写权限

7)重点看erasure-server-sets.PutObject函数:// 上传一个对象

func (z *erasureServerSets) PutObject(ctx context.Context, bucket string, object string, data *PutObjReader, opts ObjectOptions) (ObjectInfo, error) {
    
    ...}
  • 首先检查输入参数是否正确:如检查桶名是否存在,对象名长度是否小于1024,是否以斜杠开头

-> erasure-object.putObject:

  • 获取该对象对应的所有磁盘
  • 基于metadata获取数据块和校验块的Drive的数量,默认数据块和校验块都是N/2, N是Drive的总数
  • 初始化metadata
  • 根据erasure distribution对磁盘排序
  • 根据数据块和校验块的数量,生成一个新的erasure存储对象
  • 创建一个buffer用于IO
  • 生成一个临时的erasure对象
  • 对数据加密
  • 读取写入的数据,并写入编码块metadata对象
  • 设置写入时间
  • 写入meta信息文件“xl.meta"到每块磁盘
  • 把临时文件rename成最终的文件
  • 最后把metadata信息转换成ObjectInfo对象返回

问题:上传对象时,并没有做文件的MD5校验。

1.2、下载对象

下载对象:

// GetObject
bucket.Methods(http.MethodGet).Path("/{object:.+}").HandlerFunc(
			maxClients(collectAPIStats("getobject", httpTraceHdrs(api.GetObjectHandler))))

也看erasure-server-sets.GetObject:

func (z *erasureServerSets) GetObject(ctx context.Context, bucket, object string, startOffset int64, length int64, writer io.Writer, etag string, opts ObjectOptions) error {
    
    ...}

1)输入参数校验:
验证桶是否存在,前缀是否合法,是否带\

2)对zone的每个Set做遍历,对于每个Set,调用zone.GetObject函数

3)erasure-object.GetObject:跨多块磁盘读取一个对象的erasured code。

func (er erasureObjects) GetObject(ctx context.Context, bucket, object string, startOffset int64, length int64, writer io.Writer, etag string, opts ObjectOptions) error {
    
    ...}

4)在读对象前先给对象加锁

5)基于erasure distribution顺序重新排序online disks

6)基于erasure distribution顺序重新排序parts metadatas

7)获取起始的index和offset

8)计算endOffset = startOffset + length - 1

9)获取最后part的index来读取给定的长度

10)根据数据块,校验块和blockSize,生成一个新的Erasure存储对象

11)逐个遍历每个part:

  • 遍历每个磁盘:
    • 获取该part的checksum(校验码)
    • 并拼好part的path
    • 新生成BitrotReader对象
    • 解密
    • 把该部分数据放入管道中统一处理

猜你喜欢

转载自blog.csdn.net/shijinghan1126/article/details/109766902
今日推荐