学习MFS(四)

一、搭建Master Server

1、安装相关编译器、工具包

[root@master ~]# yum -y install gcc gcc-c++ zlib-devel

2、创建进程用户

[root@master ~]# useradd -s /sbin/nologin mfs

3、安装mfs软件包

[root@master ~]# tar xvfz mfs-1.6.27-5.tar.gz
[root@master ~]# cd mfs-1.6.27/
[root@master mfs-1.6.27]# ./configure --prefix=/usr/local/mfs \
--with-default-user=mfs \
--with-default-group=mfs \
--disable-mfschunkserver \
--disable-mfsmount

解析:
prefix                 #指定安装目录
with-default-user      #指定运行用户
with-default-group     #指定运行组
disable-mfschunkserver #禁用mfs的chunk功能
disable-mfsmount       #禁言mfs的mount功能

[root@master mfs-1.6.27]# make && make install #编译及编译安装

4、修改相关文件信息

[root@master ~]# cd /usr/local/mfs/etc/mfs/
[root@master mfs]# cp mfsexports.cfg.dist mfsexports.cfg #挂载以及权限设定文件
[root@master mfs]# cp mfsmaster.cfg.dist mfsmaster.cfg #主配置文件
[root@master mfs]# cp mfstopology.cfg.dist mfstopology.cfg #日志文件

[root@master ~]# cd /usr/local/mfs/var/mfs/
[root@master mfs]# cp metadata.mfs.empty metadata.mfs #master元数据文件

5、详解主配置文件

[root@master ~]# cd /usr/local/mfs/etc/mfs/
[root@master mfs]# vim mfsmaster.cfg

# WORKING_USER = mfs             #运行master服务的用户
# WORKING_GROUP = mfs            #运行master服务的组
# SYSLOG_IDENT = mfsmaster       #在syslog中表示是mfsmaster产生的日志,在syslog的标识,说明由master服务产生的
# LOCK_MEMORY = 0                #是否执行mlockall(),避免master进程溢出(默认为0)
# NICE_LEVEL = -19               #运行的优先级(如果可以,默认是-19;注意进程必须是root启动)

# EXPORTS_FILENAME = /usr/local/mfs/etc/mfs/mfsexports.cfg   #被挂载目录及其权限控制文件的存放位置

# TOPOLOGY_FILENAME = /usr/local/mfs/etc/mfs/mfstopology.cfg 
# DATA_PATH = /usr/local/mfs/var/mfs #数据存放位置

# BACK_LOGS = 50                     #metadata改变的log文件数目(默认是50)
# BACK_META_KEEP_PREVIOUS = 1

# REPLICATIONS_DELAY_INIT = 300
# REPLICATIONS_DELAY_DISCONNECT = 3600

# MATOML_LISTEN_HOST = *         #metalogger监听的IP地址(默认是*,代表任何IP)
# MATOML_LISTEN_PORT = 9419      #metalogger监听的端口地址(默认是9419)
# MATOML_LOG_PRESERVE_SECONDS = 600

# MATOCS_LISTEN_HOST = *         #用于chunkserver连接的IP地址(默认是*,代表任何IP)
# MATOCS_LISTEN_PORT = 9420      #用于chunkserver连接的端口IP地址(默认是9420)

# MATOCL_LISTEN_HOST = *         
# MATOCL_LISTEN_PORT = 9421

# CHUNKS_LOOP_MAX_CPS = 100000 
# CHUNKS_LOOP_MIN_TIME = 300    #chunks的回环频率(默认是300秒)

# CHUNKS_SOFT_DEL_LIMIT = 10
# CHUNKS_HARD_DEL_LIMIT = 25
# CHUNKS_WRITE_REP_LIMIT = 2    #在一个循环里复制到一个chunkserver的最大chunk数
# CHUNKS_READ_REP_LIMIT = 10    #在一个循环里从一个chunkserver复制的最大chunk数
# ACCEPTABLE_DIFFERENCE = 0.1

# SESSION_SUSTAIN_TIME = 86400
# REJECT_OLD_CLIENTS = 0

# deprecated:
# CHUNKS_DEL_LIMIT - use CHUNKS_SOFT_DEL_LIMIT instead
# LOCK_FILE - lock system has been changed, and this option is used only to search for old lockfile

6、详解被挂载目录及权限配置文件

