开发经理的职责

  一年前一个偶然的机会参与了公司的一个重点项目,需要长时间出差,开发团队规模在20人左右,而且时间紧迫。在异地,少了公司技术团队的支持,远程沟通不方便,很多事情都显得比较困难,碰到问题往往需要自己摸索,自己解决。有句话说,一个开发团队有的时候就像一台发动机,只要启动起来,就能有成果和产出。但如果方向存在偏差,发动机越跑越远,可能收不住脚,最终会导致项目失控。很庆幸,我们这个项目顺利上线,顺利完成电商大促,以及最后顺利交付。但中间的存在一些风险点以及经验教训还是值得拿出来总结一下。

  通常开发经理或架构师会早于开发人员介入项目,了解项目的需求,系统分析,做相关的技术选型,制定开发计划与开发规范。

  在制定技术规格说明阶段,开发经理或架构师要协调起所有的开发人员,指定相关的技术规范与开发人员保持沟通,让开发人员理解他们负责的模块或者子系统,确保开发人员能够按照架构意图实现各项功能。

 

1 基本编码规范

  这个基本上每个公司都有一份这样的文档(如果没有你基本上可以考虑job-hopping),这个文档一般跟项目无关,比如命名规范,注释规范,SQL规范等等。另外,要统一jdk,包括本地开发环境、服务器环境;定好项目名,包名,数据库名,表名,以及是否每个表需要通用字段(如version乐观锁版本号)等等。

  这里重点强调2个地方,工程规范和包名目录规范。

  工程规范:要明确定义每个工程模块的边界,尤其是在分布式系统中,这一点显得尤为重要,开发人员要对框架的层次结构非常理解,比如什么时候该定义DTO,什么时候该定义Domain。这个如果没有搞清楚,在项目的整个生命周期里面,项目的体积越来越庞大,这个问题一旦暴露出来简直就是灾难。

  包名目录规范:包目录规范要尽可能从顶层开始,开发人员的包目录权限要尽可能低,简单的说就是尽可能让开发人员少去建包,这一点很像日本的外包项目,目的就是统一规范。另外一点,要避免两个工程模块出现相同的包路径,这样在引用的时候极有可能出现冲突。

2 定义好组件的边界和职责

  系统分解之后,要定义好组件(子系统或者模块)的边界和职责,这个是项目初期开发人员最关心的问题,如果这个没有定义清楚,后面系统就要面临重构的危险。

  比如基础数据,并不是所有基础资料、配置信息都放到基础数据中,只有跨系统、跨服务、模块的基础资料、配置信息才属于基础数据管理范畴;另外基础数据服务的接口需要具备一定的通用性,尽可能减少针对某个系统、服务、模块开放特殊接口;并且不允许基础数据服务依赖上层服务。

3 项目版本定义

  项目初期,除了一些基础的模块,比如工具类等公共模块可能被打包成Release版本,其他的一般都是SNAPSHOT版本,当项目陆陆续续上线之后,比如分布式系统中RPC调用要给客户端系统提供jar包引用,版本控制的作用就会凸显出来。这里推荐一批文章《语义化版本2.0.0》,是关于语义化的版本规范,这个规范是由 Gravatars 创办者兼 GitHub 共同创办者 Tom Preston-Werner 所建立。

4 svn代码管理与发布规范

  根据项目的开发模型,定好代码的主干(trunk),分支(branches),基线(tags)的关系,以及相关环境的发布流程规范。

5 自动构建

  在分布式系统开发中,没有自动构建简直不敢想象,往往一个项目有很多个子项目部署在几十台甚至几百台服务器上。如果没有自动构建,开发人员有可能要花费大量的时间在服务打包发布上,一旦需要马上发布一个测试bug,都需要很长一段时间,这会另整个团队精疲力竭。

6 代码日志规范

  项目的日志在生产环境上禁止将日志输出到Console中。项目代码中都需要通过Logger对象来输出日志和异常,禁止使用system.out.println等方法进行输出,禁止使用e.printStackTrace来输出异常。日志需要通过日期、大小两个维度来分割文件,避免日志文件过大无法打开。生产环境的日志级别禁止开启DEBUG,如果排查确实需要则可以针对特定类打开而不能将整个环境设置为DEBUG,可能会造成系统因为文件锁卡死。

