Struct embedding in Go
最近组里在AWS开发一个云原生服务,选择的语言是Go。磕磕碰碰一年有余,却还没有正式发布。其实当初如果选择Java来开发,感觉效率能提高一倍,因为大部分组员都是Java开发出生,但是用Go的话,大家都是新手,得从头学期。既然如此,为何用Go?好问题。我算是最晚加入这个组的程序员,传言说是因为tech lead喜欢一些新玩意,觉得用Go来开发服务很酷。
闲话少说。在用Go开发的过程中,碰到了各种各样的问题。其中一个有关timestamp的问题困扰了我许久,最终通过各种hack,外加与其他组扯皮,终于把问题解决了。
问题是这样的。后台服务在API Gateway的model里用Number类定义了timestamp,然后在DynamoDB里也是用Number存储的timestamp,比如1594278495.789。后台服务某个API的request里定义了若干类型为*time.Time的fields,比如
碰到的第一个问题是,后台服务从API Gateway收到的request里timestamp都是1594278495.789这类形式的,但time.Time的JSON marshal和unmarshal只接受RFC 3339格式。于是ListDummy API报错。
碰到的第二个问题是,time.Time在DynamoDB里默认存为RFC 3339格式的string,如果要存为number,只能是Unix time,不带millisecond。而我们的timestamp要求带millisecond,second的精度不够。
两个问题其实都好解决,如果可以用string类型来定义API Gateway和DynamoDB的timestamp,这样API Gateway和后台服务(包括DynamoDB)的交互通畅无阻。但因为某些未知原因,tech lead不喜欢这样,于是只能另辟蹊径。
尝试过直接重定义time.Time的JSON和DynamoDB Attribute的marshal和unmarshal方法,但是Go不支持。最后想出的方案是用自定义的Time,里面定义了想要的JSON和DynamoDB Attribute的marshal和unmarshal方法。
对ListDummy API来说,因为我们的API client是自动生成的,没法更改request和response里timestamp的类别,所以用到了type embedding(感兴趣的请移步Effective Go探个究竟)。首先定义一个ListDummyRequest的wrapper,wrapper里定义了和ListDummyRequest同名的timestamp fields,但它们的类型是自定义的Time。
然后在API Gateway和API之间的middleware层用ListDummyRequestWrapper来接受API Gateway发来的request。request的marshal和unmarshal会使用customer.Time里定义的方法。然后在ListDummy API里做一次ListDummyRequestWrapper到ListDummyRequest的转换,但是得设置ListDummyRequest里的timestamp,因为它们被wrapper里的那些同名的fields覆盖了。
对DynamoDB来说,只要在对应的DAO里,用custom.Time定义timestamps,timestamp的存储就会按自定义的方法去marshal和unmarshal。
版权声明: 本文为 InfoQ 作者【Interstate5】的原创文章。
原文链接:【http://xie.infoq.cn/article/6a6e7c65a8dc980072c587298】。未经作者许可,禁止转载。
评论