[root@master ~]# cd /usr/local/mfs/etc/mfs/
[root@master mfs]# vim mfsexports.cfg

# Allow everything but "meta".
*                       /       rw,alldirs,maproot=0

# Allow "meta".
*                       .       rw
#详解
根据文件的显示,可以看出,该文件每一个条目分为三个部分
第一部分:客户端的IP地址
第二部分:被挂载的目录
第三部分:客户端拥有的权限

客户端的IP地址部分
*:代表所有的IP地址
192.168.100.71:代表单个IP地址
192.168.100.0/24:代表整个网段
192.168.100.71-192.168.100.100:代表指定网段

被挂载的目录部分
/:标识MFS根
.:标识MFSMETA文件系统

客户端拥有的权限部分
ro:代表只读模式
rw:代表读写方式共享
alldirs:允许挂载任何指定的子目录
admin:管理员权限
maproot:映射为root,还是指定的用户
Password:指定客户端的密码

7、优化路径

[root@master ~]# ln -s /usr/local/mfs/sbin/* /usr/local/bin/

8、启动服务

[root@master ~]# mfsmaster start
[root@master ~]# netstat -anpt | grep 'mfs'
超详细搭建MFS分布式文件系统

9、编辑启动脚本

[root@master ~]# vim /lib/systemd/system/mfs.service


[Unit]
Description=mfs
After=network.target

[Service]
Type=forking
ExecStart=/usr/local/mfs/sbin/mfsmaster start   #启动服务
ExecStop=/usr/local/mfs/sbin/mfsmaster stop    #关闭服务
PrivateTmp=true

[Install]
WantedBy=multi-user.target

[root@master ~]# systemctl enable mfs.service #服务开机自启动
[root@master ~]# systemctl stop mfs.service #关闭服务
[root@master ~]# systemctl start mfs.service #启动服务

二、搭建MetaLogger Server

1、安装相关编译器、工具包

[root@metalogger ~]# yum -y install gcc gcc-c++ zlib-devel

2、创建进程用户

[root@metalogger ~]# useradd -s /sbin/nologin mfs

3、安装mfs软件包

[root@metalogger ~]# tar xvfz mfs-1.6.27-5.tar.gz
[root@metalogger ~]# cd mfs-1.6.27
[root@metalogger mfs-1.6.27]# ./configure --prefix=/usr/local/mfs \
--with-default-user=mfs \
--with-default-group=mfs \
--disable-mfschunkserver \
--disable-mfsmount
[root@metalogger mfs-1.6.27]# make && make install

4、修改主配置文件

[root@metalogger ~]# cd /usr/local/mfs/etc/mfs/
[root@metalogger mfs]# cp mfsmetalogger.cfg.dist mfsmetalogger.cfg
[root@metalogger mfs]# vim mfsmetalogger.cfg
超详细搭建MFS分布式文件系统

5、优化路径

[root@metalogger ~]# ln -s /usr/local/mfs/sbin/* /usr/local/bin/

6、启动服务

[root@metalogger ~]# mfsmetalogger start
[root@metalogger ~]# netstat -anpt | grep 'mfsmetalogger'
超详细搭建MFS分布式文件系统

三、搭建Chunk Server(三台Chunk Server主机搭建步骤相同,其余两台搭建过程略……)

1、安装相关编译器、工具包

[root@chunk01 ~]# yum -y install gcc gcc-c++ zlib-devel

2、创建进程用户

[root@chunk01 ~]# useradd -s /sbin/nologin mfs

3、安装mfs软件包

[root@chunk01 ~]# tar xvfz mfs-1.6.27-5.tar.gz
[root@chunk01 ~]# cd mfs-1.6.27
[root@chunk01 mfs-1.6.27]# ./configure --prefix=/usr/local/mfs \
--with-default-user=mfs \
--with-default-group=mfs \
--disable-mfsmaster \
--disable-mfsmount
[root@chunk01 mfs-1.6.27]# make && make install

4、修改相应配置文件

[root@chunk01 ~]# cd /usr/local/mfs/etc/mfs/
[root@chunk01 mfs]# cp mfschunkserver.cfg.dist mfschunkserver.cfg
[root@chunk01 mfs]# cp mfshdd.cfg.dist mfshdd.cfg
[root@chunk01 mfs]# vim mfschunkserver.cfg
超详细搭建MFS分布式文件系统

