威联通折腾篇四: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,这个地址可以在 Container 界面 -> 属性中找到,或者登录 NAS,然后运行 docker inspect container-name | grep "Gateway" 来查看(查看任意一个容器的),因为我启用了 NAS 上的 MySQL(MariaDB) 服务,所以可以直接可以使用 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.json 文件

充当 moon 的机子最好有公网 IP,现在我们尝试用 qnap 搭建一个 moon 中转:

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": [ "121.121.121.121/9993" ]

121.121.121.121 为公网 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 <world-id> <seed-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

zerotier-cli

zerotier-cli 命令的基本使用。

Usage: zerotier-cli [-switches] <command/path> [<args>]

Available switches:
  -h                      - Display this help
  -v                      - Show version
  -j                      - Display full raw JSON output
  -D<path>                - ZeroTier home path for parameter auto-detect
  -p<port>                - HTTP port (default: auto)
  -T<token>               - Authentication token (default: auto)

Available commands:
  info                    - Display status info
  listpeers               - List all peers
  listnetworks            - List all networks
  join <network>          - Join a network
  leave <network>         - Leave a network
  set <network> <setting> - Set a network setting
  listmoons               - List moons (federated root sets)
  orbit <world ID> <seed> - Join a moon via any member root
  deorbit <world ID>      - Leave a moon

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 的依赖注入是运行时的特性,而是编译时的特性。使用 lombok 需要对应 IDE 插件配合,具体可参考官网。

安装配置

官网地址: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);

ToString 注解

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;
}

当一个类被标注 @Builder 后会自动产生下面 7 件事情:

  • 一个内部静态类 FooBuilder ,类内部有着和类相同类型的内部变量
  • builder 内部:One private non-static non-final field for each parameter of the target
  • builder 内部:package private no-args empty constructor
  • builder 内部:A ‘setter’-like method for each parameter of the target: It has the same type as that parameter and the same name. It returns the builder itself, so that the setter calls can be chained, as in the above example.
  • builder 内部:A build() method which calls the method, passing in each field. It returns the same type that the target returns.
  • builder 内部:A sensible toString() implementation.
  • builder 内部:A builder() method, which creates a new instance of the builder.

最后,如果在类上注解 @AllArgsConstructor(access = AccessLevel.PACKAGE) 那么可以将 @Builder 应用到类上。如果自己显示定义了构造函数,那么将 @Builder 用在该构造方法上。

总结 Lombok 实践

  • Lombok 注解的内容不要参杂任何逻辑
  • 在 DAOs 上使用 @Data
  • 对不可变对象使用 @Value
  • 当类有很多相同类型成员变量,使用 @Builder
  • 慎用其他不常用注解,比如 @Cleanup,@SneakyThrows,@Synchronized 等

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

reference


2018-06-11 lombok , java , annotation

通过 HOST 去除 Android 广告:adaway

Adaway 通过修改 Host 来去除手机上的广告。支持自定义 HOST 源,这就意味着稳定可用的屏蔽规则依赖于可靠及时更新的 Host 规则了。为了避免误伤,我尽量会不全部启用,只会启用一些特别恼人的广告。

内置 Host 地址

From: https://github.com/AdAway/AdAway/wiki/HostsSources

Others

以下地址未验证

  • https://raw.githubusercontent.com/jdlingyu/ad-wars/master/hosts
  • https://malwaredomainlist.com/hostslist/hosts.txt
  • https://raw.githubusercontent.com/jerryn70/GoodbyeAds/master/Hosts/GoodbyeAds.txt
  • https://www.hosts-file.net/download/hosts.txt
  • https://zerodot1.gitlab.io/CoinBlockerLists/hosts
  • https://someonewhocares.org/hosts/zero/hosts
  • http://winhelp2002.mvps.org/hosts.txt
  • https://github.com/neoFelhz/neohosts

2018-06-05 adaway , android , host

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 的运行环境即可。

Docker

在 QNAP,或者其他系统中使用 Docker 安装,注意将其中的变量部分替换成对应环境的内容:

version: '2'

services:
  filerun:
	image: afian/filerun
	container_name: filerun
	environment:
	  FR_DB_HOST: 10.0.3.1
	  FR_DB_PORT: 3306
	  FR_DB_NAME: filerun
	  FR_DB_USER: filerun
	  FR_DB_PASS: password
	  APACHE_RUN_USER: www-data
	  APACHE_RUN_USER_ID: 1000
	  APACHE_RUN_GROUP: www-data
	  APACHE_RUN_GROUP_ID: 100
	ports:
	  - "30080:80"
	volumes:
	  - /share/filerun/html:/var/www/html
	  - /share/filerun/user-files:/user-files
	restart: unless-stopped

默认的用户名和密码都是:superuser

两个挂载点,一个是 filerun 的数据文件,另一个是用户文件。

语言设置

如果要换成中文,可以下载这个文件 然后在后台上传上去。

配置

编辑 customizables/config.php 文件可以扩展一些设置。

更多见官方文档

开启 API 使用 NextCloud 客户端同步

开启 API 后,可以使用 NextCloud 的客户端来连接。然后使用 filerun 的地址就可以访问。

reference


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

电子书

最近文章