無標題文檔

整理汇总下最近用 Golang 写的些小玩意

由于工作的关系,所以又点了 Golang 这个技能树。几个月下来,对于这个语言以及对应技术栈的理解不算透彻,但多多少少会有些心得。

在这里整理下最近用 Golang 写的些小工具以及类库,希望能对大家有所帮助。

Genpasswd 简单的密码生成工具

源码地址:https://github.com/mingcheng/genpasswd.go ,根据名字就知道这是个生成随机密码的小工具。这个项目是本人的第一个 Goalng 项目,主要是熟练使用了些对应的脚本和配套的 CI 等工具(Travis 非常好用)。

QQWry 基于纯真 IP 库的微服务应用

源码地址:https://github.com/mingcheng/qqwry.go ,根据 IP 地址反查对应的地理位置等信息。

纯真 IP 库其实不是新东西了,早在以前 QQ 时代显示对方的 IP 信息就基本上会拿这个库来处理。其实,这个项目也只是根据线上现成的项目改进了下,使其支持在线获取以及更新最新的数据库(后续考虑增加 GRPC 的支持)。

NCMDump 云音乐 NCM 格式分析

源码地址: https://github.com/mingcheng/ncmdump.go ,云音乐出于版权方面的考虑有些歌曲在下载到本地的时候,格式是所谓的 NCM 格式。这在导入到其他设备的时候会有些麻烦。

网络上有对应的 C++ 以及 Python 的实现。其实也有对应 Golang 的实现,但会有些 Bug 例如解析某些 flac 封装的歌曲的时候会报异常,所以改进了下。通过这个项目,对于 AES 加密以及文件操作有了很直观的认知。

PIDFile 生成 PID 文件的库

源码地址:https://github.com/mingcheng/pidfile ,由于些 Golang 通常用于 Daemon 的编写,所以会考虑使用 PID 文件避免程序重复启动,因此就有了这个库。

Obsync 华为对象存储同步工具

源码地址:https://github.com/mingcheng/obsync.go ,尝试使用了下华为云的对象存储服务(OBS),发现支持的节点不错而且对比阿里云相对便宜些。诡异的是它还有非洲的节点,速度也不慢电信直连。

这样子,我的线上备份以及同步就有了另外个去处,数据方面也会安心一点。在中美这个节点上,支持下华为吧。

最后,顺便说下 Chaosblade 这个项目,它是阿里的混沌实验实施工具。其源代码相对来说不会很复杂,可以阅读下以后多给它做些改进。

One more thing. 还有个小玩意是 NSFW 的,我改造了下使其支持代理下载,具体的看链接吧:https://github.com/mingcheng/pornhub-dl.go

- eof -

记次网桥的 Mac 地址改变导致无法链接的问题

家里的小服务器装的是 ArchLinux,在有次 pacman -Syu 以后发现无法连接了,登录上去以后发现原来是网桥的 Mac 地址没有复制过来,导致路由器无法根据 Mac 地址分配正确的 IP 地址。

# ip link show br0
4: br0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP mode DEFAULT group default qlen 1000
    link/ether <mac> brd ff:ff:ff:ff:ff:ff
# ip link show eno1
2: eno1: <BROADCAST,MULTICAST,PROMISC,UP,LOWER_UP> mtu 1500 qdisc fq_codel master br0 state UP mode DEFAULT group default qlen 1000 link/ether <mac2> ff:ff:ff:ff:ff:ff

原先的网桥配置如下

$ cat /etc/netctl/bridge
Description="Bridge Connection for KVM"
Interface=br0
IP=no
Connection=bridge
BindsToInterfaces=(eno1)
MACAddressOf="eno1"
## Ignore (R)STP and immediately activate the bridge
SkipForwardingDelay=yes

根据配置上的定义 MACAddressOf="eno1" 为复制 eno1 的 Mac 地址,这样子网桥和网卡的 Mac 地址应该一致才是。不知道为何更新重启以后就无法复制 Mac 地址了。

查看了相关的文档,用换了一种方式绑定网卡的 Mac 地址:

