• 李迟2022年8月工作生活总结


    本文为 2022 年 8 月工作生活总结。

    研发编码

    Java

    接手了一个 Java 工程事务(注:不是接手工程),在其中添加一个模块。不知为何领导会找到我,或者是没有负责项目的原故,作为十分珍惜工作的人,不敢拒绝。但中途有优先级高的事务,由另一个即将休假的同事帮忙了下,月底返回我手上时,已经做好了空类,能通跑,可以在里面调试并添加代码。不过还是要熟悉工程代码的。
    由于历史原因,代码仓库是前2年创建的,最新的提交是一年前,但已是很多代人的的经验积累。为谨慎起见,向维护的同事要了生产环境的账号,自己去看程序和配置文件,经核对,生产环境的程序,和代码仓库不同一个东西,再仔细对比代码和提交日志,发现有很多稍有差异的地方,但主体框架没变,于是下载日志,对着日志跟踪代码。本来想直接在IDE中调试的,奈何环境复杂难度大,作为有多年阅读代码经验的人,虽然 Java 没有真正搞过,但分析代码的能力还是有的,经过2天时间,基本模清主线,也做了笔记。祖传代码的最重要守则是:能不改就不改,真要改,统一一个地方改,而不动原来的代码,哪怕是有重复的。
    但接下来才是大头:要搞正则表达式,要全方位测试,一个月内要从新手到完成将会,同时还要应对其它事,想想还是有点心虚。

    Makefile

    有些平台要跨平台编译、运行。因此 Makefile 模板还得修改适应。

    一般地,像编译 ARM 平台的代码,要用到交叉编译器,此时,在 Makefile 中根据编译器做判断即可。但有时会直接在 arm 板子上编译,此时编译器名称即为gccg++,所以要找其它方法判断。

    ARCH_BIT = $(shell getconf LONG_BIT)
    ARCH_NAME = $(shell uname -m)
    DATE_STR=$(shell date +"%Y-%m-%d")
    
    • 1
    • 2
    • 3

    ARCH_BIT定义了系统位数,ARCH_NAME为系统构架,DATE_STR为日期字符串(仅是为符合项目程序文件命名要求)。在编译生成可执行二进制文件后,根据变量拷贝为不同的后缀。

    ifeq ($(ARCH_BIT), 32)
    	$(Q)cp $(target) $(target).$(DATE_STR).x86
    else ifeq  ($(ARCH_NAME), aarch64)
    	$(Q)cp $(target) $(target).$(DATE_STR).arm
    else
    	$(Q)cp $(target) $(target).$(DATE_STR).x64
    endif
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    上述片段还要完善,但就目前而言,已经足够了,因为要编译的只有3种:32位的x86、64位的x86、64的arm。

    在某个目录日益庞大的工程仓库中,发现 make 编译比较慢,经查,发现是头文件的策略有问题,为图方便(所谓的“万能”模板),用find查找所有目录,作为头文件目录。但仓库比较大,.git目录较多,加上有很多测试目录,速度就下降了,为此,可以用grep -v排除掉不需要的目录,如下:

    INC += $(shell find $(SRC_DIRS) -type d | grep -v .git)
    
    • 1

    实践中,在通用性和性能上,要权衡一番。

    go

    时间相关

    最近的程序代码在较多地方使用了时间,时间戳、不同格式的时间字符串。对 golang 的时间进行了一点了解(但不研究原理)。golang 的时间串格式化必须使用"2006-01-02 15:04:05.000",当然也可以单独调用函数获取每个字段的值再组装,但有稍有麻烦。另外,没有获取毫秒的函数,只能从纳秒值转换(time.Now().Nanosecond()/1e6)。测试片段代码:

    fmt.Println("time string ", time.Now().Year(), time.Now().Month(), time.Now().Day(),
    time.Now().Hour(), time.Now().Minute(), time.Now().Second(),
    time.Now().Nanosecond()/1e6)
    fmt.Printf("time string %v \n", time.Now().Format("2006-01-02 15:04:05.000"))
    fmt.Printf("time local string %v \n", time.Now().Local().Format("2006-01-02 15:04:05.000"))
    fmt.Printf("timestamp %v s\n", time.Now().Unix())
    fmt.Printf("timestamp %v ns\n", time.Now().UnixNano())
    fmt.Printf("timestamp %v ms\n", time.Now().UnixNano()/1e6)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    上述代码输出结果:

    time string  2022 August 12 16 49 20 590 590010700
    time string 2022-08-12 16:49:20.590
    time local string 2022-08-12 16:49:20.590
    timestamp 1660294160 s
    timestamp 1660294160590010700 ns
    timestamp 1660294160590 ms
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    为了方便使用,自定义了套格式化函数接口(原代码来自github公开仓库),如下:

    func GetNowDateTime(fmt string) string {
    	return DateT(time.Now(), fmt)
    }
    
    func DateT(t time.Time, format string) string {
    	res := strings.Replace(format, "MM", t.Format("01"), -1)
    	res = strings.Replace(res, "M", t.Format("1"), -1)
    	res = strings.Replace(res, "DD", t.Format("02"), -1)
    	res = strings.Replace(res, "D", t.Format("2"), -1)
    	res = strings.Replace(res, "YYYY", t.Format("2006"), -1)
    	res = strings.Replace(res, "YY", t.Format("06"), -1)
    	res = strings.Replace(res, "HH", fmt.Sprintf("%02d", t.Hour()), -1)
    	res = strings.Replace(res, "H", fmt.Sprintf("%d", t.Hour()), -1)
    	res = strings.Replace(res, "hh", t.Format("03"), -1)
    	res = strings.Replace(res, "h", t.Format("3"), -1)
    	res = strings.Replace(res, "mm", t.Format("04"), -1)
    	res = strings.Replace(res, "m", t.Format("4"), -1)
    	res = strings.Replace(res, "sss", fmt.Sprintf("%03d", t.Nanosecond()/1000000), -1)
    	res = strings.Replace(res, "ss", t.Format("05"), -1)
    	res = strings.Replace(res, "s", t.Format("5"), -1)
    
    	return res
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    使用如下:

    GetNowDateTime("YYYY-MM-DDTHH:mm:ss.sss")
    结果 --> 2022-08-13T01:13:14.315
    
    • 1
    • 2

    读取一行文本

    bufio.ReaderReadLine函数用于读取文件的一行文本,一直如此使用未出问题,但最近用来解析一个日志文件的json数据,发现不完整,原来并没有读完一整行,初步测试,最多能读取4096个字符(网上一说单行不能超过64KB)。该函数定义如下:

    func (*bufio.Reader).ReadLine() (line []byte, isPrefix bool, err error)
    
    • 1

    如果一行文本太长,isPrefix的值为true。因此需要对此返回值作判断。另外,也可以考虑ReadBytes(\ n')ReadString('\n') 函数,但实际中日志的回车换行无法保证唯一,故不采用。代码片段:

    br := bufio.NewReader(fd)
    var oneline []byte
    for {
        line, isPrefix, c := br.ReadLine()
        if c == io.EOF {
            break
        }
    
        oneline = append(oneline, line...)
        klog.Printf("isPrefix: %v line: %v\n", isPrefix, string(oneline))
        // 还有数据
        if isPrefix == true {
            continue
        }
        klog.Printf("isPrefix: %v line: %v\n", isPrefix, string(oneline))
        oneline = []byte{}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    由于oneline是追加,因此要清零。

    读取缓冲区为空

    在用gin框架接收数据时遇到一个问题:读取缓冲区后,再读取,该缓冲区已经为空,前后“读取”可能是不同函数,在流程上并不能明显感受到。举例:调用ctx.GetRawData(),再调用ctx.ShouldBindJSON,此时有错误EOF,因为前者会将数据读完,后者就无数据可读了。反之亦然。同样原理,ctx.ShouldBindJSON不能前后调用2次。

    之前也遇到类似的问题。使用file.Read()读取文件,其指针指向文件末,如要再读取,则要调用file.Seek(0, 0)指回文件头。

    代码:

    func myHandle(ctx *gin.Context) {
        var param []byte
        // param, _ = ctx.GetRawData() // 获取最原始的数据 此处调用后,后面的ShouldBindJSON无法解析
        fmt.Printf("got : [%v] [%v]\n", string(param), ctx.Request.Header.Get("Content-Type"))
        var s MyInfo_t
        var err error
        if ctx.Request.Header.Get("Content-Type") == "application/json" {
            fmt.Println("got json request")
            // 获取json
            if err = ctx.ShouldBindJSON(&s); err != nil {
                fmt.Printf("bind json failed: %v\n", err)
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    工作记录

    国产工控机的编译基本由我操办,本来以为完结了,也消停了一大段时间,但月底又找上我,说要用最新的代码做试点测试。但工控机上那么多的工程和配置,我只熟悉2、3个,于是找同事了解,还好有当时的操作记录,花了小半天时间完成编译和测试。当前麻烦的事是,有一些子仓库代码是不兼容 arm 的,而我本地的修改还不能提交作为正式代码(虽然我做了平台的宏定义),所以每次编译,还是要花时间的。

    作为上了年纪的工具人,对于各种技术、语言不排斥,只是上手或做事快慢的问题,另外,也无甚多精力研究底层原理了,出问题能快速响应并定位到根源,有事务来快速出结果,这才是当下要做的。那个曾经研究过 u-boot 内存分布,研究过 coreboot,深入过内核驱动代码的人只存在记忆中了。像 map,知道是无序的即可,不用研究原理。像 golang 的时间格式,知道必须使用某个特定的日期时间才能正常就行,不用深入研究。我比较反对二元化,追求快速出成果和追求原理细节本就是两个维度,牛人能做到统一,但普通人按时择重点即可。快速出成果后,有时间精力再研究原理,这也是好的。
    不过就现在的我来说,面对的事务又杂多,还是比较吃力的,做着重构的 C++ 工程,搞着网页需求,写着方案,又要改个 Java 工程,突然来个紧急文档要写,某运营公司的人又在现场直接电话追着问题,还得想着下班晚了下雨了到哪里买菜。

    这个月开了一次会,领导继续谈新公司的事,牌子准备挂了,组织结构有部分调整,针对外包人员,没有过多涉及,现说法是保持。那天深夜在阳台凉衣服,想了一下,也对,外包人凭什么、有什么资本和正式员工福利、身份一样呢?2018年前来的都有十几号人,我才来2年,论资按贡献也轮不上我。睡前又想了一下,我有十年的编码经验,肯吃苦,态度好,叫加班二话不说,2年内拿了2个高级证,1个工程师职称,对前沿技术有一定掌握(自信在团队内,在docker、devops方面能排到前面),组内有些小平台还是自己一手搭建和维护的,这些不知能不能入领导的眼内。我当然会幻想自己能转正,这样,大锤和大妞的牛奶自由能实现,我也不再是办公室唯一带饭的人,还可能有公积金以减少房贷压力,而不至于现在那样拮据。

    生活记录

    月初在银行APP上申请了提前还部分房贷,因为预想的计划出错,晚了半个月申请,白白给了2000多的利息,十分后悔。算了下,一天下来就得上交银行133块钱,做啥也没劲了,只能不去想了。为了凑钱(不是筹款),算了各种账号、银行卡,找老父母,找大锤妈。在转账,扫码转账期间,有几张银行卡被风控了,不知是转账问题还是非法问题。这次还款,是有点冒险的,因为下个月初才扣款,本月扣了房贷,交了大锤的学费,所剩不多,而下个月月尾才发工资。经多轮测算,保证下个月的房贷后,生活费就非常勉强了,但可用信用卡周转,同时也和罗师傅打了招呼,到时帮忙应急一下,但万不得已不去麻烦。

    从我记账软件的报表看,月消费中房贷支付占60~70%(以前租房时代大概30%),其它项目不多是因为我缩减了自己个人的开支,包括但不限制个人的衣服、早饭、午饭、理发、消遣,即使熬夜,也只买2块5的泡面,自己考证通过也没敢犒劳自己。至于家庭伙食,则是以消耗较多个人时间购买便宜食材,而又保证家里每周有鱼、肉、骨头,偶尔还买鱿鱼、鸡、鸭,周末下厨变着法做好吃的。至于小孩开支,如奶粉、尿裤、学费、衣服、偶尔游玩等必须支出的项目,是无法节省的,但部分可选档次不高的以节省钱。如此,每月也是勉强收支抵消,一旦有小孩咳嗽感冒,电车电瓶被偷等突发事情,必然出现赤字。

    家庭关系并没有因为住上自己的房子而有大的改变,已经偶尔抱怨只有一个厕所了,2个小孩长大后,这个问题会更突出。可惜,不是所有的人对长达30年的大一百万的房贷有概念的。目前基本摸清规律了,家里不和的原因, 一是小孩不听话,去年开学第一天晚上大锤搞坏玩具,我被骂,今年开学第二天晚上大锤被骂,我也不能幸免,但问大锤问不出原因。另一原因是我没及时做应该的家务。很多年未见面的初中同学来南宁培训,晚上去找他们吃烧烤,大锤妈问是男是女,我说不管回答是男是女,问出这话后就已经不重要了。席上没喝酒,我所处的同学有个优点,不会强人所难,喝完2大瓶可乐后已经半夜了,最后我也没付钱,还收了他们买了水果。

    思想方面

    和有的同事讨论问题,有时能明显感觉到思维和看问题上的很多差异,经验还是比较重要的,但我必须保持谦卑下气,不敢越界,虽说低调做人,高调做事,但这不是当前阶段做的。

    业余研究

    上个月提到做个播放器,写了点方案计划,就没然后了,不知道后面还能不能继续做。这几个月来,用于业余的研究时间不多,一是没时间,二是没有驱动力。

  • 相关阅读:
    计算机结构体系:系统CPI计算例题(1.5)
    Nginx 负载均衡实现上游服务健康检查
    java计算机毕业设计springboot+vue青少年编程在线考试系统
    智能合约是什么?
    4.1 数据仓库基础与Apache Hive入门
    利用uvicorn、Starlette和pipeline将一个训练好的大模型发布成一个web服务
    【C++笔记】多态的原理、单继承和多继承关系的虚函数表、 override 和 final、抽象类、重载、覆盖(重写)、隐藏(重定义)的对比
    【搭建OpenCV+Tesseract】
    RIPEMD算法:多功能哈希算法的瑰宝
    geecg-uniapp 源码下载运行 修改端口号 修改tabBar 修改展示数据
  • 原文地址:https://blog.csdn.net/subfate/article/details/126621268