威联通折腾篇六:文件同步

文件同步应该算是 NAS 最最基本的一个服务了,但是为什么直到篇六才提到他呢,是因为威联通自带的 QSync ,嗯,虽然能用,但是,没有 Linux 客户端,虽然其他平台客户端 OK,但是作为我主力工作的平台没有同步客户端,只能 smb 挂载。而之前搞 zerotier 同局域网速度不佳,其他 frp 内网穿透 也最多拉一些小文件,完全做不到 Dropbox 那样无缝,无痛。

而到现在,腾讯云上的 NextCloud 已经稳定运行近半年,所以如果在威联通上装上 NextCloud 只要网络访问畅通的情况下,做到所有平台访问 NAS 应该问题不大。而说到文件同步,之前的时候还用过 Resilio Sync 和 Syncthing ,为什么不用这两个呢? 一来前者确实很好用,只要手握 key ,就能够做到随时随地同步,但是这个神器 tracker 被屏蔽了,导致根本无法连接到节点,虽然有人 VPN 搞,但终究是躲猫猫的游戏,故放弃。而 Syncthing 同为开源的点对点同步工具,其实一点都不差,虽然需要用到的客户端 Linux Android 都有,但是终究不全,虽然现在好像已经有了 gtk 版本,但也不是那么好用。关于二者的具体对比可以参考后文。

安装 NextCloud

在 Container Station 中安装 NextCloud 相对比较容易,如果熟悉 Docker 可以直接使用 Docker Compose 文件,如果不熟悉,那直接使用 GUI 也比较方便。

安装步骤简单描述如下:

  1. 打开 Container Station ,选择创建,然后搜索 nextcloud,选择 Docker Hub
  2. 安装官方提供的 NextCloud 镜像。 注:以上两步可以简化为 ssh 登陆后 docker pull nextcloud, 如果熟悉命令行推荐使用命令行下载避免界面可能出现的各种奇怪问题
  3. 等待镜像下载完成,选择从镜像创建容器
  4. 修改名字为 “nextcloud”,CPU 内存等配置自己选择
  5. 高级设置中,网络选择 NAT 模式,并且在下方选择一个合适的端口,比如 20080,这样以后访问 NAS 该端口就转发到 Docker 80 端口
  6. 设置共享文件夹,如果使用 docker 默认的 volume ,那么会存储在一个很深的目录中,这里我选择挂载本机共享文件夹,这边到 File Station 中创建一个共享文件夹 NextCloud(注意给予其他用户访问权限),然后回到设置,新增挂载,然后本地路径选择刚刚创建的共享文件夹,然后挂载路径填写 /var/www/html
  7. 完成创建。稍等片刻就会发现 NextCloud 共享文件夹中多出了很多容器的文件,其中 NextCloud 之后会同步的文件也在其中。

完成容器的创建之后,需要首次登录配置,在容器成功运行之后,在界面中会出现一个链接,http://:20080 这样的形式,点击进入,如果一切都没问题,这边应该会出现 NextCloud 首次安装的配置,需要创建管理员和连接数据库。

这边我选择使用 NAS 宿主机的 MySQL 服务,而不像之前使用 Docker Compose 那样 使用 MySQL 容器,主要是 NAS 上可以方便的使用 phpMyAdmin 来管理。NAS 上 MySQL 服务端口 3306。然后还需要注意的一个配置是,在 Container Station 属性,网络属性中有一个桥接地址,该配置为 Docker 容器提供网络访问,记住这边的网关比如我的是 10.0.3.1

在 NextCloud 容器运行成功之后也可以通过在命令行中使用 docker inspect nextcloud 然后查看 Gateway 地址 docker inspect nextcloud | grep "Gateway" 来查看 Docker 容器的网络配置。

在 Docker 中连接宿主机 MySQL 时需要特别注意,这个时候不能使用 localhost 或者 127.0.0.1,因为这个时候 localhost 或者 127.0.0.1 会指向 Docker 容器本地的地址,需要换成宿主机,比如这里 NAS 的地址,也就是容器的网关地址,比如我这边上面查看得到的 10.0.3.1

填写 NextCloud 配置之前,在 phpMyAdmin 中先创建 nextcloud 用户和 nextcloud 数据库,记住用户名密码,然后在网页中的配置中填写,然后数据库地址一定需要填写 10.0.3.1:3306 ,这边一定要记住使用容器 Gateway 地址。

选择 NextCloud 的一大原因就是因为 NextCloud 的跨平台支持太棒了,所有的平台都有非常好用的客户端。

下面是我的 Mint 上客户端和网页中同步的内容,局域网中速度还是很快的。

nextcloud qnap docker

或者如果熟悉 compose 也可以使用

version: "3"
services:
  nextcloud:
    image: nextcloud:apache
    container_name: nextcloud
    restart: always
    ports:
      - 20080:80
    volumes:
      - /share/NextCloud:/var/www/html

确保 /share/NextCloud 也就是 NextCloud 共享文件夹以及创建

Resilio Sync VS Syncthing

他们都可以免费使用,都是为了解决文件在多台设备之间同步的问题,都采用了 P2P 点对点传输技术(数据不经过第三方,从一点直接加密传输到另一点)。

Resilio Sync

特别的,Resilio Sync 拥有近乎完美的跨平台产品线,Android、iOS、Windows Phone、MacOS、Windows、Linux、BSD、以及几乎所有的商业 NAS 操作系统都有对应的客户端。