$ cat /etc/netctl/bridge
Description="Bridge Connection for KVM"
Interface=br0
IP=no
Connection=bridge
BindsToInterfaces=(eno1)
#MACAddressOf="eno1"
ExecUpPost="ip link set dev br0 address $(cat /sys/class/net/eno1/address); IP=dhcp; ip_set"
ExecDownPre="IP=dhcp"
## Ignore (R)STP and immediately activate the bridge
SkipForwardingDelay=yes

其中,主要是以下两行

ExecUpPost="ip link set dev br0 address $(cat /sys/class/net/eno1/address); IP=dhcp; ip_set"
ExecDownPre="IP=dhcp"

由于 dhcp 让 hook 去获取了,所以务必设置为 IP=no,否则会重复释放 IP 导致网桥无法建立。以下是相关的参考连接:

扩展 Helidon 的健康检查模块

Helidon Logo

Helidon 是 Oracle 提供的轻量级的微服务框架,提供了 Router、Microprofile、Security 等必要的组件。相对 Spring Boot 等老牌框架而言,它的优势在于轻量同时源代码简单适合阅读。

由于是新出的框架,所以它的文档还不够完善,有时间需要阅读其代码才能明白具体的使用方式。例如,Helidon 的健康检查这块的文档就写得非常的简单,如果想要扩展按照文档中的例子代码将会写得很混乱。

HealthSupport health = HealthSuport.builder()
    .add(() -> HealthCheckResponse.named("exampleHealthCheck")
                 .up()
                 .withData("time", System.currentTimeMillis())
                 .build())                          
    .build();

实际上,Helidon 的 HealthChecks.healthChecks() 已经提供了三个默认的健康检查的反馈,分别是死锁、磁盘以及内存的状态:

/**
    * Built-in health checks.
    *
    * @return built-in health checks to be configured with {@link io.helidon.health.HealthSupport}
    * @see io.helidon.health.HealthSupport.Builder#add(org.eclipse.microprofile.health.HealthCheck...)
    */
public static HealthCheck[] healthChecks() {
    return new HealthCheck[] {
            deadlockCheck(),
            diskSpaceCheck(),
            heapMemoryCheck()
    };
}

分别看其代码就可以发现,这三个直接调用的方法其实就是 HealthCheck 接口的实现。因此,我们自己也可以按照 Helidon 的方式来实现自定义健康检查的项目,满足本地的具体业务的情况。

例如,下面实现了个显示本地服务器时间以及虚拟机的 UPTIME 的状态:

@Health
@ApplicationScoped
public class LocalTimeCheck implements HealthCheck {

  @Override
  public HealthCheckResponse call() {
    return HealthCheckResponse.named("localtime")
        .withData("upTime", ManagementFactory.getRuntimeMXBean().getUptime())
        .withData("currentTime", System.currentTimeMillis())
        .build();
  }
}

然后,在注册到对应的 HealthSupport 中即可:

HealthSupport health = HealthSupport.builder()
    .add(new LocalTimeCheck())
    .build();

顺便说一句,需要注意下:HealthSupport.add() 里面的回调都是同步执行的。

HealthSupport 默认绑定的路由为 /health,如果需要更改这块则根据源代码的提示,增加 .webContext() 这个方法即可,或者直接将其配置到配置文件中:

HealthSupport health = HealthSupport.builder()
    .webContext("/your-health-check-path")
    // ...

- eof -

emojify.php, 让 Typecho 用上 Emoji 表情

Emoji 能让单调的沟通多些色彩,团队这边近期也开始使用 Emoji 作为 git 提交的提示符使用

其中,有很多相关的命令行工具可以使用。但翻遍了一圈,竟然没有 PHP 相关的类库,于是就自己写了一个( 很久没写 PHP 了,感觉生疏了好多 😑 )。

表情符号的数据源自好几个项目,所以从数据量上面来说应该是目前 emojify 类工具的超集了,超过了两千多条。例如,可以完全放心使用 emoji-cheat-sheet 下的定义。

具体的对应数据可以在这里查看(文件很大),顺便还可以看看自己系统对于 Emoji 字符的支持情况。

