李迟2021年9月知识总结

本文为 2021 年 9 月知识总结。

研发编码

C / C++

无。

golang

跟时间转换有关的几个片段:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
   exTime := "20210901"
mytime, _ := time.Parse("20060102", exTime)
fmt.Println(mytime.UTC().Unix(), mytime.Local().Unix())

// 用本地时间格式转
mytime, _ = time.ParseInLocation("20060102", exTime, time.Local)
fmt.Println(mytime.UTC().Unix(), mytime.Local().Unix())

exTime = "2020-09-17T20:00:27"
// 时间有2个格式,这里都判断一下
mytime, _ := time.Parse("2006-01-02 15:04:05", exTime)
themonth := int(mytime.Month())
// 如果不合法,年月日均为1
if mytime.Year() == 1 && themonth == 1 && mytime.Day() == 1 {
truemytime, _ = time.Parse("2006-01-02T15:04:05", exTime)
truethemonth = int(mytime.Month())
}

golang 转换时间时,时间模板的数值是固定的,必须为2006-01-02T15:04:05中的对应数值,但间隔的字符可以变化,如示例中的20060102。指定时间字符串转换为时间戳时,使用ParseInLocation函数,指定time.Local,否则转换出来的值会加上时区(如东八区)。

执行外部命令代码片段:

1
2
3
4
5
6
7
删除进程:
appname := "./httpforward_back.exe"
port := 9000
exec.Command("sh", "-c", fmt.Sprintf("pkill -SIGINT %s", appname[2:])).Output()
启动进程:
cmd := exec.Command("sh", "-c", fmt.Sprintf("%s -p %d -i \"run in port %d\" &", appname, port, port))
err := cmd.Start()

使用函数exec.Command执行命令时,前面2个的参数为sh-c,如果不使用,则会出错。删除进程的命令使用killall也是可行的,在外网找了一些帖子,有提到用pkill -SIGINT,所以就用了。使用Start函数是启动进程,不会阻塞。如果要执行的命令需要返回值或等待其执行完毕,可使用Run函数。

写文件的示例。
需求:在程序中自定义 nginx 的配置文件 nginx.conf 内容,根据参数,添加location中不同的 URL 和对应的 端口。
知识点:由于有特殊字符,故使用反引号,阅读和处理都方便。字符串组装使用fmt.Sprintf。调用ioutil.WriteFile写文件。

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
// 定义config字符串,一些不变的内容在此
var config = `

http {
truelog_format main '$remote_addr - $remote_user [$time_local] "$request" '
truetruetruetruetruetrue'$status $body_bytes_sent "$http_referer" '
truetruetruetruetruetrue'"$http_user_agent" "$http_x_forwarded_for"';
trueaccess_log /var/opt/rh/rh-nginx116/log/nginx/access.log main;
truesendfile on;
truetcp_nopush on;
truetcp_nodelay on;
truekeepalive_timeout 65;
trueserver {
truetruelisten 8080 default_server;
truetruelisten [::]:8080 default_server;
truetrueserver_name _;
truetrueroot /opt/app-root/src;
truetrueinclude /opt/app-root/etc/nginx.default.d/*.conf;

truetruelocation / {
truetrue}
truetruelocation /foo/test9000 {
truetruetruefastcgi_pass 127.0.0.1:9000;
truetruetruefastcgi_index index.cgi;
truetruetruefastcgi_param SCRIPT_FILENAME /scripts$fastcgi_script_name;
truetruetrueinclude fastcgi.conf;
truetrue}
`
// 这是配置文件的结束符号
trueconfigend := `
}
}`
// 格式化 只针对 location 的地址和端口。
// 添加多个的话,按格式写即可
trueurl := "test9001"
trueport := 9001
truetmpstr := fmt.Sprintf(`
location /foo/%s {
truetrue fastcgi_pass 127.0.0.1:%d;
truetrue fastcgi_index index.cgi;
truetrue fastcgi_param SCRIPT_FILENAME /scripts$fastcgi_script_name;
truetrue include fastcgi.conf;
true}
}`, url, port)

trueconfig = config + tmpstr + configend

trueklog.Println("write and reload nginx")
trueioutil.WriteFile("/etc/nginx/nginx.conf", []byte(config), 0666)

注:上述配置内容并不完整。

Docker

在 Dockerfile 中设置环境变量:

1
2
3
4
5
6
7
8
ENV MAVEN_HOME /usr/local/apache-maven-3.8.2

ENV GOROOT /usr/local/go
ENV GOBIN /usr/local/go/bin
ENV GOPROXY https://goproxy.io,direct

# seems cant pass $MAVEN_HOME here
ENV PATH $PATH:/usr/local/apache-maven-3.8.2/bin:/usr/local/go/bin

注:在构建镜像中,观察输出日志,会将物理机的环境变量解析进来,如最后一行的 $PATH 会变为物理上的 $PATH 的具体值。如果使用 Dockerfile 定义的环境变量,似乎不生效。