从软件界面来说,Resilio Sync 对桌面系统和手机系统都提供了原生的客户端界面,对 Linux、BSD、NAS 等系统则提供基于网页的管理界面。

从原理上来讲 Resilio Sync 是通过官方的 Tracker 追踪服务器建立多台设备之间的连接,最终实现数据从一台设备以最快速度传输到另外一台设备。由于软件本身是闭源的,因此软件在与 Tracker 服务器连接的过程中是否会泄露用户隐私就不得而知了。

Syncthing

有人说,Syncthing 就是为替代 Resilio Sync 而生的,它不但能实现相应的数据同步功能,它还完全开放源代码。

Syncthing 由社区驱动,即程序是由社区里的技术爱好者们共同开发的,任何人都可以参与到软件的开发中去,人人都可以看到软件的源代码。因此,社区负责人敢拍着胸脯说,我们尊重隐私、我们真正安全、我们简单好用。

然而,Syncthing 在跨平台的方面的确略逊一筹,社区仅以开发核心软件为主,如果需要某个特殊系统平台的客户端,要么你自己动手开发,要么就是等其他技术高手开发。比如苹果手机系统 iOS、Windows Phone 就没有对应版本的 Syncthing 客户端。

在软件界面方面,Syncthing 原生仅提供基于终端的命令行控制和基于网页的管理界面。移动端仅提供 Android 系统客户端。近期社区又发布了跨平台的 Syncthing-GTK 客户端支持 Linux 各发行版和 Windows 系统。

如果你想选择这两者,这里有些参考:

  • 如果你需要在多种不同系统的设备之间同步数据,那么就选择 Resilio Sync;
  • 如果你要同步的是机密级别数据,那么就选择 Syncthing;
  • 喜欢折腾就选 Syncthing,闲麻烦就选 Resilio Sync;
  • 如果你想要可靠稳定,未来有意向付费使用更多功能,Resilio Sync 是不二之选;
  • 设备的硬件配置不高的情况下 Syncthing 会更轻便;

2018-06-17 qnap , qnap-tutorial , file-sync , syncthing , dropbox , resilio-sync , resilio , nextcloud

威联通折腾篇四:Container Station 运行 Docker 容器

威联通上有一个 Container Station 的应用,可以直接用官方的 App Center 中下载安装,这其实就是一个 Docker 本地环境,如果熟悉 Docker 使用,那么其实都直接可以 ssh 登录 NAS 然后完全使用命令行来操作。

不过既然威联通提供了一个直观的界面,那么久来使用看看。官方的例子中有一个 GitLab,我的体验来看是非常完美的,几乎就是一键下载部署,即使不了解容器,也不知道 Docker 内部怎么实现,也能够快速的搭建好 GitLab 的服务。威联通 Container Station 中自带很多的服务,比如这个 GitLab,还有 WordPress, MySQL,CentOS, Ubuntu,甚至还有 TensorFlow, Caffe 等等。如果这些还不能满足需求,其实威联通是默认使用的 Docker Hub 的 registry,甚至用户也可以自定义 docker registry,只要能够得到镜像,威联通就能够跑起来,本质上他就是一台虚拟的 Linux 嘛。

使用默认配置创建 GitLab

官方例子,这边不多说了。App 为一群 Docker 镜像文件的集合,目的是提供完整的服务,如 Application+Database,即一个快速的安装包。以 GitLab app 为例,它内含了 GitLab 主程序、PostgreSQL 和 Redis 三个镜像文件。

比如我使用默认的设置创建的 GitLab 服务,在编辑的时候威联通直接展示给我了一个 Docker compose 文件,里面的配置都很详细。

gitlab:
  environment:
    DEBUG: 'false'
    GITLAB_PORT: 10080
    GITLAB_SECRETS_DB_KEY_BASE: qcs-gitlab-app
    GITLAB_SECRETS_OTP_KEY_BASE: qcs-gitlab-app
    GITLAB_SECRETS_SECRET_KEY_BASE: qcs-gitlab-app
    GITLAB_SSH_PORT: 10022
  image: sameersbn/gitlab:10.0.4
  links:
  - redis:redisio
  - postgresql:postgresql
  ports:
  - 10080:80
  - '10022:22'
  restart: always
postgresql:
  environment:
    DB_EXTENSION: pg_trgm
    DB_NAME: gitlabhq_production
    DB_PASS: password
    DB_USER: gitlab
  image: sameersbn/postgresql:9.6-2
  restart: always
redis:
  command:
  - --loglevel warning
  image: sameersbn/redis:latest
  restart: always

GitLab 实在是太占内存了

docker gitlab

使用自定义镜像安装容器

Docker hub 中有无数的应用可供选择,这边以 GitLab 的代替品 Gogs 为例,首先在 Container Station -> 创建 -> 搜索 gogs,选择 Docker Hub 然后从中选择 gogs/gogs 下载镜像。

等待镜像完成之后,创建容器,在高级中选择 NAT 模式,端口映射 10080 和 10022 ,为了不和宿主 NAS 端口产生冲突。然后我习惯创建一个共享文件夹,比如 gogs 来挂载 gogs 的文件内容,以便于快速访问。然后完成创建。

在创建完成,得到 IP 地址之后,点击进入然后开始配置,这边我容器的网关地址是 10.0.3.1,这个地址可以在界面 -> 属性中找到,或者登录 NAS,然后运行 docker inspect container-name | grep "Gateway" 来查看,因为我启用了 NAS 上的 MySQL 服务,所以可以直接可以使用 10.0.3.1:3306 作为数据库地址。然后 Gogs 的其他配置照着填写即可。

