发货服务化的工程质量实践

概述###

要构建高质量软件工程,需要构建两个关键步骤:(1)持续自动化的开发与部署流程,能够将开发、测试、部署、验收等重要环节流畅地串联起来,并增强自动化和可靠性;(2)持续增强每个环节的质量。 推荐阅读: 《持续交付:发布可靠软件的系统方法》

为方便起见,发货服务化工程简称为D,项目结构遵循标准 Java Maven 结构。

D
   src/main/java
   src/main/resources
   src/test/java
   src/test/resources
   pom.xml    

持续集成####

代码开发(包括添加相应单测) -> 提交前自动运行单测,单测不通过不能提交 -> 单测全部通过【覆盖率达标】 -> Push到代码库(触发持续集成环境单测全部通过) -> 代码Reivew通过 -> 成功构建和部署到QA环境 -> 自动触发QA验收测试(接口测试与业务场景测试) -> QA验收测试全部通过 -> 成功构建和部署到预发环境 -> 预发验收测试通过 -> 发布到正式生产环境 -> 生产环境验收通过。

代码开发###

代码开发应该注意两个基本点:(1) 可复用性; (2) 将潜在变化与不变分离出来,适当使用设计模式。

代码开发可适当考虑 TDD 开发。 对于提交结果而言, 新增方法一定要添加相应单测。 最好能有自动检测是否有相应单测增加工具。

单元测试###

更好地编写单测####

参考: 深入探究单测编写

提交前自动运行单测####

开发同学常常忘了运行单测, 其实只要一个小技巧即可:在 ~/.bash_profile 文件末尾增加一行 alias gp="mvn test && git push origin" 。 当运行 gp 分支名时, 会自动运行单测, 单测不通过是不能提交的。

单测覆盖率####

pom.xml 里配置单测覆盖率maven插件, 然后在项目根目录下运行
mvn clean && mvn cobertura:cobertura && cat /target/site/cobertura/coverage.xml | grep "<coverage"

<plugin>
    <groupId>org.codehaus.mojo</groupId>
    <artifactId>cobertura-maven-plugin</artifactId>
    <version>2.7</version>
    <configuration>
        <formats>
            <format>xml</format>
        </formats>
    </configuration>
    <executions>
        <execution>
            <phase>package</phase>
            <goals>
                <goal>cobertura</goal>
            </goals>
        </execution>
    </executions>
</plugin>

这是发货服务化工程的单测覆盖率结果:

即使核心代码单测覆盖率达到100%,从整体上也难以达到指定指标, 这主要是由于一些“无需测试的方法比如getter/setter”的存在,以及一些 utils , 缺乏单测的复杂业务方法的存在。如何达到指定单测率而且保证质量呢? 首先建立若干准则:
(1) 核心主流程经过的所有方法的行覆盖率应该达到 100%;
(2) 非核心主流程的方法,只要程序会运行到且有一定逻辑,行覆盖率 90%;
(3) Utils 的未用到的方法,要么删除,要么加上单测;
(4) 无需测试的方法, 可以生成单测允许通过,但不必过多考虑。

查看单测覆盖率报告,看看是哪些地方覆盖率低,重要性如何。 在重要性相对较高、覆盖率低的地方添加单测。

代码Review###

在持续集成环境中, 当代码 push 到代码库后,应当自动触发持续集成环境的单元测试(防止开发同学本地忘了运行单测), 单测不通过将在 CodeReview 里增加一个明显的错误标识。一般单测不通过是不允许进入人工代码 Review 的。

在代码库工具平台上创建代码Review , 并将链接发送到相关Review同学。根据代码Review 意见改善代码后提交,直到单测全部通过、代码Review全部通过后进入下一步。

CodeReview实践与总结

QA验收测试###

构建和部署到QA测试环境####

理想情况下, 当 git push 到代码库后,应当自动触发发布系统将指定分支的代码部署到QA环境,然后运行接口测试用例。

验收测试####

验收测试主要是针对业务场景的测试。可以包括单接口测试和组合接口测试。由于发货是单个接口完成,因此,可以只验收单接口测试。单接口测试必须覆盖各种场景的发货。比如普通单商品发货、普通多商品发货、送礼发货、分销发货、批量发货、有退款维权的发货、非合法发货返回的错误码。

运行接口测试用例####

发货服务化的接口测试用例放在工程 service-test 的 DeliveryServiceDubboTest 和 DeliveryServiceHttpTest 测试类里。 方便起见, 最好将 service-test 放置在与 java 工程同级目录下,即 work/java/D, work/java/service-test。先将环境切换到 QA host ; 然后在 service-test 项目根目录下, 运行 mvn test -Dtest=DeliveryService*Test 即可。

预发布及验收测试###

在发布系统选择预发布, 部署工程到预发布环境; 然后在测试店铺验证普通发货、送礼发货、分销发货、批量发货均正常。

正式发布及验收测试###

在发布系统选择正式发布, 部署工程到正式环境; 然后登录测试店铺验证普通发货、送礼发货、分销发货、批量发货均正常。

一个简单的集成脚本###

在推送代码到代码库时使用: ./push.sh 分支名

