0%

什么是rTorrent

rTorrent是一个非常简洁、优秀、非常轻量的命令行BT下载客户端,和Aria2一样,既可以使用命令行下载,也可以配合自己的Web前端进行控制操作。很多都喜欢用rTorrent配合前端ruTorrent一起使用,毕竟有个图形界面方便些,不过配置起来挺麻烦的,博主觉得单独使用rtorrent下载BT文件速度还可以,操作也不难,关键安装很简单,很适合临时下载BT文件的人。

rTorrent的优缺点

优点:

  • 性能极高,资源占用少(相当于transmission资源消耗,却拥有不不弱于qbittorrent的速度)
  • 包含丰富的命令,对于保种、做种有极大的自由度

缺点:

  • 安装配置极其复杂

版本选择

有两版本分别是官方版jesec,后者提供了更加友好的面向用户的其他功能、优化以及与 RPC 接口的现代用户的更好集成;这里主要讨论jesec的版本。

安装rTorrent

安装完全静态的二进制文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# Install rTorrent to /usr/local/bin/rtorrent
# rtorrent-linux-amd64 and rtorrent-linux-arm64 are available
sudo wget https://github.com/jesec/rtorrent/releases/latest/download/rtorrent-linux-amd64 -O /usr/local/bin/rtorrent

# Make it executable
sudo chmod +x /usr/local/bin/rtorrent

# Default configuration

sudo mkdir -p /etc/rtorrent
sudo wget https://github.com/jesec/rtorrent/releases/latest/download/rtorrent.rc -O /etc/rtorrent/rtorrent.rc

# Install as a systemd service (optional)
# This example uses "download" user. Replace it with the an existing user that rTorrent should run with.
sudo wget https://github.com/jesec/rtorrent/releases/latest/download/rtorrent@.service -O /etc/systemd/system/rtorrent@.service
sudo systemctl daemon-reload
sudo systemctl enable rtorrent@download
sudo systemctl start rtorrent@download

其它安装方式,请自行搜索。

配置

rTorrent 尝试从多个位置加载配置文件:

  • $XDG_CONFIG_HOME/rtorrent/rtorrent.rc (最高优先级)
  • $HOME/.config/rtorrent/rtorrent.rc
  • $HOME/.rtorrent.rc
  • /etc/rtorrent/rtorrent.rc (最低优先级)

配置模板

内核调整

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# /etc/security/limits.conf
* - nofile 51200

# /etc/sysctl.conf (XanMod Kernel)
vm.swappiness=10

net.core.default_qdisc=fq_pie
net.ipv4.tcp_congestion_control=bbr2

# Maximum Socket Receive Buffer. 16MB per socket - which sounds like a lot, but will virtually never consume that much. Default: 212992
net.core.rmem_max = 16777216
# Maximum Socket Send Buffer. 16MB per socket - which sounds like a lot, but will virtually never consume that much. Default: 212992
net.core.wmem_max = 16777216
# Increase the write-buffer-space allocatable: min 4KB, def 12MB, max 16MB. Default: 4096 16384 4194304
net.ipv4.tcp_wmem = 4096 12582912 16777216
# Increase the read-buffer-space allocatable: min 4KB, def 12MB, max 16MB. Default: 4096 16384 4194304
net.ipv4.tcp_rmem = 4096 12582912 16777216

# Tells the system whether it should start at the default window size only for new TCP connections or also for existing TCP connections that have been idle for too long. Default: 1
net.ipv4.tcp_slow_start_after_idle = 0
# Allow reuse of sockets in TIME_WAIT state for new connections only when it is safe from the network stack’s perspective. Default: 0
net.ipv4.tcp_tw_reuse = 1
# Do not last the complete time_wait cycle. Default: 0
# only works from Linux 2.4 to 4.11
net.ipv4.tcp_tw_recycle = 1
# Minimum time a socket will stay in TIME_WAIT state (unusable after being used once). Default: 60
net.ipv4.tcp_fin_timeout = 30

我的配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
#############################################################################
# A minimal rTorrent configuration that provides the basic features
#############################################################################

# Some default configs are commented out by #, you can override them to fit your needs
# Lines commented out by ## are merely examples (NOT default)

# It is recommended to extend upon this default config file. For example:
# override only some configs via command line: -o network.port_range.set=6881-6881
# or, on top of custom config: import = /etc/rtorrent/rtorrent.rc

# Some additional values and commands
method.insert = system.startup_time, value|const, (system.time)

# rTorrent runtime directory (cfg.basedir) [default: "$HOME/.local/share/rtorrent"]
method.insert = cfg.basedir, private|const|string, (cat,(fs.homedir),"/rtorrent/")

# Default download directory (cfg.download) [default: "$(cfg.basedir)/download"]
#method.insert = cfg.download, private|const|string, (cat,(cfg.basedir),"download/")
method.insert = cfg.download, private|const|string, (cat,"/home/data/下载/")

# Log directory (cfg.logs) [default: "$(cfg.basedir)/log"]
method.insert = cfg.logs, private|const|string, (cat,(cfg.basedir),"log/")
method.insert = cfg.logfile, private|const|string, (cat,(cfg.logs),"rtorrent-",(system.time),".log")

# Torrent session directory (cfg.session) [default: "$(cfg.basedir)/.session"]
method.insert = cfg.session, private|const|string, (cat,(cfg.basedir),".session/")

# Watch (drop to add) directories (cfg.watch) [default: "$(cfg.basedir)/watch"]
method.insert = cfg.watch, private|const|string, (cat,(cfg.basedir),"watch/")

# Create directories
fs.mkdir.recursive = (cat,(cfg.basedir))

fs.mkdir = (cat,(cfg.download))
fs.mkdir = (cat,(cfg.logs))
fs.mkdir = (cat,(cfg.session))

fs.mkdir = (cat,(cfg.watch))
fs.mkdir = (cat,(cfg.watch),"/load")
fs.mkdir = (cat,(cfg.watch),"/start")

# Drop to "$(cfg.watch)/load" to add torrent
schedule2 = watch_load, 11, 10, ((load.verbose, (cat, (cfg.watch), "load/*.torrent")))

# Drop to "$(cfg.watch)/start" to add torrent and start downloading
schedule2 = watch_start, 10, 10, ((load.start_verbose, (cat, (cfg.watch), "start/*.torrent")))

# Listening port for incoming peer traffic
network.port_range.set = 51418-51418
network.port_random.set = no

# Distributed Hash Table and Peer EXchange
# Enable tracker-less torrents but vulnerable to passive sniffing
# DHT and PEX are always disabled for private torrents
#dht.mode.set = auto
#dht.port.set = 51418
#protocol.pex.set = yes
dht.mode.set = disable
protocol.pex.set = no

# DHT nodes for bootstrapping
dht.add_bootstrap = dht.transmissionbt.com:6881
dht.add_bootstrap = dht.libtorrent.org:25401

# UDP tracker support
#trackers.use_udp.set = yes
trackers.use_udp.set = no

# Peer settings
throttle.max_downloads.set = 100
throttle.max_uploads.set = 100
throttle.max_downloads.global.set = 300
throttle.max_uploads.global.set = 300
throttle.min_peers.normal.set = 100
throttle.max_peers.normal.set = 200
throttle.min_peers.seed.set = 100
throttle.max_peers.seed.set = 0
trackers.numwant.set = 100

#protocol.encryption.set = allow_incoming,try_outgoing,enable_retry

# Limits for file handle resources, this is optimized for
# an `ulimit` of 1024 (a common default). You MUST leave
# a ceiling of handles reserved for rTorrent's internal needs!
network.max_open_files.set = 10240
network.max_open_sockets.set = 500

# Send and receive buffer size for socket. Disabled by default (`0`), this means the default is used by OS
# (you have to modify the system wide settings!) (`send_buffer_size`, `receive_buffer_size`)
# Increasing buffer sizes may help reduce disk seeking, connection polling as more data is buffered each time
# the socket is written to. It will result higher memory usage (not visible in rtorrent process!).
network.receive_buffer.size.set = 4M
network.send_buffer.size.set = 12M

# Memory resource usage (increase if you have a large number of items loaded,
# and/or the available resources to spend)
pieces.memory.max.set = 1800M
#network.xmlrpc.size_limit.set = 16M

# Preallocate disk space for contents of a torrent
#
# Useful for reducing fragmentation, improving the performance
# and I/O patterns of future read operations. However, with this
# enabled, preallocated files will occupy the full size even if
# they are not completed.
#
# If you choose to allocate space for the whole torrent at once,
# rTorrent will create all files and allocate the space when the
# torrent is started. rTorrent will NOT delete the file and free
# the allocated space, if you later mark a file as DO NOT DOWNLOAD.
#
# 0 = disabled
# 1 = enabled, allocate when a file is opened for write
# 2 = enabled, allocate the space for the whole torrent at once
system.file.allocate.set = 2

# Basic operational settings
session.path.set = (cat, (cfg.session))
directory.default.set = (cat, (cfg.download))
log.execute = (cat, (cfg.logs), "execute.log")
##log.xmlrpc = (cat, (cfg.logs), "xmlrpc.log")

# Other operational settings
encoding.add = utf8
system.umask.set = 0027
system.cwd.set = (directory.default)
#schedule2 = low_diskspace, 5, 60, ((close_low_diskspace, 500M))
#pieces.hash.on_completion.set = no
pieces.hash.on_completion.set = no
##view.sort_current = seeding, greater=d.ratio=
##keys.layout.set = qwerty

# HTTP and SSL
network.http.max_open.set = 50
network.http.dns_cache_timeout.set = 25

# Path to the CA bundle. By default, rTorrent tries to detect from:
# $RTORRENT_CA_BUNDLE (highest priority)
# $CURL_CA_BUNDLE
# $SSL_CERT_FILE
# /etc/ssl/certs/ca-certificates.crt
# /etc/pki/tls/certs/ca-bundle.crt
# /usr/share/ssl/certs/ca-bundle.crt
# /usr/local/share/certs/ca-root-nss.crt
# /etc/ssl/cert.pem (lowest priority)
##network.http.cacert.set = /etc/ssl/certs/ca-certificates.crt

# Path to the certificate directory to verify the peer. The certificates
# must be in PEM format, and the directory must have been processed using
# the c_rehash utility supplied with openssl.
#
# For advanced users only, generally you should use network.http.cacert.set
# to specify path to the bundle of certificates.
##network.http.capath.set = "/etc/ssl/certs"

#network.http.ssl_verify_peer.set = 1
#network.http.ssl_verify_host.set = 1

# Run the rTorrent process as a daemon in the background
#system.daemon.set = false

# XML-RPC interface
network.scgi.open_local = (cat,(cfg.basedir),rtorrent.sock)

# Logging:
# Levels = critical error warn notice info debug
# Groups = connection_* dht_* peer_* rpc_* storage_* thread_* tracker_* torrent_*
print = (cat, "Logging to ", (cfg.logfile))
log.open_file = "log", (cfg.logfile)
log.add_output = "info", "log"
##log.add_output = "tracker_debug", "log"

### END of rtorrent.rc ###

批量迁移种子文件

rtorrent_fast_resume.pl

rtorrent_fast_resume.pl 是一个Perl脚本,可以将快速恢复数据添加到种子文件,从而跳过rTorrent必须对所有文件进行哈希检查。

用法:

1
$ rtorrent_fast_resume.pl [文件所在目录] [种子路径.torrent] [生成种子路径.torrent]