完成安装之后对比一下和 GitLab 的内存占用会发现,GitLab 简直就是巨兽。

qnap docker gogs

更多的 Container Station 的使用可以参考下面官方的链接,Docker 真的很好用。

reference


2018-06-17 qnap , qnap-tutorial , container , docker , linux

威联通折腾篇一:使用命令行安装威联通 QNAP 的 qpkg 安装包

如果想要给威联通安装一个 qpkg 的安装包时,最直观界面方式就是在 App Center 中,右上角,将本地的 .qpkg 文件上传到 NAS 并安装。

但是这种方式在外网图形界面加载很慢的情况下是非常难用的,那么这个时候如果能使用命令行安装就非常方便。需要在系统中预先开启 SSH 连接,当然这一步早就做好了。然后使用 ssh 连接登录上。比如下面以安装 Entware 为例

wget http://bin.entware.net/other/Entware_1.00std.qpkg
sh Entware_1.00std.qpkg

经过这两个步骤就 OK 了。使用 opkg 命令可能需要重新登录 NAS SSH。

其他版本的 Entware 可以从这里 获取。

关于什么是 Entware ,可以查看该 wiki,一句话概括 Entware 是一个适用于嵌入式系统的软件包库,使用 opkg 包管理系统进行管理。

软件包列表:

需要注意的是安装 Entware 之后很多命令都存放在 /opt/bin 目录下,需要修改 /root 目录下的 .bashrc 文件中的 PATH 来使这些命令被命令行所见。

export PATH=/opt/bin:$PATH

下面是安装 ng 0.97 版本的日志:

wget http://pkg.entware.net/binaries/x86-64/installer/Entware-ng_0.97.qpkg
--2018-06-15 10:05:57--  http://pkg.entware.net/binaries/x86-64/installer/Entware-ng_0.97.qpkg
Resolving pkg.entware.net (pkg.entware.net)... 81.4.123.217
Connecting to pkg.entware.net (pkg.entware.net)|81.4.123.217|:80... connected.
HTTP request sent, awaiting response... 200 OK
Length: 25045 (24K) [application/x-gzip]
Saving to: ‘Entware-ng_0.97.qpkg’

Entware-ng_0.97.qpkg                            100%[======================================================================================================>]  24.46K  46.2KB/s    in 0.5s

2018-06-15 10:05:58 (46.2 KB/s) - ‘Entware-ng_0.97.qpkg’ saved [25045/25045]

[~] # sh Entware-ng_0.97.qpkg
Install QNAP package on TS-NAS...
33+1 records in
33+1 records out
24322 bytes (23.8KB) copied, 0.000060 seconds, 386.6MB/s
./
./qinstall.sh
./package_routines
./qpkg.cfg
0+1 records in
0+1 records out
3842 bytes (3.8KB) copied, 0.000018 seconds, 203.6MB/s
3+1 records in
3+1 records out
3842 bytes (3.8KB) copied, 0.001489 seconds, 2.5MB/s
Sym-link /opt ...
Info: Opkg package manager deployment...
Info: Basic packages installation...
Downloading http://pkg.entware.net/binaries/x86-64/Packages.gz.
Updated list of available packages in /opt/var/opkg-lists/packages.
Installing entware-opt (222108-5) to root...
Downloading http://pkg.entware.net/binaries/x86-64/entware-opt_222108-5_x86-64.ipk.
Installing libc (2.23-6) to root...
Downloading http://pkg.entware.net/binaries/x86-64/libc_2.23-6_x86-64.ipk.
Installing libgcc (6.3.0-6) to root...
Downloading http://pkg.entware.net/binaries/x86-64/libgcc_6.3.0-6_x86-64.ipk.
Installing libssp (6.3.0-6) to root...
Downloading http://pkg.entware.net/binaries/x86-64/libssp_6.3.0-6_x86-64.ipk.
Installing librt (2.23-6) to root...
Downloading http://pkg.entware.net/binaries/x86-64/librt_2.23-6_x86-64.ipk.
Installing libpthread (2.23-6) to root...
Downloading http://pkg.entware.net/binaries/x86-64/libpthread_2.23-6_x86-64.ipk.
Installing libstdcpp (6.3.0-6) to root...
Downloading http://pkg.entware.net/binaries/x86-64/libstdcpp_6.3.0-6_x86-64.ipk.
Installing ldconfig (2.23-6) to root...
Downloading http://pkg.entware.net/binaries/x86-64/ldconfig_2.23-6_x86-64.ipk.
Installing findutils (4.6.0-1) to root...
Downloading http://pkg.entware.net/binaries/x86-64/findutils_4.6.0-1_x86-64.ipk.
Installing terminfo (6.0-1c) to root...
Downloading http://pkg.entware.net/binaries/x86-64/terminfo_6.0-1c_x86-64.ipk.
Installing locales (2.23-6) to root...
Downloading http://pkg.entware.net/binaries/x86-64/locales_2.23-6_x86-64.ipk.
Installing opkg (2011-04-08-9c97d5ec-17a) to root...
Downloading http://pkg.entware.net/binaries/x86-64/opkg_2011-04-08-9c97d5ec-17a_x86-64.ipk.
Configuring libgcc.
Configuring libc.
Configuring libssp.
Configuring libpthread.
Configuring librt.
Configuring terminfo.
Configuring ldconfig.
Configuring locales.
Entware uses separate locale-archive file independent from main system
Creating locale archive - /opt/usr/lib/locale/locale-archive
Adding en_EN.UTF-8
Adding ru_RU.UTF-8
You can download locale sources from http://pkg.entware.net/sources/i18n_glib223.tar.gz
You can add new locales to Entware using /opt/bin/localedef.new
Configuring opkg.
Configuring libstdcpp.
Configuring findutils.
Configuring entware-opt.
Updating /opt/etc/ld.so.cache... done.
Link service start/stop script: Entware-ng.sh
Set QPKG information in /etc/config/qpkg.conf
Enable Entware-ng/opkg
[App Center] Entware-ng 0.97 installation succeeded.
[App Center] Entware-ng enabled.

