TFS文件系统搭建笔记

TFS文件系统搭建笔记

参考地址:

https://github.com/alibaba/tfs/blob/master/INSTALL.md

https://github.com/alibaba/tfs/blob/master/DEPLOY.md

http://tfs.taobao.org/

http://code.taobao.org/p/tfs/wiki/get/

http://csrd.aliapp.com/

 

软件下载地址(源码):

TFS:https://github.com/alibaba/tfs

Nginx模块:https://github.com/alibaba/nginx-tfs

utils:https://github.com/raywill/tb-common-utils

 

安装命令:

gcc环境官方推荐gcc-4.1.2(gcc4.8.2亲测失败)

1.安装GCC

http://ftp.gnu.org/gnu/gcc     下载GCC

wget http://ftp.gnu.org/gnu/gcc/gcc-4.1.2/gcc-4.1.2.tar.bz2    在线下载GCC

tar -jxvf gcc-4.8.2.tar.bz2    解压GCC源码

cd gcc-4.8.0

./contrib/download_prerequisites   下载编译所需配置文件

mkdir gcc-build-4.1.2

cd gcc-build-4.1.2

../configure -enable-checking=release -enable-languages=c,c++ -disable-multilib    生成安装文件

make -j4    根据电脑核心数,编译

make install

gcc -v  查看版本

 

2.安装tfs环境

yum源最好自己配置一下,我用网易yum,以下命令使用yum即可,apt-getUbuntu命令

安装依赖的软件包

  • automake TFS基于automake工具构建
  • libtool automake需要使用libtool
  • realine 用于命令行编辑的库
  • libz-devel 用于数据压缩/解压缩
  • uuid-devel 用于生成全局唯一ID
  • tcmalloc google的内存管理库
    • 下载源码安装
    • apt-get install libgoogle-perftools-dev
    • yum install google-perftools.x86_64

 

###tar包安装tcmalloc;google的内存管理工具(TFS参考了google的GFS,所以用到了这个工具)###

参考:http://blog.csdn.net/chen19870707/article/details/40301783

软件包我自己在网上找的:

libunwind-1.1.tar.gz

gperftools-2.4.tar.gz

 

libunwind安装:

tar zxvf libunwind-1.1.tar.gz

cd libunwind-1.1

./configure

make

make install

 

安装google-perftools

tar zxvf tar zxvf gperftools-2.1.tar.gz

cd gperftools-2.1

./configure

make

make install

 

TCMalloc库加载到Linux系统中:

echo "/usr/local/lib" > /etc/ld.so.conf.d/usr_local_lib.conf

/sbin/ldconfig

 

#安装uuid工具

yum install libuuid-devel

 

3.Linux环境好了,开始安装tfs环境

#安装tb-common-utils

unzip tb-common-utils-master.zip

vim /etc/profile

设置环境变量,tb-common-utils-master.zip解压目录(随意)

TBLIB_ROOT=/home/tb-common-utils-master

 

source /etc/profile            重新加载配置文件

cd tb-common-utils-master     进入解压目录开始编译

chmod +x build.sh    给文件+可执行权限

./build.sh执行编译

如果一切顺利,tb-common-utils已经安装成功到$TBLIB_ROOT路径下

 

4.编译安装tfs

cd ../                  进入github下载的源码目录

unzip tfs-master.zip        #解压tfs源码

cd tfs-master  

sh build.sh init     #初始化编译脚本生成configure等文件

./configure --prefix=/opt/tfs-master --with-release        #生成编译文件makefile

make        #执行编译,看最后有没有error,没有就是幸运

make install     #执行安装

cd /opt/tfs-master/          #进入程序安装目录可以看到可执行文件可配置文件

安装后目录结构:

复制源码中的conf下配置文件到安装目录的conf.

修改nameserver(NS)配置文件:

#work directoy

work_dir=/opt/tfs/tfsData

#ip addr(vip)

ip_addr = 192.168.20.102

ip_addr_list = 192.168.20.102|192.168.0.254

group_mask = 255.255.255.255

修改dataserver(DS)配置文件:

 

在成功安装TFS之后,在你的安装目录(默认为~/tfs_bin),包含几个子目录binconfincludeliblogsscript等。

  • bin:包含tfs所有的可执行程序文件,如nameserver(NS)dataserver(DS)tfstool
  • conf:包含tfs的配置文件,如NS的配置文件ns.confDS的配置文件ds.conf
  • include:包含TFS客户端库相关的头文件,应用程序使用TFS需要包含这些头文件。
  • lib包含TFS客户端的静/动态库,应用程序使用TFS需要连接libtfsclient
  • logs:用于存储TFS运行过程中的日志。
  • script:包含tfs常用的一些运维脚本,如stfs用于格式化DS tfs启动/停止NSDS

本文介绍如何搭建TFS存储集群,以两台存储节点(4DS进程)为例,部署拓扑图如下所示:

配置TFS

~/tfs_bin/conf目录下包含TFS的配置文件模板,在部署前需要根据环境修改配置文件,下面对NSDS一些关键的配置项进行说明,所有配置项使用key=value的形式进行配置,每行一项,在行首加#可直接注释配置项使之不生效;使用者应该根据实际情况,修改conf/ns.confconf/ds.conf这两个配置文件。

[public] 通用配置

log_size=1073741824   日志文件大小,当日志大小超过这个配置值时,TFS会进行rotate,产生新的日志文件, 建议不修改

log_num=64  最多保存的日志文件个数,超出会删除最旧的日志文件,建议不修改

log_level=info  日志级别(errorwarninfodebug),生产环境建议info级别,测试环境建议使用debug级别

task_max_queue_size=10240  server最大的请求队列长度,超出会直接丢掉请求包,可根据实际情况修改

