内容简介:前言今天我分享的主题是go的工程效率实践,做一个简单的自我介绍,我叫丁靖,8年PHP、PECL开发,2015年开始接触go,现在从事存储和图象处理以及高并发服务开发,目前是贝壳找房基础服务负责人。
丁靖: 2007年开始PHP, PECL开发,Swoole开发组成员;2015年开始Golang,从事存储,图像处理,高并发服务开发;目前是贝壳找房基础服务负责人
前言
今天我分享的主题是 go 的工程效率实践,做一个简单的自我介绍,我叫丁靖,8年 PHP 、PECL开发,2015年开始接触go,现在从事存储和图象处理以及高并发服务开发,目前是贝壳找房基础服务负责人。
先做一个铺垫,分享一下我对技术团队价值的理解,还有我们有哪些效率问题,以及我们是怎么解决的。
技术价值
技术团队的价值
首先看技术的价值,商业社会,在商言商,技术团队扮演了什么角色?我总结了三点:
第一点,做业务流程的自动化,可以提升业务的效率,在很多公司IT方面都是这个作用;第二点,可以让老板的想法快速落地,有一个指标,从老板一拍脑袋到项目上线的时间,时间越短表示我们的技术价值越大;第三,技术创新,改变世界。说的范围有点大,但是我们周围都在发生,比如说我们贝壳找房的VR看房。
技术从本质上讲都是解决效率问题
我觉得技术在本质上都是要解决效率问题,上面提到的业务流程提效、想法的快速落地以及技术创新,都是为了提高大家的工作效率和生活效率,比如在找房的过程中提升看房效率等等。
有哪些效率问题
接下来我讲一下开发过程中存在的效率问题,我总结了以下几点,可能降低我们开发的效率:
首先是我们怎么去组织工程?golang很松散,很灵活,这可能会让我们不知道怎么做,或者看不懂别人的代码;第二点是代码复用,代码复用率越高,开发效率越高;第三点问题定位,这个效率越高,故障恢复的效率越高;最后是接口设计,接口定义的通用性,权限设计的抽象性也会直接影响后期维护成本。
工程效率方面:介绍模块化、生命周期管理 、依赖注入、系统分层、现在使用的目录结构和包管理;代码复用:封装了数据库、缓存等基础组建、解决配置和路由问题;问题定位:主要介绍我们怎么做日志和监控;接口设计:简单介绍我们的接口抽象原则以及权限管理的方法。
工程组织
生命周期管理
首先从工程组织开始,PHP大概也是这样处理生命周期。不同的是,PHP只需要处理接收请求之后的逻辑,而Go还要关注怎么接收请求。大致分这么几个阶段:第一,进程启动,加载配置,创建一些临时文件,记下进程PID以方便进程管理,这些做完之后才开始进程请求。请求开始,并行的多个请求都要开始,每个请求都要分配内存,申请一些资源,比如创建数据库连接,做权限验证,签名校验,之后才到我们后面真正的业务逻辑。这样可以更好地组织工程。请求结束之后把申请的内存和资源释放掉。进程结束时,删除创建的临时文件等。
模块化
我刚刚说到生命周期,现在开始介绍模块化。基于这个生命周期,我们把很多建立的业务模型模块化,并和生命周期对应上。比如说做权限验证,有一种做法是在每个请求之前都调鉴权方法做权限验证,有了生命周期,可以定义一个权限模块,并定义请求开始时回调函数来实现。这就实现了我们常说的高内聚低耦合,把权限验证的逻辑内聚到模块里,而与其他逻辑低耦合。
依赖注入
下面一个问题,对于依赖注入可能有理解偏差和模糊的地方,这里举个例子,比如要组织一个工程,可能需要拆分成很多包,不同的包之间存在互相的调用。例如我现在有两个模块,互相之间存在调用关系,很多的做法是定义一个全局的容器(container),把各个包实例化后放到这个容器里面,运行过程中就可以调用到这些模块了。依然注入能自动化完成这个工作。我们可以在程序启动时把模块一二三实例化后放到框架里面,通过反射把实例注入到需要的模块中。这里还有个细节存在问题,如果模块一先初始化,那模块一初始化时模块二和三没有分配内存,无法在模块一初始化时使用模块二和三。因此依赖注入要分析他们之间的关系,模块三应该最先初始化,然后是模块二,最后是模块一。我们实现的依赖注入需要先注册,然后对所有注册的模块进行排序,利用反射知道模块二依赖模块三,模块一也依赖模块三,于是就排成这样的序,先对模块三分配内存,再把模块三注入到模块二里面去。后面逻辑就可以直接调用了,省去了很多手工劳动,依赖注入就是解决这个问题。
系统分层
接下来是系统分层,我们最初的想法是结合模块和生命周期,就可以比较好地组织代码了,但是实际上发现还是不行,特别是不同的人可能习惯不一样,比如有些人更习惯MVC,于是我们也支持了MVC的代码组织方式。
包管理
包管理,引用vendor属性之后各种包管理都出现了,我们选择了dep,理由是支持私有git仓库、本地代码缓存、忽略_test文件等等特性。忽略_test文件这点做得比较细,因为依赖包里面的这些测试文件,对使用来说没什么意义,忽略它们可以更快地拉取。还有一点就是Dep项目一直在更新,一直在跟进修复问题。现在官方又出来vgo,但是没有一些文档,所以我们没有深入了解。
工程目录
工程目录,vendor还在实验阶段的时期,我们把项目目录作为GOPATH,go1.7之后改为了vendor 方式管理代码仓库。编译统一使用Makefile进行,一个项目中可能存在多个service,比如这里就可以使用make app-service来编译app-service,编译用到了 go build。这里面有个坑,编译时加-race参数可以做动态并发检查,但是对并发有限制,当并发达到域值就会自动退出。所以我们做了区分,DEBUG环境编译时才加这个参数。make test执行测试。这样基本上go编译的几个环节可以比较完好的映射到 make 命令上。
代码复用
基础组建
代码复用,首先挑选了一些优秀的开源项目放到框架里,比如 gorm、redigo 等等。但我们并不是按照操作资源来封装,而是面向功能封装。这样封装是为了通过配置改变底层操作的资源,通过接口抽象出各种操作而不直接调用原生的,以便扩展。我们封装了数据库、队列、缓存、会话等功能。总结一下这样做主要的目:1. 为了方便扩展 2. 接口更稳定
配置
接下来介绍配置,因为使用了go语言,可以做更深层次优化,现在我们配置的逻辑是这样的:三个数据源:环境变量、配置文件、配置服务,最终都缓存到内存中,同时监听配置服务h和配置文件的变化,发现变化就拉到内存里缓存起来。应用层只从内存获取配置,配置又分业务配置和系统配置,业务配置为什么要分开呢?因为业务配置的数据结构更复杂,有树状结构和网络结构,但系统配置相对固定,基本都是key-value的结构。
请求路由
路由:可分请求路由和命令行路由。命令行路由有什么用呢?比如服务启动时候,运维 工具 有更多参数,不同参数有不同作用。请求路由,我们在原生路由的基础上做了扩展,原生路由是指定一个回调函数(handler),我们可以把 handler 拆成更多小handler,可以把一个过程拆分成很多小 handler,有的 handler 只负责记录访问日志,有的handler负责加 recover 把做异常恢复,捕获异常后打一条日志,还有可以加权限验证的 handler,每个 handler 一旦失败就会走终止执行 handler 。再结合刚刚介绍的模块化,可以帮助我们更好的组织代码,构建可扩展易维护的项目。
问题定位
日志
问题定位:首先分享一下我对日志的观点,总结下来有这几个日志:访问日志、错误日志、系统错误日志、调试日志。访问日志,可以搜集很多信息,可以参考常见nginx访问日志格式,能有效的帮助我们分析系统运行状态;错误日志,正常的请求响应,没有错误日志,主要目的是快速定位异常请求的问题;系统错误日志,在系统崩溃(panic)recover时会打,方便追溯问题,这属于比较严重的错误,所以独立出来没有和错误日志放在一起。同时所有日志都可以输出到不同的存储引擎里,比如在Kafka、文件、Hdfs。
日志格式
日志格式,首先介绍一个日志组件,有些同学习惯把错误日志分开打印,这样不太容易检索,比较浪费存储空间。我们的做法是当发生错误时,把调用栈拼接起来打印一条日志。所以一个请求会产生一条访问日志,如果出错则只会产生一条错误日志,这条错误日志需要包含能帮助你快速定位错误的信息。另外在处理逻辑中可以多打一些调试日志,提高在开发阶段的调试效率,在线上把调试日志大打印开关关闭。这是我们定的日志格式,通过request_id能够很快过滤出某个请求执行过程中的所有日志。另外我们把所有日志都统一输出到一个文件里,不管错误日志还是访问日志都以相对统一的格式输出,这样能方便我们做日志切分。日志里面需包含几个关键信息:时间、错误级别、来源请求ID、请求ID、日志信息、上下文。通过请求ID可以在服务间做链路追踪。
监控
后面的是监控,前面的日志搞定了,通过切割和分析,最后输出到存储里面,当然就很容易了,方法很多,这里就不展开讲了。
接口设计
接口抽象
最后介绍一下我们的接口设计。接口抽象,将所有操作都能够抽象到一个资源上,如果能把大量操作都能抽象到增删改查上,那这个建模就比较成功了。但是不可避免在某些前端界面上不能按照这个方法抽象,比如说登录等等,所以前端的我们管不了了,希望我们的后端接口是稳定的。这就是Restful的设计思想,1.资源 2. 表现层 3. 状态转换。这里面映射到接口里面,资源对应 URI,表现层对应接口返回内容,状态转换对应我们的更新和删除、获取,操作资源使它的状态变换。
如何确保身份合法
设计接口用不用Restful有什么区别?我们不光要把接口实现,还要控制权限。Restful接口做权限控制就非常简单了,要做权限首先要获取调用接口的调用者身份,身份如何标识?我们参照AWS IAM的设计通过AK来标识。那么如何保证AK合法呢?这就涉及到签名,也是接口设计的一部分。签名通过,下一步才是鉴权。
权限配置
同样参考AWS IAM,通过Policy来设置权限。一个接口能否被访问到,是应用在该调用者身上的很多条Policy综合作用的结果,每个Policy配置包含能访问什么资源,能做什么操作,这个操作是允许还是不允许。把所有规则遍历一遍,就根据这个流程图知道这个资源到底能不能被执行。实现起来非常简单,仅需要几十行代码。剩余工作只要给每个调用者添加Policy,但是前提是接口能通过资源来标识。
以上所述就是小编给大家介绍的《丁靖--go工程效率实践》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:- babel在提升前端效率的实践
- 7FRESH敏捷实战之工程效率实践
- Android 静态代码扫描效率优化与实践
- Vue项目Webpack优化实践,构建效率提高50%
- 效率提升 4 倍,Apache Kylin 在银联的实践
- 从 Storm 到 Flink,有赞五年实时计算效率提升实践
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
RESTful Web Services Cookbook
Subbu Allamaraju / Yahoo Press / 2010-3-11 / USD 39.99
While the REST design philosophy has captured the imagination of web and enterprise developers alike, using this approach to develop real web services is no picnic. This cookbook includes more than 10......一起来看看 《RESTful Web Services Cookbook》 这本书的介绍吧!