7 统一异常处理

  分布式业务处理系统,系统中存在大量的跨服务调用,并且需要对不同类型的异常做不同级别的处理,处理的方式需要随着系统的不断扩展而适应不同类型的异常处理,并且做到跨服务的异常统一定义和快速定位跨服务的异常发生的源头和原因。一般通过定义全系统统一的异常编码,并定义其产生的原因,并需要达到识别系统的目的。

8 提供批量更新方法

  批量更新必须使用提供的统一批量更新方法,对性能有很大的提升,如果有非常特殊的场景需求无法使用则必须要经过评审,否则这有可能成为一个性能瓶颈。

9 尽早提供基础服务

  如短信服务,邮件服务,这些基础服务要优先安排开发,因为几乎每个模块都有可能涉及到。

10 规范多线程写法

  如果系统中有用到多线程,不要随意开辟线程,尽可能使用统一的线程池,并封装公共的调用方法以及返回结果。

11 规范事务的处理

  11.1 避免产生一个较长时间锁定多行数据的事务,尽可能将事务拆解或改为异步。

  11.2 分布式事务

    在项目尽可能不通过数据库层面的分布式事务来实现数据的一致性而是通过异步、补偿、幂等等方式来实现数据的最终一致性。

l  幂等性:

       任何对外提供的服务入口方法都必须实现幂等性,重复使用同样的参数调用同一方法时总能获得同样的结果,而不会造成重复的处理。

       查询服务:查询本身就是幂等的,所以不需要做额外的处理。

       新增服务:通过唯一性约束来控制数据的唯一性避免重复写入数据

       更新服务:通过乐观锁来控制数据的幂等性

       删除服务:删除本身也是幂等的,所以不需要做额外的处理

l  异步消息处理:

       跨系统进行事务处理时尽可能的使用异步消息处理来进行,先将数据保存为一个中间状态,并将消息写入MQ,由下游系统订阅处理。下游系统处理完之后再将结果反馈给上游更新状态。MQ中写入的数据正常情况下不要使用完整数据,会造成MQIO压力很高而且数据可能是已经过期的,并且会将队列变成专用而不是通用,可能无法被其他服务订阅。

l  补偿

MQ处理时可能存在消息丢失、故障等问题丢失数据导致流程中断,所以需要补偿措施来保障流程不会长时间中断。由一个分布式事务的最上游系统提供一个定时补偿措施检测长时间未完成事务的流程,并重新触发这个流程(重新写入MQ)。

12 确定基础数据缓存方案

  缓存往往被忽视,等到项目后期碰到性能瓶颈的时候才发现需要重构,那个时候的代价是很大的,所以需要尽早的定好缓存方案,是用ehcache? guava? redis?这个要确定下来并让开发人员实施下去。

13 规范缓存环境使用

  memcached/redis缓存环境规划,要定义好数据结构使用规范,比如不能让所有人都用key/value这种方式来存储,最终导致缓存环境脏乱。

14 分布式文件存储

  尽早确定是在物理上做共享磁盘?还是使用分布式的文件系统?

15 单元测试

  编写单元测试要成为一种习惯,另外,单元测试应该是没有副作用的。定义良好的单元测试在运行多次的情况下,如果没有其他条件发生变化,那么每一次都应该产生完全相同的结果。比如说,在数据库中通常会插入一些假数据,然后在测试中验证这些数据,这种方式的测试不可靠,因为数据库可能发生变化。可以在单元测试过程中使用内存数据库或者每次单元测试的数据都是自动生产最后自动删除的。

  许多同事怕写单元测试的一个主要原因就是依赖太多(远程服务调用、redis、webservice等),如果一个服务因为种种原因挂掉了,那么这个测试就会失败。要解决这个痛点,可以引入mock对象可以满足这些条件的需求,而Mockito正是这样的一个框架,用Mockito来模拟相关行为,不用费力去准备各种依赖环境,这时只需专注于业务逻辑即可。

 

posted @ 2017-01-24 11:06  Jager  阅读(17390)  评论(9编辑  收藏  举报