2018-06-15 qnap , qpkg , install , qnap-usage , qnap-tutorial

威联通折腾篇二:使用 frp 内网穿透

这是 QNAP NAS 折腾第二篇,使用 frp 来从外网访问 NAS。威联通自带的 qlink.to 实在是太慢几乎到了无法使用的地步,用 Zerotier 也依然很慢,所以无奈还是用回了 frp.

之前就写过一篇很简单的 frp 使用,不过那是是在 PC 客户端和树莓派上,不过 NAS 就是一个 Linux 嘛,所以也很简单。不过需要注意的是,映射本地 8080 端口,如果需要其他端口,需要相应的添加配置:

[common]
server_addr = frp.server.com          # frp 服务端地址,可以为 IP 或者域名
server_port = 7000                    # 服务端 端口

[ssh-qnap]
type = tcp
local_ip = 127.0.0.1
local_port = 22                       # qnap 22 端口
remote_port = 6022                    # 映射到 6022 端口,这样就可以通过 6022 端口来访问 NAS 22 端口
use_encryption = true
use_compression = true

[qnap-web]
type = tcp
local_ip = 127.0.0.1
local_port = 8080                     # 同理 qnap 8080 管理页面
remote_port = 6080                    # 通过服务端 6080 端口访问 NAS 8080 端口
use_encryption = true
use_compression = true

我现在把所有的 frp 配置放到一个 git 项目中配置,所以一般我也很少更新 frp 的二进制,如果你想要使用最新的 frp 二进制可以到 GitHub 页面


2018-06-15 frp , qnap , qnap-tutorial , qnap-usage , qnap

使用 Zerotier 组建虚拟局域网实现内网穿透

首先 Zerotier 是什么呢? Zerotier 是一拨人为了解决网络复杂,以及因特网过于中心化而提出的解决方案。他们使用点对点 的网络,并且在上面附加一层 VXLAN-like 虚拟网络层 来提高局域网的可见性,安全性。

所以简单来说,Zerotier 通过创建一个虚拟网络,把你的设备添加到这个网络,那么在这个网络内,你的设备就像在同一个路由器内那样可以使用各种端口。

免费版 Zerotier 支持局域网内 100 个设备。Zerotier 支持 Windows、macOS、Linux 三大桌面平台,iOS、Android 两大移动平台,QNAP(威连通)、Synology(群晖)、Western Digital MyCloud NAS(西部数据)三个 NAS 平台,还支持 OpenWrt/LEDE 开源路由器项目。

官网地址:

使用

  • 注册 ZeroTier
  • 创建 Network 私有局域网,得到一个 16 位的 Network ID
  • 在需要加入虚拟局域网的设备上安装各个平台客户端,设备会生成一个 10 位的 ZeroTier address
  • 在设备上加入刚刚创建的 Network ID zerotier-cli join <network id>(或在网页上添加 ZeroTier address)
  • 在官网 network 页面上找到设备,在设备前勾选,信任设备,获得局域网 IP

Windows 系统用命令 IPconfig,Linux/Unix 用命令 ifconfig,然后会看到一个虚拟网卡,有一个 IP 地址。这个 IP 和在官网看到的 network 下的 IP 是一致的,只有同在该虚拟网络下的机器才能访问。

接下来,你可以设置远程桌面(端口号 3389),或者 FTP 服务(端口 21),或者搭建内网网站(端口 80),各种玩法都可以尝试咯。

图文教程可以参考这篇文章

对比

和 ngrok 和 frp 功能类似,但是 ZeroTier 明显入手简单。ZeroTier 只有客户端开源,服务端并没有开源。而 ngrok 和 frp 都是完全开源。但是 ngrok 和 frp 严重依赖于公网固定 IP 的 VPS,是一个中性化的内网穿透工具,一旦中心挂掉,那么所有的节点都无法工作。Zerotier 帮助用户实现了服务端,虽然安全性有待考验,但至少还是能用状态。

另外很多人提到的 N2N 开上去也不错,不过我还没怎么用过。等以后尝试过后再补上。

建立中转服务器 moon

Zerotier 的官方服务器在国外,国内客户端使用时延迟较大,网络高峰期时甚至各个客户端节点之间访问不了。此时,“自定义根服务器”,又称 moon 中转服务器就显得非常重要,它的主要功能是通过自定义的服务器作为跳板加速内网机器之间的互相访问。

Zerotier 定义了几个专业名词:

  • PLANET 行星服务器,Zerotier 各地的根服务器,有日本、新加坡等地
  • moon 卫星级服务器,用户自建的私有根服务器,起到中转加速的作用
  • LEAF 相当于各个枝叶,就是每台连接到该网络的机器节点。

在使用 zerotier-cli listpeers 命令时能看到这几个名词。充当 moon 的机子最好有公网 IP,现在我们尝试用 qnap 搭建一个 moon 中转:

获取 moon.json 文件

zerotier 安装好之后会带有 zerotier-idtool 这个命令,之后的内容需要依赖该命令。假设现在有一台公网固定 IP 的 VPS,在上面安装完 Zerotier 之后。

cd /var/lib/zerotier-one
zerotier-idtool initmoon identity.public > moon.json

获得 moon.json 文件。查看文件内容,其中 id 为 VPS 的 Zerotier ID。 vi 编辑 moon.json,修改 “stableEndpoints” 为 VPS 的公网的 IP,以 IPv4 为例:

"stableEndpoints": [ "8.8.8.8/9993" ]

8.8.8.8 为公网 IP,9993 为 Zerotier 默认端口。

另外,记录下 json 中的 id 值,是一个 10 位的字符串。

生成签名文件

用到上一步中的 moon.json, 执行

zerotier-idtool genmoon moon.json

执行之后生成 000000xxxx.moon 文件。

将 moon 节点加入网络

在 VPS 的 Zerotier 安装目录下(/var/lib/zerotier-one)建立文件夹 moons.d,将生成的 .moon 文件拷贝进去。

重启 zerotier,重启电脑。至此,VPS 上(moon 服务器)配置完成。

其他客户端机器连接 moon 节点

其他虚拟局域网中的机器想要连接到 moon 节点的话有两种方法,第一种方法就是使用 zerotier-cli orbit 命令。连接 moon 节点方法一,使用之前步骤中 moon.json 文件中的 id 值 (10 位的字符串)

分别在客户端机器里执行:

zerotier-cli orbit <id> <id>

完成

第二种方法是需要在 /var/lib/zerotier-one 目录下新建 moons.d 文件夹和 moon 节点一样,将 000000xxxx.moon 文件放到其中,并重启 zerotier。

测试

zerotier-cli listpeers

如果有 moon 服务器 IP 地址的那一行后面有 moon 字样,证明 moon 节点已经被本机连接。

不同系统下的 ZeroTier 目录位置:

  • Windows: C:\ProgramData\ZeroTier\One
  • Macintosh: /Library/Application\ Support/ZeroTier/One)
  • Linux: /var/lib/zerotier-one
  • FreeBSD/OpenBSD: /var/db/zerotier-one

2018-06-14 zerotier , linux , nas , network , virtual-networks

Celery 又一坑:时区错误

Celery 使用过程中又遇一坑,最近升级项目中使用的 Celery 到 4.1.1,突然发现一些定时任务突然不执行了。开始还以为代码哪里做了变化,尝试找了很久,然后打开 scheduler 的日志观察了一段时间。

Celery config 中的配置是如下所示,理论上,早间的任务应该在 8 点到 12 点 每隔 5 分钟执行一次,然后午间和晚间的以此类推。

app.conf.timezone = 'Asia/Shanghai'

app.conf.beat_schedule = {
    'morning': {
        'task': 'worker.xxx.get_xxx',
        'schedule': crontab(minute='*/5', hour='8-12'),
        'args': ('早间',),
        'options': {
            'queue': 'xxx'
        }
    },
    'afternoon': {
        'task': 'worker.xxx.get_xxx',
        'schedule': crontab(minute='*/5', hour='12-18'),
        'args': ('午间',),
        'options': {
            'queue': 'xxx'
        }
    },
    'evening': {
        'task': 'worker.xxx.get_xxx',
        'schedule': crontab(minute='*/5', hour='19-21'),
        'args': ('晚间',),
        'options': {
            'queue': 'xxx'
        }
    },
}

但是观察日志发现,即使现在是下午 4 点,但是上午的任务依然在跑,这个时候突然想起来,是不是因为版本升级导致,果不然,一查 GitHub 就有人反馈这个问题,目前解决方法很简单,为了恢复可用状态,将 celery 回滚到了 4.0.2 版本。暂时没有发现问题。

看 issue 已经意识到该问题,希望能在 4.2 版本中修复吧,有时间的话我再看看他的源码。


2018-06-13 celery , python , linux , message , queue , bug

使用 lombok 简化 Java 代码

lombok 在编译器编译时通过操作 AST(抽象语法树)改变字节码生成。也就是说他可以改变 Java 语法。lombok 不像 Spring 的依赖注入是运行时的特性,而是编译时的特性。

安装配置

官网地址:https://projectlombok.org/

添加 maven

<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.16.20</version>
</dependency>

最新的版本号,可以在官网或者 mvnrepository 找到。 如果使用 Intellij IDEA 还需要安装一个插件。

使用

Data 注解

类注解

import lombok.Data;

@Data
public class Thing {
    private Long id;
    private String desc;
}

通过添加注解 @Data 可以给类快速添加 getset 方法,toString() 方法等等。 @Data 注解其实是 @ToString@Getter@SetterRequiredArgsConstructor@EqualsAndHashCode 注解的缩写。

其实等效于

import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.Setter;
import lombok.ToString;

@ToString
@RequiredArgsConstructor
@EqualsAndHashCode
public class Thing {
    @Setter @Getter private Long id;
    @Setter @Getter private String desc;
}

