分布式文件系统平台化API协议设计文档 - shadowofs/RestAPI GitHub Wiki
版本 | 描述 | 日期 | 修改人 |
---|---|---|---|
v0.9 | REST API初稿 | 2014/3/21 | 佘影 |
v1.0 | 流式写入初稿 | 2014/3/21 | 童锁 |
v1.1 | 评审修订 | 2014/3/25 | 佘影 |
v1.2 | 更新PATCH | 2014/4/8 | 佘影 |
v1.3 | 修正后缀 | 2014/5/6 | 佘影 |
v1.4 | 去除Location,List方法增加参数仅返回文件名 | 2014/6/16 | 佘影 |
v1.5 | 还原Location,修改Stream协议描述,增加Stream协议的读操作 | 2014/6/30 | 佘影 |
v1.6 | 增加checksum和recover两种方法 | 2014/6/30 | 佘影 |
v1.7 | loc方法兼容社区 | 2014/9/10 | 佘影 |
v1.8 | 支持hardlink | 2014/9/12 | 佘影 |
当前API过于底层,导致Client端需要处理各种异常,Client被迫纠缠于繁冗的异常处理逻辑中。系统只提供了java的客户端,非java用户只能通过shell或者内嵌jvm的方式来使用,使用起来非常不方便,比较好的方法是实现一个非java客户端,但是现阶段由于实现难度过大,可能性几乎为0。
为了以后方便提供非java版的客户端,以及更遥远的支持公有云的目标,因此需要有一套比较开放的协议。比较好的选择是提供HTTP+JSON的RESTFUL API,HTTP协议已经比较成熟,用得很广,JSON同样,用HTTP+JSON前期测试和开放都很方便。但仅仅是REST API是不够的,REST方式写数据要么是整个文件一次传输,要么是拆成很多小的package,但是前一种方案无法满足错误恢复的需求,后一种方案又不能保证吞吐,因此还需要支持流式写数据。
Name | Description |
---|---|
REST | 含状态传输(英文:Representational State Transfer,简称REST)通常基于使用HTTP,URI,和XML以及HTML这些现有的广泛流行的协议和标准。REST风格中,资源是由URI来指定,对资源的操作包括获取、创建、修改和删除资源,这些操作正好对应HTTP协议提供的GET、POST、PUT和DELETE方法。 |
MPA | 元数据代理(Meta Proxy Adapter)完成Client的元数据操作请求 |
DPA | 数据代理(Data Proxy Adapter)完成Client的流式数据读写请求,通常与Client部署在同节点,Client也可以指定数据代理 |
平台化API从架构上分为两层,元数据代理和数据代理。元数据代理为当前系统的Adapter角色。数据代理又分为Http协议数据代理和流式协议数据代理,Http协议代理为集群的DataNode,Client访问MPA的时候,如果是读写请求,会被重定向到某个DN来完成读写请求;流式协议的代理跟随Client部署在同节点,之所以采用这个架构主要是考虑以下几点:
- 如果将两个代理合二为一,都放在Client端,那么如果Client ls一个超大的目录,可能会导致代理内存飙升
- 之所以选择Adapter作为元数据代理是由于Adapter都是Master机器,内存大,并且当前CPU也比较闲
- Http协议代理与流式协议数据代理分开主要考虑到Http协议尽量保证用户能够直接用,比如wget能够直接使用,如果还需要再在本地部署一个数据代理就很不方便了
- 流式读写主要满足高吞吐和容错的读写需求,将流式读写数据代理放在Client端,Client可以做成请求应答的模式,实现简单不容易出错,写入由于是本地socket不走网卡,所以性能能够保证
集群内部存储的数据每512字节都会有4字节的校验数据,每14天集群会对所有数据块的校验数据进行比对,发现有corrupt的文件会自动进行数据恢复。另外,在集群内部的数据传输都会在接受端做数据校验,例如Proxy在读取DN的数据时,会经过校验后再返回给Client,如果发现不一致会自动换到下一个副本,对Client透明。除非所有副本校验都失败,会报错给Client。
所以,理论上Client可以无需对数据进行二次校验(除非是本机数据代理到Client的网络出错,但几率相比数据节点磁盘故障的要小很多)。一期暂时先不提供数据校验的接口,后续有需要再考虑。
请求及返回结果都使用 UTF-8 字符集进行编码。
本节包含HTTP REST API的介绍,通过调用REST的API,可以完整地实现以前FileSystem包含的接口。
下面的例子实现了从集群中删除一个文件的操作。
DELETE /restfs/v1/testFile HTTP/1.1
Host: szwg-ecomon-hdfs.dmop.baidu.com:54310
Date: Tue Mar 18 15:05:04 CST 2014
x-baidu-ugi: <Ugi>
x-bce-request-id: <requestID>
目前支持方法和操作类型说明如下:
Method Name | Description |
---|---|
PUT | 修改资源属性 |
POST | 创建资源或者追加写入 |
DELETE | 删除资源 |
GET | 获取资源 |
一期API实现了以下操作:
- StatFS
- Create
- Delete
- Get File Content
- Get Attributes
- Get Listing
- Locate Resource
- Set Attributes
- Rename
- Checksum
下面的表格描述了REST API各类请求可能用到的header。
Header Name | Description | Example | Required |
---|---|---|---|
Host | 请求服务的URI,一期暂时为当前各集群的地址 | Host: szwg-ecomon-hdfs.dmop.baidu.com:54310 | 必选 |
Date | 请求提交时候请求端的时间 | Date: Tue Mar 18 15:10:22 CST 2014 | 可选 |
Content-Length | 请求body的byte长度(不包括请求头部),参考RFC 2616 | Content-Length: 1024 | 请求携带body的请求为必须 |
x-baidu-ugi | 用户访问集群时所使用的用户名和密码 | x-baidu-ugi: eky,123456 | 必选 |
x-baidu-credential | 使用新门神登陆之后的认证信息,Server端会使用公钥来解密这个票据,得到用户对应的角色 | 参见 Authentication | 可选 |
x-bce-request-id | 所有请求都应该唯一地对应一个ID,可用于问题定位、性能分析等等多个场景。requestId使用UUID version 4,暂时由各服务自行生成,后续考虑统一由BFE生成。 | x-bce-request-id: 9899dc5a-c5c7-4175-b851-0eef65cb6543 | 必选 |
x-baidu-excluded-node | 选择DPA时,excluded的节点集合,用逗号分隔 | 10.220.30.43,10.220.30.45 | 可选 |
下面的表格描述了响应中包含的header。
Header Name | Description | Example |
---|---|---|
Content-Length | 响应body的长度(bytes),如果未携带表示Chunked传输模式 | Content-Length: 0 |
Content-MD5 | 请求文件资源的MD5值,参考RFC 2616,在调用获取资源Checksum时会返回此Header | Content-MD5: fb817de5edfb53e6b7a7bf043c2ae6f5 |
Content-Type | 响应的MIME类型 | Content-Type: application/json |
Date | Server响应请求的时间 | Date: Tue Mar 18 15:10:22 CST 2014 |
Location | 读写请求数据代理所在的URI,响应Http协议的数据读写请求 | Location: http://10.215.139.31:7001 |
Transfer-Encoding | 设置为chunked表示响应长度不确定,通过http chunk传输协议确定长度,参加Chunked Transfer Coding | Transfer-Encoding: chunked |
x-bce-request-id | 所有请求都应该唯一地对应一个ID,可用于问题定位、性能分析等等多个场景。requestId使用UUID version 4,暂时由各服务自行生成,后续考虑统一由BFE生成。 | x-bce-request-id: 9899dc5a-c5c7-4175-b851-0eef65cb6543 |
Http Get方法表示获取一个资源,我们使用Get方法来实现获取资源的属性(Get Attributes)、内容(Get Content)、位置(Get Location)以及列举资源操作(Get Listing)。三种语义通过URI后缀来区分,默认的语义为Get Content,各后缀名和意义如下表:
Operation | Suffix | Description |
---|---|---|
Get Attributes | attr | 获取资源的属性信息 |
Get Content | content | 获取资源的内容,对目录调用Get Content会返回异常,错误码为Conflict(该方法只在数据代理上支持) |
Get Listing | list | 对目标资源执行ls操作,语义同linux文件系统,即对于目录返回所有child的属性信息,对于文件则返回目标资源的属性信息 |
Get Location | loc | 获取资源的位置信息,对于目录返回所有child的位置,对于文件则返回目标资源的位置 |
Get Checksum | checksum | 获取资源的checksum,仅对文件有效 |
后缀与Path通过冒号(:)分隔,请求格式如下:
GET /restfs/v1/<Path>:<Suffix> HTTP/1.1
Host : <Host>:<Port>
x-baidu-ugi : <ugi>
x-bce-request-id: <requestID>
URI后缀默认为content,因此获取资源内容时可以省略。
调用接口出错后,将不会返回结果数据。调用方可根据错误码和错误信息来定位错误原因。 当调用出错时,HTTP 请求返回一个 4xx 或 5xx 的 HTTP 状态码,返回的消息体中是具体的错误代码及错误信息和requestId。
例如:
HTTP/1.1 404 Not Found
Date: Wed, 12 Oct 2009 17:50:00 GMT
Content-Type: application/json
Content-Length: 104
x-bce-request-id: 9899dc5a-c5c7-4175-b851-0eef65cb6543
{
"requestId" : "9899dc5a-c5c7-4175-b851-0eef65cb6543",
"code" : "NoSuchObject",
"message" : "File does not exist: /nonExist"
}
下表列出了所有errorCode和其含义:
Error Code | Description | HTTP Status Code |
---|---|---|
BadDigest | 请求MD5与内容不匹配 | 400 Bad Request |
Conflict | 语义冲突 | 409 Conflict |
EOF | truncate操作越过文件末尾 | 400 Bad Request |
IncompleteBody | 请求长度与HTTP头部指定的Content-Length不匹配 | 400 Bad Request |
InsufficientStorage | 超quota | 507 Insufficient Storage |
InternalError | 服务器内部错误,包括读到坏块,服务不可用等 | 500 Internal Server Error |
InvalidArgument | 非法参数 | 400 Bad Request |
InvalidConnectionID | ConnectionID不存在 | 400 Bad Request |
InvalidRange | Range越界或参数不合法 | 416 Requested Range Not Satisfiable |
InvalidURI | 无法解析URI | 400 Bad Request |
MethodNotAllowed | 不支持该方法 | 405 Method Not Allowed |
MissingContentLength | 请求需要指定Content-Length | 411 Length Required |
MissingSecurityElement | 请求未发送UGI | 400 Bad Request |
NonAuthorized | 用户权限验证失败 | 403 Forbidden |
NoSuchObject | 找不到请求的对象 | 404 Not Found |
SlowDown | 降低请求频度 | 503 Slow Down |
一期需要支持两种方式的认证,Hadoop Ugi和Giano,传输采用明文方式(内网环境可以不用考虑被监听)。Ugi验证需要在Header中指定x-baidu-ugi,Giano方式需要在Header中指定指定x-baidu-credential,两种方式二选一。每个请求都需要附带验证信息,这样做的好处是可以做到请求无状态。
使用ugi方式来验证需要附带x-baidu-ugi头部信息,请求语法如下:
DELETE /restfs/v1/testFile HTTP/1.1
Host: szwg-ecomon-hdfs.dmop.baidu.com:54310
x-baidu-ugi: eky,123456
x-bce-request-id: <requestID>
使用ugi方式来验证需要附带x-baidu-ugi头部信息,请求语法如下:
DELETE /restfs/v1/testFile HTTP/1.1
Host: szwg-ecomon-hdfs.dmop.baidu.com:54310
x-baidu-credential: CtQCCo8CCiQ2ZDhlMzExYS04YzkyLTQ2MzMtYmI5YS0wM2VjM2ZjYmJlOTkSB0JBQVNfQ0EaLwoJc2hleWluZzAxEg5iYWFzX2FsbF9yb2xlcyISYmFhc19kZWZhdWx0X2dyb3VwIp4BLS0tLS1CRUdJTiBSU0EgUFVCTElDIEtFWS0tLS0tCk1FWUNRUURLTVh2Vkd2QUdWeXVIMU9sdWh5NWQ3WjQvVzJpcjVNUjhEQnIxWG9LNTVmWC9oWjc4aEZ4cm92djMKMlRMYVkzT3h0OXFiejFNcWU1R0tZMW5xREVZUkFnRUQKLS0tLS1FTkQgUlNBIFBVQkxJQyBLRVktLS0tLQoosMnWzP1RMLCJkurYdhJAlvMQ87dmATCQu71uDmf35b9OYOeZ7kD4TihpjNjA7v0ZI68ABuR4AaaRGWv/U0lddh8CWJSwYp+9yXZExfFGQRJcChgKCXNoZXlpbmcwMRiKrq/8ASDIh+nN/VESQEYLHlXWAtnv+hHJhPfW/an4ezmW9Ng8Fp0ek+kB0IKXKslfJLHBtBObvVqVQqUnWr1oG5CmlnnnJfgFvQ7dX2k=
x-bce-request-id: <requestID>
Hadoop Ugi为原有的校验方式,Giano校验需要依赖Giano的Library来login生成Credential,具体接口和使用方法请参考Giano。
StatFS请求由MPA处理,该请求获取当前文件系统的状态信息,包括存储已使用容量和剩余容量大小。
GET /restfs/v1 HTTP/1.1
Host: <HOST>:<PORT>
x-baidu-ugi: <ugi>
x-bce-request-id: <requestID>
该方法没有特殊的parameter。
该方法没有特殊的header。
该方法不支持URI Suffix。
HTTP Status Code | Description |
---|---|
200 OK | 正常响应 |
403 Forbidden | 无访问权限 |
500 Internal Server Error | Server内部错误,异常信息包含在body中 |
该方法响应没有特殊的header。
响应Body为json对象,各字段含义如下:
Name | Type | Description |
---|---|---|
used | Number | 存储系统已使用空间大小(bytes) |
avail | Number | 存储系统剩余空间大小(bytes) |
capacity | Number | 存储系统总容量(bytes) |
如果操作成功,Server可能会返回如下的响应:
HTTP/1.1 200 OK
Date: Wed, 12 Oct 2009 17:50:00 GMT
Content-Type: application/json
Content-Length: 104
x-bce-request-id: <requestID>
{
"used" : 20524013415132,
"avail" : 23881916491780,
"capacity" : 43668113649217
}
Delete请求由MPA处理,用于删除用户指定的集群路径的一个资源(文件 or 目录)。
DELETE /restfs/v1/<PATH> HTTP/1.1
Host: <HOST>:<PORT>
x-baidu-ugi: <ugi>
x-bce-request-id: <requestID>
Parameter Name | Description | Example | Default |
---|---|---|---|
recursive | 是否递归删除文件,如果指定为false,删除一个非空目录时会返回异常,错误码为Conflict | true | true |
该方法没有特殊的header。
该方法不支持URI Suffix。
HTTP Status Code | Description |
---|---|
204 NoContent | 删除成功 |
404 Not Found | 待删除文件不存在 |
409 Conflict | 删除目录非空,且参数recursive指定为false |
403 Forbidden | 无访问权限 |
500 Internal Server Error | Server内部错误,删除文件/目录失败,错误原因会包括在body中 |
该方法响应没有特殊的header。
该方法响应没有Body。
例如,要删除hdfs://szwg-ecomon-hdfs.dmop.baidu.com:54310/testFile这个文件的请求:
DELETE /restfs/v1/testFile HTTP/1.1
Host: szwg-ecomon-hdfs.dmop.baidu.com:54310
x-baidu-ugi: eky,123456
x-bce-request-id: 9899dc5a-c5c7-4175-b851-0eef65cb6543
删除成功,Server会返回如下的响应:
HTTP/1.1 204 NoContent
Date: Wed, 12 Oct 2009 17:50:00 GMT
Content-Length: 0
x-bce-request-id: 9899dc5a-c5c7-4175-b851-0eef65cb6543
如果删除失败,则Server会根据失败的原因返回不同的状态码。例如,待删除文件不存在会返回:
HTTP/1.1 404 Not Found
Date: Wed, 12 Oct 2009 17:50:00 GMT
Content-Type: application/json
Content-Length: 104
x-bce-request-id: 9899dc5a-c5c7-4175-b851-0eef65cb6543
{
"requestID" : "9899dc5a-c5c7-4175-b851-0eef65cb6543"
"code" : "NoSuchObject",
"message" : "File does not exist: /testFile"
}
Get Content请求由MPA转发给DPA处理,用来读取一个文件的内容,如果对一个目录调用Get Content则返回异常,错误码为Conflict。
GET /restfs/v1/<PATH>[:content] HTTP/1.1
Host: <HOST>:<PORT>
x-baidu-ugi: <ugi>
x-bce-request-id: <requestID>
该方法不支持参数设置。
Header Name | Description | Example | Default |
---|---|---|---|
Range | 用来指定读取的范围 | Range: 0-1024 | Range: 0-<FILE_LEN> |
请求后缀表示请求期望访问对象的类型,参见URI Suffix。请求资源内容的后缀为content,默认后缀也是content,因此可以设置为content或者不设置。
HTTP Status Code | Description |
---|---|
200 OK | 正常返回 |
404 Not Found | 文件不存在 |
403 Forbidden | 无访问权限 |
409 Conflict | Get Content不支持访问目录 |
500 Internal Server Error | Server内部错误,无法获取文件/目录内容,错误原因会包括在body中 |
Header Name | Description | Example |
---|---|---|
Location | Http协议数据代理所在的URI | Location: http://10.215.139.31:7001 |
Response body为读取文件的内容。
例如,要获取hdfs://szwg-ecomon-hdfs.dmop.baidu.com:54310/example的内容,Client的提交如下请求到MPA:
GET /restfs/v1/example HTTP/1.1
Host: szwg-ecomon-hdfs.dmop.baidu.com:54310
x-baidu-ugi: eky,123456
x-bce-request-id: 9899dc5a-c5c7-4175-b851-0eef65cb6543
MPA响应307 TEMPORARY_REDIRECT将请求重定向到某个DN节点,由其代理执行:
HTTP/1.1 307 TEMPORARY_REDIRECT
Date: Wed, 12 Oct 2009 17:50:00 GMT
Location: http://<DATAPROXY>:<PORT>
Content-Length: 0
Client遵从重定向,获取的数据响应如下:
HTTP/1.1 200 OK
Date: Wed, 12 Oct 2009 17:50:00 GMT
Content-Type: application/octet-stream
Content_Length: 10
x-bce-request-id: 9899dc5a-c5c7-4175-b851-0eef65cb6543
Hello, eky!
Locate Resource请求由MPA处理,用来获取资源的所有副本位置,如果资源为文件,返回的结果为该文件的所有block的副本位置;如果资源为目录,返回的结果为目录包含的一级文件的所有block的副本位置。
请求格式如下:
Get /restfs/v1/<PATH>:loc HTTP/1.1
Host: <HOST>:<PORT>
x-baidu-ugi: <ugi>
x-bce-request-id: <requestID>
该方法没有特殊的Parameter。
该方法没有特殊的Header。
URI后缀表示请求期望访问对象的类型,参见URI Suffix。请求获取资源的副本位置操作需要显示指定URI后缀为loc。
HTTP Status Code | Description |
---|---|
200 OK | 正常响应 |
403 Forbidden | 无访问权限 |
404 Not Found | 资源不存在 |
500 Internal Server Error | Server内部错误,异常信息包含在body中 |
该方法响应没有特殊的header。
Server响应的body为json对象,每个数组成员为路径下的文件所包含所有块的副本信息,json对象各包含的字段和解释如下:
Name | Type | Description |
---|---|---|
basedir | String | 父目录绝对路径 |
children | Array | 父目录包含的所有子节点信息 |
atime | Number | 资源的访问时间,自1970年1月1日0时起的毫秒数,目录的atime为0 |
bsize | Number | 资源的块大小,目录的块大小为0 |
group | String | 资源的组名称 |
len | Number | 资源的大小,目录大小为0 |
mtime | Number | 资源的修改时间,自1970年1月1日0时起的毫秒数 |
owner | String | 资源的所有者 |
name | String | 资源的名字(不包含路径信息) |
perm | String | 资源的权限信息,包含所有者权限、同组权限和其他权限,用9个字符的字符串表示,例如:"rwxr-xr-x" |
repl | Number | 资源的副本数,目录的副本数表示该目录的直接child的默认副本 |
type | String | 表示资源是目录还是文件,取值为[FILE, DIRECTORY] |
chunks | Array | 文件包含的所有块信息 |
如果资源是一个文件,那么返回的结果是:
HTTP/1.1 200 OK
Date: Wed, 12 Oct 2009 17:50:00 GMT
Content-Type: application/json
Transfer-Encoding: chunked
x-bce-request-id: 9899dc5a-c5c7-4175-b851-0eef65cb6543
{
"basedir" : <parentDir>,
"children" :
[
{
"name" : "aFile",
"atime" : 1320173277227,
"bsize" : 268435456,
"group" : "eky",
"len" : 268435456,
"mtime" : 1320173277227,
"owner" : "eky",
"perm" : "r--------",
"repl" : 3,
"type" : "FILE"
"chunks" :
[
[
"10.215.139.31","10.215.139.32","10.215.139.33"
],
...
]
}
]
}
如果访问的资源是目录,children字段会包含该目录下所有子文件的块信息:
HTTP/1.1 200 OK
Date: Wed, 12 Oct 2009 17:50:00 GMT
Content-Type: application/json
Transfer-Encoding: chunked
x-bce-request-id: 9899dc5a-c5c7-4175-b851-0eef65cb6543
{
"basedir" : <parentDir>,
"children" :
[
{
"name" : "aFile",
"atime" : 1320173277227,
"bsize" : 268435456,
"group" : "eky",
"len" : 268435456,
"mtime" : 1320173277227,
"owner" : "eky",
"perm" : "r--------",
"repl" : 3,
"type" : "FILE"
"chunks" :
[
[
"10.215.139.31","10.215.139.32","10.215.139.33"
],
...
]
},
{
"name" : "aDirectory",
"atime" : 1320173277227,
"bsize" : 268435456,
"group" : "eky",
"len" : 0,
"mtime" : 1320173277227,
"owner" : "eky",
"perm" : "r--------",
"repl" : 3,
"type" : "Directory"
"chunks" : []
},
...
]
}
Get Attributes请求由MPA处理,用来获取一个资源(目录 or 文件)的属性信息。
GET /restfs/v1/<PATH>:attr HTTP/1.1
Host: <HOST>:<PORT>
x-baidu-ugi: <ugi>
x-bce-request-id: <requestID>
该方法不支持参数设置。
该方法无特殊头部。
请求后缀表示请求期望访问对象的类型,参见URI Suffix。请求资源属性的后缀为attr,必须显示在URI中指定。
HTTP Status Code | Description |
---|---|
200 OK | 正常返回 |
404 Not Found | 文件/目录不存在 |
403 Forbidden | 无访问权限 |
500 Internal Server Error | Server内部错误,无法获取文件/目录的属性,错误信息会包括在body中 |
该方法响应没有特殊的header。
Get Attributes方法的响应为Json对象,该对象包括的字段名和各字段的解释如下:
Name | Type | Description |
---|---|---|
atime | Number | 资源的访问时间,自1970年1月1日0时起的毫秒数,目录的atime为0 |
bsize | Number | 资源的块大小,目录的块大小为0 |
group | String | 资源的组名称 |
len | Number | 资源的大小,目录大小为0 |
mtime | Number | 资源的修改时间,自1970年1月1日0时起的毫秒数 |
owner | String | 资源的所有者 |
name | String | 资源的名字(不包含路径信息) |
perm | String | 资源的权限信息,包含所有者权限、同组权限和其他权限,用9个字符的字符串表示,例如:"rwxr-xr-x" |
repl | Number | 资源的副本数,目录的副本数表示该目录的直接child的默认副本 |
type | String | 表示资源是目录还是文件,取值为[FILE, DIRECTORY] |
例如,下面的请求获取了hdfs://szwg-ecomon-hdfs.dmop.baidu.com:54310/example的属性信息:
GET /restfs/v1/example:attr HTTP/1.1
Host: szwg-ecomon-hdfs.dmop.baidu.com:54310
x-baidu-ugi: eky,123456
x-bce-request-id: 9899dc5a-c5c7-4175-b851-0eef65cb6543
MPA会以json格式返回文件/目录的属性信息:
HTTP/1.1 200 OK
Date: Wed, 12 Oct 2009 17:50:00 GMT
Content-Type: application/json
Content-Length: 140
x-bce-request-id: 9899dc5a-c5c7-4175-b851-0eef65cb6543
{
"atime" : 0,
"bsize" : 0,
"group" : "eky",
"len" : 0,
"mtime" : 1320173277227,
"owner" : "eky",
"name" : "example",
"perm" : "r--------",
"repl" : 3,
"type" : "DIRECTORY"
}
Get Listing请求由MPA处理,语义同linux shell中的ls,对于目录返回一个该目录所有child属性信息,对于文件则是返回该文件的属性信息。考虑到在表达单个文件和包含单个文件的目录时存在歧义,所以增加了一个basedir的字段,用来表述返回对象的公共父节点绝对路径。
GET /restfs/v1/<PATH>:list HTTP/1.1
Host: <HOST>:<PORT>
x-baidu-ugi: <ugi>
x-bce-request-id: <requestID>
Parameter Name | Description | Example | Default |
---|---|---|---|
details | 是否显示所有属性,默认为false,仅显示资源名称 | true | false |
该方法没有特殊的Header。
请求后缀表示请求期望访问对象的类型,参见URI Suffix。Get Listing方法的后缀为list,必须显示在URI中指定。
HTTP Status Code | Description |
---|---|
200 OK | 正常返回 |
404 Not Found | 目录不存在 |
403 Forbidden | 无访问权限 |
500 Internal Server Error | Server内部错误,无法获取文件/目录的属性,错误信息会包括在body中 |
该方法响应没有特殊的header。
Get Listing方法的响应结果为json对象,所有字段的解释如下:
Name | Type | Description |
---|---|---|
basedir | String | 父目录绝对路径 |
children | Array | 父目录包含的所有子节点信息 |
atime | Number | 资源的访问时间,自1970年1月1日0时起的毫秒数,目录的atime为0 |
bsize | Number | 资源的块大小,目录的块大小为0 |
group | String | 资源的组名称 |
len | Number | 资源的大小,目录大小为0 |
mtime | Number | 资源的修改时间,自1970年1月1日0时起的毫秒数 |
owner | String | 资源的所有者 |
name | String | 资源的名字(不包含路径信息) |
perm | String | 资源的权限信息,包含所有者权限、同组权限和其他权限,用9个字符的字符串表示,例如:"rwxr-xr-x" |
repl | Number | 资源的副本数,目录的副本数表示该目录的直接child的默认副本 |
type | String | 表示资源是目录还是文件,取值为[FILE, DIRECTORY] |
默认不指定details为true的情况下,MPA的响应类似于:
HTTP/1.1 200 OK
Date: Wed, 12 Oct 2009 17:50:00 GMT
Content-Type: application/json
Transfer-Encoding: chunked
x-bce-request-id: 9899dc5a-c5c7-4175-b851-0eef65cb6543
{
"basedir" : "/example",
"children" :
[
{
"name" : "aDirectory",
"type" : "DIRECTORY"
},
{
"name" : "aFile",
"type" : "FILE"
},
...
]
}
当用户指定了details为true时,MPA返回的结果会包含完整的信息:
HTTP/1.1 200 OK
Date: Wed, 12 Oct 2009 17:50:00 GMT
Content-Type: application/json
Transfer-Encoding: chunked
x-bce-request-id: 9899dc5a-c5c7-4175-b851-0eef65cb6543
{
"basedir" : "/example",
"children" :
[
{
"atime" : 0,
"bsize" : 0,
"group" : "eky",
"len" : 0,
"mtime" : 1320173277227,
"owner" : "eky",
"name" : "aDirectory",
"perm" : "r--------",
"repl" : 3,
"type" : "DIRECTORY"
},
{
"atime" : 1320173277227,
"bsize" : 268435456,
"group" : "eky",
"len" : 10,
"mtime" : 1320173277227,
"owner" : "eky",
"name" : "aFile",
"perm" : "r--------",
"repl" : 3,
"type" : "FILE"
},
...
]
}
Set Attribute请求由MPA处理,用来更改一个文件/目录的属性信息。包括文件副本,文件长度截断,或者将已有的文件/目录重命名到新的路径下。目前暂时仅支持一个请求修改一个属性信息。
PUT /restfs/v1/<PATH> HTTP/1.1
Host: <HOST>:<PORT>
x-baidu-ugi: <ugi>
x-bce-request-id: <requestID>
Parameter Name | Description | Example | Default |
---|---|---|---|
permission | 文件/目录的访问权限,3位8进制的数 | 755 | <CURRENT_PERMISSION> |
replication | 文件/目录的副本数,可以设置[1,100]的值 | 3 | <CURRENT_REPLICATION> |
mtime | 文件/目录的修改时间 | 1320173277227 | <CURRENT_MTIME> |
atime | 文件的访问时间(目录不记录访问时间) | 1320173277227 | <CURRENT_ATIME> |
owner | 文件的所有者,只有root权限才能做修改操作 | eky | <CURRENT_OWNER> |
group | 资源的组名称,只有root权限才能做修改操作 | eky | <CURRENT_GROUP> |
path | 文件的路径,设置该参数表示将文件/目录重命名到新的路径下 | /dstPath | <CURRENT_PATH> |
length | 文件的长度,截断文件到指定长度,如果长度为负值,则表示对文件做recover操作 | 0 | <CURRENT_LEN> |
该方法没有特殊的Header。
该方法不支持URI Suffix。
HTTP Status Code | Description |
---|---|
200 OK | 正常返回 |
400 Bad Request | 请求参数错误,例如截断长度越界 |
404 Not Found | 文件/目录不存在 |
403 Forbidden | 无访问权限 |
500 Internal Server Error | Server内部错误,无法设置文件/目录的属性,错误原因会包括在body中 |
该方法响应没有特殊的header。
该方法响应没有Body。
下面的请求设置文件/example的副本为20:
PUT /restfs/v1/example?replication=20 HTTP/1.1
Host: szwg-ecomon-hdfs.dmop.baidu.com:54310
x-baidu-ugi: eky,123456
x-bce-request-id: 9899dc5a-c5c7-4175-b851-0eef65cb6543
Client收到的响应格式如下:
HTTP/1.1 200 OK
Date: Wed, 12 Oct 2009 17:50:00 GMT
Content-Length: 0
x-bce-request-id: 9899dc5a-c5c7-4175-b851-0eef65cb6543
通过path参数可以将指定路径下的文件/目录重命名到新的路径下,例如:
PUT /restfs/example?path=/destPath HTTP/1.1
Host: szwg-ecomon-hdfs.dmop.baidu.com:54310
x-baidu-ugi: eky,123456
x-bce-request-id: 9899dc5a-c5c7-4175-b851-0eef65cb6543
Create分为创建文件和创建目录,通过URI的格式来区别两种操作,以非/结尾的URI表示创建文件,以/结尾的URI表示创建目录。创建文件/目录需要对该目录的写权限,如果收到创建成功的响应,那么该文件可以确保已经成功创建,否则会收到响应的报错信息。创建文件包括创建通常文件和创建hardlink,通过是否有参数linkTarget来区分。
对于创建文件的Case,参数append为true表示追加写入,append为false且overwrite为true表示覆盖已有文件。另外创建文件/目录时,可以通过参数指定文件/目录的属性。
创建文件分为两个步骤,创建文件的元数据,和实际的数据写入。元数据请求发给MPA,MPA完成原数据操作后会响应201 Created,将请求重定向到某个DN完成剩余的数据协议。如果采用Http协议写入,Client可以遵从MPA的重定向,将携带数据的请求发给DN完成数据写入。Client也可以采用流式协议来写入,完成元数据创建后,同本地数据代理完成剩余的数据写入工作,参见Stream协议。Client可以根据需求自行选择合适的写入模式。
创建目录请求直接由MPA完成,并响应成功或失败。
POST /restfs/v1/<PATH> HTTP/1.1
Host: <HOST>:<PORT>
x-baidu-ugi: <UGI>
x-bce-request-id: <requestID>
Parameter Name | Description | Default |
---|---|---|
append | 是否追加写入,设置为true表示追加写入 | false |
overwrite | 写入文件时是否覆盖已有文件 | true |
permission | 文件/目录的访问权限,3位8进制的数 | 755 |
replication | 文件/目录的副本数,可以设置[1,100]的值 | 3 |
linktarget | 创建hardlink的target文件,该参数具有最高优先级,指定之后创建的hardlink文a件的所有属性和target | null |
blocksize | 文件的块大小 | 268435456 |
该方法不支持特殊的header。
HTTP Status Code | Description |
---|---|
200 OK | 正常返回 |
403 Forbidden | 无访问权限 |
409 Conflict | 指定路径上已存在文件/目录 |
500 Internal Server Error | Server内部错误,创建文件/目录,错误原因会包括在body中 |
Header Name | Description | Example |
---|---|---|
Location | Http协议数据代理所在的URI | Location: http://10.215.139.31:7001 |
该方法响应没有Body。
Create操作分为创建文件和创建目录,通过URI的格式来区别两种操作,以/结尾表示目录。
-
创建文件需要先向元数据代理提交一个不带数据的POST请求来创建文件的INODE,创建文件/example的INODE请求如下:
POST /restfs/v1/example HTTP/1.1 Host: szwg-ecomon-hdfs.dmop.baidu.com:54310 x-baidu-ugi: eky,123456 x-bce-request-id: 9899dc5a-c5c7-4175-b851-0eef65cb6543
返回的响应格式如下:
HTTP/1.1 201 Created Date: Wed, 12 Oct 2009 17:50:00 GMT Content-Length: 0 Location: http://10.215.139.31:7001 x-bce-request-id: 9899dc5a-c5c7-4175-b851-0eef65cb6543
Location表示Http协议数据代理的URI,ClienT再提交另一个携带写入数据的HTTP请求到Location指定的数据代理:
POST /restfs/v1/<PATH> HTTP/1.1 Host: <LOCAL_HOST>:7001 Content-Length: 11434 Content-Type: text/plain x-baidu-ugi: eky,123456 x-bce-request-id: 9899dc5a-c5c7-4175-b851-0eef65cb6543 [11434 bytes of object data]
写入完成请求方会接受到201 Created的响应:
HTTP/1.1 200 OK Date: Wed, 12 Oct 2009 17:50:00 GMT Content-Length: 0 x-bce-request-id: 9899dc5a-c5c7-4175-b851-0eef65cb6543
-
创建目录/example/的请求如下:
POST /restfs/v1/example/ HTTP/1.1 Host: szwg-ecomon-hdfs.dmop.baidu.com:54310 x-baidu-ugi: eky,123456 x-bce-request-id: 9899dc5a-c5c7-4175-b851-0eef65cb6543
Client收到的请求格式如下:
HTTP/1.1 201 Created Date: Wed, 12 Oct 2009 17:50:00 GMT Content-Length: 0 x-bce-request-id: 9899dc5a-c5c7-4175-b851-0eef65cb6543
-
创建hardlink的/hardlink/的请求如下:
POST /restfs/v1/hardlink HTTP/1.1 Host: szwg-ecomon-hdfs.dmop.baidu.com:54310 x-baidu-ugi: eky,123456 x-bce-request-id: 9899dc5a-c5c7-4175-b851-0eef65cb6543
Client收到的请求格式如下:
HTTP/1.1 201 Created Date: Wed, 12 Oct 2009 17:50:00 GMT Content-Length: 0 x-bce-request-id: 9899dc5a-c5c7-4175-b851-0eef65cb6543
Checksum请求由MPA处理,用来获取一个文件的md5值,对目录调用checksum方法会返回Conflict.
GET /restfs/v1/<PATH>:checksum HTTP/1.1
Host: <HOST>:<PORT>
x-baidu-ugi: <ugi>
x-bce-request-id: <requestID>
该方法不支持参数设置。
该方法没有特殊的Header。
请求后缀表示请求期望获取对象的md5值,参见URI Suffix。请求资源属性的后缀为checksum,必须显示在URI中指定。
HTTP Status Code | Description |
---|---|
200 OK | 正常返回 |
400 Bad Request | 请求参数错误 |
404 Not Found | 文件/目录不存在 |
403 Forbidden | 无访问权限 |
409 Conflict | 无法对目录类型资源获取checksum |
500 Internal Server Error | Server内部错误,无法设置文件/目录的属性,错误原因会包括在body中 |
该方法响应没有特殊的header。
该方法响应没有Body。
下面的请求设置文件/example的副本为20:
PUT /restfs/v1/example:checksum HTTP/1.1
Host: szwg-ecomon-hdfs.dmop.baidu.com:54310
x-baidu-ugi: eky,123456
x-bce-request-id: 9899dc5a-c5c7-4175-b851-0eef65cb6543
Client收到的响应格式如下:
HTTP/1.1 200 OK
Date: Wed, 12 Oct 2009 17:50:00 GMT
Content-Length: 0
Content-MD5: fb817de5edfb53e6b7a7bf043c2ae6f5
x-bce-request-id: 9899dc5a-c5c7-4175-b851-0eef65cb6543
相对于Http协议而言,流式协议可以既满足错误恢复的需求,又保证吞吐,适用于对性能要求比较高的情况。流式协议的数据代理部署在Client本地,这样Client可以简单地实现成请求应答的形式,因为本地socket不走网卡,所以性能能够保证。
Stream协议目前只支持数据流,Client通过Socket向本地数据代理建立连接,通信包由 header和body 组成。 元数据操作需要先向MPA发送请求,因此写入的流程如下图:
Stream协议除了写入,还支持读和恢复写两种操作。这三种操作无需访问MPA,直接向本地的DPA发起Connect请求,在Op中指明即可。
STRM
<HEADER-LEN>
<HEADER>
<BODY-LEN>
<BODY>
放问的请求和应答都是以上面的格式来发送,各字段解释如下:
STRM : 4 bytes magic header
<HEADER-LEN> : 4 bytes int
<HEADER> : total length is <HEADER-LEN>
<BODY-LEN> : 4 bytes int
<BODY> : <BODY-LEN> bits of data
<HEADER> : n * <HEADER-LINE>
<HEADER-LINE> : <key>=<value>LF
LF : \n
<key>,<value> : any bytes of data
Stream协议进行读写,Client需要先创建连接,参数必须要指明RequestID,用来区别不同的请求。Connect的响应中会包含ConnectionID,后续所有的请求都需要包含Connect请求响应中相同的ConnectID,否则会报错。
创建连接请求必须以字符串STREAM开头,用于标识STREAM协议。创建连接的请求语法如下:
Op=[OPEN_WRITE|OPEN_READ|OPEN_RECOVER]LF
Host=<scheme>://<host>:<port>LF
Path=<path>LF
[Ugi=<user>:<pwd>LF |
Credential=<signatureValue>LF]
[Offset=<offset>LF]
RequestID=<requestID>LF
[BufferSize=<bufferSize>LF]
$<user.conf.name>=<value>LF
参数含义如下表:
Name | Description | Required |
---|---|---|
OP | 操作类型,OPEN_WRITE表示写入,OPEN_READ表示读取,OPEN_RECOVER表示恢复写入失败 | 必须 |
RequestID | RequestID唯一地对应了一个请求,可用于问题定位、性能分析等等多个场景。requestId使用UUID version 4,暂时由Client自行生成,后续考虑统一由BFE生成。 | 必须 |
Offset | 恢复写入失败的truncate长度,没有设置默认truncate到上一次的sync点 | 非必须 |
Host | 访问的集群,例如hdfs://szwg-dmop-ecomon.szwg01:54310 | 必须 |
Path | 操作路径 | 必须 |
Ugi | 用户权限信息,与Credential二选一 | 非必须 |
Credential | 使用新门神登陆之后的认证信息,Server端会使用公钥来解密这个票据,得到用户对应的角色,参见 Authentication | 非必须 |
$<user.conf.name> | 用户自定义参数,$开头用于区分内建参数,例如写入慢节点配置,例如$dfs.client.slow.write.limit=1 | 非必须 |
OP参数合法值以及解释如下:
Name | Descirption |
---|---|
OPEN_WRITE | 打开写入流 |
OPEN_READ | 打开读入流 |
OPEN_RECOVER | 写入过程中,Client和代理之间的异常(包括网络异常、代理异常)可能导致写入失败,由Client向DPA发起写入恢复请求,操作成功后可以继续写入。client可设置Offset参数来指定恢复后的长度,如果不指定默认恢复到上一次sync点 |
Connect请求不包含body,所以为0。
连接建立成功:
Status=${Current_Offset_in_File}LF
ConnectionID=<connectionID>LF
RequestID=<requestID>LF
连接建立失败:
Status=${ErrorCode}LF
RequestID=<requestID>LF
ErrorMessage=${ErrorMessage}LF
各字段含义如下表:
Name | Description |
---|---|
Status | 操作的状态,成功为当前的Offset,失败为ErrorCode,ErrorCode类型参见Error Responses |
RequestID | 对应请求中的RequestID。 |
ConnectionID | 对应请求的上下文,后续请求需要携带响应的ConnectionID |
ErrorMessage | 错误信息 |
Connect请求的响应不包含body,所以为0。
创建连接之后,Client可以发送具体的写入数据和控制指令,请求的语法如下:
OP=<OP>LF
[Offset=<offset>LF]
[Len=<len>LF]
RequestID=<requestID>LF
ConnectionID=<connectionID>LF
参数含义如下表:
Name | Description | Required |
---|---|---|
OP | 操作类型 | 必须 |
Offset | 写入的Offset | 除OP=WRITE情况时必须 |
Len | 写入的长度,长度和<BODY_LEN>不一致会报错 | OP=WRITE情况时必须 |
RequestID | RequestID唯一地对应了一个请求,可用于问题定位、性能分析等等多个场景。requestId使用UUID version 4,暂时由Client自行生成,后续考虑统一由BFE生成。 | 必须 |
ConnectionID | 创建连接请求响应的ConnectionID,DPA用来查找请求的上下文 | 必须 |
OP参数合法值以及解释如下:
Name | Descirption |
---|---|
WRITE | 写入数据 |
CLOSE | 关闭流 |
SYNC | 将数据从代理的buffer写入到DataNode中,同时最新写入的数据对其他用户可见 |
FLUSH | 将数据从代理的buffer写入到DataNode中,最新写入的数据对其他用户不可见,只用来保证数据不丢,便于写入异常时通过recover恢复 |
HEARTBEAT | Client必须向Server发送心跳,Server一段时间之内(默认1分钟)没有收到client的心跳会认为client已经挂掉,并回收资源 |
OP为WRITE的情况下可以携带BODY,BODY为写入的内容,<BODY_LEN>为写入长度,其余情况无BODY。
DPA将数据刷到本地Buffer中之后,DPA会返回一个响应,表示数据写入成功,只要DPA不是异常退出,数据就不会丢失。
另外收到sync/flush写入命令的时候也会返回此响应,对于sync命令来说,DPA会同步收到sync成功的指令再返回;flush指令会等待所有DN ACK之后再响应。
响应语法为:
Status=${Status}LF
RequestID=<requestID>LF
[ErrorMessage=${ErrorMessage}LF]
参数含义如下表:
Name | Description | Required |
---|---|---|
Status | 返回的状态码,OK表示成功,否则为错误码,错误码参见Error Responses | 必须 |
RequestID | 对应于请求携带的RequestID | 必须 |
ErrorMessage | 错误码的详细解释· | Status不为OK时携带 |
读数据请求,在建立连接时指定Op为OPEN_READ,语法参见Conntect。
建立连接后可以进行数据读取,读取的请求需要携带RequestID字段语法如下:
OP=<OP>LF
[Offset=<offset>LF]
[Len=<len>LF]
[Pread=<true|false>LF]
RequestID=<requestID>LF
ConnectionID=<connectionID>LF
参数含义如下表:
Name | Description | Required |
---|---|---|
Offset | 读取的Offset | OP=READ时必须 |
Len | 读取的长度 | OP=READ时必须 |
RequestID | RequestID唯一地对应了一个请求,可用于问题定位、性能分析等等多个场景。requestId使用UUID version 4,暂时由Client自行生成,后续考虑统一由BFE生成。 | 必须 |
ConnectionID | 创建连接请求响应的ConnectionID,DPA用来查找请求的上下文 | 必须 |
Pread | Read会修改游标,因此线程不安全,多线程访问同一个文件句柄,可能会数据混乱;Pread不会修改游标,因此线程安全,多线程访问同一个文件句柄,数据不会混乱。不设置的情况下默认为false | 非必须 |
OP参数合法值以及解释如下:
Name | Descirption |
---|---|
READ | 读取数据 |
CLOSE | 关闭流 |
SEEK | seek到新的position |
TELL | 返回当前的position |
HEARTBEAT | Client必须向Server发送心跳,Server一段时间之内(默认1分钟)没有收到client的心跳会认为client已经挂掉,并回收资源 |
响应语法为:
Status=${Status}LF
RequestID=<requestID>LF
[Len=<len>LF]
[ErrorMessage=${ErrorMessage}LF]
参数含义如下表:
Name | Description | Required |
---|---|---|
Status | 返回的状态码,OK表示成功,否则为错误码,错误码参见Error Responses | 必须 |
RequestID | 对应于请求携带的RequestID | 必须 |
Len | 读取的长度 | OP=READ时必须 |
ErrorMessage | 错误码的详细解释 | Status不为OK时携带 |