安装:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 安装Bencode_XS
wget http://search.cpan.org/CPAN/authors/id/I/IW/IWADE/Convert-Bencode_XS-0.06.tar.gz
wget https://rt.cpan.org/Ticket/Attachment/1433449/761974/patch-t_001_tests_t
tar zxf Convert-Bencode_XS-0.06.tar.gz
cd Convert-Bencode_XS-0.06
patch -uNp0 -i ../patch-t_001_tests_t
perl Makefile.PL
make
make test
sudo make install
## The patch-t_001_tests_t patch itself:
--- t/001_tests.t.orig<>Sat Nov 15 14:41:13 2014
+++ t/001_tests.t<----->Sat Nov 15 14:41:27 2014
@@ -109,6 +109,7 @@ SKIP: {
#we use Storable so we do not rely on bencode
eval q{use Storable qw(freeze)};.
skip "Storable not available", 12 if $@;
+ local $Storable::canonical = 1;
local $Convert::Bencode_XS::COERCE = 0;
is( freeze(bdecode('le')), freeze([]) );
is( freeze(bdecode('l0:0:0:e')), freeze(['', '', '']) );
# 安装rtorrent_fast_resume.pl脚本
sudo wget https://github.com/jesec/rtorrent/raw/master/doc/rtorrent_fast_resume.pl -O /usr/local/bin/rtorrent_fast_resume.pl

迁移脚本

move_torrent.sh

1
2
3
4
5
6
#!/bin/bash

for i in `cat $1`; do
echo ${i}
rtorrent_fast_resume.pl $3 "${2}/${i}.torrent" "${4}/${i}.torrent"
done

用法

1
bash move_torrent.sh [需要迁移种子列表] [需要迁移种子所在目录] [文件所在目录] [生成种子目录]
  • 需要迁移种子列表:不含后缀名
  • 需要迁移种子所在目录:略
  • 文件所在目录:略
  • 生成种子目录:略

BUG与处理方式

random_device

rTorrent 无法启动/循环启动。

systemd日志

1
2
3
4
5
6
7
8
9
10
11
12
13
14
$ sudo systemctl status rtorrent.service
● rtorrent.service - rTorrent service for Docker
Loaded: loaded (/etc/systemd/system/rtorrent.service; disabled; vendor preset: enabled)
Active: failed (Result: exit-code) since Wed 2022-05-18 06:17:52 CST; 10s ago
Process: 140420 ExecStart=/usr/local/bin/rtorrent -o system.daemon.set=true (code=exited, status=255/EXCEPTION)
Main PID: 140420 (code=exited, status=255/EXCEPTION)
CPU: 912ms

May 18 06:17:51 nas systemd[1]: Started rTorrent service for Docker.
May 18 06:17:51 nas rtorrent[140420]: rTorrent: loading 939 entries from session directory
May 18 06:17:52 nas rtorrent[140420]: rTorrent: started, 939 torrents loaded
May 18 06:17:52 nas rtorrent[140420]: rtorrent: random_device::random_device(const std::string&): device not available
May 18 06:17:52 nas systemd[1]: rtorrent.service: Main process exited, code=exited, status=255/EXCEPTION
May 18 06:17:52 nas systemd[1]: rtorrent.service: Failed with result 'exit-code'.

处理方式一

1
2
3
screen -S rt
export XDG_CONFIG_HOME=/usr/local/etc
/usr/local/bin/rtorrent -o system.daemon.set=true

处理方式二

1
2
3
crontab -e
# 增加一行
*/5 * * * * /usr/bin/bash -c "export XDG_CONFIG_HOME=/usr/local/etc && test `pidof rtorrent` || /usr/local/bin/rtorrent -o system.daemon.set=true &"

介绍

Torrust是一个开源项目,它为您提供了托管自己的(私人)BitTorrent跟踪器和在线洪流索引所需的所有工具。

项目结构

Torrust 分为两个独立的应用程序

  • Torrust Tracker:一款高性能且功能丰富的(私有)BitTorrent跟踪器。
  • Torrust Index:一个依赖于Torrust跟踪器的种子索引网站。

    特征

  • 列表项自托管高性能 BitTorrent 跟踪器。
  • 列表项支持公共,私人和白名单跟踪器模式。
  • 列表项用于将种子列入白名单和发布跟踪器密钥的 API。
  • 列表项电子邮件/密码身份验证(可选电子邮件验证)。
  • 列表项种子上传/下载(包括磁力链接)。
  • 列表项洪流审核。
  • 列表项可定制的洪流类别。
  • 列表项无需外部服务。
  • 列表项完全用 Rust 编写!

安装Torrust Tracker

安装依赖

  • OpenSSL:
    • Arch Linux: sudo pacman -S pkg-config openssl
    • Debian/Ubuntu: sudo apt-get install pkg-config libssl-dev
  • SQLite3:
    • Debian/Ubuntu: sudo apt-get install libsqlite3-dev
  1. 克隆源码
1
2
3
mkdir /opt/torrust
cd /opt/torrust
git clone https://github.com/torrust/torrust-tracker.git
  1. 编译源码
1
2
cd torrust-tracker
cargo build --release

如果有错,请运行 rustup update stable 再编译

  1. 运行一次生成 config.toml 文件
1
./target/release/torrust-tracker 
  1. 编辑配置文件config.toml (查看: 配置文档)
1
nano config.toml 

模板 config.toml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
log_level = "info"
mode = "private"
db_driver = "Sqlite3"
db_path = "data.db"
announce_interval = 120
min_announce_interval = 120
max_peer_timeout = 900
on_reverse_proxy = false
external_ip = "0.0.0.0"
tracker_usage_statistics = true
persistent_torrent_completed_stat = false
inactive_peer_cleanup_interval = 600
remove_peerless_torrents = true

[[udp_trackers]]
enabled = false
bind_address = "0.0.0.0:6969"

[[http_trackers]]
enabled = true
bind_address = "0.0.0.0:6969"
ssl_enabled = false
ssl_cert_path = ""
ssl_key_path = ""

[http_api]
enabled = true
bind_address = "127.0.0.1:1212"

[http_api.access_tokens]
admin = "MyAccessToken"
  1. 打开 bind_address端口 (default: 6969):

如果要配置NGINX请跳过此步骤。

1
sudo ufw allow 6969 

(可选) 你的 SSL 证书

如果要配置NGINX请跳过此步骤。

  1. 编辑 nano.config 文件并更改以下设置
1
2
3
4
5
6
7
...
[[http_trackers]]
...
ssl_enabled = true
ssl_cert_path = "YOUR_CERT_PATH"
ssl_key_path = "YOUR_CERT_KEY_PATH"
...

安装 NGINX

按照上述安装步骤 1-4 进行操作。

  1. 更改以下设置config.toml
1
2
3
4
5
6
7
...
on_reverse_proxy = true
...
[[http_trackers]]
bind_address = "127.0.0.1:6969"
ssl_enabled = false
...
  1. 为跟踪器创建一个 NGINX 配置(例如:tracker.torrust.com)

请务必改用您自己的域名。

1
sudo nano /etc/nginx/sites-available/tracker.torrust.com 
  • 插入示例配置

请勿复制 SSL 注释,并确保将域名更改为您的域名。

1
2
3
4
5
6
7
8
9
10
11
# without SSL

server {
listen 80;
server_name tracker.torrust.com;

location / {
proxy_set_header X-Forwarded-For $remote_addr;
proxy_pass http://127.0.0.1:6969;
}
}

确保更改ssl_certificate路径。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# with SSL

server {
listen 80;
server_name tracker.torrust.com;

return 301 https://$host$request_uri;
}

server {
listen 443;
server_name tracker.torrust.com;

ssl_certificate CERT_PATH
ssl_certificate_key CERT_KEY_PATH;

location / {
proxy_set_header X-Forwarded-For $remote_addr;
proxy_pass http://127.0.0.1:6969;
}
}
  1. 通过创建指向sites-enabled 目录中配置的符号链接来启用配置

将 tracker.torrust.com 替换为您的域/NGINX 配置。

1
ln -s /etc/nginx/sites-available/tracker.torrust.com /etc/nginx/sites-enabled/ 
  1. 在此之后,您可以通过nginx -t执行来测试配置的有效性,如果配置有效,您可以安全地重新加载Nginx以使新配置处于活动状态:
1
sudo systemctl reload nginx 

用法

  1. 在文件夹中,您可以像这样运行torrust跟踪器
1
./target/release/torrust-tracker 

与 Tmux 的用法

  1. 打开新的 Tmux 会话
1
tmux new -s torrust-tracker 
  1. Tmux 会话中,运行 torrust 跟踪器
1
2
cd /opt/torrust/torrust-tracker
./target/release/torrust-tracker
  1. Tmux 会话中分离
1
2
CTRL+B
D

安装Torrust

安装必备组件

前端不能单独运行,需要外部Web服务器,如ApacheNGINX。在本指南中,我们将使用Nginx

  • Node (version >= 12.0.0)
    • Debian/Ubuntu:
1
2
curl -fsSL https://deb.nodesource.com/setup_12.x | bash -
apt-get install -y nodejs
  • NPM
    • Debian/Ubuntu: sudo apt-get install npm
  • NGINX:
    • Debian/Ubuntu: sudo apt install nginx
    • 有关其他发行版:请参阅 Nginx 安装教程

安装后端

  1. 创建 torrust 安装目录(如果尚未创建)并克隆存储库
1
2
3
mkdir /opt/torrust
cd /opt/torrust
git clone https://github.com/torrust/torrust.git
  1. 切换到后端目录并创建一个名.env为:
1
2
cd torrust/backend
echo "DATABASE_URL=sqlite://data.db?mode=rwc" > .env
  1. 然后,我们必须创建 SQLite 数据库并运行迁移。安装sqlx-cli并创建数据库
1
2
cargo install sqlx-cli
sqlx db setup
  1. 现在构建后端
1
cargo build --release 
  1. 运行后端一次以生成 config.toml 文件:
1
2
cd /opt/torrust/torrust/backend
./target/release/torrust
  1. 然后编辑config.toml并更改至少以下键:
1
nano config.toml 

[tracker]

  • url: 设置为跟踪器的连接字符串。例如: udp://TRACKER_IP:6969.
  • api_url: 设置为跟踪器 API URL。默认:http://localhost:1212.
  • token: 将其设置为 Torrust Tracker config.toml 中的访问令牌。

[net]

  • port: 默认设置为 3000 。如果您选择其他端口,请确保也更改Nginx配置。

[auth]

  • secret_key: 设置为安全随机生成的字符串。

运行后端

  1. 使用以下命令运行后端
1
2
cd /opt/torrust/torrust/backend
./target/release/torrust

使用 Tmux 运行后端

  1. 使用 Tmux 运行后端
1
2
3
tmux new -s torrust-index
cd /opt/torrust/torrust/backend
./target/release/torrust

按下CTRL+B D以退出 tmux 会话而不终止它。

安装前端

  1. 首先创建一个名为.env的文件

请务必更改域名。

1
2
cd /opt/torrust/torrust/frontend
echo "VITE_API_BASE_URL=https://YOUR_DOMAIN/api" > .env
  1. 构建前端
1
2
npm i
npm run build

成功完成此命令后, dist 该文件夹中将包含前端的构建版本。这些文件将由Nginx在后续步骤中提供。

NGINX 配置

  1. 创建一个包含以下内容/etc/nginx/sites-available/torrust.conf文件

需要更改 YOUR_DOMAIN x2, CERT_PATH 和 CERT_KEY_PATH.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
server {
listen 80;
server_name YOUR_DOMAIN;

return 301 https://$host$request_uri;
}

server {
listen 443 ssl;
server_name YOUR_DOMAIN;

ssl_certificate CERT_PATH;
ssl_certificate_key CERT_KEY_PATH;

root /opt/torrust/torrust/frontend/dist/;
location / {
try_files $uri $uri/ /index.html;
}

location /api/ {
proxy_pass http://127.0.0.1:3000/;
}
}
  1. 通过在sites-enabled目录中创建指向配置的符号链接来启用配置
1
ln -s /etc/nginx/sites-available/torrust.conf /etc/nginx/sites-enabled/ 
  1. 在此之后,您可以通过nginx -t执行来测试配置的有效性,如果配置有效,您可以安全地重新加载Nginx以使新配置处于活动状态
1
sudo systemctl reload nginx 

来源

该工具受 Nali C版本 和 nali-cli js版本的启发.

我想要在终端对IP地理信息和CDN服务提供商进行查询,发现了Nali这个工具,Nali与哪里谐音,非常适合这类工具

经过简单的使用,我发现最初的C语言版本功能缺失,而js版本包实在大的恐怖、而支持的平台非常有限,所以我用golang重写了这个工具,在原有功能的基础上增加了对IPv6的支持,并且增加了Geoip2数据库

功能

  • 支持多种数据库
    • 纯真 IPv4 离线数据库
    • ZX IPv6 离线数据库
    • Geoip2 城市数据库 (可选)
    • IPIP 数据库 (可选)
    • ip2region 数据库 (可选)
    • DB-IP 数据库 (可选)
    • IP2Location DB3 LITE 数据库 (可选)
  • CDN 服务提供商查询
  • 支持管道处理
  • 支持交互式查询
  • 同时支持IPv4和IPv6
  • 支持多语言
  • 查询完全离线
  • 全平台支持
  • 支持彩色输出

安装

从源码安装

Nali 需要预先安装 Go >= 1.18. 安装后可以从源码安装软件:

$ go install github.com/zu1k/nali@latest

下载预编译的可执行程序

可以从Release页面下载预编译好的可执行程序: Release

你需要选择适合你系统和硬件架构的版本下载,解压后可直接运行

使用说明

查询一个IP的地理信息

1
2
$ nali 1.2.3.4
1.2.3.4 [澳大利亚 APNIC Debogon-prefix网络]

或者 使用 管道

1
2
$ echo IP 6.6.6.6 | nali
IP 6.6.6.6 [美国 亚利桑那州华楚卡堡市美国国防部网络中心]

同时查询多个IP的地理信息

1
2
3
4
$ nali 1.2.3.4 4.3.2.1 123.23.3.0
1.2.3.4 [澳大利亚 APNIC Debogon-prefix网络]
4.3.2.1 [美国 新泽西州纽瓦克市Level3Communications]
123.23.3.0 [越南 越南邮电集团公司]

交互式查询

使用 exitquit 退出查询

1
2
3
4
5
6
7
8
$ nali
123.23.23.23
123.23.23.23 [越南 越南邮电集团公司]
1.0.0.1
1.0.0.1 [美国 APNIC&CloudFlare公共DNS服务器]
8.8.8.8
8.8.8.8 [美国 加利福尼亚州圣克拉拉县山景市谷歌公司DNS服务器]
quit

dig 命令配合使用

需要你系统中已经安装好 dig 程序

1
2
3
4
$ dig nali.zu1k.com +short | nali
104.28.2.115 [美国 CloudFlare公司CDN节点]
104.28.3.115 [美国 CloudFlare公司CDN节点]
172.67.135.48 [美国 CloudFlare节点]

nslookup 命令配合使用

需要你系统中已经安装好 nslookup 程序

1
2
3
4
5
6
7
8
9
10
11
$ nslookup nali.zu1k.com 8.8.8.8 | nali
Server: 8.8.8.8 [美国 加利福尼亚州圣克拉拉县山景市谷歌公司DNS服务器]
Address: 8.8.8.8 [美国 加利福尼亚州圣克拉拉县山景市谷歌公司DNS服务器]#53

Non-authoritative answer:
Name: nali.zu1k.com
Address: 104.28.3.115 [美国 CloudFlare公司CDN节点]
Name: nali.zu1k.com
Address: 104.28.2.115 [美国 CloudFlare公司CDN节点]
Name: nali.zu1k.com
Address: 172.67.135.48 [美国 CloudFlare节点]

与任意程序配合使用

因为 nali 支持管道处理,所以可以和任意程序配合使用

Nali 将在 IP后面插入IP地理信息,CDN域名后面插入CDN服务提供商信息

支持IPv6

和 IPv4 用法完全相同

1
2
3
4
5
6
7
8
9
$ nslookup google.com | nali
Server: 127.0.0.53 [局域网 IP]
Address: 127.0.0.53 [局域网 IP]#53

Non-authoritative answer:
Name: google.com
Address: 216.58.211.110 [美国 Google全球边缘网络]
Name: google.com
Address: 2a00:1450:400e:809::200e [荷兰Amsterdam Google Inc. 服务器网段]

查询 CDN 服务提供商

因为 CDN 服务通常使用 CNAME 的域名解析方式,所以推荐与 nslookup 或者 dig 配合使用,在已经知道 CNAME 后可单独使用

1
2
3
4
5
6
7
8
9
10
11
12
13
$ nslookup www.gov.cn | nali
Server: 127.0.0.53 [局域网 IP]
Address: 127.0.0.53 [局域网 IP]#53

Non-authoritative answer:
www.gov.cn canonical name = www.gov.cn.bsgslb.cn [白山云 CDN].
www.gov.cn.bsgslb.cn [白山云 CDN] canonical name = zgovweb.v.bsgslb.cn [白山云 CDN].
Name: zgovweb.v.bsgslb.cn [白山云 CDN]
Address: 103.104.170.25 [新加坡 ]
Name: zgovweb.v.bsgslb.cn [白山云 CDN]
Address: 2001:428:6402:21b::5 [美国Louisiana州Monroe Qwest Communications Company, LLC (CenturyLink)]
Name: zgovweb.v.bsgslb.cn [白山云 CDN]
Address: 2001:428:6402:21b::6 [美国Louisiana州Monroe Qwest Communications Company, LLC (CenturyLink)]

用户交互

程序第一次运行后,会在工作目录生成配置文件 config.yaml (默认~/.nali/config.yaml),配置文件定义了数据库信息,默认用户无需进行修改

数据库格式默认如下:

- name: geoip
name-alias:
- geolite
- geolite2
format: mmdb
file: GeoLite2-City.mmdb
languages:
- ALL
types:
- IPv4
- IPv6

其中,languagestypes 表示该数据库支持的语言和查询类型。 如果你需要增加数据库,需小心修改配置文件,如果有任何问题,欢迎提 issue 询问。

查看帮助

1
2
3
4
5
6
7
8
9
10
11
12
13
14
$ nali --help
Usage:
nali [flags]
nali [command]

Available Commands:
help Help about any command
update update qqwry, zxipv6wry, ip2region ip database and cdn

Flags:
-h, --help help for nali
-t, --toggle Help message for toggle

Use "nali [command] --help" for more information about a command.

更新数据库

更新所有可以自动更新的数据库

1
2
3
$ nali update
2020/07/17 12:53:46 正在下载最新纯真 IP 库...
2020/07/17 12:54:05 已将最新的纯真 IP 库保存到本地 /root/.nali/qqwry.dat

或者指定数据库

1
2
3
$ nali update --db qqwry,cdn
2020/07/17 12:53:46 正在下载最新纯真 IP 库...
2020/07/17 12:54:05 已将最新的纯真 IP 库保存到本地 /root/.nali/qqwry.dat

自选数据库

用户可以指定使用哪个数据库,需要设置环境变量: NALI_DB_IP4NALI_DB_IP6 或者两个同时设置

支持的变量内容:

  • Geoip2 ['geoip', 'geoip2']
  • Chunzhen ['chunzhen', 'qqwry']
  • IPIP ['ipip']
  • Ip2Resion ['ip2region', 'i2r']
  • DBIP ['dbip', 'db-ip']
  • IP2Location ['ip2location']

Windows平台

使用geoip数据库
1
2
3
4
5
set NALI_DB_IP4=geoip

或者使用 powershell

$env:NALI_DB_IP4="geoip"
使用ipip数据库
1
2
3
4
5
set NALI_DB_IP6=ipip

或者使用 powershell

$env:NALI_DB_IP6="ipip"

Linux平台

使用geoip数据库
使用ipip数据库

多语言支持

通过修改环境变量 NALI_LANG 来指定使用的语言,当使用非中文语言时仅支持GeoIP2这个数据库

该参数可设置的值见 GeoIP2 这个数据库的支持列表

1
2
# NALI_LANG=en nali 1.1.1.1
1.1.1.1 [Australia]

更换工作目录

如果未指定数据库存放目录,数据库默认将存放在 ~/.nali

设置环境变量 NALI_HOME 来指定工作目录,数据库存放在工作目录下。也可在配置文件中使用绝对路径指定其他数据库路径。

1
2
3
4
5
set NALI_HOME=D:\nali

or

export NALI_HOME=/var/nali

作者

Nali © zu1k, 遵循 MIT 证书.

Blog zu1k.com · GitHub @zu1k · Twitter @zu1k_lv · Telegram Channel @peekfun

什么是ZFS文件系统

ZFS文件系统的英文名称为Zettabyte File System,也叫动态文件系统(Dynamic File System),是第一个128位文件系统。最初是由Sun公司为Solaris 10操作系统开发的文件系统。作为OpenSolaris开源计划的一部分,ZFS于2005年11月发布,被Sun称为是终极文件系统,经历了 10 年的活跃开发。而最新的开发将全面开放,并重新命名为 OpenZFS


ZFS文件系统特点

强大的实时数据缩减技术

在线实时处理的区块层级 (Block-based) 数据重复删除机制 (Inline Data Deduplication),让数据在写入磁盘前就已经过删减演算,大幅节省存储空间占用;加上让大档变小档的数据压缩 (Inline Compression),以及先进的实时数据压实 (Inline Compaction) 技术,对于存储空间的节省程度带来重大的突破。在数据重复性质高或大量小型数据存取的情境下,提升 SSD 空间使用率的效益尤其明显,不仅显著提升全快闪配置的写入效能,对 SSD 的耐用性与使用寿命也有帮助,让全快闪存储架构成本效益更高。

突破想象的 PB 级巨量存储

128 位的 ZFS 文件系统拥有巨大的存储潜力,且 ZFS 原生就有强大的 RAID 逻辑磁盘管理功能。基于 ZFS 文件系统的存储方案,单一共享文件夹空间高达 1 PB 容量,更能协助企业克服当前大数据分析、边缘预算、AI 应用等巨量数据存储的挑战。面对 PB 等级的存储容量配置时,RAID Z 磁盘阵列架构亦也有传统磁盘阵列所无法实现的高速建置效率,提供弹性更大、存取效率更高的磁盘空间运用。

零秒瞬间,实时的 SnapSync 快照同步

ZFS 支持 iSCSI LUN 与共享文件夹的快照,快照数量高达 65,535 份 (假设每小时拍 1 张快照,1 天 24 张,可维持 7 年不用删除!),实现缜密的多版本快照保护。运用 Copy-on-Write 技术,拍摄快照近乎实时,且不影响当下的系统运作性能。利用快照文件还原数据也无需中断系统正在进行的服务,短时间内即刻启动灾难复原计划。更进阶的区块层级 Real-time SnapSync 实时快照同步功能,让本地 NAS 与备份 NAS 随时随地保持数据同步,关键数据得以获得更新、更完整而实时的备份。

至今最快的 LZ4 数据压缩算法

LZ4 是一种无损数据压缩算法,可提供极快的压缩和解压缩速度,是商用领域中所需 100 MB/s 以上的高速传输存储里,能够提供低延迟、高 IO 量的压缩选择。数据存储进采用 ZFS 文件系统的都会经过在线压缩处理 (Inline Compression),对于需要频繁压缩、时时快速解压的大量数据处理 (例如:虚拟机) 更具存储效率的优势。尤其再搭配数据重复删除机制,数据减量的效果更为显著。


为什么适合挂PT

什么是PT

PT全名是Private Tracker。很简单的说,就是基于Bittorrent协议的“点”对“点”的文件传输服务。由于是基于Bittorrent协议,在传输过程中要不断与其他节点交流,彼此分享对方所需要的文件块。因此,会对磁盘进行频繁的读写操作;尤其是在早期出种的时候,更为明显。

ZFS文件系统的缓存机制ARC

ZFS的可调缓存机制ARC是一种同时缓存数据块请求以及频繁的数据块请求的缓存机制。这是IBM专利自适应替换缓存的基础上的一些修改和扩展。它依赖内存作为高频的Cache缓存;所以,内存稳定对于ZFS文件系统至关重要。

由于ARC缓存机制的存在可以大大降低硬盘的读写;提高机械硬盘的使用寿命与读写速度。

一个月长时间测试

系统:Gentoo
硬件:HP Gen8 (G2020T 低配版本)
内存:8G(ECC)
硬盘:1T(机械硬盘)

ZFS文件系统配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
NAME   PROPERTY              VALUE                  SOURCE
data1 type filesystem -
data1 creation Sat 01 Apr 8:35 2021 -
data1 used 614G -
data1 available 285G -
data1 referenced 614G -
data1 compressratio 1.00x -
data1 mounted yes -
data1 quota none default
data1 reservation none default
data1 recordsize 128K default
data1 mountpoint /media/data1 local
data1 sharenfs off default
data1 checksum on default
data1 compression off default
data1 atime off local
data1 devices on default
data1 exec on default
data1 setuid on default
data1 readonly off default
data1 zoned off default
data1 snapdir hidden default
data1 aclmode discard default
data1 aclinherit restricted default
data1 createtxg 1 -
data1 canmount on default
data1 xattr on default
data1 copies 1 default
data1 version 5 -
data1 utf8only off -
data1 normalization none -
data1 casesensitivity sensitive -
data1 vscan off default
data1 nbmand off default
data1 sharesmb off default
data1 refquota none default
data1 refreservation none default
data1 guid 6389372042506996733 -
data1 primarycache all default
data1 secondarycache all default
data1 usedbysnapshots 0B -
data1 usedbydataset 614G -
data1 usedbychildren 55.8M -
data1 usedbyrefreservation 0B -
data1 logbias latency default
data1 objsetid 54 -
data1 dedup off default
data1 mlslabel none default
data1 sync standard default
data1 dnodesize legacy default
data1 refcompressratio 1.00x -
data1 written 614G -
data1 logicalused 614G -
data1 logicalreferenced 614G -
data1 volmode default default
data1 filesystem_limit none default
data1 snapshot_limit none default
data1 filesystem_count none default
data1 snapshot_count none default
data1 snapdev hidden default
data1 acltype off default
data1 context none default
data1 fscontext none default
data1 defcontext none default
data1 rootcontext none default
data1 relatime off default
data1 redundant_metadata all default
data1 overlay on default
data1 encryption off default
data1 keylocation none default
data1 keyformat none default
data1 pbkdf2iters 0 default
data1 special_small_blocks 0 default

测试结果

使用的是transmission作为PT客户
port2iz.1month.png

下图显示的是内存中块的命中情况

zfs2z.1month.png
本月实际上传量: 1.633 TB
本月实际下载量: 639.5 GB


之前有写过几篇关于如何下载、部署Aria2并且进行离线BT下载的文章;但是对于BT的基本原理,如何实现的还是不太明白;

今天RSS上看到一篇非常好的文章,讲的很清楚,但是是英文的,就翻译了一下,分享给大家;

原文链接:

对于搭建Aria2离线下载服务器感兴趣的可以看我的这几篇文章:

【译】BT下载的工作原理

BitTorrent是用于传输大文件的最常见协议之一。2013年2月,BitTorrent占全球所有带宽的3.35%,占文件共享专用总带宽6%的一半以上。– 3.35% of all worldwide bandwidth

本文不会讲解如何通过BT下载文件(这是一个好的客户端需要做的);

我们之间进入正题,来看一看BT使用到了哪些技术;

任何人都可以阅读这篇文章,即使是那些对于网络或BitTorrent的知识一无所知的读者;

谁创造了BitTorrent?

Bram Cohen于2001年发明了BitTorrent协议,并且Cohen用Python编写了第一个客户端实现。

科恩收集了一些免费的不可描述内容,来吸引Beta测试人员在2002年夏天使用BitTorrent。

BT vs 传统CS下载模式

在传统下载中,服务器上传文件,而客户端下载文件。

bt1.png

而由于服务器的带宽限制,导致一些热门的文件下载效率变得十分低下,例如:500个人下载同一个文件将使服务器承受巨大压力。这种压力会限制服务器的上传速度,因此客户端无法快速下载文件。

其次,CS模式的成本很高。我们支付的带宽会随着文件的热门程度而增加。

最后,CS模式是中心化的。如果服务器挂了,则这个文件就无法被任何人下载了!

而BT下载致力于解决这些问题:

Client-Server BitTorrent
中心化 去中心化
热门资源限制服务器性能 热门资源无性能限制
设备费用昂贵;资源热门程度决定了设备开销 设备开销不会随着资源热门而变化

我们知道,BT下载是通过对等网实现的(peer-to-peer,P2P);而在对等网络中,每个对等点都连接到网络中的每个其他对等点。

bt2.svg

而半中心化的对等网络是:拥有一个或多个权限比其他大多数对等点更高的对等网络。

bt3.svg

BitTorrent概述

BitTorrent是一种共享文件的方式,通常用于大型文件。 BitTorrent是单个下载源共享文件(例如服务器)的一种替代方法。并且,BitTorrent可以有效地在较低带宽上工作。

BitTorrent客户端的第一版没有搜索引擎,也没有对等交换,想要上传文件的用户必须创建一个小的torrent描述符文件(torrent descriptor file),然后将其上传到torrent索引站点。

当用户想要共享文件时,他们会将文件做成种子文件(seed their file)。该用户称为做种人(seeder)。他们将种子文件上传到交换站点(exchange)(我们稍后再讨论)。想要下载该文件的任何人都将首先下载此种子描述符。

bt4.png

我们称呼那些下载用户为对等点(peers)。他们的BT客户端将连接到BT tracker服务器(稍后讨论),并且tracker将向对等点发送种子集群中其他种子和对等点的IP地址列表。此处的集群指的是与某种子相关的所有PC。

种子文件描述符中包含了我们正在下载的文件的tracker服务器和元数据的列表。

bt5.png

对等点将会连接到种子对应的IP并下载文件的一个部分

bt6.png

当对等点完成下载(一个文件分片)后,它们可以充当种子提供者。虽然,可以在下载种子的同时充当种子提供者(这是很常见的)。

种子被文件共享给对等点后,该对等点也将充当种子提供者。在BitTorrent中,多个人可以上传相同的文件,而对于CS来说,只有一个服务器可以上传文件。

BitTorrent会将文件切分成称为pieces的块,每个块都有固定的大小(最后一个文件除外)。但是切分的块大小是不固定的,有时是256KB,有时是1MB。当每个对等点收到一个块时,它们就成为这个文件块的其他对等点的种子(become a seed of that piece for other peers)。

在使用BitTorrent时,我们没有使用单一的下载源。因此,我们可能会从国内下载一些文件块,然后从国外下载一些国内没有的文件块。

协议会对文件块进行哈希(hashes)处理,以确保种子对应的原始文件没有被篡改。然后将哈希值存储在torrent描述符中,并上传至tracker服务器。

这就是BT下载的基本概述了。接下来我会深入讲解BT下载的底层原理。并回答下列问题:

  • 如果对等点仅下载文件但从不上传,会发生什么?
  • 我们从哪里下载,又在给谁上传?
  • 什么是磁力链接( magnet link )?
  • 什么是种子描述符( torrent descriptor )?
  • 哈希使用到的是什么算法?
  • BT是如何选择下载的文件块的?
  • ……

种子描述文件中有什么?

种子描述文件是一个字典(dictionary)(或被称为哈希表,HashMap)文件。

文件被定义为:

① 声明(Announce)

这个字段中包括了tracker服务器的URL。还记得我们之前需要连接tracker服务器来查找使用同一文件的其他对等点吗?我们通过使用torrent描述文件中的announce key来找到tracker服务器。


② 信息(Info)

这个字段映射到了另一个词典列表,而字典列表中的元素个数取决于该种子共享的文件个数。字典列表中的key包括:

  • **Files(Info中的子字典,是一个列表)**:Files仅在共享多个文件时存在,Files中的每个字典对应一个文件。这些列表中的每一个字典都有2个key:
    • Length:文件大小(以字节为单位)。
    • Path:对应于子目录名称的字符串列表,最后一个是实际文件名。

③ 共享文件大小(Length)

文件大小(以字节为单位)(仅在共享一个文件时)


④ 文件名(Name)

建议的文件名。或建议的目录名称。


⑤ 文件块大小(Pieces Length)

单个文件块字节数。

文件块大小必须是2的幂,并且至少为16Kb;

注: 2^8Kb = 256Kb = 262144b


⑥ 文件块(Pieces)

一个存放文件块哈希值的列表:我们将文件数据分成几块。分别计算这些块的哈希值,并将其存储在列表中。

BitTorrent使用SHA-1算法,而SHA-1返回160位的哈希值,所以所有文件块的哈希值都将是一个长度为20个字节的倍数的字符串。

如果单个种子文件中包含了多个文件,则将会按照文件在文件目录中出现的顺序串联起来,形成文件块。

种子中的所有文件块均为完整块长度,只有单个文件中的最后一个块可能较短。

现在,我能猜到你在想什么。

这都0202年了,还在用SHA-1?

我也同意,并且现在BT下载的哈希算法已经慢慢迁移到了SHA265:BitTorrent is moving from SHA-1 to SHA256.

如果你对于种子描述文件的结构还是很困惑也不用担心!我设计了下面这个JSON文件,描述了种子文件的结构。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
{
"Announce": "url of tracker",
"Info": {
"Files": [
{
"Length": 16,
"path": "/folder/to/path"
},
{
"length": 193,
"path": "/another/folder"
}
]
},
"length": 192,
"name":" Ubuntu.iso",
"Pieces length": 262144,
"Pieces": [AAF4C61DDCC5E8A2DABEDE0F3B482CD9AEA9434D, CFEA2496442C091FDDD1BA215D62A69EC34E94D0]
}

BT下载中的块选择算法(Piece Selection Algorithm)

BitTorrent下载中最大的问题之一是“我应该选择下载哪些文件?”

对于传统的CS模型,我们将下载整个文件;但是现在,我们需要选择要下载的部分。

选择一个好的顺序来下载片断,对提高性能非常重要。一个差的文件块选择算法可能导致所有的文件块都处于下载中,或者另一种情况,没有任何一个片段会被上载给其它对等点。

在BT中块选择的算法思想就是:下载其他人没有的文件块,即稀有文件块。通过下载稀有的文件块,我们可以通过上传该块来减少稀有文件块的稀有度。

什么是子块(Sub-Pieces)和块选择算法(Piece Selection Algorithm)

BitTorrent使用TCP,一种用于数据包的传输协议。TCP具有一种被称为慢启动(slow start)的机制。

慢启动是一种平衡TCP网络连接速度的机制。它会逐步增加传输的数据量,直到找到网络的最大承载能力,如下图:

其中,cwdn代表拥塞窗口。

TCP之所以这样做,是因为:如果我们一次发送16个连接请求,可能会导致服务器无法使用该流量,最终网络发生拥塞。所有,如果我们不定期发送数据,则TCP可能会以比正常速度慢的速度限制网络连接。

BitTorrent协议会保证将数据细分为更多的子数据块来发送数据,每个子块的大小约为16KB。一个块的大小不是固定的,但大约为1MB。

同时BitTorrent协议始终有一定数量的请求连接(五个),用于子块管道(sub-piece pipe-lined)。当下载一个新的子块时,客户端将发送一个新请求,从而有助于加快速度。

同时子块可以被其他的对等点下载;

对于块选择算法来说,有两大原则:

  • 严格规则(Strict Policy);
  • 稀有度优先(Rarest First);

① 严格优先级(Strict Policy)

一旦BitTorrent客户端发起了一个文件块的子块的请求,则该文件块的任何剩余子块都将先于其他文件块的任何子块被请求。 这样,可以尽可能快的获得一个完整的片断。

Once the BitTorrent client requests a sub-piece of a piece, any remaining sub-pieces of that piece are requested before any sub-pieces from other pieces.

在此图中,就是需要先下载该文件块的其他的所有子块,而不是开始下载另一个文件块。


② 稀有度优先(Rarest First)

BitTorrent下载的核心策略就是选择最稀有的文件块(pick the rarest first),所以我们要下载其他对等点拥有的最少的文件块。

这样我们就可以将那些稀有的文件块变得不再稀有(‘un-rare’)。因为,如果只有一个对等点有这个文件块并且他下线了,则将没有人能够获得完整文件。

这个原则带来了很多好处:

Ⅰ。提高种子可靠性(Growing the seed)

首先,选择最稀有的文件块确保了我们仅从种子中下载新的文件块。

例如:稀有种子在一开始会成为下载瓶颈,仅有单个对等点含有该文件快。

而一个下载者可以看到他的对等点拥有哪些文件块,从而最稀有优先原则将使我们从种子中获取那些尚未由其他对等点上传的文件块。

让我们将其可视化:

虽然图中没有画出,但是各个对等点之间也是是相互连接的。

每个指向子文件块的箭头代表该对等点已下载对应内容。所以可以看到,我们已经下载了除了种子没有其他人拥有的子块,这意味着此子块很稀有。

同时可以看到,我们的上传速度高于种子的上传速度,因此所有其他的对等点都希望从我们这里下载。此外,他们也希望首先下载到最稀有的子块,因为我们是最稀有子块的2位持有者之一。

当每个对等点都从我们这里下载时,我们也可以从他们那里更快地下载。这就是针锋相对算法(“tit-for-tat algorithm”)(稍后讨论)。

Ⅱ。提高下载速度(Increased download speed)

持有文件块的对等点越多,下载速度就越快。这是因为我们可以从其他对等点那里下载子块。

Ⅲ。上传支持(Enable uploading)

稀有文件块是其他对等点最想要的,而获得稀有文件块也意味着对等点会对从我们的上传感兴趣。稍后我们将看到,我们上传的越多,我们可以下载的也越多。

Ⅳ。最常见的块在最后(Most common last)

将最常见的文件块留在下载末尾是明智的。由于许多对等点都拥有此文件块,因此能够下载它们的可能性比稀有文件块大得多。

Ⅴ。避免稀有块丢失(Prevent rarest piece missing)

当种子提供者断开连接时,稀有度优先原则保证了文件的所有部分都会分配到其余对等点。


③ 随机开始下载的第一个文件块(Random First Piece)

当我们刚下载时,我们还没有任何可以上传的内容,所以我们需要很快的获取到第一块。而此时,稀有度优先原则是很慢的,所以BT协议会随机选择一块开始下载;直到第一块下载并检查完成。此后,“稀有优先”策略才会开始。


④ 残局模式(Endgame Mode)

当(某个块)下载接近完成,但是还需要等待传输速率较慢的对等点上传时,可能会造成下载延迟,从而降低了传输效率。为了防止这种情况,其余的子块是从当前集群中的所有对等点中请求的。

还记得严格原则(Strict-Policy)吗?BT下载时,总是有数个待处理的子块请求:

假设我们正在从2个对等点下载,而还有另外1个我们未从中下载的对等点。

当请求的对等点缺少相应的子块时,BT协议会将请求广播给集群中所有对等点,而这有助于我们获取文件的最后一块。

如果对等点缺少子块,他们会将消息发送回我们。

一旦一个子块到达,我们将发送一条取消消息,告诉其他对等方忽略我们的请求。

使用“以牙还牙”算法分配资源

以牙还牙(英语:tit-for-tat)是一个用于博弈论的重复囚徒困境(reiterated prisoner’s dilemma)非常有效的策略。这策略最先由数学家阿纳托·拉普伯特(Anatol Rapoport)提出,并在密歇根大学社会学家罗伯特·阿克塞尔罗(Robert Axelrod)有关囚徒困境的研究中击败其他方法,脱颖而出,成为解决囚徒困境的最佳策略。

这一策略有两个步骤:

  1. 第一个回合选择合作;
  2. 下一回合是否选合作要看上一回对方是否合作,若对方上一回背叛,此回合我亦背叛;若对方上一回合作,此回合继续合作;

“以牙还牙”策略有四个特点:

  1. 友善:“以牙还牙”者开始一定采取合作态度,不会背叛对方;
  2. 报复性:遭到对方背叛,“以牙还牙”者一定会还击报复;
  3. 宽恕:当对方停止背叛,“以牙还牙”者会原谅对方,继续合作;
  4. 不羡慕对手:“以牙还牙”者个人永远不会得到最大利益,整个策略以全体的最大利益为依归;

阻塞算法(The choking Algorithm)

当一个对等点收到另一个对等点的请求时,它可以选择拒绝向该请求发送文件块(但我们仍然可以从他们那里下载)。如果发生这种情况,则称对方被“choked”了。

当对等点合作时,他们上传文件;而当对等点不合作时,他们“阻塞”了与其他对等点的连接;而原则就是上传文件块给那些已上传给过我们的对等点;

而阻塞算法最终理想的结果就是:同时进行多个双向连接并最终达到帕累托最优(Pareto Efficiency)

如果没有其他分配方式能够使某个人的状况更好也没有一个人的状况更差,那么我们认为分配是帕累托最优的。

因此,最大的问题是,如何确定哪些对等点会被阻塞而哪些不会被阻塞呢?

在默认情况下,客户端将仅保留默认的同时上传数量(max_uploads,max_uploads的默认值为4),而所有对该客户端的其他请求都将标记为“choked”。

bt15.png

如上图中,种子阻塞了与对等点的连接,因为它已达到其最大上传数量。此后,对等点将保持阻塞状态,直到发送了取消阻塞消息为止。

而当前的下载速率决定了要取消阻塞对等点。这里以每10秒计算一次最近20秒的下载速率平均值来决定。因为BT下载使用的是TCP协议(慢启动),因此快速阻塞和取消阻塞的效率并不高( 频繁的阻塞和疏通peers造成资源浪费)。

所以,根据这个原则:如果我们的上传速率很高,则会有更多的对等点允许我们从他们那里下载。即,**如果我们是优秀的上传者,我们可以获得更高的下载率(This means that we can get a higher download rate if we are a good uploader)**。这是BitTorrent协议的最重要功能。

文件下载片断选择是为了提高系统的总效率,而阻塞算法是为了提高个人用户的公平性和效率:作为取自于用户并用之于用户的分布式系统,整个系统的效率和个人用户的公平性至关重要!

BitTorrent的片断下载策略保证了最大的下载效率和稳健的完整性,而阻塞算法鼓励个人用户上传。BitTorrent的阻塞算法并不记录历史,也就是对用户以前的上传、下载行为没有记录。

而有些最近版本的BitTorrent已经能对帐户的上传、下载信息做出统计,然后转化为积分,但积分还没有和用户的下载优先级绑定,而且积分也只是简单的统计上传流量,上传的内容和上传的目标用户也没有分析,简单的积分策略并不能应对五花八门Spamming技术,积分算法应该是上传和下载流量的比数,且积分增加速度随着上传的不同目标用户和不同上传内容数量的增加而增加!

这个协议禁止了许多“搭便车的人(free riders)”,即:只下载但不上传的对等点。这是为了使整个对等网络高效,所以所有对等都需要为网络做出贡献;

除此之外:

对方被阻塞的另一个常见例子是,对等点不需要下载文件块。

同时,为了确保对等点之间的公平,有一个适当的机制确保了各对等点可以轮流下载其他对等点的文件块,这个机制被称为:“开放检测”(optimistic unchoking)。

开放检测(optimistic unchoking)

如果只是简单的为提供最好的下载速率的对等点们提供上传速度,那么就没有办法来发现那些空闲的连接是否比当前正使用的连接更好。

为了解决这个问题,在任何时候,每个对等点都拥有一个被称为“optimistic unchoking”的连接,这个连接总是保持畅通状态,而不论它的下载速率是怎样的。

每隔30秒,会重新计算一次哪个连接应该是“optimistic unchoking”。如果此“optimistic unchoking”连接比当前连接的某个非阻塞的连接下载速率要快,那么这个“optimistic unchoking”将会取代那个连接;

因为即使是TPC协议,30秒足以让上传能力达到最大,下载能力也相应的达到最大。

“optimistic unchoking”连接是随机选择的。同时,这也允许了那些不上传并仅下载文件的对等设备下载文件,即使他们拒绝合作(尽管它们将以慢得多的速度下载)

反对歧视(Anti-snubbing)

某些情况下,一个对等点可能会被它所有的对等点都阻塞了,这种情况下,它将会保持较低的下载速率直到通过“optimistic unchoking”找到了更好peers。

为了减轻这种问题,如果某个对等点在60秒钟内没有收到来自特定对等点的任何文件,则将认为它已被对方对等点 “怠慢”了,于是不再为对方提供上传,除非对方是“optimistic unchoking”。如果这种情况频繁发生,会导致多于一个的并发的“optimistic unchoking”。

仅上传对等点(Upload Only)

我们看到,使用在BitTorrent中实现的阻塞算法,我们更喜欢对我们友好的对等点。如果我可以从他们那里快速下载,我们也允许他们从我那里快速上传。

但是如果某个对等点完成了下载,它就无法使用此阻塞算法判断要取消阻塞哪些对等点;

此时,将使用新的阻塞算法:不再通过下载速率(因为下载速率已经为0了)来决定为哪些对等点提供上载,而是优先选择那些从它这里得到更好的上载速率的对等点。这样做的理由是可以尽可能的利用上载带宽,从而确保文件块上传速度更快,并且复制速度更快。

什么是跟踪器(Tracker)?

Tracker是一种特殊类型的服务器,可帮助对等点之间进行通信。

BitTorrent中的通信很重要。想一下,我们是如何了解其他对等点的存在的?

Tracker知道文件的拥有者以及拥有量。

但是,当对等下载开始后,就无需Tracker即可继续通信。

实际上,自从为无追踪程序的种子创建分布式哈希表(distributed hash table,DHT)方法以来,BitTorrent Tracker在很大程度上是多余的。

公共Tracker

公共Tracker是任何人都可以使用的Tracker。

海盗湾(Pirate Bay)是最受欢迎的公共Tracker之一,直到2009年其禁用了Tracker,而选择了磁链(即将讨论)。

引自: The Pirate Bay operated one of the most popular public trackers until disabling it in 2009


私有Tracker

私人Tracker是私人的,它们通过要求用户在网站上注册来限制使用。

控制注册的方法通常是邀请系统。要使用此类Tracker,我们需要被邀请。


多Tracker种子

多Tracker种子是在一个种子文件中包含多个Tracker。如果一个Tracker发生故障,这将提供冗余,其他Tracker可以继续维护种子文件创建的集群。

但是,使用此配置,单个种子可能有多个未互相连接的集群:一些用户可以连接到一个特定的Tracker,而无法连接到另一个,这很糟糕!因为,这会创建很多不相交的集群,这会阻碍torrent传输其描述的文件的效率。

之前,我谈到了Pirate Bay停用了Tracker并开始使用无tracker的种子。

当我们下载种子时,我们会得到该种子的哈希值。要在没有tracker的情况下下载种子,我们需要找到其他也在下载种子的对等点。为此,我们需要使用分布式哈希表(distributed hash table,DHT)。

下面让我们探索分布式哈希表。

分布式哈希表(Distributed Hash Tables)和Kademlia协议

分布式哈希表是一种分布式存储方法。这种网络不需要中心节点服务器,而是每个客户端负责一个小范围的路由,并负责存储一小部分资料,从而实现整个DHT网络的定位和存储。和中心节点服务器不同,DHT网络中的各节点并不需要维护整个网络的信息,而是只在节点中存储其临近的后继节点信息,大幅减少了带宽的占用和资源的消耗。DHT网络还在与关键字最接近的节点上复制备份冗余信息,避免了单一节点失效问题。

BitTorrent的DHT网络是使用Kademlia协议(以下简称Kad),一个点对点(P2P)的< 键, 值>元组存储和查询系统。

Kad是美国纽约大学的PetarP. Maymounkov和David Mazieres.在2002年发布的一项研究结果:《Kademlia: A peerto -peer information system based on the XOR metric》。

Kademlia拥有许多的令人惊喜的特点,这些特点是任何以前的P2P系统所无法同时提供的。它减少了节点必须发送的用来相互认识的配置消息的数量。在做键查询的同时, 配置消息将会被自动传播。节点拥有足够的知识和灵活性来通过低时延路径发送查询请求。

Kademlia使用平行的、异步的查询请求来避免节点失效所带来的超时时延。通过节点记录相互的存在的算法可以抵抗某些基本的拒绝服务(DoS)攻击。

分布式哈希表为我们提供了类似于字典的接口,但是节点分布在整个网络中。DHT的核心在于:通过散列特定的key可以找到存储对应的特定key的节点。

实际上,这意味着,每个对等点都将成为一个微型tracker(mini-tracker)。

每个节点(实现DHT协议的客户端/服务器)都有一个唯一的标识符,称为“节点ID”(Node Id)。我们从与BitTorrent信息哈希相同的160位空间中随机选择节点ID。

信息哈希(Infohashes)是以下内容的SHA-1哈希:

  • ITEM:文件大小和路径(带文件名的路径);
  • Name:被索引时的名称;
  • Piece length:单块文件大小;
  • Pieces:该种子文件的每一块的SHA-1哈希值;
  • Private:限制访问标记;

关于Infohashes:What exactly is the info_Hash in a torrent file

我们使用距离度量(distance metric)来比较两个节点ID或 一个节点ID和一个infohash的“接近度”(closeness);

节点必须具有包含其他几个节点的联系信息的路由表。从而,节点在DHT中知道彼此。他们知道许多节点的id与他们自己的很接近,但也有很少节点的id与他们很远。

距离度量是XOR(异或),被解释为一个整数:

distance(A,B)=|A⊕B|

数值越小越接近。

当一个节点想要为一个种子找到对等节点时,他们使用distance metric来比较种子的infohash和路由表中节点的ID,或者一个节点的ID和另一个节点的ID。

然后,他们联系路由表中最靠近infohash的节点,并向他们请求下载种子的对等点的联系信息。

如果这个被联系的节点知道种子的对等点,他们返回对等点的联系信息响应。否则,被联系的节点必须用他的路由表中最靠近infohash的节点来响应请求种子的信息。

原始节点查询更接近infohash的节点,直到找不到更接近的节点为止。在节点完成搜索后,客户端将自己的对等联系信息(peer contact information)插入到与种子信息最接近的id响应节点上。在未来,其他节点可以很容易地找到我们(原始节点)。

对等点查询的返回值包括一个被称为“token”的不透明值(opaque value)。一个节点要宣布它的控制对等点正在下载一个种子,它必须在最近的一次对等查询中展示从同一被查询的对等点接收到的令牌。

当一个节点试图“发布(announce)”一个种子时,被查询的节点会根据查询节点的IP地址检查令牌。这是为了防止恶意主机为种子注册其他主机。(This is to prevent malicious hosts from signing up other hosts for torrents)

查询节点也会将一个令牌返回到他接收令牌的同一个节点。在令牌被分发后,我们必须在一个合理的时间内接受令牌(We must accept tokens for a reasonable amount of time after they have been distributed.)。

BitTorrent对于这个令牌的实现是:使用IP地址和每5分钟更改一次的密码的SHA-1哈希,并且接受最多10分钟以前的令牌。

路由表(Routing Table)

每个节点都会维护一个已知的好节点(known good nodes)的路由表。我们在DHT中使用路由表起始点(starting points)进行查询,同时我们从路由表返回节点以响应来自其他节点的查询。

并非我们了解的所有节点都是平等的。有些是“好”的,有些则不是。许多使用DHT的节点可以发送查询和接收响应,但是不能响应来自其他节点的查询。并且每个节点的路由表必须只包含已知的好节点(good node):

一个好的节点是一个节点在过去15分钟内响应了我们的一个查询。如果一个节点在过去15分钟内响应了我们的查询并向我们发送了一个查询,那么它也是一个好节点。

一个节点在15分钟不活动之后,节点就会变得可疑(questionable)。

而当节点无法响应一个或者多个连续的查询时(fail to respond to multiple queries in a row),它们就会变得糟糕(bad)。

并且我们在查询时良好的节点会优先于状态未知的节点。

一个路由表覆盖了从0到2^160的整个节点ID空间。我们将路由表细分为“桶(buckets)”,每个桶覆盖部分空间。

一个空表有一个bucket,其ID空间范围为min=0, max=2^160。

空表只有一个bucket,因此任何节点都必须包含在其中。在一个桶变“满(full)*”之前,每个桶只能容纳K个节点,目前是8个。

当一个bucket中已经填满已知的好节点(full of known good nodes)时,我们可以不再添加节点,除非我们自己的节点ID在bucket的范围内。此时,这个桶会被两个桶替换,每个桶都是旧桶的一半。旧桶中的节点被分布在新桶中。

对于只有一个bucket的新表,我们总是将完整的bucket分割为覆盖范围为0 - 2^159 和 2^159 - 2^160。

当桶中满是好的节点时,我们只需丢弃新节点。当桶中的节点变坏(被确认变坏)时,我们才用一个新节点替换它们。

当节点被认为是有问题(questionable)的,并且在过去15分钟内没有任何响应,那么最近最少无响应的节点将被ping。节点响应或不响应。响应意味着我们移动到下一个节点。我们重复这样做,直到找到一个没有响应的节点。

如果我们没有找到,那么这个桶就被认为是好的。

当我们确实找到一个节点无响应时,我们会在丢弃该节点并用新的好节点替换它们之前,再尝试ping一次。

同时,每个桶都维护了一个“最后更改(last changed)”属性,以显示其存储内容的“新鲜程度(fresh)”。

当桶中的一个节点被ping并响应,或一个新节点被添加到桶中,或一个节点被另一个节点替换时,将会更新桶的last changed属性。

如果last changed属性在最近15分钟内没有更新,则会刷新bucket。

攻击BitTorrent

对BitTorrent网络的攻击很少,因为一切都是公开的,比如:我们的IP地址,我们下载的东西等;

有人可能会问,为什么要攻击开放网络?为什么要攻击一个完全开放的网络?

Exploit-DB上只列出了7个条目,并且大多数都是针对特定的客户��

对BitTorrent网络的主要攻击目的是阻止盗版。我们谈到这里还没有谈到盗版,但它通常是BitTorrent的同义词。

bt的主要攻击手段是bt中毒(Torrent Poisoning)。

Torrent Poisoning

这种攻击的目的是获取盗版内容的对等点的IP地址或以某种方式修改文件内容。

麦当娜(Madonna)发行的《Madonna’s American Life》专辑就是内容中毒的一个例子。在发行之前,曲目发行的长度和文件大小类似。歌曲中有一段麦当娜说:

“What the fuck do you think you’re doing?”

随后是几分钟的沉默。

下面是一些使种子中毒的方法。

① Index Poisoning

索引允许用户定位具有所需内容的对等节点的IP地址。这种攻击方法可以让对等点的搜索变得困难。

攻击者会在索引中插入大量无效信息,以阻止用户找到正确的信息。

其思想是通过让对等点尝试从无效对等点下载片段来减慢下载速度。

② Decoy Insertion

他们将文件的损坏版本插入网络。

想象一下,一个文件有500份拷贝,其中只有2份是真正的文件,这会阻止盗版者找到真正的文件。

但是,大多数有种子列表的网站都有投票系统。这阻止了这种攻击,因为搜索的顶部充满了未损坏的文件。

在GameDevTycoon中,损坏的文件是在正版文件上传到盗版网站之前发布的。盗版者不知道的是,文件已经损坏。在盗版游戏中除了无法获得胜利,其他一切都是正常的!

防御BitTorrent攻击

最受欢迎的种子是由多年来建立融洽关系的个人或团体发布的。而在私人tracker上,种子的资源可以指向个人。所有,有毒的种子可以很快被贴上标签,从而传播也可以被禁止。

换而言之,在公共tracker上,下载由受信任团体制作的种子更可取。毕竟,你是喜欢从Ubuntu团队下载Ubuntu,还是从用户xxx- hackers - elite - ghost - protocol- xxx下载?

在公共tracker中,如果一个种子是有毒的,该种子会被报告和删除。

防御bt攻击最简单的方法是使用一个与你无关的IP地址,无论这是通过VPN还是其他服务(The simplest way to defend against a BitTorrent attack is to use an IP address not associated with you. Whether this is through a VPN or some other service)。

总结

通过阅读本文,我想你学到了:

  • 什么是BitTorrent;
  • 什么是Torrent描述符文件;
  • BitTorrent如何选择对等点;
  • BitTorrent如何选择文件块;
  • 严格的优先级(Strict Priority);
  • 最少的优先(Rarest First)
  • 残局模式(Endgame Mode)
  • 阻塞算法(Choking Algorithms)
  • 以牙还牙算法(tit-for-tat);
  • 帕累托效率(Pareto Efficiency)
  • 开放检测(Optimistic Unchoking)
  • 反对歧视(Anti-snubbing)
  • 仅仅上传(Upload Only)
  • 什么是tracker;
  • 对BitTorrent网络的攻击;
  • ……

如果你想继续深入了解BitTorrent:

附录

原文链接:

对于搭建Aria2离线下载服务器感兴趣的可以看我的这几篇文章:

前言

Aria2屏蔽迅雷等吸血客户端,自动更新tracker,移除wantDigest头的Docker镜像。

创建镜像

1
2
3
4
5
6
7
8
docker create --name=aria2 \
-v <path to downloads>:/mnt \
-v <path to config>:/config \
-e PGID=<gid> -e PUID=<uid> \
-e TZ=<timezone> -e SECRET=<admin> \
-e RPC=<6800> PORT=<16881> WEB=<80> \
-p 6800:6800 -p 16881:16881 -p80:80 \
auska/docker-aria2

参数解释

  • -p 80 网页管理端口
  • -p 6800 PRC端口
  • -p 16881 - BT软件通讯端口
  • -v /config - 配置文件目录
  • -v /mnt - 下载文件目录
  • -e PGID 用户的GroupID,留空为root
  • -e PUID 用户的UserID,留空为root
  • -e SECRET 登录密钥,默认admin
  • -e TZ 时区 默认 Asia/Shanghai

版本介绍

latest 自带WEB。
no-web 不包括WEB_UI。

更新日志

  • 2022-03-17 增加屏蔽迅雷等吸血客户端,webui更新到1.2.3。
  • 2022-03-31 升级openssl版本
  • 2022-04-07 升级openssl-3.0.2 zlib-ng-2.0.6
  • 2022-04-23 增加移除wantDigest头变量
  • 2022-05-02 修复屏蔽迅雷
  • 2022-05-18 升级 openssl-3.0.3 libxml2-2.9.14 sqlite-3380500

    地址

Docker https://hub.docker.com/repository/docker/auska/docker-aria2

自从疫情期间,圈养在家无事可做,只能没事玩玩PT。那么即使然是玩PT就不然少不了qBittorrent。但是当时软件版本只有4.19.1,像我这样追新的人肯定不满意,故编译了4.2.5版本。


安装

安装方法网上有很多,这里简单说一下注意点:

  1. 首先启动家目录服务。
  2. 确认启用了admin账户(需要以自定用户启动的请往下看)。

推荐几个链接不再赘述。

参考1——点击前往
参考2——点击前往


调整

本段需要一定的Linux知识。
目录说明:

功能 路径 备注
存放配置 /var/services/homes/${USER}/.config USER默认为admin
安装位置 /var/packages/${NAME}/target NAME默认为qBittorrent
启动脚本 /var/packages/${NAME}/scripts NAME默认为qBittorrent

调整启动用户

修改/var/packages/qBittorrent/scripts/start-stop-status文件,将第三行USER=adminadmin改成自己用户的,重启插件。

备份种子信息及设置

停止插件,复制/var/services/homes/admin/.config文件夹。

直接更换qBittorrent版本

qBittorrent的二进制文件替换到/var/packages/qBittorrent/target/qbittorrent-nox,重启插件即可。

请使用全静态编译的二进制文件,否则无法启动。


版本说明

后面带+为增强版,否则为原版。

增强版

即为使用qBittorrent-Enhanced-Edition源码编译的版本,适合BT任务。

特色功能:会默认断开吸血客户端,根据网址自动添加tracker。


下载地址

下载链接 提取码urwi

前言

两年前,我买了一台 HP Gen8 微型服务器,其功能之一是作为网络存储。当时它只接了一块 SSD 作为系统盘和一块 2 TB HDD 作为存储盘。随着存储文件的增多,我又先后增加了两块 4 TB HDD,现在它已经接了共计 10 TB 的存储空间。我觉得有必要分享一下我用来将这些硬盘的空间合并在一起的工具——mergerfs。


一、网络存储之硬盘困境

在讲工具之前,我有必要先说明一下我目前的存储方案。

我的 Gen8 没有直接装通用操作系统,而是先通过 ESXi 实现了虚拟化,再将存储盘通过 RDM 的方式完整地映射给其中的一台虚拟机(Arch Linux)。在 Arch Linux 里运行了 Samba, NFS, aria2 RPC, Transmission daemon, BorgBackup 等服务,供局域网电脑存取文件、远程下载,以及备份。

我在存储盘里的东西分为两类:一类是多份备份中的一份(BorgBackup);另一类是从互联网上下载的可再生资源。前者本身有冗余,后者丢了不心疼。出于以上考虑,为了硬盘空间利用率的最大化,我并没有采用 RAID 1 或 RAID 5 之类的冗余存储的方案,而是采用了 JBOD 方案——Just Bunch of Disks。

不使用 RAID 做冗余还有一个原因:我希望这些硬盘从 Gen8 上拔下来之后接到别的电脑上我能直接读取它们。

当我的第一块存储盘快要装满的时候,我买了第二块盘,这时候就面临了一个问题:如何把两块硬盘的空间合并?考虑到我已经在运行的那些服务,我自然不想再增加一个额外的挂载点。我曾考虑过三个硬盘空间合并方案:

  • RAID 0。与 RAID 1 或 RAID 5 不同,RAID 0 是对多块相同容量的硬盘进行平行读写,从而提升性能,其额外效果就是硬盘空间也被合并了。但这种方案非常危险:多块硬盘中的任意一块挂了,所有的数据都将无法读取。这个方案不行。
  • LVM。相比 RAID 0 的原理,LVM 只是将空间连接起来了,而没有平行读写,所以多块硬盘中的一块挂了,也只是丢了那一块的数据。但创建过 LVM PV 的硬盘,在别的机器上读取起来比较麻烦,所以这个方案我也不喜欢。
  • MooseFS。相对前两种方案,由于它在 FUSE 层面实现,所以更灵活一些,甚至可以通过网络,把没有挂在 Gen8 上的硬盘也纳入存储空间。但这个方案和 LVM 一样,协作性不强,硬盘在别的机器上只能看到一堆数据碎块文件,因此也被我否决了。
    以上三种方案还有一个问题:我需要把硬盘里现有的数据全部倒出来再倒进去……我需要的是能将文件分散存储到多块硬盘,同时又不改变文件形态的方案。

二、mhddfs 与 mergerfs

早有人遇到过像我一样的困境,于是他开发了 mhddfs。在用了它一段时间之后,我又发现了一个更好的实现 mergerfs。两者的思路类似,但后者比前者功能更丰富、更安全、更稳定。本文以后者为例说明。

mergerfs 的思路是用 FUSE 实现一个新的文件系统,它的下层存储并不是直接的块设备,而是别的已经挂载的文件系统。mergerfs 接收到读写请求时,它会根据约定好的策略,从下层文件系统中读取文件,或是将数据写入下层文件系统。mergerfs 所呈现的文件系统,容量是所有下层文件系统之和,而内容则是所有下层文件系统的合并。

引用 mergerfs README 里的 ASCII art:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
A         +      B        =       C
/disk1 /disk2 /merged
| | |
+-- /dir1 +-- /dir1 +-- /dir1
| | | | | |
| +-- file1 | +-- file2 | +-- file1
| | +-- file3 | +-- file2
+-- /dir2 | | +-- file3
| | +-- /dir3 |
| +-- file4 | +-- /dir2
| +-- file5 | |
+-- file6 | +-- file4
|
+-- /dir3
| |
| +-- file5
|
+-- file6

如图所示,/merged 是 mergerfs 的挂载点,其下层两个文件系统的挂载点是 /disk1 和 /disk2。

这样一个文件系统完全符合我的需求:读写文件时能获得合并空间的优势,而当硬盘损坏或是想要直接读取硬盘里的数据的时候又可以单独把硬盘拆出来读取。而且我不用把现有的数据倒腾来倒腾去了,无痛迁移!

三、mergerfs 的安装与配置

mergerfs 的作者非常勤奋,每个版本都会为 RHEL / CentOS, Fedora, Debian, Ubuntu 不同发行版的不同版本、不同架构组合打包 30 多个 rpm 和 deb 安装包,其中包括了 ARM 甚至 PowerPC 架构,方便使用 Raspberry Pi 或是老 Mac 作为网络存储设备的用户。Arch Linux 用户则可以通过 AUR 安装。

安装之后通过编辑 /etc/fstab 来挂载 mergerfs。我使用的 fstab 如下:

1
2
3
4
/dev/sdb1               /media/disk1    ext4            defaults,noauto                 0 0
/dev/sdc1 /media/disk2 ext4 defaults,noauto 0 0
/dev/sdd1 /media/disk3 ext4 defaults,noauto 0 0
/media/disk1:/media/disk2:/media/disk3 /media/vdisk fuse.mergerfs defaults,noauto,allow_other,use_ino,minfreespace=100G,ignorepponrename=true 0 0

前三行是三块存储盘的普通挂载,第四行是 mergerfs 的条目,它的挂载源是前三块盘的的挂载点,用冒号分隔。最后一列的参数说明:

  • defaults: 开启以下 FUSE 参数以提升性能:atomic_o_trunc, auto_cache, big_writes, default_permissions, splice_move, splice_read, splice_write;
  • noauto: 禁止开机自动挂载。意外关机重启之后我可能需要手动检查文件系统后再挂载,所以我不希望它自动挂载;
  • allow_other: 允许挂载者以外的用户访问 FUSE。你可能需要编辑 /etc/fuse.conf 来允许这一选项;
  • use_ino: 使用 mergerfs 而不是 libfuse 提供的 inode,使硬链接的文件 inode 一致;
  • minfreespace=100G: 选择往哪个下层文件系统写文件时,跳过剩余空间低于 100G 的文件系统;
  • ignorepponrename=true: 重命名文件时,不再遵守路径保留原则,见下一节详解。
    写完 fstab 之后就可以让 mergerfs 跑起来了:
    1
    mount /media/disk1 && mount /media/disk2 && mount /media/disk3 && mount /media/vdisk
    效果:
    1
    2
    3
    4
    5
    Filesystem      Size  Used Avail Use% Mounted on
    /dev/sdb1 1.8T 1.7T 179G 91% /media/disk1
    /dev/sdc1 3.6T 3.4T 215G 95% /media/disk2
    /dev/sdd1 3.6T 89M 3.4T 1% /media/disk3
    1:2:3 9.0T 5.0T 3.8T 57% /media/vdisk
    disk3 是我今天刚装上的,所以它还是空的。

四、mergerfs 的读写策略

如果多块硬盘里同名的目录或文件,从哪儿读?往哪儿写?如果多块硬盘都有足够的剩余空间,在哪块硬盘创建新文件?mergerfs 对 FUSE 的不同操作有着不同的读写策略。默认的策略是:

  • action 类别:对于 chmod, chown 等改变文件或目录属性的操作,mergerfs 检索所有下层文件系统,确保所有文件或目录都得到更改;

  • search 类别:对于 open, getattr 等读取文件或目录的操作,mergerfs 按挂载源列表的顺序检索下层文件系统,返回第一个找到结果;

  • create 类别:对于 create, mkdir 等创建文件或目录的操作,mergerfs 优先选择相对路径已经存在的下层文件系统中剩余空间最大的那个作为写入目标。
    前两条很好理解,最后一条比较拗口。举例来说是这样:

  • disk1 剩余 100 GiB 空间,有 /dir1 目录;

  • disk2 剩余 200 GiB 空间,有 /dir2 目录;

  • disk3 剩余 300 GiB 空间,有 /dir3 目录;

  • mergerfs 将这三块硬盘的文件系统合并成一个,可以同时看到 /dir1, /dir2, /dir3 三个目录。
    这时在 mergerfs 对于上层文件系统写入一个 150 GiB 的文件到 /dir2/foo.bin 位置,按照默认的策略,mergerfs 会选择 disk2 写入。因为:disk1 剩余空间不足(小于 minfreespace 或是只读文件系统也会被跳过选择),而虽然 disk3 比 disk2 剩余空间更多,但因为 disk2 已经有 /dir2 目录了,所以 mergerfs 会优先选择写入 disk2 而不是 disk3。

这个策略的意义在于,当下层文件系统的剩余空间差不多时,你的文件不会被分散开。比如你正在将你的相机图片文件夹复制到 mergerfs 里,一个文件夹里有 999 张图片,第一张图片的落点也将决定接下来 998 张文件的落点,而不会因为下层文件系统剩余空间的交替变化而一会儿落到这个文件系统,一会儿落到那个文件系统。最终下层文件系统会被平衡地使用,但相同目录的文件会尽可能地在同一个文件系统里,这非常棒。

但这个策略一直有一个痛点让我难受了很久:移动文件。比如 2016 年份的文件位于 disk1,而 2017 年份的文件因为 disk1 已经满了写到 disk2 来了,在 2018 年的时候我想把三年的文件都归到一个新目录里。此时 2016 年的文件可以瞬间完成,2017 年的文件则由于上述策略会优先选择 disk1,于是就从瞬间完成变成了缓慢的跨盘移动,当这些文件数量巨大的时候,已经开始的传输我又不敢贸然中止……这样的坑我在整理文件时掉过很多次。终于,mergerfs 2.23.0 版本新增了 ignorepponrename 选项,使得在重命名文件的时候,忽略路径保留规则,避免了简单的文件整理操作变成痛苦的跨盘移动的悲剧。

如果 mergerfs 的默认读写策略不适用于你的应用场景,可以通过挂载参数选用别的策略。

前言

在数据存储领域,「备份」和「冗余」是两种常见的数据保护策略。两种策略各有不同的使用场景,对于重要数据,两者一起使用自然是最好了。本文介绍使用 SnapRAID 实现灵活的数据冗余存储。


一、神奇的奇偶校验

小时候一直觉得 WinRAR 的「恢复卷」功能非常神奇。比如有一个 100 MiB 的压缩文件分成 10 个分卷,每个 10 MiB,创建者又创建了 3 个恢复卷,每个也是 10 MiB。当复制、分发这些卷的时候,如果因为数据传输、磁盘存储等各种原因,导致 10 个数据卷中有部分文件损坏或丢失,只要损坏或丢失的数据卷的数量小于等于恢复卷的数量,就可以用对应数量的恢复卷来修复压缩包。

小时候不理解为什么丢失一部分数据之后可以用现有的数据重新算出来,现在明白了,最简单的实现便是奇偶校验。

奇偶校验常见于数据传输中。比如 1 个字节(byte)由 8 个比特(bit)组成,但双方约定:只用 7 bit 来存储数据,剩下 1 bit 作为校验位(parity bit)。校验规则是:如果前面 7 bit 里 1 的数量为奇数(1, 3, 5, 7),则 parity bit 为 1;如果前面 7 bit 里 1 的数量为偶数(0, 2, 4, 6),则 parity bit 为 0。这样最终这个 byte 里 1 的数量一定是偶数,如果接收方发现 1 的数量不是偶数,那就说明出错了,一定是在传输过程中发生了比特翻转(bit flip),即本来是 0 的变成了 1,或本来是 1 的变成了 0。当然,如果这个 byte 在传输过程中发生了偶数个 bit flip,那校验倒也恰好能通过,但由于同一 byte 里有大于一个 bit flip 的概率非常低,所以奇偶校验在实际应用中还是非常简单有效的。

然而,上述奇偶校验只能知道「出错了」,但是无法知道「哪里出错了」,也无法修复出错的部分。但是在存储的时候,人们往往是知道哪里出错的。想像一下,如果上述 8 bit 不是存在同一 byte 里而是分散在 8 块磁盘上,这时候某一块磁盘突然挂了,你是明确地知道「哪里出错了」的(磁盘不转了),因此你根据其他 7 块磁盘里的 bit 值,来反推出坏掉的磁盘里存储的是 0 还是 1(把 1 的总数凑成偶数即可),也就是说,你可以利用 7 块健在的磁盘上的数据,修复坏掉的磁盘上的数据。

显然地,如果 8 块磁盘同时坏了 2 块或以上,那就有 00, 01, 10, 11 四种可能了,只有一个奇偶校验位的情况下是修复不了的。幸运的是,计算机科学家和数学家们早就研究出了其他更高级的冗余算法,可以使一组数据有两个或两个以上的校验位,用来在已知「哪里出错了」的情况下,修复出错的部分。SnapRAID 所用的冗余算法,用 N 个存储器用来存储数据,同时用 P 个存储器用来存放校验数据(P ≤ 6, P ≤ N),在总数 P + N 的存储器中,任意坏掉 X 个,只要 X ≤ P,就能用剩下存储器里的数据计算出坏掉的存储器里的数据。

这便是冗余存储的理论基础。

二、备份与冗余存储

虽说「备份」和「冗余」两大数据保护策略有各自的使用场景,冗余并不能取代备份,但如果要将这两者进行比较的话,冗余相比备份最大的优点,是可以相对节省存储空间。回顾上一节里提到的 7 + 1 块磁盘的场景,假设每块磁盘是 1 TB,则有效存储空间是 7 TB,剩下 1 TB 是校验数据。在磁盘使用过程中,任意一块磁盘坏掉,你都可以重新买一块新的替换磁盘,然后把坏掉磁盘的内容完整地推算出来,相当于你用 1 TB 的空间,换取了 7 TB 数据的「相对安全」(仅就对抗硬件故障而言),比完整备份 7 TB 数据省了很多空间。

然而,实际生产生活中,7 + 1 这样的组合风险比较高,因为同时开始使用的磁盘,很可能一起坏掉,一旦有两块磁盘同时坏掉,就会有数据无法被修复了。悲剧往往是这样发生的:一组磁盘里坏了一块,然后换了块新的上去,开始根据旧磁盘的数据推算出坏掉磁盘里原有的数据并写入新磁盘,由于这个推算过程需要将所有旧磁盘里的数据全部读一遍(视磁盘大小,这个过程可能会持续一两天),大量的读取操作成了最后一根稻草,还没等新磁盘填满,又一块旧磁盘挂了。

根据存储及备份提供商 BackBlaze 公开的信息,他们使用的是 13 + 2 的组合,即 15 块磁盘一组,最多能同时坏 2 块磁盘而不丢数据。

三、RAID 与 ZFS

在数据中心里,冗余存储最常见实现是 RAID。使用带冗余的 RAID 级别,如 RAID 5 和 RAID 6,可以在空间利用率和容错性之间达到一个平衡。存储服务器一般会安装硬件 RAID 卡,实现对操作系统透明的 RAID(操作系统看到的就是一个已经带冗余存储的磁盘,并不用关心下面是几块磁盘、怎么实现的)。在 GNU/Linux 中也可以使用 mdadm 工具实现软件 RAID,无需专门的硬件即可实现冗余存储。

在《使用 mergerfs 合并多块硬盘的剩余空间》一文的「读者来信」一节,有读者推荐了 ZFS。这套来自 Sun 的存储方案也可以实现类似 RAID 的功能,包括冗余存储。

然而,使用 RAID 和 ZFS 进行冗余存储都存在一个问题:data lock-in。即,要在已有数据的磁盘上使用 RAID / ZFS,需要把数据先导出,将磁盘清空,然后重新导入数据,并且你想要将某块磁盘脱离 RAID / ZFS 单独读写里面的数据也非常麻烦(如果不是不可能)。

这在数据中心看来也许不是什么问题,但是我希望能有一个更加灵活、自由的冗余存储方案。

四、似 RAID 而非 RAID 的 SnapRAID

SnapRAID 是一个目录级别的冗余存储方案,它与 RAID 的原理有相似的地方,但它并不是 RAID。SnapRAID 与 RAID 的主要区别有:

  • SnapRAID 不会对数据进行条带化存储。RAID 通常会使用数据条带化,一个文件可能会被分散存储到多块磁盘上,这样的优点是读取的时候可以加速(多块磁盘同时读取),但条带化也是上节所说的 data lock-in 的根源——你不能拆出一块盘单独读写。

  • SnapRAID 是工作于文件系统之上的。RAID 工作于文件系统之下,直接对磁盘区块进行操作,用磁盘区块上的比特计算校验数据,而 SnapRAID 是通过读取文件系统里的文件之后再进行计算的。

  • SnapRAID 是非实时的。RAID 每时每刻都在工作,磁盘区块上的数据一旦发生变更就会重新计算校验数据,而 SnapRAID 可以在用户选择的时间进行重新计算。
    SnapRAID 相比 RAID 的优点主要有:

  • 数据独立。不需要对磁盘做特殊处理,可以直接将已有数据的磁盘(甚至可以是不同文件系统的)加入 SnapRAID,SnapRAID 也不会改变这些已有的数据;一个文件不会被分散到多个磁盘,随时可以拆下来一块磁盘正常读写里面的数据;当磁盘阵列收到文件读写请求时,也只需要一块磁盘响应,而不是所有的磁盘全部从待机状态启动,开始寻道。

  • 抗灾能力。当磁盘列阵中同时损坏的磁盘数量超出预期而无法修复数据时,SnapRAID 的抗灾能力更强。例如:在 3 + 1 的 RAID 场景下,坏一块没事,如果同时坏了两块,所有的磁盘上的数据都将无法读取(因为条带化);但如果是 3 + 1 的 SnapRAID,就算同时坏两块,剩下两块里的数据依然可以正常读取。

  • 配置灵活。标准的 RAID 等级中,RAID 5 最多承受 1 块磁盘同时损坏,RAID 6 最多承受 2 块磁盘同时损坏;而 SnapRAID 可以配置 1 到 6 块校验盘,最多承载 6 块磁盘同时损坏,因此可以组建更大的磁盘阵列而不提升风险(维持数据盘与校验盘的比例不变)。更重要的是,无论是增加还是减少磁盘,SnapRAID 都可以无痛完成,无需清空磁盘数据。

  • 恢复误删文件。由于 RAID 是实时计算校验数据的,当文件被删除时,这一改动立刻就会被同步到校验数据里;而 SnapRAID 在用户请求的时候才进行同步,因此用户可以用 SnapRAID 从校验数据重新构建被误删除的文件。当然了,更可靠、更持久的的误删除防护还是应该用增量备份来完成。

  • 空间利用率高。在磁盘阵列中,校验盘的大小应大于等于数据盘中最大的那块。使用 SnapRAID 时,你可以「超售」。比如数据盘是 6 TB 的但是只装了一半(3 TB),你把 4 TB 的磁盘作为校验盘也是可以的(因为此时校验数据最多只有 3 TB),只要在校验文件膨胀到接近 4 TB 的时候将校验文件挪到更大的磁盘里即可。同样的,校验盘里未被校验文件填满的剩余空间也可以用来存储一些「丢了也无所谓」的不重要数据。此外,由于 SnapRAID 工作于文件系统之上,你可以选择性地排除掉一些不想做冗余的目录和文件,以节省空间。

    五、SnapRAID 的配置与使用

    SnapRAID 提供了 Windows 版本的二进制文件下载;GNU/Linux、macOS,以及各种 Unix-like 可以从源码编译或从软件仓库中安装。SnapRAID 的配置文件简洁且注释详尽,读注释就能明白怎么配了。

目前我的 Gen8 里有三块 SATA 磁盘,容量分别是 2 TB, 4 TB, 4 TB。前两块服役多年,几乎满了,第三块是新买的,还是空的,我想把第三块磁盘作为校验盘。我的相关配置是这样的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
# 校验文件的位置
# 显然,校验文件不能放在数据盘上,否则就没有意义了
parity /media/disk3/snapraid.parity

# 如需添加更多的校验文件则继续添加
# 最多是 6 份校验,承受磁盘磁盘阵列中最多同时坏掉 6 块盘的情况
#2-parity /media/disk4/snapraid.2-parity
#3-parity /media/disk5/snapraid.3-parity

# 重要的索引文件,建议保存多份(内容是一样的)
# 我在系统盘(SSD)上存了一份,然后在三块磁盘上都各存一份
# 系统盘上的这份同时又会被 BorgBackup 异地备份
content /home/snapraid/snapraid.content
content /media/disk1/snapraid.content
content /media/disk2/snapraid.content
content /media/disk3/snapraid.content

# 指定数据盘及其挂载点
# 这里不一定要写确切的挂载点,可以是这块盘上的任意目录
# 目录以外的内容会被完全忽略
data d1 /media/disk1/
data d2 /media/disk2/

# 忽略所有隐藏文件和目录(不做冗余)
# 在 Unix-like 里就是 . 开头的文件和目录
# 在 Windows 里就是带隐藏属性的文件和目录
nohidden

# 排除列表与包含列表,注意顺序!下文详解
exclude *.unrecoverable
exclude *.nobackup
exclude *.nobackup/
exclude /tmp/
exclude /lost+found/
#include /foo
#include /bar/

# 生成校验数据时,每处理 10 GiB 数据自动保存一次,方便断点继续
autosave 10

写好配置文件之后,使用 snapraid sync 进行首次同步,也就是根据数据盘的内容生成校验盘的内容。我的第一次同步花了 24 小时:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Scanning disk d1...
Scanning disk d2...
Using 221 MiB of memory for the FileSystem.
Initializing...
Resizing...
Saving state to /home/snapraid/snapraid.content...
Saving state to /media/disk1/snapraid.content...
Saving state to /media/disk2/snapraid.content...
Saving state to /media/disk3/snapraid.content...
Verifying /home/snapraid/snapraid.content...
Verifying /media/disk1/snapraid.content...
Verifying /media/disk2/snapraid.content...
Verifying /media/disk3/snapraid.content...
Syncing...
Using 24 MiB of memory for 32 blocks of IO cache.
0%, 959 MB, 40 MB/s, CPU 1%, 24:08 ETA

常用的 SnapRAID 命令:

  • snapraid sync:根据数据盘生成校验盘;
  • snapraid diff:查看有哪些数据需要 sync;
  • snapraid status:查看磁盘阵列的状态;
  • snapraid scrub:进行数据擦洗,提早发现磁盘阵列中的错误。
    SnapRAID 首次同步完成之后,可以将 snapraid sync 和 snapraid scrub 加入 cron / systemd timer,定时运行。后者默认配置下每次运行擦洗全部数据的 8%,并且会挑选最近 10 天内没有被擦洗过的数据进行擦洗。如果每天运行一次 snapraid scrub 的话,每 12.5 天所有数据都会被擦洗一遍,形成一个健康的循环。

当擦洗发现有数据损坏,或是更糟糕地,某天整块磁盘挂了(不转了),就需要修复数据了。这时候应该做的是停掉所有的定时任务,然后换上新的磁盘,然后用 snapraid fix -d name_of_disk 命令,根据健在磁盘的内容,在新磁盘里重建坏掉磁盘里的内容。只要坏掉的磁盘数量小于等于校验盘的数量,SnapRAID 都能完整地修复数据。

由于 snapraid sync 是定期执行的,这意味着在下次同步之前,磁盘阵列是有机会恢复到上次同步的状态的,因此 snapraid fix 除了可以重建整个磁盘,也可以重建单个文件,也就是反删除。如果你误删除了文件,可以用 snapraid fix -f path/to/file 来恢复文件到上次同步时的状态。

六、SnapRAID 最佳实践

事实上我也就昨天才开始用 SnapRAID,所以这所谓的「最佳实践」,其实也只是我在阅读文档和配置使用中觉得需要注意的地方。

排除列表与包含列表
因为 SnapRAID 是工作在文件系统之上、基于目录的冗余存储方案,因此可以很方便选择哪些目录和文件需要做冗余,哪些不需要。在配置文件中 include 和 exclude 的规则如下:

  • 可以使用 * ? [1-3] 这样的简单通配符;
  • 以 / 开头的路径匹配的是数据盘的根目录,而不是系统的根目录;
  • 以 / 结尾的路径只会匹配目录;
  • 不以 / 结尾的路径只会匹配文件;
  • 如果最后一条规则是包含(include),则所有未匹配的路径都会被排除;
  • 如果最后一条规则是排除(exclude),则所有未匹配的路径都会被包含。
    适合使用 SnapRAID 的文件
    因为 SnapRAID 是定期运行的,在两次 snapraid sync 之间新增的数据是有一段时间没有冗余的,这时候如果磁盘挂了,那这些数据就丢失了。因此,SnapRAID 并不适合用来对频繁变动的文件(如:系统盘)做冗余。

SnapRAID 比较适用的场景是体积巨大、但是很少更改的文件。比如对摄影爱好者来说,磁盘中可能会有好几个 TiB 的 RAW 照片或是未剪辑的 4K 视频文件。这些原始文件因为体积巨大,很难通过互联网做异地备份,而它们本身几乎不会再发生变化,因此非常适合用 SnapRAID 做冗余。由于 SnapRAID 的灵活配置,用户可以方便地选择对哪些文件做冗余,也可以随时将单个磁盘从阵列中临时脱离出来,直接插到图形工作站上进行高速读写。

与 mergerfs 配合
SnapRAID 提供了类似 RAID 的冗余功能,但是 RAID 还能将磁盘阵列里的磁盘合并成一个大磁盘。SnapRAID 本身并不提供合并磁盘的功能,但是 mergerfs 可以达成这个目的