Category Archives: Golang

Go net/http 与 nodejs Koa 的 Σ(・□・;)

最近跟猫猫正在写一个一起听音乐的项目「側に」,然后昨天晚上就在一起通宵写代码的说~我对 stream 和 P2P 也了解得蛮少的,就跟着猫猫一起一边学一边写~是 Pair Programming (/ω\)!

猫猫找到了一个 Go 里面的 P2P 的库 libp2p。于是一番商量之后,打算先写一个可以通过 P2P 方式相互连接的小 demo 出来~猫猫跑去用 nodejs 写服务器端的 tracker,我则是跑去研究这个库里的 example 写 client 出来~

一边开着 TeamViewer 一起听歌,一边开了几个 VSCode 的 Live Share 开始写~其实到这里都还好喵,还是蛮顺利的!然而在测试的时候发现, Go 这边一 POST JSON 数据过去,那边服务器就 throw error 了 Σ(・□・;)

一开始的时候在想为什么 Koa 一直没有收到数据,后来用「手动挡」发现,自己给 Koa addListener 的话,的确数据都是正常的样子><

然后就陷入了沉思,莫非是又遇到了什么神奇的坑了么(゚o゚;; 而且猫猫那边用 nodejs axios 发给 server 却又都是正常的……咕噜咕噜呜(>﹏<)

啊,这实在是不够科学!于是找猫猫要了一下 tracker 部分的代码喵,在本地跑了一下之后发现,这边本地 POST 也炸了!

炸了之后跟猫猫怀疑是不是 Go 里面 net/http POST 的时候默认 gzip 了,但是 Content-Length 又的确是原始的 JSON 字符串的长度的说……

Continue reading Go net/http 与 nodejs Koa 的 Σ(・□・;)

记 Golang 下遇到的一回「毫无由头」的内存更改

前两天看到 @Nova Kwok@BennyThink 做了一个 WebP Server,于是 clone 下来玩了一下,发现貌似没有做“原始图像更新后,重新生成相应的 WebP 图像”的功能。好的,这个说起来简单,做起来也很简单,就是 os.Stat 一下,然后取到图像最后修改时间的 UNIX timstamp,STAT.ModTime().Unix(),最后再跟先前生成好的 WebP 文件名比较一下就好(timestamp 会放在生成的 WebP 文件名里)。

上面为止真的都很简单,在 macOS 上测试了一下,看起来没问题~于是就提交 Pull Request

然而 Nova 告诉我说,

Nice PR, but there seems a little problem that the older converted images are not deleted after the change of the original image, this might cause a possible leakage of the original one's content.

显然我是一头雾水,一开始还以为自己提交 PR 的时候是不是手滑删掉了几行,检查了一下之后发现并没有!然后姑且先把 macOS 上测试的截图 comment 在了 PR 下面。

接着我估计 Nova 应该是在 Linux 下跑的测试,于是就在一台新的 VPS 上安装了 go,把我 fork 且修改过的那份代码 clone 在 VPS 里测试。本来我预估的时候要么是我搞错了文件,要么也许是 Nova 不小心用了以前编译好的文件。然后一测试我就惊了,居然真的没有删除以前生成的 WebP 图像 Σ(・□・;)?!

由于没有 Linux 机器,也懒得安装虚拟机了,只能一头雾水的在 VPS 用 fmt.Println 输出来简易 debug 了。根据 fmt.Println 的输出,发现 ImgName 不知道为什么就突然之间被改了!

[1]ImgName: webp_server.png
[2]ImgName: webp_server.png
[3]ImgName: webp_server.png
[4]ImgName: webp_server.png
[5]ImgName: root/webp_serve

上面是在 5 处不同的地方 fmt.Println("ImgName", ImgName) 的输出,虽然我放了这么多,但是实际上在代码里 ImgName 在其作用域内只有过一次赋值,

ImgPath := c.Path()
// ... 略去 10 行左右判断文件扩展名的代码
ImgName := path.Base(ImgPath)

然后就没写过了,仅有读的操作,没有任何赋值,中间只有一次被用来当作 Sprintf 的一个参数

WebpImgPath := fmt.Sprintf("%s/%s.%d.webp", DirPath, ImgName, ModifiedTime)

但显然这个也不会更改 ImgName 的内存嘛。“这不科学!” 虽然想这么叫出来,但是想想这个肯定还是有原因的!

Continue reading 记 Golang 下遇到的一回「毫无由头」的内存更改

使用 Prometheus + Grafana 来监控 Mac Mini 的风扇与温度

其实程序部分也没什么复杂的,就当是个笔记吧~

上次给 Mac Mini 安装了 Ubuntu,然后因为学习 Rust,就用 Rust 写了一个 RESTful 的控制风扇的服务;这次就是记录一下使用 Prometheus,Grafana 与 Golang,写个导出 Mac Mini 风扇与温度监控信息到 Prometheus 的坑吧~

(这里我的 Mac Mini 的 IP 地址是 10.0.1.45,Docker 部署的 Prometheus + Grafana 的 Mac 是 10.0.1.46,下面某些配置或者访问的 URL 自行改一下 IP 地址~)

首先就是直接拿 Docker 部署一下 Prometheus + Grafana,这里暂时没有什么好说的。目录结构是

.
└── metrics
    ├── configs
    │   └── prometheus
    │       └── prometheus.yml
    ├── data
    │   └── grafana
    └── docker-compose.yml

data/grafana 是一个空的目录,在下面 docker-compose 设置中会映射给给 Grafana(^O^)

docker-compose.yml 如下

version: '3'
services:
  prom:
    image: prom/prometheus
    ports:
      - "9090:9090"
    volumes:
      - ./configs/prometheus/prometheus.yml:/etc/prometheus/prometheus.yml

  grafana:
    image: grafana/grafana
    ports:
      - "3000:3000"
    volumes:
      - ./data/grafana:/var/lib/grafana
    environment:
      - GF_SECURITY_ADMIN_PASSWORD=secret

上面的 GF_SECURITY_ADMIN_PASSWORD=secret 则是设置了 Grafana 的 admin 用户的密码为 secret,可以根据需要更改一下~

然后是 Prometheus 的配置文件,./configs/prometheus/prometheus.yml,每 5 秒从我的 Mac Mini 上 pull 一次

Continue reading 使用 Prometheus + Grafana 来监控 Mac Mini 的风扇与温度

Toy BlockChain with GoLang

啊,這個只是一個玩具類型的區塊鏈而已~主要關注在區塊鏈的一部分實現上,暫時沒有涉及到分布式相關的問題。這個項目的文件組織如下~

.
 ├── block.go       // Block struct 的聲明以及相關函數
 ├── blockchain.go  // BlockChain struct 的聲明以及相關函數
 ├── cli.go         // 與 command line 相關的代碼
 ├── pow.go         // 一個簡單的 Proof of Work 實現
 └── utils.go       // 輔助函數

首先是 utils.go,這裏聲明了兩個輔助函數,一個是將 int64 類型的數字轉為其對應的字節表示,另一個是檢查 err 的函數

package main

import (
    "bytes"
    "encoding/binary"
    "fmt"
    "os"
)

//  int64 類型轉為其 Byte Respresentation
//
// Parameter
// ---------
//   num: 要轉換的 int64 數字
//
// Return
// ------
//   該數字對應的 Byte Respresentation
func IntToByte(num int64) []byte {
    var buffer bytes.Buffer
    err := binary.Write(&buffer, binary.BigEndian, num)
    CheckErr("IntToByte(num int64) []byte", err)
    return buffer.Bytes()
}

// 檢查是否出錯
// 
// Parameter
// ---------
//   info: 用戶定義的信息
//   err: error 類的實例
func CheckErr(info string, err error) {
    if err != nil {
        fmt.Printf("[ERROR] %s:%s\n", info, err)
        os.Exit(1)
    }
}

接下來是一個簡單的 Proof of Work 的實現,pow.go,這裡我們假定要求的是 SHA256 的最高 16 個 bit 都為 0 才行

Continue reading Toy BlockChain with GoLang