[root@chunk01 mfs]# vim mfshdd.cfg
超详细搭建MFS分布式文件系统

5、创建MFS分区目录

[root@chunk01 mfs]# mkdir /data
[root@chunk01 mfs]# chown -R mfs:mfs /data/

6、优化路径

[root@chunk01 ~]# ln -s /usr/local/mfs/sbin/* /usr/local/bin/

7、启动服务

[root@chunk01 ~]# mfschunkserver start
[root@chunk01 ~]# netstat -anpt | grep 'mfschunkserver'
超详细搭建MFS分布式文件系统

四、配置客户端

1、安装相关编译器、工具包

[root@client ~]# yum -y install gcc gcc-c++ zlib-devel

2、安装fuse软件包

[root@client ~]# tar xvfz fuse-2.9.2.tar.gz
[root@client ~]# cd fuse-2.9.2
[root@client fuse-2.9.2]# ./configure && make && make install

3、设置环境变量

[root@client ~]# vim /etc/profile

#末行添加
export PKG_CONFIG_PATH=/usr/local/lib/pkgconfig:$PKG_CONFIG_PATH

[root@client ~]# source /etc/profile #重新加载系统环境变量配置文件

4、创建进程用户

[root@client ~]# useradd -s /sbin/nologin -M mfs

5、安装mfs软件包

[root@client ~]# tar xvfz mfs-1.6.27-5.tar.gz 
[root@client ~]# cd mfs-1.6.27
[root@client mfs-1.6.27]# ./configure --prefix=/usr/local/mfs --with-default-user=mfs --with-default-group=mfs --disable-mfschunkserver --disable-mfsmaster --enable-mfsmount
[root@client mfs-1.6.27]# make && make install

6、挂载MFS文件系统

[root@client ~]# mkdir /mnt/mfs #创建挂载点
[root@client ~]# modprobe fuse #加载fuse模块到内核
[root@client ~]# /usr/local/mfs/bin/mfsmount /mnt/mfs/ -H 192.168.100.71 #挂载MFS
[root@client ~]# df -h #查看挂载情况
超详细搭建MFS分布式文件系统

7、master开启web监控服务

[root@master ~]# mfscgiserv start

8、访问MFS监控工具

http://192.168.100.71:9425/mfs.cgi #client端访问web
超详细搭建MFS分布式文件系统

 

灾难测试、恢复及其他测试

1、client 机器无论怎样操作都不会影响master。

 

客户端强制kill -9杀掉mfsmount进程,需要先umount,然后再mount。否则会提示:

fuse: bad mount point `/usr/mfstest/': Transport endpoint is not connected

see: /usr/local/mfs/bin/mfsmount -h for help

 

2、matser、metalogger、chunker、client端,服务器关机(init0)和重启(init6)时,程序都是正常关闭,无需修复。

 

3、master启动后,metalogger、chunker、client三个元素都能自动与master建立连接。

正常启动顺序:matser---chunker---metalogger---client

关闭顺序:client---chunker---metalogger---master

但实际中无论如何顺序启动或关闭,未见任何异常。

 

4、整个mfs体系中,直接断电只有master有可能无法启动。

使用mfsmetarestore -a修复才能启动,如果无法修复,使用metalogger上的备份日志进行恢复。(几次测试发现:如果mfsmetarestore -a无法修复,则使用metalogger也无法修复)。

 

强制使用metadata.mfs.back创建metadata.mfs,可以启动master,但丢失的数据暂无法确定(此问题已经请教MFS开发小组)。

 

直接断电测试过程,使用mfsmetarestore –a无法修复,使用metalogger也无法修复的情况较少发生。5次只有一次无法修复。

 

5、chunker的维持:chunker的块(chunks)能够自动复制或删除

对一个目录设定“goal”,此目录下的新创建文件和子目录均会继承此目录的设定,但不会改变已经存在的文件及目录的copy份数。但使用-r选项可以更改已经存在的copy份数。

 

goal设置为2,只要两个chunker有一个能够正常运行,数据就能保证完整性。假如每个文件的goal(保存份数)都不小于2,并且没有under-goal文件(可以用mfsgetgoal –r和mfsdirinfo命令来检查),那么一个单一的chunkserver在任何时刻都可能做停止或者是重新启动。以后每当需要做停止或者是重新启动另一个chunkserver的时候,要确定之前的chunkserver被连接,而且要没有under-goal chunks。

 

实际测试时,传输一个大文件,设置存储2份。传输过程中,关掉chunker1,这样绝对会出现有部分块只存在chunker2上;启动chunker1,关闭chuner2,这样绝对会有部分块只存在chuner1上。

 

把chunker2启动起来。整个过程中,客户端一直能够正常传输。在客户端查看,一段时间内,无法查看;稍后一段时间后,就可以访问了。文件正常,使用mfsfileinfo 查看此文件,发现有的块分布在chunker1上,有的块分布在chuner2上。使用mfssetgoal 2和mfssetgoal -r 2均不能改变此文件的目前块的现状。但使用mfssetgoal -r 1后,所有块都修改成1块了,再mfssetgoal -r 2,所有块都修改成2份了。

 

测试chunker端,直接断电情况下,chunker会不会出问题:

 

a、数据传输过程中,关掉chunker1,等待数据传输完毕后,开机启动chunker1.

chunker1启动后,会自动从chunker2复制数据块。整个过程中文件访问不受影响。

 

b、数据传输过程中,关掉chunker1,不等待数据传输完毕,开机启动chunker1.

chunker1启动后,client端会向chunker1传输数据,同时chunker1也从chunker2复制缺失的块。

 

如果有三台chunker,设置goal=2,则随机挑选2个chunker存储。

 

如果有一个chunker不能提供服务,则剩余的2个chunker上肯定有部分chunks保存的是一份。则在参数(REPLICATIONS_DELAY_DISCONNECT = 3600)后,只有一份的chunks会自动复制一份,即保存两份。

 

保存两份后,如果此时坏掉的chunker能够提供服务后,此时肯定有部分chunks存储了三份,mfs会自动删除一份。

 

6、chunks修复 :mfsfilerepair

mfsfilerepair deals with broken files (those which cause I/O errors on read operations) to make them partially readable. In case of missing chunk it fills missing parts of file with zeros; in case of chunk version mismatch it sets chunk version known to mfsmaster to highest one found on chunkservers. Note: because in the second case content mismatch can occur in chunks with the same version, it’s advised to make a copy (not a snapshot!) and delete original file after "repairing".

 

Mfsfilerepair主要是处理坏文件的(如写操作引起的I/O错误)使文件能够部分可读。作用如下:在丢失块的情况下使用0对丢失文件进行填充;在块的版本号不匹配时设置快的版本号为master上已知的能在chunkerservers找到的最高版本号;注意:因为在第二种情况的内容不匹配,可能发生在块具有相同的版本,建议进行文件的拷贝(而不是进行不快照!),并删除原始文件再进行文件的修复。

 

Client端大文件传输过程中,强制拔下master主机电源,造成master非法关闭,使用mfsmetarestore -a修复后,master日志报告有坏块:

Jan 19 17:22:17 ngmaster mfsmaster[3250]: chunkserver has nonexistent chunk (000000000002139F_00000001), so create it for future deletion

Jan 19 17:22:18 ngmaster mfsmaster[3250]: (192.168.5.232:9422) chunk: 000000000002139F creation status: 20

Jan 19 17:25:18 ngmaster mfsmaster[3250]: chunk 000000000002139F has only invalid copies (1) - please repair it manually

Jan 19 17:25:18 ngmaster mfsmaster[3250]: chunk 000000000002139F_00000001 - invalid copy on (192.168.5.232 - ver:00000000)

Jan 19 17:26:43 ngmaster mfsmaster[3250]: currently unavailable chunk 000000000002139F (inode: 135845 ; index: 23)

Jan 19 17:26:43 ngmaster mfsmaster[3250]: * currently unavailable file 135845: blog.xxx.cn-access_log200904.tar.gz

 

Client端使用mfsfilerepair修复

[root@localhost mfstest]# /usr/local/mfs/bin/mfsfilerepair blog.xxx.cn-access_log200904.tar.gz

blog.xxt.cn-access_log200904.tar.gz:

chunks not changed: 23

chunks erased: 1

chunks repaired: 0

查看master日志,发现:

Jan 19 17:30:17 ngmaster mfsmaster[3250]: chunk hasn't been deleted since previous loop - retry

Jan 19 17:30:17 ngmaster mfsmaster[3250]: (192.168.5.232:9422) chunk: 000000000002139F deletion status: 13

Jan 19 17:35:16 ngmaster mfsmaster[3250]: chunk hasn't been deleted since previous loop - retry

Jan 19 17:35:16 ngmaster mfsmaster[3250]: (192.168.5.232:9422) chunk: 000000000002139F deletion status: 13

Client端执行以下操作后,master不再报告相关信息:

mv blog.xxt.cn-access_log200904.tar.gz blog.xxt.cn-access_log200905.tar.gz

 

7、chunker的空间

每一个chunkserver的磁盘都要为增长中的chunks保留些磁盘空间,从而达到创建新的chunk。只有磁盘都超过256M并且chunkservers报告自由空间超过1GB总量才可以被新的数据访问。最小的配置,应该从几个G 字节的存储。

 

When doing df -h on a filesystem the results are different from what I would expect taking into account actual sizes of written files.

 

在文件系统上做df –h得到的结果和我已经写进去的文件大小不一致?

 

Every chunkserver sends its own disk usage increased by 256MB for each used partition/hdd, and a sum of these master sends to the client as total disk usage. If you have 3 chunkservers with 7 hdd each, your disk usage will be increased by 3*7*256MB (about 5GB). Of course it's not important in real life, when you have for example 150TB of hdd space.

 

每个chunkerserver每次以256M的磁盘空间进行申请,而发给客户端的是这个空间加起来的总和。例如:如果你有3个chunkerserver7个分区磁盘,每次你的硬盘使用会增加3*7*256MB (大约5GB)。当然如果在现实生活中你的硬盘大小为150T的大小,这是对你来说是不重要的。

 

There is one other thing. If you use disks exclusively for MooseFS on chunkservers df will show correct disk usage, but if you have other data on your MooseFS disks df will count your own files too.

 

另外,如果你的chunkservers使用专用的磁盘,df将显示正确的磁盘使用情况。但是如果你有其他的数据在你的MooseFS磁盘上,df将会计算你所有的文件。

 

If you want to see usage of your MooseFS files use 'mfsdirinfo' command.

如果你想看你的MooseFS文件的使用情况,请使用'mfsdirinfo'命令。

 

8、快照snapshot

可以快照任何一个文件或目录,语法:mfsmakesnapshot src dst

但是src和dst必须都属于mfs体系,即不能mfs体系中的文件快照到其他文件系统。both elements must be on the same device。元素必须是相同的设备上。

 

Mfsappendchunks:追加chunks到一个文件

append file chunks to another file. If destination file doesn't exist then it's created as empty file and then chunks are appended.

 

追加文件块到另一个文件。如果目标文件不存在,则会创建一个空文件,然后继续将块进行追加。

 

9、回收站 trash bin

设置文件或目录的删除时间。一个删除的文件能够存放在“ 垃圾箱”中的时间称为隔离时间, 这个时间可以用mfsgettrashtime 命令来查看,用mfssettrashtime 命令来设置。单位为秒。

 

单独安装或挂载MFSMETA 文件系统,它包含目录/ trash (包含仍然可以被还原的删除文件的信息)和/ trash/undel (用于获取文件)。

 

把删除的文件,移到/ trash/undel下,就可以恢复此文件。在MFSMETA 的目录里,除了trash 和trash/undel 两个目录,还有第三个目录reserved,该目录内有已经删除的文件,但却被其他用户一直打开着。在用户关闭了这些被打开的文件后,reserved 目录中的文件将被删除,文件的数据也将被立即删除。此目录不能进行操作。

 

10、文件描述符

1.5.12版本,进行大量小文件写时,出现了一个严重错误,有可能和操作系统文件描述符有关。操作系统默认文件描述符为1024.1.6.11版本默认为100000,建议上线时,master和chunker修改文件描述符,即修改/etc/security/limits.conf添加'* - nofile 65535'。

 

最初我们尝试过采用HeartBeat来在两台主控间切换,依靠官方提供的mfs元日志恢复工具来在每次切换时恢复日志,但实际测试中发现这方案依旧不可靠,一旦发生浮动IP切换,日志仍然可能在恢复中丢失少量信息,导致群集中文件损坏。为此,在HA的基础上又构筑了DRBD来确保关键数据的不可中断性。

 

11、对master的单点故障的解决办法

 

其官方所提供的解决方案:

从备份中恢复一个master。

1)、安装一个mfsmaster

2)、利用同样的配置来配置这台mfsmaster(利用备份来找回mfsmaster.cfg),可见配置文件也是需要备份的。

3)、找回metadata.mfs.back文件,可以从备份中找,也可以中metalogger主机中找(如果启动了metalogger服务),然后把metadata.mfs.back放入data目录,一般为${prefix}/var/mfs。

4)、从在master宕掉之前的任何运行metalogger服务的服务器上拷贝最后metadata文件,然后放入mfsmaster的数据目录。

5)、利用mfsmetarestore命令合并元数据changelogs,可以用自动恢复模式mfsmetarestore –a,也可以利用非自动化恢复模式,语法如下:

mfsmetarestore -m metadata.mfs.back -o metadata.mfs changelog_ml.*.mfs

评价:这种在有些情况下无效,当在你刚写完文件时,在断掉master的网卡后,在metalogger机器上做恢复后,客户端上不能对某些文件正常访问,会长时间地卡在那里。通过mfsfileinfo在查看文件属性时,会发现一些的块无效提示,在文件文件里也能看到一些提示信息。数据会丢失,完整性得不到保障。

 

MFS+HA+DRBD的效果

DRBD相信很多人都有了解,它是一个类似于网络RAID1的实现,在双机之间进行实时的块设备数据同步,通常会和HA结合使用。我们在这个应用环境中,将主控的日志目录单独存储在一个块设备上(日志的增长是可控且能设定回滚周期的,所以日志容量问题不存在),并部署两个相同的主控环境,将一个同样容量的块设备和对应的主控机日志存储设备设为HA+DRBD同步,在这个环节中,非常关键的一点是在HA资源定义配置中,必须先切DRBD,再切换mfs服务,否则mfs客户端不能及时探测到虚拟浮动IP的的变化,导致挂载失效。

 

haresource示例:

master1 drbddisk::r0 Filesystem::/dev/drbd0::/mnt/drbd::ext3 mfs Ipaddr::192.168.2.103/24/eth0

 

在读写测试中,我们尝试了往mfs挂载点中不间断写入十万个文件,并统计了md5值列表。然后在其它mfs挂载目录中,再一次重复上述步骤,期间多次切换主备,可观察到每次切换时虽然I/O会进入等待循环,但一旦另一端的主控点正常启动、浮动IP恢复,写入会马上继续,这一步结束后,我们再次统计新写入的十万个文件的md5列表,途中同样多次人工切换主备,最终仍然不影响数据的可靠性,得到的md5值列表和之前的完全一致。类似目的的测试,我们也反复尝试了单个大文件的读写校验,结果令人比较满意。

 

要是出现网络中断及master机器突然断电,mfsmaster非正常关闭,没有将其运行过程所掌握的文件信息回写到磁盘的话,在备机取得相关ip及资源后,也不能正常启动,会出现像第一次开启mfsmaster时那样的提示:“将'metadata.mfs.empty'复制成'metadata.mfs'”等。

 

‘drbd+heartbeat(keepalived)’这种方法能提供对元数据的完整性更有保障,当master在出现断网后,在backup上无法启动mfsmaster进程,除非在原master机器上正常关闭了mfsmaster进程。因为在mfsmaster开始运行时,会将其data目录下的metadata.mfs文件复制为metadata.mfs.back,将现有的metadata.mfs文件读入到内存中,并删除了metadata.mfs,而内存中的此文件则发生了改变,但它不会在文件系统里'有所表现'。这样在mfsmaster主机出现异常时,或mfsmaster没有正常关闭时,该文件都不会写到磁盘上(或定期地将新增内容更新到metadata.mfs.back文件中?)!而这段时间所产生的文件信息将会丢失。

 

在使用drbd方案时,在进行切换时要保证master机上的mfsmaster进程要能正常关闭且不能断网,当然,你不能使用像dell r610这样的机器了(打了网卡补丁后还会有断网现象!410也有该现象)。

 

综其观之,MFS是一个非常具有潜力的分布式存储方案,结合HA+DRBD来弥补了它本身的主控单点后,在企业应用中的价值更上一层。虽然它还没有经历真正的长期应用的考验,但目前已经达到的程度,已有足够的理由来支持我们对它作进一步的推广和关注,假以时日,相信还会有更多的经验和价值能从中开掘出来。

posted on 2018-11-29 17:06  五光十色  阅读(290)  评论(0编辑  收藏  举报

导航