最近基于golang重构之前用php实现的webservice,也在开发新的webservice。目前已经搭好整体开发框架,走了不少弯路,也踩了不少坑。这篇文章主要记录一些技术选型相关的细节,希望有点参考价值。
第三方框架
基于urfave/negroni实现无侵入的中间件模块
选用gorilla/mux实现接口路由定义
基于gorilla/schema实现接口参数解析
选用asaskevich/govalidator实现接口参数校验
选用uber-go/zap实现应用日志打印
基于natefinch/lumberjack实现日志切割
选用jmoiron/sqlx实现数据存取模块
基于Rican7/retry实现必要的重试逻辑
应用相关
对于接口返回给客户端的数据,个人认为越简单越好维护。正常情况好说,坑主要出现在对于错误情况的处理,尤其是针对需要和多个系统交互的服务而言。KISS原则在这里是非常必要的,这能极大减轻后期维护的成本,所以这也是我接下来要坚持的,极简原则。
日志相关
在不影响应用性能的前提下,日志是多多益善的,这会非常便于后期调查各类问题。但是如何恰当的打印日志,需要对后台开发有较深的理解,并且也要懂得一定的运维知识,所以这也是我们踩坑最多的地方。
关键点的定义:考虑到不同人的理解程度不一,于是我抽象出了几个关键点。主要分为接口access,请求redis,请求db,请求第三方接口等,这几个点都是调用封装好的函数实现的。因此在开发过程中可以避免日志未打印的问题。
日志过多的问题:为方便问题查询,最初我们是把这些关键点的日志都入了ELK,但是后来发现日志量过大,kibana渲染数据尤其慢,以致于运维只开放了最近三天的日志,于是我们决定只保留了接口access日志。但是这又加大了调查问题的难度,后面我会简单介绍解决这个问题的方案。
日志切割的问题:最初因为没有发现合适的切割库,于是引入了logrotate来做日志切割。开始时用的是copytruncate模式,后来运维发现磁盘io在高峰期会告警,于是改用了create模式解决了这个问题,通过改进应用支持SIGHUP来reopen日志实现的。不过现在已改为应用自己切割日志了,蛮稳定的。
TraceID的引入:为了更有效的调查问题,接口返回数据增加了TraceID消息头。客户端有错误日志上报机制,若接口返回出错则会带上这个TraceID。通过这种方式,就能在ELK中查询到对应的这个请求相关日志。后期计划会弃用kibana,开启关键点日志。
监控相关
最初是基于kibana来做应用监控,后期发现kibana渲染效率过低,于是就调研并引入了prometheus/grafana实现了nginx、golang及php等应用的相关监控。目前已经搭好了框架,只需要再细化监控指标就可以考虑接入告警系统了。
关键点的定义:与日志类似,我抽象了几个关键点,包括服务接口、lru、redis、db及第三方服务的请求次数与延时分布,相关错误次数的统计。定义好合适的服务等级协议(SLA)以及Apdex(Application Performance Index) score。
自动服务发现:最初是通过修改配置来实现监控服务的增删,后来引入了consul实现自动服务发现,这也可以更好保证监控数据的正确。
稳定高效:prometheus是通过pull的方式获取监控数据的,因此即便挂了不会对应用有任何影响。grafana相对于kibana则异常高效,渲染7天的数据只是毫秒级,而kibana则极大可能会超时导致渲染不出。
有一句话说得好,“no measurement no improvement”。不管我们是改进应用逻辑还是性能,如果没有度量或监控,就无法做好优化,或者说即便优化了也无法自证。这会是我一直推崇的信念,也已在我们的线上环境得到无数次验证,很多线上问题也都是通过这个监控系统发现的。
欢迎交流 :-)