work_dir=/home/xxxx/tfs  TFS工作目录,强烈建议直接设置为TFS的安装目录,以方便运维管理

dev_name=bond0  server使用的网卡名,需要修改为你机器的某个网卡名

ip_addr=10.232.36.201  server服务的ip地址,必须是dev_name网卡上的IP地址,如果NS配置了HA,此处配置vip

port=8100  server服务的端口号,根据实际环境修改, 该配置项建议所有DS保持相同

 

[nameserver] 该区域的配置只针对NS有效

safe_mode_time=360  seconds NS启动后的保护时间,在该时间内,NS不会构建任何复制、迁移等任务

ip_addr_list=10.232.36.201|192.168.0.254  如果使用HA,则配置vip对应的两个节点的实际ip;如果没有使用HA,第一项配置与ip_addr相同,第二项可以随便配置一个无效的地址(如本例中192.168.0.254是无效的IP地址)

group_mask=255.255.255.255  用于区分机架的子网掩码,可根据实际网络环境修改。如果你配置为255.255.255.255,那么任意两个ip不同的机器都被认为在不同的机架;如果你配置为255.255.255.0那么192.168.0.x192.168.0.y将被认为在同一个机架,依次类推;TFS数据块的多个副本不能分布在同一个机架内,以提高可靠性。

max_replication=2  副本数,机器数必须大于副本数,单机环境测试,只能设置为1,否则不能存数据

cluster_id=1  集群ID,存文件后生成的文件名会包含这个ID,建议不修改

block_max_size=75497472  Bytes,保持与dataservermainblock_size配置一致,建议不修改

repl_wait_time=180  seconds NS检测到block丢失时,等待多长时间开始复制block,可根据实际情况修改

compact_delete_ratio=10  代表10%,当NS发现block中文件删除比例超出该比例,开始对block进行压缩,回收删除文件的存储空间

compact_hour_range=1~10  代表1~10点,NS只会在1-10点间进行block压缩,建议设置为访问低峰时间段

balance_percent=0.05  代表5%,负载均衡时,当DS存储容量超出平均值5%时,就会被选择为数据迁移源,低于平均值5%,就会被选为数据迁移目标

 

[dataserver] 该区域的配置只针对DS有效

ip_addr=10.232.36.203  NSip地址,如果配置了HA,则为vip

ip_addr_list=10.232.36.201|192.168.0.254  nameserver区域的ip_addr_list配置保持一致

port=8100  NS服务监听的端口

heart_interval=2  secondsDSNS发送心跳的时间间隔,可根据实际环境修改

mount_name=/data/disk  DS数据目录,如果机器上有2块盘,则它们应该挂载在/data/disk1/data/disk2,依次类推

mount_maxsize=961227000  KB, 磁盘实际使用的空间,应该小于df命令Available一项的输出

base_filesystem_type=1  1代表ext43代表ext3,强烈建议使用ext4文件系统

avg_file_size=15360 Bytes  集群内平均文件大小,预测值,可根据实际负载调整

mainblock_size=75497472  Bytes 块的大小,建议不修改

extblock_size=4194304  Bytes 扩展块,扩展块主要用于更新,建议不修改

运行Nameserver

# cd ~/tfs_bin

# ./scripts/tfs start_ns  或者 ./bin/nameserver -f conf/ns.conf -d

如果没有提示错误,则NS就已经在后台开始运行了,可通过ps查看相应进行,或进入logs下,查看nameserver.log,如包含“nameserver running”则说明启动正常

运行Dataserver

在本例的环境中,10.232.36.203/204上都有两块磁盘/dev/sda/dev/sdb,磁盘空间均为1TBTFS采用每个DS进程管理一块磁盘的方式,也就是说,每个机器上都会运行2DS进程,分别管理/dev/sda/dev/sdb, 并且这两块盘应该挂载到/data/disk1/data/disk2两个目录(因为配置文件中指定mount_path/data/disk),启动DS时指定序号(如12)这个DS进程就会加载对应目录里的数据(1对应/data/disk12对应/data/disk2,依次类推)开始服务。

对每个机器上多个DS进程使用的端口,TFS也做了统一以方便运维,比如DS配置port8200,那么序号为1DS进程会在8200端口监听,序号为2DS进程会在8202端口监听,依次类推,序号为nDS8200 + n - 1* 2 号端口上监听。强烈建议每个DS public下的port项都配置相同的值,这样根据端口号就能直接换算出DS的序号(序号一旦确定,通过df命令就能确定具体磁盘),对定位问题很方便。

另外,TFSDS对存储空间有自己独特的管理方式,将可用的存储空间分成多个block进行管理,使用前DS需要先进行format(使用TFS提供的工具),预分配各个block的存储空间,保证block的存储空间在磁盘上连续(理论上),从而避免动态分配时产生大量的磁盘碎片,降低服务效率。

在两台机器上分别进行如下操作,准备好数据目录

# mkfs.ext4 /dev/sda  // 请确保你有相应权限

# mount -t ext4 /dev/sda /data/disk1

# mkfs.ext4 /dev/sdb

# mount -t ext4 /dev/sdb /data/disk2

在两台机器上,对数据目录进行format

# cd ~/tfs_bin

# ./script/stfs format 1  // 结果会在命令行提示

# ./script/stfs format 2

 以上两条命令也可合并为 ./script/stfs format 1-2 ./script/stfs format 1,2

 如果要清理格式化产生的数据,将上述命令中format换成clear即可

格式化成功后,会发现/data/disk1/data/disk2下面产生了一堆以数字命名的文件(1-n),因为TFSblock文件是以数字命名的,从1开始递增编号。

在两台机器上,启动DS服务