#!/bin/bash
#-----------------------------------------
# add line below to ~/.bash_profile
# alias gp="mvn test && git push origin "
#------------------------------------------
alias gp="mvn clean test && git push origin "
shopt -s expand_aliases
if [ x$1 != x ]
then
    echo "git push origin $1"
    gp $1
else
    gp
fi
echo "code review passed ?"
read answer
if [ "$answer"x != "y"x -a "$answer"x != "Y"x ]
then
    echo "code review not pass, please fix and recommit"
    exit 1
fi
echo "deploy to qa ?"
read answer
if [ "$answer"x != "y"x -a "$answer" != "Y"x ]
then
    echo "not deploy to qa not pass, please fix and recommit"
    exit 1
fi
# set qa enviroment
echo "switch qa environment."
sudo python ./tools/switchhost.py qa
echo "run interface tests."
cd ../service-test
mvn test -Dtest=DeliveryService*Test

使用 SwitchHost 工具时自动切换 HOST 的脚本

#!/usr/bin/python
#_*_encoding:utf-8_*_
#------------------------------------------------------
# switchhost.py  automatically switch hosts managed by SwitchHosts tools
# usages: sudo python switchhost.py [qa] [q] [local] [l] [pre] [p] [online] [o] [c]
#------------------------------------------------------

import json
import os
import sys
homedir = os.environ['HOME']
hostcodemap = {"local": "My Hosts", "l": "My Hosts", "qa": "qa", "q": "qa", "online":"online", "o": "online", "pre":"pre", "p": "pre", "c": "camen-dev"}

def switch(env):
    with open(homedir+'/.Switchhosts/data.json') as data_file:
        hostobj = json.load(data_file)
        hostmap = {}
        for host in hostobj['list']:
            hostmap[host['title']] = host['content']
        content = hostmap[env]
        with open('/etc/hosts', 'w') as hostfile:
            hostfile.write(content)
            hostfile.close()

def getEnv():
    if len(sys.argv) == 1:
        return "online"
    if len(sys.argv) > 1:
        env = sys.argv[1]
        env = hostcodemap[env]
        if env is None:
            env = "online"
    return env
if __name__ == '__main__':
    switch(getEnv())

发货服务化的分流心得###

严格代码质量####

使用设计模式确保代码组织的可扩展性, 并严格进行代码 Review 。

  1. 使用模板方法模式,确保通用的发货流程模板,以及不同的发货流程之间的解耦(普通发货、送礼发货、分销发货、批量发货);
  2. 确保业务方法只做一件事;

双重护航测试####

QA 提测之前,编写发货工程的单测及服务接口的可重复自动化测试用例(检测到数据库字段级别)尽可能覆盖到业务流程、方法及工具类,并确保全部通过。双重护航保证每一行代码改动都能快速回归有效;开发必须对自己的代码质量负最主要责任;

发布谨慎平滑####

分流步骤严格循序渐进,按“除TOP20外 0.001, 0.005, 0.01, 0.05, 0.1, 0.3, 0.5, 0.7, 1.0, TOP20加入”平滑进行,切忌贪急求快。每一次发布要谨慎:

  1. 登录服务器查看启动日志service.log , 确保发布后是服务正常启动的;
  2. 到测试店铺进行验证发货是否正常;
  3. 查看发货日志是否正常;查看监控是否正常。

变更回归测试####

  1. 每一行代码改动,都必须运行单测及接口自动测试用例,保证全部通过;
  2. 每个BUG修复,都要增加相应的单测和接口测试用例覆盖;
  3. 分流接口也要增加接口测试用例,并同步运行测试用例;

全天覆盖监控####

打开Grafana服务器监控、日志平台与服务接口监控平台,全天候不定时刷新日志和监控曲线:

  1. 关注CPU/内存/IO/网络流量是否正常及报警;
  2. 评估和解决发货日志其中出现的错误和警告;
  3. 关注发货接口的调用次数和失败次数。

日志分级统计####

日志分级,便于排查真正的系统问题。

  1. 非系统级别,比如参数不合法等,使用警告级别,不必加入错误统计中;
  2. 系统错误,必须使用错误级别,加入错误统计;

局部升级####

若在分流中需要模块升级、字段升级、配置改动等,尽量保持小的改动(否则要进行全量回归测试);协调好发布顺序和时机;控制好服务工程与外部依赖的边界,保持边界兼容即可;

不放过测试用例的失败####

当服务接口的可重复自动化测试用例运行失败,要仔细排查问题的原因所在。在common-model升级后,发现发货测试用例运行失败,经仔细排查后,发现有两个代码上的小问题,存在潜在风险,并及时修复了这两个问题。

不放过Error日志和Warn日志####

Error 日志通常是系统有潜在问题的表现, 比如排查一个发货没有插入发货数据库表的问题,就发现商品传空的时候就有问题,甚至发现之前的事务引用修复分支并没有合并到master 分支发布(发布遗漏); Warn 日志通常是用户使用不当导致的, 就需要跟商家进行沟通,停掉这种无用的浪费资源的行为。

posted @ 2017-02-05 14:37  琴水玉  阅读(396)  评论(0编辑  收藏  举报