标签归档:tfs

TFS架设案例

tfs_struct
这里使用nginx_tfs扩展作为操作TFS的接口。请求TFS的,有外部用户,只是取数据;内部用户,上传修改删除图片等;通过PHP访问TFS的内部或外部用户。通过PHP访问TFS主要是希望通过PHP的处理添加额外逻辑,比如内部用户,可以先认证,对来自外部的用户,可以对文件进行加工后返回…..

在Nginx中设置三个Host模拟:

tfs_upstream tfs_ns {
        server 192.168.1.102:8000;
        type ns;
}
#对外部用户,只允许GET
server {
	listen 80;
	server_name files.ifeeline.com;

        tfs_keepalive max_cached=10 bucket_count=2;

        location / {
	      if ($request_method != GET) {
	      	   return 403;
	      }
              tfs_pass tfs://tfs_ns;
        }
}
#对内部用户,可以进行所有操作,可能不好控制权限,如果这样可以走PHP
#让PHP返回调用
server {
        listen 80;
        server_name api.ifeeline.com;

        tfs_keepalive max_cached=10 bucket_count=2;

        location / {
              tfs_pass tfs://tfs_ns;
        }
}
#通过PHP层访问,PHP层可以直接使用PHP针对TFS的扩展,也可以在PHP中对nginx_tfs扩展提供的方法进行访问
server {
	listen 90;
	server_name tfs.ifeeline.com;
	root /data/web/tfs;
	location ~ .*.(php|php5)?$ {
		fastcgi_pass 127.0.0.1:9000;
		fastcgi_index index.php;
		include fastcgi.conf;
	}
}

以下是一段管理程序:

<?php
set_include_path(implode(PATH_SEPARATOR, array(
        realpath(__DIR__ . '/../library'),
        get_include_path(),
)));

if(file_exists('../vendor/autoload.php')){
    $loader = include '../vendor/autoload.php';
}else{
    exit("Autoload Failed. ");
}
$loader->setUseIncludePath(true);

$params = array(
        'username' =>'root',
        'password' =>'',
        'dbname' =>'tfs',
        'charset' =>'utf8'
);
$db = Zend_Db::factory('MYSQLI', $params);
Zend_Db_Table_Abstract::setDefaultAdapter($db);

$httpClient = new Tfs\HttpClient();

// 自己构建
// $reponse = $httpClient->setBaseUrl('http://files.ifeeline.com')->get('/v1/tfs/T1IRETByxT1RXrhCrK.jpg');
// if($reponse->isOk()){
//     $content = $reponse->getContent();
//     echo $content;
// }
// exit;

// 删除
if(isset($_REQUEST['type']) && ($_REQUEST['type'] == 'del') && isset($_REQUEST['file'])){
    $url = "http://api.ifeeline.com";
    $delUrl = "/v1/tfs/".trim($_REQUEST['file']);
    
    $ret = $httpClient->setBaseUrl($url)->delete($delUrl);
    
    if($ret->isOk()){
        //$rett = json_decode($ret->getContent(),'true');
        $db->delete("files","name_tfs='".$_REQUEST['file']."'");
    }
    header("Location:tfs.php");
}

// 批量添加
if(isset($_REQUEST['type']) && ($_REQUEST['type'] == 'add')){
    $dir = new DirTreee();
    $files = $dir->getTree("D:/files");
    
    $url = "http://api.ifeeline.com";
    $postUrl = "/v1/tfs?suffix=&simple_name=0";
    
    foreach($files as $fle) {
        $pinfo = pathinfo($fle);
        $fileName = $pinfo["filename"];
    
        $d = file_get_contents($fle);
        $fileMd5 = md5($d);
    
        $has = $db->fetchRow("SELECT * FROM files WHERE file_md5='".$fileMd5."'");
        if(empty($has)) {
            $ret = $httpClient->setBaseUrl($url)->post($postUrl,$d);
    
            if($ret->isOk()){
                $rett = json_decode($ret->getContent(),'true');
                $tfsFileName = $rett["TFS_FILE_NAME"];
    
                $db->insert("files", array("name"=>$fileName,"name_tfs"=>$tfsFileName,"file_md5"=>$fileMd5));
            }
        }   
    }
    header("Location:tfs.php");
}