# cd ~/tfs_bin

# ./scripts/tfs start_ds 1-2  // 序号的使用与stfs类似,可通过","分隔序号,通过"-"指定范围序列

  ./bin/dataserver -f conf/ds.conf -i 1 -d  (-i 指定序号)

通过ps可查看DS是否正常启动,也可进入logs目录,查看dataserver_i.logi用相应的序号代替),如果包含“dataservice start”,则说明DS启动正常。

开始TFS存储之旅

此时NSDS的服务已经成功的启动了,TFS客户端可以向TFS存取文件,tfstooltfs的命令行工具,可通过该工具向TFS存文件,从TFS取文件。

# ./bin/tfstool -s 10.232.36.201:8100 -i "put testfile"

将本地testfile存入tfs -s 指定nameserverip:port如果成功,会打印 put testfile => T1QEBXXDpXXXXXXXXX success. 类似的消息,其中T开头一串字符是TFS为这个文件生成的文件名,一共18个字符;要想从TFS取出该文件时,需要指定该文件名。

 

# ./bin/tfstoo -s 10.232.36.201:8100 -i "get T1QEBXXDpXXXXXXXXX localfile"

TFS读取T1QEBXXDpXXXXXXXXX,存储到本地localfile中;如果成功,会打印fetch T1QEBXXDpXXXXXXXXX => localfile success.类似的消息。

部署FAQ

Q: format TFS数据目录的过程中失败了?

A: 可能的原因:你对这个目录写权限;目录空间不足,先检查mount_maxsize的配置;目录已经被格式化过,先clear,再format;磁盘换了;具体错误,根据提示的errno来分析。

 

Q: 启动服务的过程中,提示ip 'xxxx' is not local ip, local ip: yyyy类似错误?

A: 确认dev_name对应的网卡是否存在;ip_addr的配置是否是该网卡上的ip地址。

 

Q: 通过tfstool存数据一致不能成功,错误码-5001

A: 检查max_replication的配置,机器数一定要大于副本数。

 

Q: ns.confds.conf中为什么要配置ip_addr_list,而不是都直接使用vip

A: 主备NS为了实现宕机切换后,备NS能够立即服务;主ns会异步将操作日志同步到备NS;同时所有的DS会同时向主备NS发心跳;这样备NS上拥有的信息与主NS基本相同(异步同步会有延时,做不到完全相同),可随时提供服务。基于此原因,nameserver必须知道备NSreal ipDS也必须知道vip对应的主备NSreal ip

 

Q: TFS HA怎么配置?

A: 这个让google回答你吧,淘宝HA的使用方式和配置可能并不适合你;tfsHA软件及配置没有特殊要求,只要能实现主NS宕机vip飘移到备NS即可。

 

Q: 我部署好了TFS服务,有压力测试工具么,我想测试下性能?

A: TFS源代码下test/batch目录里有test_batch_writetest_batch_readtest_batch_mix三个工具。

   示例:./test_batch_write -d ns-ip:port -c 1000 -r 10240:20480 -t 4

   该命令代表往ns-ip:port对应的集群,开启4个线程,每个线程写100010K-20K范围内大小的文件。

 

 

 

 

虚拟磁盘(如果有条件肯定是服务器挂多块物理硬盘测试环境条件不允许那就自己创建虚拟磁盘玩呗,我只给每块虚拟磁盘分了1G容量,以后不够再动态分配)


需要挂载4块硬盘

1.在第一台dataNode上挂载第2块虚拟磁盘:

dd if=/dev/zero of=hdisk1.img bs=1k count=10240000            #创建虚拟磁盘10G

file hdisk1.img                        #查看磁盘信息;data

losetup /dev/loop1 hdisk1.img    #挂载虚拟设备

losetup -a                #显示所有虚拟设备,losetup -d /dev/loop1   卸载设备

fdisk /dev/loop1#查看虚拟磁盘初始状态DOS

mkfs -t ext4 /dev/loop1#格式化成ext4

mkfs.ext4 /dev/loop1        #格式化ext4;如上

file hdisk1.img#查看格式化后属性

    hdisk1.img: Linux rev 1.0 ext4 filesystem data (extents) (large files) (huge files)

mount /dev/loop1 /data/disk1        #挂载虚拟磁盘到/data/disk1目录

mount -t ext4 /dev/loop1 /data/disk1    #挂载虚拟磁盘到/data/disk1目录,并指定格式

 

2.在第一台dataNode上挂载第2块虚拟磁盘:

 

dd if=/dev/zero of=hdisk2.img bs=1k count=10240000            #创建虚拟磁盘10G

losetup /dev/loop2 hdisk2.img  #挂载虚拟设备  

losetup -a                #显示所有虚拟设备

mkfs -t ext4 /dev/loop2#格式化成ext4

mkfs.ext4 /dev/loop2        #格式化ext4;如上

file hdisk2.img#查看格式化后属性

    hdisk2.img: Linux rev 1.0 ext4 filesystem data (extents) (large files) (huge files)

mount /dev/loop2 /data/disk2        #挂载虚拟磁盘到/data/disk1目录

mount -t ext4 /dev/loop2 /data/disk2    #挂载虚拟磁盘到/data/disk1目录,并指定格式

 

3.另一台机器同理

 

4.stfs脚本执行tfs格式化和启动dataNode

./scripts/stfs format 1-2

./scripts/tfs start_ds 1-2

./bin/tfstool -s 192.168.20.102:8100 -i "put testfile"    #上传文件


##因为在配置文件指定文件大小,文件太小刚创建的没有写入任何内容会存入失败###


 ./bin/tfstool -s 192.168.20.102:8100 -i "put bin.tar.gz"    #这个文件较大