因为是 PHP 相关,所以在完成这个简单的类库以后顺便做成了 Typecho 插件。这个插件安装很简单,只要在 Typecho 的 Plugin 目录下无脑 git clone :

git clone git@github.com:mingcheng/emojify.php.git  Emojify

然后使用管理员登录,开启插件就可以使用了 😘。Github 的地址在 https://github.com/mingcheng/emojify.php 这里,欢迎测试以及提交 Issues(当然,你也可以留言测试下 Emoji 😄 )。

- eof -

rx-filewatcher.java,基于 NIO WatchService 的 Reactive 封装

嫌我啰嗦的话,直接看代码: https://github.com/mingcheng/rx-filewatcher.java

在写个小东西需要监控文件改变状态的时候,用到了 NIO 东西很方便也好用。但是,在使用 WatchService 的时候碰到了些问题。

首先,使用原生的接口必须开启个线程,然后使用个持续循环去读取 WatchEvent (这也是大部分教程描述这样子做的):

WatchKey key;
while ((key = watchService.take()) != null) {
    for (WatchEvent<?> event : key.pollEvents()) {
        //process
    }
    key.reset();
}

这样子做有几个不好的地方:一是开了个匿名线程,如果后期代码也有类似的情况,线程就不好处理,而且很容易无法监控到这个线程的状态;第二就是破坏了代码原有的业务逻辑,读到这里如果阅读代码的小伙伴不熟悉 WatchService 就很难理解。

其次,WatchService 的注册是一次性的,也就是说无法监控到子目录以下的情况,必须随时监控是否创建了子目录然后再对应 register 子目录 watch(这样子一个简单的功能就会搞得非常的复杂)。

我考虑到使用 Rx 重写这块的代码,然后翻阅 Github 的时候发现已经有人造了这个轮子。代码量不大,但核心功能是我想要的,只是最新更新已经在三年前了。

集成了 Helmbold 这个哥们的库以后,又发现了几个问题:

  1. 这个库是写在三年前的,还在用 Rx1 的库,和我目前的 Rx2 有部分的不兼容;
  2. dispose 部分有问题,dispose 以后线程没有终止还在运行;
  3. 获取到 WatchEvent 的 Context 只有文件名没有路径。

因为代码量本身就不是很多,同时考虑到这块还是需要以后自己维护,因此 fork 了他的代码自己做了些改造

对比以上的原生代码,使用 Rx 封装的代码就会干净和直观很多:

PathObservables
  .subscribeOn(Schedulers.io())
  .watchRecursive(Paths.get("some/directory/"))
  .subscribe(event -> System.out.println(event));

然后,基本上已经满足了我的需求。

顺便说下,WatchService 里面还有个坑,就是使用网络磁盘映射的时候,可能会造成 WatchEvent 会有丢失,在这里有详细的讨论,这是目前 NIO 已知的 Bug。如果你对文件状态监控要求实时性比较高,建议不要使用 WatchService 使用其他的方案。

相关的资源:

  1. https://github.com/mingcheng/rx-filewatcher.java
  2. https://docs.oracle.com/javase/tutorial/essential/io/notification.html

后记,原作者已经 merge 了我的 pull requests

我的照片

嗨!我叫「明城」,八零后、码农、宁波佬,现居杭州。除了这里,同时也欢迎您关注我的 GitHubTwitterInstagram 等。

这个 Blog 原先的名字叫 Gracecode.com 、现在叫 「無標題文檔」 。 其实无所谓叫什么名字,作为码农知道取名是件很难的事情。最后想到的这个名字,其实都没啥特别的含义,系统默认的文件名而已。

作为八零后,自认为还仅存点傲娇式的幽默感,以及对平淡生活的追求和向往。 为了免得对号入座和不必要的麻烦,声明本站点所持观点仅代表个人意见,不代表自己所服务公司的立场。

如果您想联系我,可以发我邮件 `echo bWluZ2NoZW5nQGdyYWNlY29kZS5jb20K | base64 -d`

文章

项目