相关的注解:

  • @Getter/@Setter:用在属性上,再也不用自己手写 setter 和 getter 方法了,还可以指定访问范围
  • @ToString:用在类上,可以自动覆写 toString 方法,当然还可以加其他参数,例如 @ToString(exclude=”id”) 排除 id 属性,或者 @ToString(callSuper=true, includeFieldNames=true) 调用父类的 toString 方法,包含所有属性
  • @EqualsAndHashCode:用在类上,自动生成 equals 方法和 hashCode 方法
  • @NoArgsConstructor, @RequiredArgsConstructor and @AllArgsConstructor:用在类上,自动生成无参构造和使用所有参数的构造函数以及把所有 @NonNull 属性作为参数的构造函数,如果指定 staticName = “of”参数,同时还会生成一个返回类对象的静态工厂方法,比使用构造函数方便很多
  • @Data:注解在类上,相当于同时使用了@ToString@EqualsAndHashCode@Getter@Setter@RequiredArgsConstrutor这些注解,对于 POJO 类十分有用
  • @Value:用在类上,是 @Data 的不可变形式,相当于为属性添加 final 声明,只提供 getter 方法,而不提供 setter 方法
  • @NonNull 用在方法参数上,该变量不能为空,否则就抛出异常
  • @Builder:用在类、构造器、方法上,为你提供复杂的 builder APIs,让你可以像如下方式一样调用 Person.builder().name(“Adam Savage”).city(“San Francisco”).job(“Mythbusters”).job(“Unchained Reaction”).build(); 更多说明参考 Builder
  • @SneakyThrows:自动抛受检异常,而无需显式在方法上使用 throws 语句
  • @Synchronized:用在方法上,将方法声明为同步的,并自动加锁,而锁对象是一个私有的属性 $lock 或 $LOCK,而 java 中的 synchronized 关键字锁对象是 this,锁在 this 或者自己的类对象上存在副作用,就是你不能阻止非受控代码去锁 this 或者类对象,这可能会导致竞争条件或者其它线程错误
  • @Getter(lazy=true):可以替代经典的 Double Check Lock 样板代码
  • @Log:根据不同的注解生成不同类型的 log 对象,但是实例名称都是 log,有六种可选实现类

    @CommonsLog Creates log = org.apache.commons.logging.LogFactory.getLog(LogExample.class); @Log Creates log = java.util.logging.Logger.getLogger(LogExample.class.getName()); @Log4j Creates log = org.apache.log4j.Logger.getLogger(LogExample.class); @Log4j2 Creates log = org.apache.logging.log4j.LogManager.getLogger(LogExample.class); @Slf4j Creates log = org.slf4j.LoggerFactory.getLogger(LogExample.class); @XSlf4j Creates log = org.slf4j.ext.XLoggerFactory.getXLogger(LogExample.class);

Slf4j 注解

正如上面所例举,lombok 可以使用的日志框架有很多,拿最常见的 @Slf4j 来举例

@Slf4j
public class A {
    public A() {
        log.info("I'm sectionVO : {}", toString());
    }
}

编译会自动生成

private static final Logger log = LoggerFactory.getLogger(A.class);

Builder 注解

如果在类上使用 @Builder 注解,则会给该类直接生成 builder 模式,然后可以使用 builder() 方法返回的 builder 来构造类。

如果要对 Builder 中的值赋予默认值,有两种方式,比如对于

@Builder
public class Person {
  private String firstname = "John";
  private String lastname = "Doe";
}

第一种方式就是手动编写一个静态内部类

@Builder
public class Person {
  private String firstname;
  private String lastname;
  private String middleName;

  public static class PersonBuilder {
    private String firstname = "John";
    private String lastname = "Doe";
  }
}

或者在 v1.16.16 之后添加的新功能

@Builder
public class Person {
  @Builder.Default private String firstname = "John";
  @Builder.Default private String lastname = "Doe";
  private String middleName;
}

其他的特性可以在这里看到:https://projectlombok.org/features/all


2018-06-11 lombok , java

Kafka 副本备份机制

leader 维护了 ISR(能完全赶得上 leader 的副本集), 每个 Partition 当前的 leader 和 ISR 信息会记录在 ZooKeeper 中。leader 会跟踪与其保持同步的 Replica 列表,该列表称为 ISR。如果一个 follower 宕机,或者落后太多,leader 将把它从 ISR 中移除。只有 leader 才能知道哪些 Replica 能够及时完全赶得上。所有 follower 都会和 leader 通信获取最新的消息。但是 follower 之间并不互相知道彼此的信息。所以由 leader 来管理 ISR 最合适了。leader 还可以决定移除落后太多的 Replicas.

每个 Replica 都在自己的 local log 中存储消息,并在日志中维护了重要的 offset 位置信息。LEO 代表了日志的最新的偏移量,HW 是最近提交消息的偏移量。

每个日志都会定时地同步到磁盘。在 flushed offset 之前的数据一定能保存成功持久化到磁盘上。flush offset 可以在 HW 之前或者之后(因为 follower 只是先写到内存中然后返回 ack 给 leader,hw 增加时, follower 在内存中的消息不一定什么时候写到磁盘上,即可能在 hw 增加前就写到磁盘,或者等 hw 增加后才写到磁盘)。

leader 也会定时地将 HW 广播给所有的 followers. 广播消息可以附加在从 follower 过来的 fetch 请求的结果中。同时,每个副本(不管是 leader 还是 follower) 也会定时地将 HW 持久化到自己的磁盘上。当 follower 向 leader 提交 fetch 请求时,leader 也会告诉所有的 follower 说,我现在的 hw 是多少了。这是一种保护机制。 假设只有 leader 一个人保护了 hw 这个重要的信息,一旦 leader 不幸挂掉了,就没有人知道 hw 现在到底是多少了。所以只要一有 follower 过来获取消息时,leader 就不厌其烦地像个老太婆不断地唠叨说我这一次的 hw 更新到了哪里。每个 follower 也就都会知道 leader 的最新 hw. 这样即使 leader 挂掉了,hw 仍然在其他 follower 上都备份有这个重要信息。几个 follower 在一阵商量后,选举出了新的 leader, 这些人都知道上一个 leader 最新的 hw, 因此 hw 会继续传承下去。