Linux

zip压缩

对于相同文件/目录,不同时间点用 zip 压缩,得到的 zip 文件,md5 值会不相同(时间间隔非常短则无此现象)。使用-X -D可解决,示例:

1
zip -qr -X -D c.zip hello/

参考

编码其它

有些工程跟踪较麻烦,有些是因为条理性和逻辑性不够好,也有些是因为线索不明导致的。比如一个 C++ 类,某个业务处理示例如下:

1
2
3
4
ret = get(m_path);  // 使用成员变量m_path
search_dir(m_path); // 使用成员变量m_path
mysort() // 不带参数,函数内使用了前面赋值了的成员变量m_path,同时设置了成员变量m_file
changedate(m_file) // 这里直接用m_file传参

该示例的具体处理见上面的注释,从注释看是没问题,但是,单纯从函数调用及参数,是比较难看到逻辑的,需要跟踪每个函数及对应的成员变量才能理清数据流向。
对此,个人看法是:
如果类中有工具函数的话,不要使用类成员变量。
业务函数中,能从成员变量看到主线。或加上注释。

研发知识

参考一些开源项目的 git 提交日志格式,分为“头部 正文 尾部”三部分。根据实际情况,自己整理一下。

1
2
3
头部: 给出本次提交的类型及影响范围。如:格式修改 测试 重构 bug修正  (影响范围)
正文: 描述本次提交的变化,可列表。
尾部:一些备注或注意事项

实际示例:

1
2
3
头部         bug修正(时间同步模块)
正文 时间同步模块减小时间差判断的阈值,避免波动。
尾部(附录) 关闭#1

研发思考

适当时候做适当事

当初想入门后台管理系统,选定 vue + golang 路线。查了很多资料,也下载了很多工程源码,发现学不进去。后来才发现是因为工程太复杂,并不适合新手。遂放弃,至今因其它事缘由,一直未能再动手写。
本月开始真正搞组织革新,jenkins 倒好,但 Java 只看了一点语法,工程是 springboot 的,在初期连 IDEA 工程也不懂建立,对于 jdk 那些 1.8、16等版本号也没理解。硬着头皮搞了几天才有点眉目。只感叹 IDEA 太强大了,Java 框架太多太成熟了。但就是因为太成熟了反而不喜欢研究,还是习惯 C++。

工作记录

某工程有自定义的日志函数,有操作 sqlite3 数据库,等等。在设备突然断电再启动后,sqlite3 数据库文件被损坏,经查其二进制,竟然在某个偏移量出现了部分的日志。百思不解,一直未解决。实然没招,后使用 cppcheck 和 valgrind 检查,修正一些编码错误/警告。将没有实质用处的函数删除(调用了但没产生作用)。

某工程需要兼容 32 位和 64 位,在 64 位系统中用%ld打印 uint64_t 类型变量,正常,在 32 位系统运行则报段错误,使用%lld打印,但在 64 位又出现警告,无奈,将 uint64_t 改为 long long 类型。幸好是在试点阶段发现,否则影响大了。

某工程连接的主备系统两个 sftp 服务器,该工程下载 xml 文件并判断哪个系统的文件版本较新即下载之。后发现同一文件的 md5 值不同,导致不断下载。经查,发现该文件在不同时间打包压缩,即使内容无变化,但压缩的 zip 文件 md5 值变化了。经查,可以通过参数保持 md5 一致。因工程是其它小组负责且用 Java 实现,未知如何修改。

某工程需下载远程服务器某个目录的文件,设置一 数组变量char remoteFile[128],在之前一切安好,但最近切换备份系统后,中秋节前一晚,某生产环境出现错误,无法下载,造成堵车,运维同事联系紧急处理。经查,是因为切换后,远程目录文件长度超过 128 字节了,且只超过2~3字符。试点现场采用的服务器IP比较短(如10.0.45.16),未被发现,但有的服务器IP较长(如10.100.168.231),刚好超过几个字符,于是失败。定位原因后,将相关的变量大小改大,解决问题。

参与了某路段开通的一些工作。初期,经工具检查发现某项数据不符合要求,与其它同事确认不影响,在试点测试时,另一服务器工具无法正常工作,数据未更新,问题在上线前2天被发现,并因此加班。
复盘后得到经验:与既定规则不符合的,多请教同事,并汇报领导(当初未坚持,也未上报);参数修改,需多方参与评估(其他同事负责的);生产环境需要有监控手段。

本月做的事及计划

抽时间完成转发工具编写和测试,研究并实现了一些负载均衡算法。

近来发现自己表达能力和思考能力下降了,计划看看非技术类的书。

其它点滴

本月首次还贷,正式开始房奴生活。个人的开支须更加节约了。

  • 本文作者:李迟
  • 版权声明:原创文章,版权归署名作者,转载建议注明出处(当然不注明亦可)。
  • 本文链接:/my-library/code-for-2021-09.html