红框内提示成功,并且声成了一个唯一文件名称(put bin.tar.gz => T19yETByJT1RCvBVdK success.)


 

##tfstool操作文件上传下载删除等###

 

./bin/tfstool -s 192.168.20.102:8100 -i "put bin.tar.gz" 执行此命令将文件存入TFS

(-i 立即执行,-h help,-s nameserver ip port,-r rcserver ip port)

./bin/tfstool -s 192.168.20.102:8100 -i "get T19yETByJT1RCvBVdK tangwan.gz"  执行此命令将存入的文件取出

./bin/tfstool -s 192.168.20.102:8100 -i "put 8.png"    #上传一个图片一会用浏览器查看

./bin/tfstool -s 192.168.20.102:8100 -i "rm T1SaETByJT1RCvBVdK"    #删除tfs中文件

 

###TFS命令工具

$ /opt/tfs/bin/ssm -s 172.29.1.101:8100

show >server -b    #查看节点

show >server -w

show > machine -a        #查看硬盘使用信息

 

 

tfstool的其他命令行使用方法请参考文档:

 

<<TFS官方文档>>.pdf    运维篇--->工具介绍


 
 

##安装Nginx和Nginx-tfs模块,实现TFS的rest API

 

1.编译环境

yum -y install ruby cmake pcre pcre-devel  openssl openssl-devel  perl perl-devel perl-ExtUtils-Embed    #安装几个软件,安装yajl,nginx-tfs编译需要

 

export GIT_SSL_NO_VERIFY=true    

ruby -v    #查看ruby版本

cd /home/

tar -zxvf yajl-2.0.1.tar.gz     #解压yajl,版本严格按照官方要求2.0.1

cd yajl-2.0.1

 

./configure     #生成编译配置文件

make        #编译

make install        #安装yajl

vim /etc/ld.so.conf.d/ymjl.conf    #加一个配置文件

写入:/usr/local/lib/    #导入yajl的lib库

ldconfig -v    #重新加载lib

tar -zxvf nginx-1.4.7.tar.gz     #

unzip nginx-tfs-master.zip     #解压github下载的模块代码

tar -zxvf tengine-1.5.1.tar.gz    #解压tengine,这个是淘宝开源的基于Nginx的改装的服务器软件,和Nginx用法完全一致

cd tengine-1.5.1    

./configure --add-module=/home/nginx-tfs-master    #带tfs模块编译tengine

make && make install    #编译安装tengine;默认安装到/usr/local/nginx下

cd /usr/local/nginx/conf

mv nginx.conf nginx.conf.bak    #备份nginx.conf,也可以直接删除,里面有一个备份

touch nginx.conf    #创建一个nginx.conf配置文件

vim nginx.conf    #内容参考下面我的配置

./nginx -t    #检查nginx.conf是否有语法错误

sh ../sbin/nginx    #启动nginx

#启动Nginx时如果报错找不到yajl.so库文件,执行下面命令把编译在32位lib下的库软链接到lib64下

 

ln -s /usr/local/lib/libyajl.so.2 /usr/lib64/libyajl.so.2  

ldd $(which /usr/local/nginx/sbin/nginx)

sh ../sbin/nginx -s reload  #重启nginx

 

关键时刻来了:

http://192.168.20.102/v1/tfs/T1SRETByKT1RCvBVdK        #直接浏览器访问地址可以查看之前上传的图片;最后一个参数是上传图片是返回的是文件唯一标识ID;访问路径格式都是

http://ip:port/v1/tfs/ID

 

 

#下面红色部分不需要了,但是个经验

cd objs/

vim Makefile     #忽略警告,不然警告也被当成错误

删掉:-Werror    (大约第3行)

参考我的其他文档:

<<TFS官方文档>>.pdf    看使用篇--Nginx模块 内容即可.

<<TFS的java客户端>>.pdf    c++篇我就不整理的,看不懂c++

 

配置参考:官方文档

nginx.conf

 

 

  1. #user  nobody;
  2. worker_processes  1;
  3. #error_log  logs/error.log;
  4. #error_log  logs/error.log  notice;
  5. #error_log  logs/error.log  info;
  6. #pid        logs/nginx.pid;
  7. events {
  8.     worker_connections  1024;
  9. }
  10. http {
  11.     tfs_upstream tfs_rc {
  12.         server 192.168.20.102:8100;
  13.         type ns;//这里我改了,我没用rcs,所以我就配置nameserver即可
  14.         rcs_zone name=tfs1 size=128M;
  15.         rcs_interface eth0;
  16.         rcs_heartbeat lock_file=/logs/lk.file interval=10s;
  17.     }
  18.    # sendfile        on;
  19.    # keepalive_timeout  65;
  20.     server {
  21.         listen       80;
  22.         server_name  localhost;
  23.                 tfs_keepalive max_cached=100 bucket_count=10;
  24.                 tfs_log "pipe:/usr/sbin/cronolog -p 30min /path/to/nginx/logs/cronolog/%Y/%m/%Y-%m-%d-%H-%M-tfs_access.log";
  25.                 location /{
  26.                         tfs_pass tfs://tfs_rc;
  27.                 }
  28.         error_page   500502503504  /50x.html;
  29.         location =/50x.html {
  30.             root   html;
  31.         }
  32.     }
  33. }
 

#TFS的java接口(tfs-javaclient)

java-client我下载源码然后maven打包后已上传私服20.198(3个版本1.3.0   1.3.1     2.1.1)

 

 

  1. <dependency>
  2.   <groupId>com.taobao.common.tfs</groupId>
  3.   <artifactId>tfs-javaclient</artifactId>
  4.   <version>2.1.1</version>
  5. </dependency>

 

nexus上传tfs-javaclient-2.1.1.jar包:

进入jar包目录,用压缩软件打开jar包,找到里面的pom,按照里面的坐标配置下面的命令

源码地址:http://code.taobao.org/p/tfs-client-java/src/tags/

svn地址:http://code.taobao.org/svn/tfs-client-java/

#jar包安装到本地仓库

mvn install:install-file -Dfile=tfs-javaclient-2.1.1.jar -DgroupId=com.taobao.common.tfs -DartifactId=common-tfs -Dversion=2.1.1 -Dpackaging=jar

#上传到nexus

 

mvn deploy:deploy-file -Dfile=tfs-javaclient-2.1.1.jar -DgroupId=com.taobao.common.tfs -DartifactId=common-tfs -Dversion=2.1.1 -Dpackaging=jar -Durl=http://192.168.20.198:8081/nexus/content/repositories/thirdparty/ -DrepositoryId=thirdparty
-- Durl私服上仓库的url精确地址(打开nexus左侧repositories菜单,可以看到该路径)
 
 
#####################################################################TFS JAVA-API#####################################################################
我已经把源码拖到src下,可以随意Debug
 
TFS学习参考地址:
https://github.com/alibaba/tfs/blob/master/INSTALL.md
https://github.com/alibaba/tfs/blob/master/DEPLOY.md
http://tfs.taobao.org/
http://code.taobao.org/p/tfs/wiki/get/
http://csrd.aliapp.com/
 
TFS软件下载地址(源码):
TFS:https://github.com/alibaba/tfs
Nginx模块:https://github.com/alibaba/nginx-tfs
utils:https://github.com/raywill/tb-common-utils
 
###########################################################客户端接口列表##########################################################
int setMasterIP(String ipaddr);            //改变master ip地址,ip地址格式为 ip:port[:tfsarea]
 
String newTfsFileName(String suffix);    //生成一个新的tfs文件名,如果失败,返回null
 
boolean fetchFile(String tfsFileName, String tfsSuffix, String localFileName);    //读取一个tfs的文件到本地文件
 
boolean fetchFile(String tfsFileName, String tfsSuffix, !OutputStream output);    //读取一个tfs的文件到输出流
 
String saveFile(String localFileName, String tfsFileName, String tfsSuffix);    //保存一个本地文件到TFS,失败返回null
 
String saveFile(String mainName, String suffix, byte[] data, int offset, int length);    //保存一个字节流data到TFS
 
String saveFile(byte[] data, String mainName, String suffix);    //保存一个字节流data到TFS
 
FileInfo statFile(String tfsFileName, String tfsSuffix)    //stat一个tfs文件,成功返回FileInfo, 失败返回nul
 
boolean unlinkFile(String tfsFileName, String tfsSuffix);    //删除一个文件
 
boolean hideFile(String fileName, String tfsSuffix, int option);    //临时隐藏/反隐藏一个文件
 
String saveUniqueFile(String localFileName, String tfsFileName, String tfsSuffix);    //排重保存文件
 
String saveUniqueFile(String mainName, String suffix, byte[] data, int offset, int length);    //排重保存字节流
 
int unlinkUniqueFile(String tfsFileName, String tfsSuffix);    //排重删除文件
 
======================================================写大文件接口======================================================
String saveLargeFile(String localFileName, String tfsFileName, String tfsSuffix)    //保存一个大文件到tfs,成功返回tfs文件名(L开头),失败返回null
 
String saveLargeFile(byte[] data, String tfsFileName, String tfsSuffix,String key);    //保存一个字节流data到tfs,成功返回tfs文件名,失败返回null
 
String saveLargeFile(String mainName, String suffix, byte[] data, int offset, int length, String key);    //保存一个字节流data到tfs,成功返回tfs文件名,失败返回null
 
======================================================数据多次读写接口======================================================
int openReadFile(String tfsFileName, String tfsSuffix)    //打开一个tfs文件读,成功返回大于0的fd,失败返回负值
 
int readFile(int fd, byte[] data, int offset, int length)    //读取不超过length大小的数据到一个字符数组,成功返回读取到的数据长度,失败返回负值。
 
int readFile(int fd, long fileOffset, byte[] data, int offset, int length)    //从文件的fileoffset开始,读取不超过length大小的数据到一个字符数组,成功返回读取到的数据长度,失败返回负值。
 
int openWriteFile(String tfsFileName, String tfsSuffix, String key)    //打开一个tfs文件写,成功返回大于0的fd,失败返回负值
 
int writeFile(int fd, byte[] data, int offset, int length)    //写data到tfs文件,成功返回写入的长度,失败返回负值
 
String closeFile(int fd)    //关闭tfs文件句柄,释放相关资源。成功返回最后tfs文件名,失败返回null
 
void destroy();    //销毁占用的资源 (主进程调用一次)
 
===================================================1.3.1版本新加API加入四个新的接口==============================================================================
String saveFile(String mainName, String suffix, byte[] data, int offset, int length);
 
String saveUniqueFile(String mainName, String suffix,  byte[] data, int offset, int length);
 
int unlinkUniqueFile(String tfsFileName, String tfsSuffix);
 
void destroy();
 
====================================================2.1.1版本新加API加入以下新的接口============================================================================
FileInfo statFile(String tfsFileName, String tfsSuffix)
  
String saveLargeFile(String localFileName, String tfsFileName, String tfsSuffix) 
 
String saveLargeFile(String mainName, String suffix, byte[] data, int offset, int length, String key);
 
int openReadFile(String tfsFileName, String tfsSuffix) 
 
int readFile(int fd, byte[] data, int offset, int length)
 
nt readFile(int fd, long fileOffset, byte[] data, int offset, int length) 
 
