一、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对象
- 解密
- 把该部分数据放入管道中统一处理