为了简单起见,只有 leader 可以提供读消息的服务。并且最多只到 hw 位置的消息才会暴露给客户端。

Producer 在发布消息到某个 Partition 时会经过如下的步骤:

  • 先通过 Zookeeper 找到该 Partition 的 leader, 然后无论该 Topic 的 Replication Factor 为多少(也即该 Partition 有多少个 Replica),Producer 只将该消息发送到该 Partition 的 leader。
  • leader 会将该消息写入其本地 Log, 每个 follower 都从 leader pull 数据。这种方式上,follower 存储的数据顺序与 leader 保持一致。follower 在收到该消息并写入其 Log 后,向 leader 发送 ACK。
  • 一旦 leader 收到了 ISR 中的所有 Replica 的 ACK,该消息就被认为已经 commit 了,leader 将增加 HW 并且向 Producer 发送 ACK。为了提高性能,每个 follower 在接收到数据后就立马向 leader 发送 ACK,而非等到数据写入 Log 中。

因此,对于已经 commit 的消息,Kafka 只能保证它被存于多个 Replica 的内存中,而不能保证它们被持久化到磁盘中, 也就不能完全保证异常发生后该条消息一定能被 Consumer 消费。但考虑到这种场景非常少见,可以认为这种方式在性能和数据持久化上做了一个比较好的平衡。在将来的版本中,Kafka 会考虑提供更高的持久性。Consumer 读消息也是从 leader 读取,只有被 commit 过的消息(offset 低于 HW 的消息)才会暴露给 Consumer。 Kafka 的复制机制既不是完全的同步复制,也不是单纯的异步复制。事实上,同步复制要求所有能工作的 follower 都复制完,这条消息才会被认为 commit,这种复制方式极大的影响了吞吐率。而异步复制方式下,follower 异步的从 leader 复制数据,数据只要被 leader 写入 log 就被认为已经 commit,这种情况下如果 follower 都复制完都落后于 leader,而如果 leader 突然宕机,则会丢失数据。而 Kafka 的这种使用 ISR 的方式则很好的均衡了确保数据不丢失以及吞吐率。follower 可以批量的从 leader 复制数据,这样极大的提高复制性能(批量写磁盘),极大减少了 follower 与 leader 的差距。

如果 follower 失败了,在超过一定时间后,leader 会将这个失败的 follower (follower 没有发送 fetch 请求)从 ISR 中移除。由于 ISR 保存的是所有全部赶得上 leader 的 follower replicas, 失败的 follower 肯定是赶不上了。虽然 ISR 现在少了一个,但是并不会引起的数据的丢失,ISR 中剩余的 replicas 会继续同步数据(只要 ISR 中有一个 follower, 就不会丢失数据)(注意:这里讨论的是一个 Partition 的 follower 副本,而不是节点,如果是一个节点,它不止存储一个 Partition, 而且不都是 follower)

如果失败的 follower 恢复过来,它首先将自己的日志截断到上次 checkpointed 时刻的 HW. 因为 checkpoint 记录的是所有 Partition 的 hw offset. 当 follower 失败时,checkpoint 中关于这个 Partition 的 HW 就不会再更新了。而这个时候存储的 HW 信息和 follower partition replica 的 offset 并不一定是一致的。比如这个 follower 获取消息比较快, 但是 ISR 中有其他 follower 复制消息比较慢,这样 leader 并不会很快地更新 HW, 这个快的 follower 的 hw 也不会更新 (leader 广播 hw 给 follower) 这种情况下,这个 follower 日志的 offset 是比 hw 要大的。

所以在它恢复之后,要将比 hw 多的部分截掉,然后继续从 leader 拉取消息(跟平时一样). 实际上,ISR 中的每个 follower 日志的 offset 一定是比 hw 大的。因为只有 ISR 中所有 follower 都复制完消息,leader 才会增加 hw。也就是说有可能有些 follower 复制完了,而有些 follower 还没有复制完,那么 hw 是不会增加的,复制完的 follower 的 offset 就比 hw 要大。

一个消费者组可以有多个消费者,Kafka 中的一个 Partition 只会被消费者组中的一个消费者消费,但可以被多个消费组同时消费。

对于多个 partition 和多个 consumer 有以下这样的限制条件:

  • 如果 consumer 比 partition 多,是浪费,因为 kafka 的设计是在一个 partition 上是不允许并发的,所以 consumer 数不要大于 partition 数
  • 如果 consumer 比 partition 少,一个 consumer 会对应于多个 partitions,这里主要合理分配 consumer 数和 partition 数,否则会导致 partition 里面的数据被取的不均匀。最好 partiton 数目是 consumer 数目的整数倍,所以 partition 数目很重要,比如取 24,就很容易设定 consumer 数目
  • 如果 consumer 从多个 partition 读到数据,不保证数据间的顺序性,kafka 只保证在一个 partition 上数据是有序的,但多个 partition,根据你读的顺序会有不同

增减 consumer,broker,partition 会导致 rebalance,所以 rebalance 后 consumer 对应的 partition 会发生变化。High-level 接口中获取不到数据的时候是会 block 住消费者线程的