class DirTreee {
    public $tree = array();
    public $treeView = '';
     
    public function __construct($source='') {
        if(!empty($source) && is_dir($source)){
            $this->tree = $this->getTree($source);
        }
    }
     
    public function getTree($source) {
        $dir = array();
        if(is_dir($source)){
            if ($dh = opendir($source)) {
                while (($file = readdir($dh)) !== false) {
                     
                    if(is_file($source.'/'.$file)){
                        
                        $ext = pathinfo($file);
                        $ext = $ext['extension'];
                        // 不读取图片文件
                        if(($file == '.') || ($file == '..')){ continue; }
                        $dir[] = $source.'/'.$file;
                    }
                }
                 
                closedir($dh);
            }
        }
        return $dir;
    }
}
?>
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title></title>
</head>
<body>
<?php 
$list = $db->fetchAll("SELECT * FROM files");
echo "<table>";
foreach($list as $lst){
    echo "<tr>";
    echo "<td><a href='http://files.ifeeline.com/v1/tfs/".$lst['name_tfs']."'>".$lst['name'].' - '.$lst['name_tfs'].' - '.$lst['file_md5']."</a></td>";
    echo "<td><a href='tfs.php?type=del&file=".$lst['name_tfs']."'>删除</a></td>";
    echo "</tr>";
}
echo "</table>";
?>
<br />
<a href="tfs.php?type=add">批量添加</a>
</body>
</html>

TFS的Nginx扩展nginx_tfs 与 PHP包装器

Taobao官方有提供一个针对TFS的Nginx扩展,地址https://github.com/alibaba/nginx-tfs,设计成REST风格API,它就是一个适配器,任何的编程语言执行简单发送指令就可以操作TFS。

官方描述的安装:
1 TFS模块使用了一个开源的JSON库来支持JSON,请先安装yajl-2.0.1(http://lloyd.github.io/yajl/)。
2 下载nginx或tengine。
3 ./configure –add-module=/path/to/nginx-tfs
4 make && make install

实际操作过程:

#先安装Nginx需要的包
yum -y install zlib zlib-devel openssl openssl-devel pcre pcre-devel

#获取yajl
wget http://github.com/lloyd/yajl/zipball/2.1.0
mv 2.1.0 yajl-2.0.1.zip
unzip yajl-2.0.1.zip
cd yajl-2.0.1
##默认安装到/usr/local,库放入了/usr/local/lib中
./configure 
##这个玩意使用了cmake      
yum install cmake 
make && make install
#避免后面找不到这个库,玛尼,这里拷贝一下
cp -a /usr/local/libyajl* /lib64/

#获取nginx-tfs扩展,解压一下就好了
wget https://github.com/alibaba/nginx-tfs/archive/master.zip
unzip master.zip

#Ngingx-1.8.0无法适配这个扩展,所以找一个低一点的版本
wget http://nginx.org/download/nginx-1.2.9.tar.gz
tar zxvf nginx-1.2.9.tar.gz
cd nginx-1.2.9
useradd www
./configure --user=www --group=www --prefix=/usr/local/nginx-1.2.9 --add-module=/root/nginx-tfs-master
make && make install

配置:

vi nginx.conf 添加:
tfs_upstream tfs_ns {
        server 192.168.1.102:8000;
        type ns;
    }

server {
	listen       80;
	server_name  192.168.1.102;

        tfs_keepalive max_cached=10 bucket_count=2;

        location / {
              tfs_pass tfs://tfs_ns;
        }
}

我这里不使用rcServer,所以配置异常简单。具体配置参考:https://github.com/alibaba/nginx-tfs/blob/master/ReadMe.markdown

然后用如下程序POST一张图片:

/////////////////////////////////////////////////////
$url = 'http://192.168.1.102/v1/tfs?suffix=.jpg&simple_name=0';

$postBinary = file_get_contents('D:\v.jpg');

//初始化
$ch = curl_init();
curl_setopt($ch, CURLOPT_HEADER, '');
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_HEADER, false);
curl_setopt($ch, CURLOPT_TIMEOUT, 30);
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10);
//
curl_setopt($ch, CURLOPT_POST, false);
//
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS,$postBinary);
//
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
//
curl_setopt($ch,CURLOPT_RETURNTRANSFER,true);
curl_setopt($ch,CURLOPT_FOLLOWLOCATION,true);
curl_setopt($ch,CURLOPT_MAXREDIRS,10);
//
curl_setopt($ch, CURLOPT_USERAGENT, "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:21.0) Gecko/20100101 Firefox/21.0");

