golang + docker 写服务的部分感受
以前写用 go 语言主要是为了代替 c 语言写算法。直到最近几年 docker,k8s 等容器化部署方式的普及,java写的服务越发感觉臃肿,因此有计划用go实现服务。
使用 go 写服务的原因
以前写的服务大部分是 java 实现的,但是随着容器化部署的普及,Java写的服务越来越大,打包起来也特别麻烦
在封装java服务的image时,需要打包的有
- jvm(jar或jdk)
- tomcat(或jetty等)
- 服务相关jar包
这几项加起来很容易就能到 200-300M,如果碰上引用较多的巨无霸包,轻松破 400M,即使使用从 java11 开始的特性,只打包用到的模块,占用空间也不小,而且如果使用了 springboot 等全家桶的话配置起来还比较麻烦。
而用golang编译服务的话,只用一个文件即可实现上面3部分的内容,纯服务打包后基本也就 5-6M,
有前端代码的话,前端代码也可以很方便的打包进二进制文件中。
用golang写服务的优点
下面结合我的感受说一下用golang写服务的优点
- 可使用协程,执行速度快,内存占用小
- 编译出来的时机器码,发布内容足够小,编译出来的只有一个可执行文件
- 跨平台编译,甚至直接在 linux 环境中可编译 windows 下可执行的文件
- 不再依赖其他虚拟机等,部署方便,能很好的配合 k8s、 docker 等发布
- 资源依赖 github 等,开源组件多
缺点
当然用go也是需要一些付出的,使用过程中也有很多感觉相当麻烦的地方
- 部分语言的设计较为奇怪
- 可用资源太多导致轮子太多……需要一定的技巧把各种轮子合并在一起
- 满屏err处理
- 缺少泛型支持(最新版本已支持)
部分使用感受
代码结构
golang说简单也简单,但是复杂起来也是真复杂,看 github 上的代码结构,除了都放根目录的,基本上很难找到两个相同结构的代码。这里我基本参考了 golang-standards/project-layout 。这套结构和以前写过的语言有很大不同,例如以功能分文件夹(包),而不是层级。 举例 java 工程中 service,controller 等包结构看不到了,而是纵向拆分到了不同以功能分类的文件夹中。
对接口的使用
golang接口感觉设计的特别巧,可以先写实现再写接口,灵活程度极高,使用方式多样。
例如可以把目前使用的库的方法抽象成接口,可以不用实现接口就能直接使用方法,如果某天准备换另一个库,再进行实现。
Error及日志记录处理
首先特别吐槽一下 golang 里面 error 的处理方式,一页代码里至少有一半在处理错误。这里我目前也没啥好接口办法。
另一个问题是错误日志的记录,默认的error过于简单,只是个接口,不能记录堆栈信息,我后来用的 juju/errors 可以弥补一些原生err的问题。
Rest服务
其实 golang 已经封装了很大一部分功能。建议使用比较小的框架,例如 gin 或 echo等。这些框架只是对golang原生框架的一个封装,都是及其轻量级的。我建议使用 echo,因为gin没有错误的统一处理,在err满天飞的情况下更加显得雪上加霜。而 echo 有相应的错误统一处理,和一些集成在内部的中间件,可以直接用,稍微方便一些。
其他
其实能写的还有很多,要睡觉了,下次再说……