2018-06-04 kafka , message , broker , replication

FileRun 又一款文件同步工具

这两天浏览开源项目 的时候又 1 发现了一款文件同步工具 FileRun,打开官网 一看发现设计非常简洁,功能也同样强大。

最吸引我的是和 Google Docs ,Office Web View ,Pixlr 等等的支持,这样就可以在线预览 docx 等等文件。虽然是闭源产品,但是如果类似官网显示的那样,也是非常不错的文件同步管理的选择。

FileRun 是用 PHP 开发,从部署的文档可以看出来,部署的过程也可以使用 Docker,所以可以非常方便的部署到 Docker 环境中。如果自己配置环境,和 NextCloud 一样有 PHP 的运行环境即可。


2018-06-02 filerun , file-syncing , file-sharing , php , self-host

Kafka 资料收集整理

学 Kafka 的时候找到了一些非常友好的资料,这里整理下。

教程

不得不说的官方教程,另外我正在翻译官方教程

cloudurable 这个网站提供了非常详细的 Kafka 教程,从入门 Kafka 是什么,到写 Java 代码,到 Kafka 项目各个部分架构 都有着非常详细的介绍。

第三个要推荐的就是一本 Gitbook ,尤其是第二章使用 Unix 管道类比来解释 Kafka 的工作流,非常的生动。

常用 Shell 命令

Kafka 提供了一些命令行工具,用于管理集群的变更。这些工具使用 Java 类实现,Kafka 提供了一些脚本来调用这些 Java 类。不过,它们只提供了一些基本的功能,无怯完成那些复杂的操作 。

创建主题

主题名字可以包含字母、数字、下划线以及英文状态下的破折号和句号。

bin/kafka-topics.sh --create --zookeeper localhost:2181 --replication-factor 3 --partitions 3 --topic my-example-topic

主题名字的开头部分包含两个下划线是合法的,但不建议这么做。具有这种格式的主题一般是集群的内部主题(比如 __consumer_offsets 主题用于保存消费者群组的偏移量)。也不建议在单个集群里使用英文状态下的句号和下划线来命名,因为主题的名字会被用在度量指标上,句号会被替换成下划线 (比如 topic.1 会变成topic_1 )。

描述主题

查看某个 Topic 详情

bin/kafka-topics.sh --describe --zookeeper localhost:2181 --topic my-example-topic

列出主题

bin/kafka-topics.sh --list --zookeeper localhost:2181

删除主题

bin/kafka-topics.sh --zookeeper localhost:2181 --delete --topic my-example-topic

增加分区

bin/kafka-topics.sh --zookeeper localhost:2181 --alter --topic my-example-topic --partitions 16

Kafka 目前是暂时不支持减少主题分区数量的。

无顺序,一行一个

修改分区

bin/kafka-topics.sh --zookeeper localhost:2181 --alter --topic my_topic_name  --partitions 40

Partition 个数只能增加,不能减少。对于采用默认 Partitioner 的 Producer,Message 是按照 Key 的哈希值“规律”分布的(hash(key) % number_of_partitions),如果增加 Partition 个数,会打破现有分布规律。如果业务依赖于此哈希分布,请谨慎操作。

增加或修改 Config

bin/kafka-topics.sh --zookeeper localhost:2181 --alter --topic my_topic_name --config x=y

可配置的 config 可以参考官网

删除 Config

bin/kafka-topics.sh --zookeeper localhost:2181 --alter --topic my_topic_name --deleteConfig x

启动生产者发送消息

bin/kafka-console-producer.sh --broker-list localhost:9092 --topic my-example-topic

启动消费者接受消息

bin/kafka-console-consumer.sh --zookeeper localhost:2181 --topic test --from-beginning

指定 from-beginning 则从“最老”(最早、最开始)的数据开始读;否则从“最新”的数据开始读(启动后等待新数据的写入并读取)。

Kafka 管理

yahoo 开源了一个 kafka-manager GitHub


2018-05-31 kafka , message

电子书

最近文章

  • MySQL 中的日志配置和管理 MySQL 中默认是没有开启日志记录的,所以需要手动修改配置文件开启日志。而在 MySQL 中我们需要关心的有三类日志:
  • Java 查漏补缺之:ThreadLocal 使用 ThreadLocal 线程本地变量,每个线程保存变量的副本,对副本的改动,对其他的线程而言是透明的。
  • 为知笔记导出和备份 WizNote 已经用了好几年,虽然也一直在续费,但总感觉将死不死,基于整理这几年近 4000 条的笔记的目的,也一方面为迁移出 WizNote 的目的,研究一下 WizNote 笔记导出和备份的方法。
  • Nginx location 匹配规则 之前的关于 Nginx Config 的文章是当时看 Nginx 书记录下来的笔记,很大略,没有实际操作,等终究用到 location 的时候发现还是有很多需要注意的问题,比如匹配的优先顺序,比如 root 和 alias 的区别等等,所以单独拿一篇文章来记录一下目前遇到的问题,并来解决一下。
  • koajs 简单使用 Koa 是一个背靠 Express 的团队设计的全新的 Web 框架,旨在使之成为一个更轻量,更丰富,更加 robust 的基础框架。通过促进异步方法的使用,Koa 允许使用者抛弃 callback 调用,并且大大简化了错误处理。Koa 并没有将中间件绑定到核心代码,而是提供了一组优雅的方法让编写服务更加快速,通过很多第三方的扩展给 Koa 提供服务,从而实现更加丰富完整的 HTTP server。