$result = curl_exec($ch);
$errn = curl_errno($ch);
curl_close($ch);

print_r($result);

######################################
输出:
{ "TFS_FILE_NAME": "T1IRETByxT1RXrhCrK" } 

说明:这个POST跟一般常见的POST是不一样的(RESTful风格),首先,参数作为URL的一部分,然后就是POST的数据不需要对应一个名称,直接是原生数据。

直接访问这个链接:
tfs_output

其它的API参考:https://github.com/alibaba/nginx-tfs/blob/master/TFS_RESTful_API.markdown,这些API的调用可以通过CURL来非常容易的使用。

针对一个nginx_tfs的这个扩展,在Github有一个针对它的PHP包装器:https://github.com/ifa6/php_tfs_client
这个包里面提供的HttpClient.php是对Curl的封装,HttpResponse.php是响应对象的封装,HttpClient.php提供了RESTfull风格的几个方法:

    public function get($url)
    {
        return $this->prepareCurlHandler(array(
            CURLOPT_URL => $this->prepareUrl($url),
            CURLOPT_CUSTOMREQUEST => 'GET',
        ))->send();
    }

    public function put($url, $content=null)
    {
        return $this->prepareCurlHandler(array(
            CURLOPT_URL => $this->prepareUrl($url),
            CURLOPT_CUSTOMREQUEST => 'PUT',
            CURLOPT_POSTFIELDS => $content,
        ))->send();
    }

    public function post($url, $content=null)
    {
        return $this->prepareCurlHandler(array(
            CURLOPT_URL => $this->prepareUrl($url),
            CURLOPT_CUSTOMREQUEST => 'POST',
            CURLOPT_POSTFIELDS => $content,
            CURLOPT_POST => true
        ))->send();
    }

    public function delete($url)
    {
        return $this->prepareCurlHandler(array(
            CURLOPT_URL => $this->prepareUrl($url),
            CURLOPT_CUSTOMREQUEST => 'DELETE',
        ))->send();
    }

    public function head($url)
    {
        return $this->prepareCurlHandler(array(
            CURLOPT_URL => $this->prepareUrl($url),
            CURLOPT_CUSTOMREQUEST => 'HEAD',
            CURLOPT_NOBODY => true
        ))->send();
    }

除了这些方法,还有一系列的set方法,比如设置请求URL,CURL用的头信息等:

$httpClient = new HttpClient();
$reponse = $httpClient->setBaseUrl('http://blog.ifeeline.com')->get('/xxx/api.php');
if($reponse->isOk()){
    $content = $response->getContent();
    // ......
}

这个包下面Client.php是利用HttpClient对nginx_tfs提供的API进行了二次封装,不过我觉得使用HttpClient已经足够好了。

TFS安装配置笔记

CentOS 6.5 64位最小化安装,默认没有安装编译软件的基本工具。先补齐:

yum install libtool autoconf automake gcc gcc-c++ make

yum install readline-devel zlib-devel libuuid-devel ncurses-devel

