内容来源:2018 年 5 月 19 日,G7汇通天下技术合伙人廖强在“PHPCon China 2018 技术峰会”进行《敏捷工程实践与自动化》演讲分享。IT 大咖说(微信id:itdakashuo)作为独家视频合作方,经主办方和讲者审阅授权发布。
阅读字数:5125 | 13分钟阅读
摘要
本次演讲会深入介绍G7在产品研发过程中去除重复性工作的技术规范、细节和背后的思考,以及G7的技术体系如何平衡效率和规范的冲突。
嘉宾演讲视频回放及PPT,请复制链接:http://t.cn/Rg3sWjk,粘贴至浏览器地址栏即可。
业务对技术的要求
之前有人提问“如何提高自己的工资”,其实个人认为这取决于你贡献的价值到底有多大。我09年进入百度的时候,高并发的场景很是流行,当时那些技术较强的人都能够解决关于高并发的问题,会采用各种技术手段进行极致的调优。
我们再回过头来看下,现今的互联网经过不断发展之后,很多基础组件已经变得非常丰富和完善,比如web Server 从Apache到Nginx,缓存也开始出现了,数据库索引的正确使用方式也被大家所熟知等等。慢慢的关于性能瓶颈的问题大家很少再遇到了,这其实是由于我们的系统变得越来越复杂,业务对技术的要求变成了既快又好,“快”指的当然就是工期短,而“好”则是可维护性强,能支持后续的发展。如果能做到这两点,那么你的价值就会得到很大的提升。
要做到又快又好,还有一个核心的原则——所有重复的都应该被自动化。它带来了两个好处,一是标准,不用再写过多的代码,二就是“快”。接下来的内容将会反复提到这一理念,首先让我们从研发的角度来过一遍整个项目的流程。
项目流程
标准API定义
随着微服务化的流行,团队之间的协作变得越发重要。原先的接口定义一般采用的是IDL的方式,不过IDL的问题在于标准是自己定义的,且无法在浏览器上直观的阅读和感受。所以整个流程的第一件事情就是标准API的定义,为此需要用到Swagger,但不必预先就定义好,因为长期来看业务的发展会促使代码变更,而接口却并不会跟随改变,这时接口文档和代码的表现就会不一致。因此为了减少人为因素的干扰,我们需要做到的是100%同步。
当配置好Swagger之后就可以通过一些方式自动的生成接口定义,同时访问服务地址也能获取到接口的标准,还可以进一步的通过页面展示接口的具体内容。不过这种方式需要重复写很多的注释,而且这些注释并没有作用于代码,同时代码和注释也无法保障同步。
正常来说各种框架中都会有路由功能,能够直接定义路由的处理方法和请求方式。有了这一层支持就意味着在Swagger中的注释已经无用,我们只需要分析这里的语法拿到对应的值就够了,这样也可以实现接口的同步。
上图这种方式保证了开发人员在写代码的时候一定会去写注释,而这样的注释才算是真正有意义且能够成为对API的描述。
枚举的可维护性
可以发现上面的API描述,其实是定义了一个枚举,用不同的数字表示不同的状态。这样虽然可以节省字节,但是其他人员在使用的时候却需要查看描述来确定各数字代表的含义。
为了解决这样的问题,我们又新定义了一个enum标签。因为这里所有的代码都可以通过语法树查看到,所以我们这里做了一些注释。同时Go可以通过interface定义如何解析注入的字段,CERT_TYPE__ID_CARD和CERT_TYPE__PASSPORT在注入的时候就会被转成对应的数字。 图中左边就是实际的使用情况,可以看到传递的直接是字段名,这样的方式能让人很好的理解参数的含义。
上图的界面大概的展示了最终的效果。
数据库操作的重复性
当接口定义好之后,到了正式的开发阶段首先要做的肯定是定义数据结构。这个阶段也会出现各种问题,比如要写很多重复的增删查改方法;在定义完数据结构之后还得生成创建对应table的SQL语句,当model发生改变的时候需要重新调整修改SQL,如果有多种环境,一段时间之后对应的数据库可能还会不一致。
我们之前是通过写代码来定义外部的元素,而如果定义一个model让它完成所有的工作,那么就只需要定义正确的数据库的表结构就能完成创建工作。如上图定义了数据库的各种结构细节,通过解析它就能自动的执行对数据库的相关操作,比如检测数据库和表是否存在,没有的话就自动创建;检测表的字段和定义是否一致。这样在启动的时候就能保证model和表结构的一致,需要注意的是这种方式我们只在非线上环境中使用。通过检测env环境变量来获知当前运行环境,从而决定是否启用该功能。
另外针对第一个问题也有了解决方案。如果发现第一个ID为主键就能够自动生成所有的增删查改方法,唯一索引同样也是这种原理,对于普通索引只要符合最左前缀原则其实组合也没有太多。
Client代码的重复性和准确性
正常来说数据库代码写完之后,就轮到了逻辑代码的编写。这个过程中会请求很多的外部服务,开发人员需要写大量的请求代码,同时这些代码还需要经过测试人员的测试。
手动的编写无疑是繁琐的,有了标准API之后其实完全可以通过上图的方法自动生成请求代码。
在微服务中我们经常需要做服务治理,一般的做法是根据Request ID获取到请求链,但是这种方式准确率并非100%,因为有可能请求只监控到一天中的某个阶段的流量。
采用自动化生成之后就意味着能够完全确定所调用的服务,每个服务的依赖层级也能明确的知晓。最后我们就能绘制出上面这样的结构图,其中圆点越大表示依赖越多。
Mock
在测试的时候一旦碰到与环境相关的内容就会变得非常麻烦,那么可不可在不部署任何东西的情况下就能测试到所有的业务逻辑呢?其实在以上的所有部分全部标准化之后,测试的时候不是必须要去请求外部。比如在知道了client拥有的方法之后,只需要定义好interface,然后编写测试代码的时候实现另一个interface注入其中就能测试所有的业务逻辑,并且还可以自由的构造下游想返回的数据。
配置管理
上线时候的配置管理有很多种方案,有中心化管理、通过代码仓库管理,其中还会涉及到数据库密码这样的私密信息,以及各种其他的问题。
我们对此的思路稍微有些不同——配置被直接当成了代码,不存在配置文件。配置文件的问题在于程序的逻辑被外部的因素控制,而且控制细节也不容易被知晓。
配置代码内部首先有一些默认值方便处理,然后会定义一些configTag用来表明该参数可以从外部注入,具体的注入方式通过环境变量完成。程序启动的时候会读取其中的环境变量,然后去匹配配置代码中的参数更改对应的值。
CI Flow
配置管理问题解决之后就轮到了CI Flow。为了应对业务高速发展的场景,我们实现了一种新的Flow,所有的代码都是在master上,每次开发新功能的时候都会新建一个feature,开发完成之后会打上规定格式的tag,然后我们会对应的将程序打包并发布到测试环境,测试没问题之后会再次打上demo的tag,识别到这个tag之后就会再将程序放到demo环境中验证,最后合并到master上。这样在并行开发的时候互相之间就不会产生影响。
未来待做的
原先产品提出需求之后,是先开发完之后再进行测试,这种方式效率并不高。其实开发定义好标准接口以及逻辑处理方法之后,开发部分的内部实现和测试部分的编写是可以并行的。所有我们希望构建这样一种模式,即开发定义好基础框架后,测试能够紧随其后编写代码。虽然这样会模糊测试和开发的边界,但是我们的另外一个理念就是所有人都是开发。
以上为今天的全部分享内容,谢谢大家!
推荐文章