int openWriteFile(String tfsFileName, String tfsSuffix, String key)
 
int writeFile(int fd, byte[] data, int offset, int length)
 
String closeFile(int fd
 
=======================================================文件去重复=================================================================================
#使用排重接口写时,通过计算数据md5值,保证同一个文件,tfs上只会有一份。
#排重使用tair存放排重数据,应用需要排重功能时,要使用DefaultTfsManager的setUniqueStore来初始化tair排重。
 
1.setMasterIP
函数:int setMasterIP(String ipaddr)
描述:设置NameServer的ip地址。
参数:
ipaddr 需要连接的NameServer的IP。
示例:
DefaultTfsManager tfsManager  = new DefaultTfsManager();
tfsManager.setMasterIP("172.29.1.101:8100");
 
2.setUniqueStore
函数:void setUniqueStore(List<String> serverList, String groupName, int namespace
描述:设置排重使用的tair
参数:
    serverList: tairconfigserver地址列表。 
    groupName: tair中的排重组名 
    namespace: tair中的排重namespace
示例:
    TfsManager tfsManager = new DefaultTfsManager();
    tfsManager.setMasterIP("192.168.208.21:3100");
    List<String> serverList = new ArrayList<String>();
    serverList.add("127.0.0.1:5198");
    serverList.add("127.0.0.2:5198");
    tfsManager.setUniqStore(serverList, "group_tfsunique", 102);
 
3.newTfsFileName
函数:String newTfsFileName(String suffix); 
描述:根据后缀名suffix获取tfs文件名,为保持与老版本兼容而保留的接口,可通过savefile函数直接获取,不推荐使用。
参数:
suffix 任意字符串,建议设为NULL。
返回:新的文件名
示例:
    DefaultTfsManager tfsManager  = new DefaultTfsManager();
    String newTfsName = tfsManager.newTfsFileName(NULL);
 
4.fetchFile
函数:boolean fetchFile(String tfsFileName, String tfsSuffix, String localFileName);
    boolean fetchFile(String tfsFileName, String tfsSuffix, !OutputStream output);
描述:从tfs上取一个文件存到本地,前一个函数将数据存到本地文件,后一个函数将数据存到输出流中。
参数:
    tfsFileName 需要读取的tfs文件名。
    tfsSuffix 需要读取的文件名后缀,需要和存入时后缀相同。
    localFileName 本地文件名。
    Output 数据流。
返回:读操作成功返回true,读操作失败返回false。
示例:
    String tmpfile = "tmp";
    String tfsname = T16FXXXd4aXXaZaWbX ;
    boolean ret = tfsManager.fetchFile(tfsname, null, tmpfile);
    ByteArrayOutputStream output = new ByteArrayOutputStream();
    String tfsname = T16FXXXd4aXXaZaWbX ;
    boolean ret = tfsManager.fetchFile(tfsname, null, output);
 
5.saveFile
函数:String saveFile(String localFileName, String tfsFileName, String tfsSuffix); 
String saveFile(String mainName, String suffix, byte[] data, int offset, int length); 
描述:将本地文件或数据存到TFS上,前一个函数将本地文件存入TFS上,后一个函数将本地byte数组存到TFS上。
参数:
    localFileName 本地文件名。
    tfsFileName 给文件指定一个TFS文件名,通常建议设为NULL。
    tfsSuffix 任意字符串,可设为NULL。
    mainName 与前一个函数的参数tfsFileName相同,给文件指定一个TFS文件名,通常建议设为NULL。
    suffix 与前一个函数的参数tfsSuffix相同,任意字符串,可设为NULL。
    data 要写入的byte数组。
    offset 要写入的数据在data中的位置。
    length 要写入的数据长度。
返回:TFS文件名。
示例:
    String localfile = "test.jpg";
    String tfsname = tfsManager.saveFile(localfile, null, ".jpg ");
    String data_string = "this is a test.";
    String tfsname = tfsManager.saveFile(data_string.getBytes(), null, null);
 
6.saveUniqueFile
函数:String saveUniqueFile(String localFileName, String tfsFileName, String tfsSuffix); 
    String saveUniqueFile(String mainName, String suffix, byte[] data, int offset, int length); 
描述:将本地文件或数据排重存到TFS上,前一个函数将本地文件存入TFS上,后一个函数将本地byte数组存到TFS上。
参数:
    localFileName 本地文件名。
    tfsFileName 给文件指定一个TFS文件名,通常建议设为NULL。
    tfsSuffix 任意字符串,可设为NULL。
    mainName 与前一个函数的参数tfsFileName相同,给文件指定一个TFS文件名,通常建议设为NULL。
    suffix 与前一个函数的参数tfsSuffix相同,任意字符串,可设为NULL。
    data 要写入的byte数组。
    offset 要写入的数据在data中的位置。
    length 要写入的数据长度。
返回:TFS文件名。
示例:
    String localfile = "test.jpg";
    String tfsname = tfsManager.saveUniqueFile(localfile, null, ".jpg ");
    String data_string = "this is a test.";
    String tfsname = tfsManager.saveUniqueFile(data_string.getBytes(), null, null);
 
7.unlinkFile
函数:boolean unlinkFile(String tfsFileName, String tfsSuffix) 
描述:在tfs上删除一个文件。
参数:
    tfsFileName 需要读取的tfs文件名。
    tfsSuffix 需要读取的文件名后缀,需要和存入时后缀相同。
返回:删除成功返回true,删除失败返回false。
示例:
    String tfsname = T16FXXXd4aXXaZaWbX ;
    String suffix = ".jpg";
    boolean ret = tfsManager.unlinkFile(tfsname, suffix);
    unlinkUniqueFile
函数:boolean unlinkUniqueFile(String tfsFileName, String tfsSuffix) 
描述:在tfs上排重删除一个文件,直到引用计数为0时才真正删除tfs数据
参数:
    tfsFileName 需要读取的tfs文件名。
    tfsSuffix 需要读取的文件名后缀,需要和存入时后缀相同。
返回:删除成功返回当前文件的refer count,删除失败返回-1。
示例:
    String tfsname = T16FXXXd4aXXaZaWbX ;
    String suffix = ".jpg";
    int ret = tfsManager.unlinkUniqueFile(tfsname, suffix);
 
8.hideFile
函数:boolean hideFile(String fileName, String tfsSuffix, int option); 
描述:临时隐藏/反隐藏一个文件。
参数:
    tfsFileName 需要读取的tfs文件名。
    tfsSuffix 需要读取的文件名后缀,需要和存入时后缀相同。
    option 标识隐藏还是反隐藏,该值为1时表示隐藏,该值为0时表示反隐藏。
返回:操作成功返回true,操作失败返回false。
 
9.statFile
函数:FileInfo statFile(String tfsFileName, String tfsSuffix) 
描述:stat一个tfs文件。
参数:
    tfsFileName 需要读取的tfs文件名。
    tfsSuffix 需要读取的文件名后缀,需要和存入时后缀相同。
返回:操作成功返回FileInfo,操作失败返回null。
     FileInfo fileInfo = tfsManager.statFile(name, null); [[BR]]
     System.out.println("file status " + fileInfo.getFlag());  // 文件的状态有0(正常), 1(删除), 4(隐藏)。[[BR]]
 
10.destroy
函数: void destroy() 
描述: 销毁资源,该接口程序中请只掉用一次。 
参数: 空 
返回: 空 
 
==================================================常用API代码===============================================================
初始化java客户端
#配置文件;tfs.xml
#####################################################配置文件################################################################
    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
    <beans>
        <bean id="tfsManager" class="com.taobao.common.tfs.impl.DefaultTfsManager" init-method="init" >
          <!-- 整个进程中系统最多等待多少个请求,取决于你有多少个线程并发的请求TFS -->
           <property name="maxWaitThread">
            <value>100</value>
          </property>
          <!-- 单个请求最大的等待时间(ms) 超过这个时间放弃这次请求-->
          <property name="timeout">
            <value>2000</value>
          </property>
          <!-- Tfs master nameserver ip address -->
          <property name="nsip">
            <value>ip:port</value>
          </property>
          <!-- TFS 集群的编号,这个编号只是一种参考,系统初始化的时候会从ns上取,取不到才用本地设置的.!-->
          <property name="tfsClusterIndex">
            <value>1</value>
          </property>
          <!-- TFS在读取文件的时候会缓存block所在的数据服务器ip,这个参数配置了最多缓存的记录个数!-->
          <property name="maxCacheItemCount">
            <value>10000</value>
          </property>
          <!-- 上一项缓存最大有效的时间(ms)!-->
          <property name="maxCacheTime">
            <value>5000</value>
          </property>
           <!-- 不需要排重功能时,下面配置项可以不设置 -->
           <!-- tair排重数据库的serverlist -->
          <property name="uniqueServerList">
              <list>
                <value>127.0.0.1:5198</value>
                <value>127.0.0.2:5198</value>
              </list>
          </property>
           <!-- tair排重数据库的groupName -->
          <property name="groupName">
              <value>group_tfsunique</value>
          </property>
           <!-- tair排重数据库的namespace -->
          <property name="namespace">
              <value>102</value>
          </property>
       </bean>
    </beans>
 
######################################################初始化代码,加载配置文件########################################################
ClassPathXmlApplicationContext appContext = new ClassPathXmlApplicationContext(new String[] { "tfs.xml" }); 
TfsManager tfsManager = (DefaultTfsManager) appContext.getBean("tfsManager");
 
######################################################写文件;可使用两个接口:#########################################################
String saveFile(String localFileName, String tfsFileName, String tfsSuffix); 
String saveFile(String mainName, String suffix, byte[] data, int offset, int length);
 
#以第一个接口为例,使用方法如下:
    ClassPathXmlApplicationContext appContext = new ClassPathXmlApplicationContext(new String[] { "tfs.xml" }); 
    TfsManager tfsManager = (DefaultTfsManager) appContext.getBean("tfsManager");
    String localfile = "test.jpg";
    
    //不传入suffix和tfsname
    void save_file_without_param ()
    {
        String tfsname = tfsManager.saveFile(localfile, null, null);
        System.out.println("save with no parameters " + tfsname);
    }
    //只传入suffix
    save_file_with_suffix()
    {
        String tfsname = tfsManager.saveFile(localfile, null, ".jpg");
        System.out.println("save with suffix " + tfsname);
    }
    //只传入tfsname
    void save_file_with_tfsname()
    {
        String newtfsname = tfsManager.newTfsFileName(".jpg ");
        String tfsname = tfsManager.saveFile(localfilenewtfsname, null);
        System.out.println("save with tfsname " + tfsname);
     }
    //同时传入suffix和tfsname
    void save_file_with_tfsname_suffix()
        {
            String newtfsname = tfsManager.newTfsFileName(".jpg ");
            String tfsname = tfsManager.saveFile(localfilenewtfsname, ".jpg");
            System.out.println("save with tfsname and suffix " + tfsname);
         }
    }
#以上采用了四种方法向TFS写入文件,但是不建议用第3种和第4种,因为这两种方式要与nameserver多一次交互,损失了效率。第二个函数的使用方法和第一个函数类似,不再赘述。
 
 
########################################################读文件#############################################################
#可使用两个接口:
    boolean fetchFile(String tfsFileName, String tfsSuffix, String localFileName); 
    boolean fetchFile(String tfsFileName, String tfsSuffix, !OutputStream output); 
    
    //以第一个接口为例,使用方法如下:
    ClassPathXmlApplicationContext appContext = new ClassPathXmlApplicationContext(new String[] { "tfs.xml" }); 
    TfsManager tfsManager = (DefaultTfsManager) appContext.getBean("tfsManager");
    String localfile = "test.jpg";
    String tmpfile = "tmp";
 
    //不带后缀名
    void fetch _file_without_param ()
    {
        String tfsname = tfsManager.saveFile(localfile, null, null);
        System.out.println("fetch with no parameters " + tfsname);
        tfsManager.fetchFile(tfsname, null, tmpfile);
    }
    //带后缀名
    void fetch_file_with_suffix()
    {
        String tfsname = tfsManager.saveFile(localfile, null, ".jpg");
        System.out.println("save with suffix " + tfsname);
        tfsManager.fetchFile(tfsname, ".jpg", tmpfile);
    }
    //newtfsfilename后取文件名的方法,后缀要使用saveFile的后缀
    void fetch _file_with_tfsname()
    {
        String newtfsname = tfsManager.newTfsFileName(".jpg ");
        String tfsname = tfsManager.saveFile(localfilenewtfsname, null);
        System.out.println("save with tfsname " + tfsname);
        tfsManager.fetchFile(tfsname, null, tmpfile);
    }
    //用返回的tfsname和直接newtfsname得到的文件名两种方式取文件
    void fetch _file_with_tfsname_suffix()
    {
        String newtfsname = tfsManager.newTfsFileName(".jpg ");
        String tfsname = tfsManager.saveFile(localfilenewtfsname, ".jpg");
        System.out.println("save with tfsname and suffix " + tfsname);
        String tmpfile1 = "tmp1";
        tfsManager.fetchFile(tfsname, NULL, tmpfile1);
        String tmpfile2 = "tmp2";
        tfsManager.fetchFile(newtfsname, ".jpg", tmpfile2);
    }
以上代码针对四种写文件的方式分别读取文件,在读文件时需注意两点:
1.建议Fetchfile传入的tfsname以存入文件时返回的文件名为准,如果用newTfsFileName()的返回值来作为文件名,必须添加saveFile时候传入的后缀。
2.后缀必须和存文件时输入的后缀一致,如果传入为null,那么fetchfile时suffix也应该会null,传入错误的后缀将无法找到正确的文件。
 
 
 
#########################################################删除文件##################################################################
#使用方法如下:
    ClassPathXmlApplicationContext appContext = new ClassPathXmlApplicationContext(new String[] { "tfs.xml" }); 
    TfsManager tfsManager = (DefaultTfsManager) appContext.getBean("tfsManager");
    String localfile = "test.jpg";
    String tfsname = tfsManager.saveFile(localfile, null, null);
    tfsManager.unlinkFile(tfsname, null);
#以上代码时对删除文件的简单应用,其注意要点和读文件类似,不再赘述。
 
 
 
#########################################################隐藏文件##################################################################
#使用方法如下:
    ClassPathXmlApplicationContext appContext = new ClassPathXmlApplicationContext(new String[] { "tfs.xml" }); 
    TfsManager tfsManager = (DefaultTfsManager) appContext.getBean("tfsManager");
    String localfile = "test.jpg";
    String tfsname = tfsManager.saveFile(localfile, null, null);
    tfsManager.hideFile(tfsname, null, 0);
    tfsManager.hideFile(tfsname, null, 1);
以上代码时对隐藏文件和反隐藏文件的简单应用,其注意要点和读文件类似,不再赘述。
 
 
##########################################################写大文件##################################################################
#tfs文件写大文件,为了断点续传,必须传入一个key参数,来标识此次大文件的写。一次写失败后,再次传入相同的key写,tfsclient会根据key找到前一次已经写完成的部分重用。 
#为了确保key的唯一性,当前tfsclient接受的key必须是本地文件系统上存在的一个文件路径,该key文件的内容无所谓. key的唯一性请调用者自己负责。
1)一次写入。大文件现在不支持更新。所以文件名请传入null.
    //不需要传入key,默认使用localFileName作为key。
    String tfsName = saveLargeFile(localFileName, null, null);
    String tfsName = saveLargeFile("test write".getBytes(), null, null, keyName);
 