安装的gcc和gcc-c++版本是4.4.x,如果就这样去编译TFS,最终会出错,经查询说是gcc版本过高,请使用低版本gcc。既然TFS基于gcc 4.1.2,为了保证可靠性,那就切换到这个版本吧,去CentOS 5.* 64位ISO包下找到如下安装包:(http://blog.ifeeline.com/downlaod/gcc.zip)

gcc-4.1.2-55.el5.x86_64.rpm
	cpp-4.1.2-55.el5.x86_64.rpm

gcc-c++-4.1.2-55.el5.x86_64.rpm
	libstdc++-4.1.2-55.el5.x86_64.rpm
	libstdc++-devel-4.1.2-55.el5.x86_64.rpm

#这个可以不用
libgcc-4.1.2-55.el5.x86_64.rpm

然后分别卸载系统中已经安装的对应的以上的软件包:

##c++安装包
rpm -qa | grep c++
libstdc++-4.4.7-11.el6.x86_64
libstdc++-devel-4.4.7-11.el6.x86_64
gcc-c++-4.4.7-11.el6.x86_64

##强制杀掉
rpm -e --nodeps libstdc++-4.4.7-11.el6.x86_64
rpm -e --nodeps libstdc++-devel-4.4.7-11.el6.x86_64
rpm -e --nodeps gcc-c++-4.4.7-11.el6.x86_64

##gcc安装包
rpm -qa | grep gcc
gcc-4.4.7-11.el6.x86_64
libgcc-4.4.7-11.el6.x86_64 #这个别杀

##杀掉gcc
rpm -e --nodeps gcc-4.4.7-11.el6.x86_64
[/shll]

清理干净后开始安装低版本软件:

rpm -ivh --force libgcc-4.1.2-55.el5.x86_64.rpm
rpm -ivh --force libstdc++-4.1.2-55.el5.x86_64.rpm
rpm -ivh --force libstdc++-devel-4.1.2-55.el5.x86_64.rpm
rpm -ivh --force cpp-4.1.2-55.el5.x86_64.rpm
rpm -ivh --force gcc-4.1.2-55.el5.x86_64.rpm
rpm -ivh --force gcc-c++-4.1.2-55.el5.x86_64.rpm

这个过程如果提示安装glibc之类的,直接使用yum安装就好了。最后确认安装:

ls -lah /lib64/libgcc*
/lib64/libgcc_s-4.1.2-20080825.so.1
/lib64/libgcc_s-4.4.7-20120601.so.1
/lib64/libgcc_s.so.1 -> libgcc_s-4.4.7-20120601.so.1

软件包libgcc-4.1.2-55.el5.x86_64.rpm强制安装成功,但是系统使用的还是最新的库,这个只是个库而已,应该没有问题。检查gcc版本:

gcc -v
Using built-in specs.
Target: x86_64-redhat-linux
Configured with: ../configure --prefix=/usr ********
Thread model: posix
gcc version 4.1.2 20080704 (Red Hat 4.1.2-55)

成功切换到gcc 4.1.2。

———————————————
在实际安装时,把libgcc-4.4.7-11.el6.x86_64这个安装包给强制卸载了,导致yum无法使用(依赖这个包提供的动态库):

rpm -ql libgcc-4.4.7-11.el6.x86_64
/lib64/libgcc_s-4.4.7-20120601.so.1
/lib64/libgcc_s.so.1

ls /lib64/libgcc_s*
/lib64/libgcc_s-4.4.7-20120601.so.1
/lib64/libgcc_s.so.1 -> libgcc_s-4.4.7-20120601.so.1

libgcc_s.so.1是一个符号链接。如果强制安装了libgcc-4.1.2-55.el5.x86_64.rpm,这个链接不会被改变,所以如果确定需要使用可以修改这个符号链接的指向。

这个仅仅是一个动态库,所有必须去其它系统拷贝这个库放回来解决。
———————————————

下载TFS依赖的公共库:

#没有SVN先yum install svn
svn co -r 18 http://code.taobao.org/svn/tb-common-utils/trunk tb-common-utils

这里打算安装TFS 2.0.4版本,所以tb-common-utils也使用了一个低版本。

设置环境变量并安装:

##########最好写入 ~/.bash_profile
vi ~/.bash_profile
# .bash_profile
 
# Get the aliases and functions
if [ -f ~/.bashrc ]; then
	. ~/.bashrc
fi
 
# User specific environment and startup programs
 
PATH=$PATH:$HOME/bin
 
export PATH
export TBLIB_ROOT=/usr/local/tb-common-utils
 
# source ~/.bash_profile

##########也可以直接运行
export TBLIB_ROOT=/usr/local/tb-common-utils

##########安装
cd tb-common-utils
sh build.sh

注意,这个脚本运行结束,这个公共库就安装完毕(如果没有错误),安装目录就是TBLIB_ROOT制定的目录,注意,后面的其它软件可能都依赖这个库,所以最好是写入.bash_profile文件中。

由于TFS需要使用MySQL,并打算编译PHP的客户端,这里先添加两个第三方提供的yum源,下载安装如下两个RPM:

#http://dl.iuscommunity.org/pub/ius/stable/CentOS/6/x86_64/repoview/epel-release.html
#http://dl.iuscommunity.org/pub/ius/stable/CentOS/6/x86_64/repoview/ius-release.html

rpm -ivh epel-release-6-5.noarch.rpm
rpm -ivh ius-release-1.0-14.ius.centos6.noarch.rpm

安装完毕后yum源就添加了。

接下把MySQL和PHP安装上(对于单独运行的机器,只需要安装开发包即可),系统默认可能已经安装了低版本MySQL某部分,先杀掉(否则会冲突):

rpm -e --nodeps mysql-5.1.73-5.el6_6.x86_64
rpm -e --nodeps mysql-libs-5.1.73-5.el6_6.x86_64

接下来安装MySQL5.5 和 PHP 5.5

##yum search mysql55*
yum install mysql55.x86_64 mysql55-devel.x86_64 mysql55-libs.x86_64 mysql55-server.x86_64

##yum search php55
yum install php55u.x86_64 php55u-bcmath.x86_64 php55u-cli.x86_64 php55u-common.x86_64 php55u-devel.x86_64 php55u-enchant.x86_64 php55u-fpm.x86_64 php55u-gd.x86_64 php55u-intl.x86_64 php55u-mbstring.x86_64 php55u-mcrypt.x86_64 php55u-mysqlnd.x86_64 php55u-opcache.x86_64 php55u-pdo.x86_64 php55u-soap.x86_64 php55u-xml.x86_64 php55u-xmlrpc.x86_64

下载安装FTS 2.0.4(目前有2.2/2.4/2.6):

svn co http://code.taobao.org/svn/tfs/tags/release-2.0.4 tfs-2.0.4
cd tfs-2.0.4/
sh build.sh init

##接下来跟一般安装相同
./configure --prefix=/usr/local/tfs-2.0.4 --with-tblib-root=/usr/local/tb-common-utils
make
make install

如果之前设置了TBLIB_ROOT环境变量,这里的–with-tblib-root指令可以忽略。如果MySQL不是安装在标准位置,可以通过–with-mysql=[mysql_config]来指定,参考:

./configure --prefix=/usr/local/tfs-2.0.4 --with-tblib-root=/usr/local/tb-common-utils --with-mysql=/usr/bin/mysql_config

————————————————————
TFS 2.2.6安装:

svn co http://code.taobao.org/svn/tfs/tags/release-2.2.6 tfs-2.2.6
cd tfs-2.2.6/
sh build.sh init

./configure --prefix=/usr/local/tfs-2.2.16 --with-release --without-tcmalloc
make -j 4
/usr/bin/ld: cannot find -ljemalloc

在编译时提示jemalloc出错,安装一下这个lib:(第三方源)

yum install jemalloc

rpm -ql jemalloc-3.6.0-1.el6.x86_64
/usr/bin/jemalloc.sh
/usr/lib64/libjemalloc.so.1

执行了/usr/bin/jemalloc.sh之后再次Make,还是一样的提示,玛尼,于是:

ln -s /usr/lib64/libjemalloc.so.1 /usr/lib64/libjemalloc.so

再次Make才通过。由于不使用tcmalloc,导致TFS选择了jemalloc了替代,要是再禁掉它应该会使用原生的那个的那个malloc吧?
————————————————————

接下来是运行TFS,这个部分最好先了解了解原理。简单说就是前面的NS做主备,后面的DS实际存储,DS可横向扩展,每台DS中的硬盘也可以横向扩展,从配置来看应该是DS主动向NS报告,因为每台DS都需要配置NS相关的信息。

NS如果做主备,就有VIP的概念,这个还需要其它软件的复杂(检查谁挂了,VPI飘到哪台机器),以下配置只考虑一台NS情况,即不做主备。

#config/ns.conf
[public]
log_size=1073741824
log_num = 4 
log_level=info
task_max_queue_size = 10240
port = 8000 #NS的端口
work_dir=/usr/local/tfs-2.0.4 #TFS的工作目录
dev_name= eth0 #设备
thread_count =4 
ip_addr = 192.168.1.102 #VIP
[nameserver]
safe_mode_time = 300
ip_addr_list = 192.168.1.102|192.168.0.1 #主备IP,由于只有一条NS,第一个设置为VIP,第二个设置为假IP即可
group_mask = 255.255.255.255
max_write_timeout = 3
cluster_id = 1 
block_max_use_ratio = 98
block_max_size = 75497472 #块大小
max_replication = 1 #如果只有一台DS这里需要改为1
min_replication = 1 #如果只有一台DS这里需要改为1
replicate_ratio = 50
max_write_filecount = 64 
use_capacity_ratio = 96
heart_interval = 4
object_dead_max_time = 300
object_clear_max_time = 180 
heart_thread_count = 4 
heart_max_queue_size = 2048
report_block_thread_count = 6
report_block_max_queue_size = 32
report_block_hour_range = 2~4
report_block_time_interval = 1
repl_wait_time = 180
compact_delete_ratio =  10  
compact_max_load = 200
compact_hour_range = 1~10
dump_stat_info_interval = 60000000 
balance_percent = 0.05
add_primary_block_count = 3
task_percent_sec_size = 200 
oplog_sync_max_slots_num = 1024
oplog_sync_thread_num = 1
group_count = 1
group_seq  = 0
discard_newblk_safe_mode_time = 360 
choose_target_server_random_max_num = 128

其它参数可以搜索一下,做相应修改。NS和DS谁先启动是无关重要的。NS启动:

cd scripts
./tfs start_ns

以下配置和启动DS:

#config/ds.conf
[public]
log_size=1073741824
log_num = 8
log_level=error
task_max_queue_size = 10240
port = 8001 #端口
work_dir=/usr/local/tfs-2.0.4
dev_name= eth0
thread_count = 32 
ip_addr = 192.168.1.102 #DSIP
[dataserver]
ip_addr = 192.168.1.102 #NS的VIP,需要和NS配置对应
ip_addr_list = 192.168.1.102|192.168.0.1 #NS的IP列表,需要和NS配置对应
port = 8000 #NS的端口号,需要和NS配置对应
heart_interval = 2
check_interval = 2
replicate_threadcount = 2
block_max_size = 75497472 #块大小
dump_visit_stat_interval = 60
backup_type = 1
mount_name = /usr/local/tfs-2.0.4/data/tfs #挂载点,可以挂载多个分区,用tfs(i)表示
mount_maxsize = 15728640 #挂载大小,这个一定要小于分区可用空间,否则无法预分配空间,导致失败
base_filesystem_type = 1
superblock_reserve = 0
avg_file_size = 15360
mainblock_size = 75497472 
extblock_size = 4194304
block_ratio = 0.5
hash_slot_ratio = 0.5

配置设置完成了之后还无法启动。首先需要知道,一台DS可以挂载多个分区(可以一个磁盘分一个区),它的挂载点就是配置指令mount_name的值后加i(1,2,3…),每个分区需要需要TFS提供的命令进行所谓的格式化(实际就是预分配块),完成之后才能启动DS。

详细操作:

#添加一块磁盘,分两个区,用fdisk /dev/sdb,n->p->1,n->p->2,w完成
   Device Boot      Start         End      Blocks   Id  System
/dev/sdb1               1        2612    20980858+  83  Linux
/dev/sdb2            2613        5224    20980890   83  Linux

#分区格式化
mkfs -t ext4 /dev/sdb1
mkfs -t ext4 /dev/sdb1

#挂载到TFS指定目录
cd /usr/local/tfs-2.0.4/data
mkdir tfs1 tfs2
mount -t ext4 /dev/sdb1 //usr/local/tfs-2.0.4/data/tfs1
mount -t ext4 /dev/sdb2 //usr/local/tfs-2.0.4/data/tfs2

#查看挂载情况
df -l 
Filesystem     1K-blocks     Used Available Use% Mounted on
/dev/sda2       38981252  2943832  34057260   8% /
/dev/sdb1       20650996 15823572   3778384  81% /usr/local/tfs-2.0.4/data/tfs1
/dev/sdb2       20651028 15822804   3779180  81% /usr/local/tfs-2.0.4/data/tfs2

#设置fstab,让其开机自动挂载
vi /etc/fstab
/dev/sdb1 /usr/local/tfs/tfs1 ext4 defaults 1 2
/dev/sdb2 /usr/local/tfs/tfs2 ext4 defaults 1 2

#TFS格式化
cd scripts
./stfs format 1(1为挂载点顺序)
./stfs format 2(1为挂载点顺序)

#查看结果,在其中生成一个的块(75M,配置文件中指定)

这个操作完成后就可以启动DS了:

cd scripts
./tfs start_ds 1 #表示启动第一个存储区可以指定1-5,表示穷的那个1到5共5个存储区

NS和DS的停止:

cd scripts
./tfs stop_ns
./tfs stop_ds_all

验证启动:

tcp        0      0 0.0.0.0:8000                0.0.0.0:*                   LISTEN      20391/nameserver    
tcp        0      0 0.0.0.0:8001                0.0.0.0:*                   LISTEN      36018/dataserver    
tcp        0      0 0.0.0.0:8002                0.0.0.0:*                   LISTEN      36018/dataserver 

这里是NS和DS都放在一台机器上,DS配置中指定端口是8001,由于绑定了两个分区,所以它在第二个分区上启动了一个进程(端口号为8002),依次类推。

NS管理命令:

#NS上的操作 这个在查看总体情况时有用
/usr/local/tfs-2.0.4/bin/ssm -s 192.168.1.102:8000
server -b #随机列出dataserver的block块
server -w #随机列出dataserver的可写块
machine -a#列出dataserver的使用情况

文件上传原来看如下描述:
“客户端首先向nameserver发起写请求,nameserver需要根据dataserver上的可写块,容量和负载加权平均来选择一个可写的block。并且在该block所在的多个dataserver中选择一个作为写入的master,这个选择过程也需要根据dataserver的负载以及当前作为master的次数来计算,使得每个dataserver作为master的机会均等。master一段选定,除非master宕机,不会更换,一旦master宕机,需要在剩余的dataserver中选择新的master。返回一个dataserver列表。
客户端向master dataserver开始数据写入操作。master server将数据传输为其他的dataserver节点,只有当所有dataserver节点写入均成功时,master server才会向nameserver和客户端返回操作成功的信息。”

简单说就是DS层实现主从复制,保证一致性的手段是复制完成后才返回成功。

cd /usr/local/tfs/bin
./tfstoll -s 192.168.1.102:8000
TFS> put /root/view.jpg
.....
put /root/view.jgp => Txxxxxxxxx success

返回的信息中,有BlockID和Block中的File ID。

原理参考http://blog.yunnotes.net/index.php/taobao_tfs_design/
配置参考http://blog.yunnotes.net/index.php/deploy_document_for_tfs/
运维工具http://blog.yunnotes.net/index.php/tfstools_document/