2)多次写入。 
    int fd = openWriteFile(null, ".dat", keyName);
    byte[] data = "write data";
    writeFile(fd, data, data.length);
    writeFile(fd, data, data.length);
    //close file, 获得最后tfs文件名。即使写失败,也必须掉用close
    String name = closeFile(fd);
    
##########################################################读大文件##########################################################
1)一次读取直接使用fetchFile接口即可,会根据文件名判断是大文件。
 
2)多次读取
    int fd = openReadFile("L16FXXXd4aXXaZaWbX", null);
    byte[] data = new byte[1024000];
    readFile(fd, data, data.length);
    closeFile(fd);
     
对于上述方式保存的TFS文件,同样的,可以用TFS返回的tfsName来访问,小文件以T开头,大文件以L开头。假设saveFile返回的文件名是T16FXXXd4aXXaZaWbX,而newTfsFileName返回的文件名是T16FXXXdXXXXXXXXXX,传入的suffix是any_suffix_you_want。
T16FXXXd4aXXaZaWbX 
T16FXXXd4aXXaZaWbX.any_suffix_you_want 
T16FXXXdXXXXXXXXXX.any_suffix_you_want 
这三种文件名形式都是合法的。  
博采众长才能相互印证,故步自封必将粗陋浅薄!
原文地址:https://www.cnblogs.com/tangwan/p/5842965.html