作者归档:罗小东

关于罗小东

其实,这个是黑色心态!

【需求】我是怎么带几个学生从零开始做一个研发中台的

接触中台的概念是在18年,互联网还在流行DevOps,微服务,资料异常多,但是却又是异常的敷衍,几乎没有一套完整的可落地的开源框架,大多就是一个权限系统、一套微服务脚手架,或者说一套似乎有完善落地的Docker或者K8s,但是能不能真正落地起来,到底怎么落地,不清楚,也不知道,符合不符合场景,查找到的资料都是说好,但是能不能落地,心里很是没底。

考虑左右,从零搭建了一套开源的研发中台,带着几个大学生,利用业余时间,用了大概1年多的时间,从过程的团队建设,试点尝试,过程分工,到形成一套中台项目管理流程,整个最终可以勉强实现下来,也是感慨万千,唏嘘不已。

整个研发中台架构 (http://cloud.linesno.com)

为什么要做这样的中台,还要从零开始

1、需要知识积累和沉淀,不断的反思,不断的吸收,不断成长

有一定工作的经验的开发肯定知道,企业开发中包含有大量的、重复的CURD(增删查改),以及重复性组件,还有不断的获取和重复使用的学习资料等等,这些重复性的东西,每次使用都有新的感悟和感受,如果没有积累,每次都可能从零开始,无法达到沉淀和积累,从而浪费大量的时间和精力在上面,如温水煮青蛙,久而不知,而自茧作缚。

而相对的另一个例子,滴水穿石,之所以可以穿石,就是因为不断在前面的积累基础上,加上不断的坚持,如果每次从零开始滴,也许也可以穿石,但是消耗的成本和时间,也是很巨大的。学习也是如此,加上不断的积累,在一项技能上不断的学习积累,日积月累,不管有没有达到”穿石“,但是一定有会不小的进步。

2、不想把时间浪费在无用的点上,更想专注,不用站在巨人的肩膀,自己就有肩膀

有一种慢慢的体会和感悟,叫“ 欲速则不达 ”, 每个事情都有消耗时间、精力、过程,而人本身就是一个有限的特种,怎么样可以在有限的时间里面,更好的完成和学习,需要的不是其它,而是【专注】。所谓的专注并不是要时时刻刻的去想,去学习,而是坚持长久的做这个事情。

类似的如常见的1万小时成专家说法。 中台搭建的目的之一,就是节省用时间,去掉重复无用的东西,在有限的时间里面,更好的专注在新的点上,不断的前进,不断的提升自我。

3、落地不是一个人的事情,是整个团队的事情,要学会带团队一起成长

一套中台,需要涵盖的内容很全面,每个模块,领域都需要有对应的人员,这个时候,不是一个人能完成,而是需要调用团队的力量去实现。 就像打球,即使说有一个人球技出神入化,但是他一样也需要传球的人,助攻的人。想要落地这个平台,就需要有团队参与,发挥团队优势,提升整个团队气氛,团队合作,合理分工等,这些都需要学习和实践。

最后获利的不是某一个人,也不是具体谁谁,而是团队的每一个成员,在这个过程中能发挥、创造、体现出他们自己的价值,团队成员的见识、协作、信心等的成长。不管这个过程是艰辛也好,埋怨也罢,你走你才知道,而不是听谁说你才知道,这就是你自己的经验,自己的价值。

带人的过程是要学会发挥自己的能力的同时,更要学会带团队一起成长。

4、需要一套完善的研发中台打磨,站在前面的基础上提升

在出来一套可见东西之后,可能比想像的还要“千疮百孔”,还要不堪,甚至连往回看的勇气都没有。但是正是这样的东西,摆出来,拿出来,虚心接受所有的问题和建议,然后在后期一步步消化,一步步打磨,而形成出来的,就是自己的产品,或者说,把自己打磨成一套精品。

技术总会在不断的发展,需要有一个产品,一个中台可以让你,或者我在更好的中台上面做预研,而出来的成果,也会比从零起个“Hello World”强得多,毕竟你已经在场景里面,已经在平台里面,安全,测试,规范,可用性,健壮性等等都有中台的验证和支撑。

开发接入研发中台的HelloWorld说明

没有实战基础的学生,是否能组成团队

1、为什么要选择学生团队,不找有工作经验或者社会经验的人

找学生,这也是B方案,前面的A方案,最初规划,团队组件的是找的是同学和有工作经验,还找了以前老师作为指导,沟通好,拉了10几个人成团,便开始。但是这个过程经过不到2个月就发现问题,异地沟通困难,团队鸡血也很快消磨,有转成观望态度,原因不免是 工作忙,时间精力不足,整体的技术预研也几乎停滞不前,原计划的分工,很快无法往下,研发任务开始体现出敷衍趋势,成立的微信群很快形成死群。开始信息发出的时候,还有几个人附和,到后面,可能连附和都没有。

很快,就有提出要退出研发计划,同时各种小群也开始建立,这些都意味着团队有危机的趋势,不管是信任和人心开始出现浮动,放置观察了1个多月,考虑同学,朋友,还有后面的各个因素,无奈放弃此方案,成员停止研发沟通联系,慢慢的转向分享文章,咨询经验等方式,进而消停。

由于之前在校时有一定的学生接触、组织经验,B方案实施也是很快落地,不到2个星期的时间,也成立了10几个人的群。学生没有实战经验,不管是沟通还有动手,都与实际工作人员有天跟地的区别,而能不能落地,却成为一个最担心的事情。不仅仅如此,学生成团的现象,抱团等情况,过于依赖于师生级别关系(比如较愿意听老师的话),这些都是问题,不过既然A方案放弃,就得全力保障B方案的实施,而且这步的操作过程,更需要严格,可控。

2、怎么进行团队的培养、磨合,最终成为可作战的兵团

学生团队开始份两个组别进行,互相互相隔离的分开,原考虑是分开培养,然后两边达到可控,可对比,以小组作为培养线。一开始活跃气氛远高过之前的团队,也是理解,毕竟学生很有学习的积极欲望。开始的时候,流程按 【培训_学习_考试_分级_练习_评审_重构】 这几个流程,按这几个流程下来,这个过程很快,几乎一个月内就可以完成。

当时整理的等级技能图

过于顺利的流程让我感觉,应该问题不大了,而且很快就可以进入提升学习阶段。考虑左右,虽然考虑可能有难度,由于之前的表现,还是比较有信心,毕竟流程,团队制度有建立雏形。决定开始接入新项目,进行实战练兵。

很显然,我的判断是错的,而且过于过早的接入实战,实战性的要求,沟通,压力等,让组长无法承受压力,而且之前做了一个最大的失误,考虑组长退了,副组长可以顶上,也就是常见的AB岗形式,结果却是组长退了,下面的学生人心异动,而且又是核心成员,很快引起其它成员信任危机,深度做安抚工作,而且减少对应的任务安排,但是最终还是造成内部沟通的不利,需要在任务上进行妥协,而这样的妥协却极度不利的中台的研发和后期的任务安排。这个近乎两个月的组团培养,组团受挫,这不得不让我思考之前的一些策略与方式。

明显,资源开始主要集中在第二组,这个时候,已经开始感觉到,组团的风险和管理上的缺失,不过幸运的是,前面两组留下了很多资料和资源,留下的这组人与上一组留下的成员,从素质上确实也比较高,很快完成了新的组团,而且意识较为统一,在沟通明显都成一气。

也发现,找人组团,不管是人员素质,意志力还有自控力等都有一定的要求,是否看个人有成长的追求等,能克服困难,能坚持。在进行多次任务安排和沟通之后,最终确定留下人员也就只有几个学生。 由原来的差不多30人, 差不多半年的时间里面,经历两轮的练兵和实战,后面一年的时间里面,也有进进出出的,但是最终来说,还是这几个人为核心。

3、怎么消掉学生气,形成接近社会的工作状态

学生团队与实战要是能对接得上,最大的问题是怎么样调整状态,即执行力,沟通能力,协助能力等,合理安排好自己的学习和工作时间,不影响学习,然后又能参与到项目建设中来,学会怎么样安排,怎么样协助,遇到困难的时候怎么面对,使达到任务目标。

而想到的,最好的练习方式,也是要接一个实战,所以,校内跑腿项目就建立了。

校园跑腿项目后台(测试界面)

校内跑腿是考虑到的一个比较接近于实际,涉及到各个方面的能力的项目。从开始的开始的项目立项,然后到团队执行,再到人员计划安排都走过一次,比如计划方案怎么写,中间沟通问题怎么考虑,遇到一些问题,怎么处理。如与社团合作的时候,达不到效果,然后就找会长一起沟通,然后面对问题,而不是说,逃避问题。开始常常是一个问题一遇到就想着可能会失败,“哎,可能就这样了”,这个时候就引导找到问题点,解决问题点。也有说以为安排就需要马上完成的,就告诉他们要有计划,然后哪天完成哪些,一步步执行,不要急于这一天等。

他们有思路,听取他们的意见,尊重他们的意见,有一些想法就多鼓励去做,在执行过程中,也配合他们一起处理,让他们有意识的知道,原来这个是错的,毕竟新人,带有一些学生气,有冲劲。如果执行过程有困难,就多鼓励,如果有错误,就引导并适当批评,有成果,就奖励等。

在两个多月的时间里面,跑腿人员大概有70多人,公众号关注人1000多人,然后订单2000多单,整理了从申请,接单,跑腿,扩展等流程。团队也开始慢慢会了讨论,沟通,使用一些常用的工具,体会互相理解,互相宽容,执行力和作事思维也慢慢了有了提升,了解到生产中实际项目,包括工程代码的研发过程过程,比如怎么对接微信,登陆,支付等(跑腿项目是自研的),至少达到这一步,也是多少有些欣慰的。

关于公众号,原本可以让他自运营的,毕竟流程制度都在,对跑腿人员也有利益,却因为公司的问题,暂时停止了项目,也是较为可惜。

平台研发过程中怎么安排学生团队,学生团队可以做什么

1、过程成员缺少很多东西,怎么安排任务

最主要的是怎么安排研发任务,然后又不能一下给太多任务,还要有一定的成就感,这个倒是为难到我。整体的研发平台涉及到的面太多,从文档,服务器,运行,执行集成,项目代码等都有一整套,这些都要落地,让他们实现任务的同时,能有自己的成果。

前面的整体架构,包括规划这层,在前面团队组建的时候,我这就把整个蓝图做了规划(组团的时间远超过之前的估计,原计划是3个月,最后想不到用了大半年),这也是特别麻烦的一点,无法让他们有参与感,考虑左右,就只能从最简单的开始。

规划的第一版本整体研发中台架构

开始从jenkins 的使用,然后到Git导入批量工程,怎么团队协助,合并代码,怎么帮别人运行中台,从最简单的操作开始,然后怎么本地部署,比如Zookeeper,redis使用场景,工程怎么打包,服务器怎么查看异常,怎么查看日志等,都从最简单的做起,一点点的完成带入感,慢慢体会到平台的过度。在有些了解之后,开始学会使用代码生成器,生成服务工程 ,生成一些组件,生成CURD,做一个学生管理系统。比如日志组件,通知组件,这些都可以让他们使用代码生成器一步完成初版,后期的我再在上面进行优化处理。

规划的第一版本整体学习计划

目标就是让有了解之后,有自己的想法,可以实现自己的想法,从而提升自己,增加自己的知识面,这也是引发出其它的问题,就是知识深入的问题。毕竟面的代价就是深度,而这个,当前做的最好的办法就是,鼓励认真学习大学老师的课程并说明重要性(我们大学都是以学习基础为主),然后计划的时间拉长,比如完成一个Git工程导入的任务可能要1周。

2、消极,缺乏自信,缺乏学习主动性,怎么办

平台研发过程中,最为难的事情就是接触新的事务,常常接触到的回应是 ”我不知道“ ,”我没接触过“ 等,或者说,一接触到新的东西,就莫名的害怕,还有一种比较常见的情况就是,任务往往都是最后一天才有去执行,其实这个问题倒不大,但是却有各种理由来表达 如 “学习任务太多” 或者说 “正在看” 。开始就发现问题,然后就往下问,却不是这么回事。主要还是缺少学习的主动性,然后在追问的时候,又知道这样不对,内心存在愧疚,然后又有点畏惧罢了,这也许是学生团队的可爱之处。

这种情况还是比较常见的,无可否认,即使在实际工作中,这种也是屡见不鲜的。没有接触过就先让他们百度,网上参考,找解决方案,多问人,不要怕问,也不要害怕说问多别人会生气,更不要怕百度拿别人的代码(这是与应试教育不同的地方之一了)。软件以实现功能为主,也鼓励创新,但是更多的是,解决问题为主,做事要有始有终,有成果。针对于另一种情况,却是批评为主,或者说有时候激动,就真要多说几句。并不是不允许最后一天执行,但是最主要的是不能敷衍,对就是对,错就是错,要敢于面对问题,敢于表达自己的问题,也不要怕被别人看见,怕被别人知道,学会面对,然这样才有发现问题,解决问题,避免自欺欺人。

过程以慢慢的培养主动性,自主学习能力,自我提升为主,同时,也要树立正确的三观,工作观,培养的做事的魄力。

3、团队过程最为合理的,感觉还是激起人善意和潜力

其实这个过程中,最害怕也是最难做的,就是怎么带团队的问题,结果不是怕不出成果,而是误导。带的过程,并不是说不敢给压力,更不是说不敢给努力,而是在有限的能力里面,让他们最大的发挥自己,然后创造他们自己的价值,发挥他们自己的价值。其实这个并不是自己的觉悟,而是在自己莫大的团队管理困扰之后,在读到一篇微信文件,介绍“彼得 德鲁克”的时候,一个自己的真实体会。

现实中,不仅仅是这几个学生,在公司内部也一样涉及到一些管理性的工作,带队工作,这个比起学生团队,更加残酷得多,也现实得多。团队成员并不是我们的上下级,而是大家的一种互助,一种共赢。自己在培养别人的同时,别人也在培养你,提高自己更高的标准,锤炼自己的人格,同时超过我们自己的局限,做出自己可能自己都没有想到的事情。提升他们,帮助他们,感恩他们,同时他们会感恩你,跟随你,展现自己的人格魅力,而其它的管理工具,还有书籍,更多的感觉是一种辅助。

带团队过程中,本身也要学会反省,检讨自己,必须要清楚自己在做什么。反之,正如一部电视剧里面说的,孝庄对康熙说的:“鳌拜可能不是自己要反的,而是你把它逼反的”(大意如此,原话不记得了)。而相反的,激起他内心的善意,学会感恩,这样,也许会更好的让他发展自己,即使不在这个团队,在另一个团队,公司也能有这样的心态,久而久之,也肯定会赢得别人的认可 ,创建自己应该有成就。

我们要做成怎么样的目标

1、要形成大平台作为后盾,小兵团进行作战的战略

平台建设从开始的团队组建、培养、到一起作战,项目从开始的架构设计,技术选型,第一行代码,到第一个访问链接,都包含着一层层的努力和坚持。这个过程大概过了1年多,从计划到出来第一版本,基本上能达到“大平台,小兵团“作战的目标。

中台的形象示例

当然,目前的版本还是千疮百孔,有些地方甚至可能不堪,或者不完善,这些都是开始,一个初型。需要不断的实践,团队不断的去上面去锤炼它,让他成长,然后添加自己的想法,表达自己,它就像一个土壤,为下一步的完善的过程提升了基础。

为团队的下一步,后来,甚至未来的蓝图规划, 带来了一种可能,而不再是纸面上理论,更不再是网上这里缺少一块,那里缺少一块,不可落地的东西,而是一个完善的架构,一个完整的研发中台,希望可以为项目管理,开发,管理带来战略上的共赢。

2、持续迭代,不断更快更好的升级和优化

在后续不断的学习中,会不断的去学习,实践自己的想法,整合更好的资源,技术,吸收更稳定的,可用的东西,不断的提升研发中台的能力。

这是一个很长的过程,可能是一年,两年,甚至是五年十年,学习不止,步伐不停,积跬步以至千里,积小流以成江河。打造研发中台的过程其实出来这个并不是中台的精品,而是这个精品是你自己,磨炼的不是所谓的中台,而是你自己。

最后

以上就是带学生团队1年多来走过的历程,从零实现,走过的路程。不算长,也不算短,只是一个过程。

企业统一研发中台实战视频教程

设计

中台研发设计

  • 研发中台架构整体概括和演示
  • 大中台小前台架构模式的适用场景
  • 企业统一研发中台设计基本原则
  • 企业项目管理整体架构设计
  • 中台整体规划和技术
  • 中台技术选型和基础准备

研发

当前只是基础版

  • 基线规划和组织规划
  • 中台研发基础环境搭建
  • 实现基础工程包
  • 实现代码生成器
  • 实现基础服务工程初版
  • 实现基础配置平台
  • 实现基础网关平台
  • 实现平台基础对外门户
  • 测试基础接口实现
  • 测试基础UI实现
  • 运维基础框架
  • 运维基础监控框架
  • 第一个HelloWorld项目

落地

企业落地

  • 第一个项目组接入
  • 多个项目组接入研发平台
  • 项目组沟通计划落地

迭代

后续迭代

  • 业务中台抽取搭建
  • 企业平台迭代和思路

【转载】我的一年中台实战录

令人唏嘘的一年

故事的开端

2018 年 3 月份,我正式成为一名中台产品经理,在这之前的一两个月,我已经和 Sunner 就中台的发展有了多次沟通。我们要做一个在线教育领域的中台产品——爱多思(EduOS),顾名思义,就是一个在线教育的操作系统。线下教育的教学工具有桌椅板凳,黑板、粉笔、投影仪等教学设备,组合成物理世界的课堂,爱多思的目标是构建出线上教育里的桌椅板凳,让其能够自由组合成一整个在线教学管理系统(LMS),并形成标准。

这是一个有挑战的活儿:

首先,当时中台在互联网公司是个新概念,如何在互联网公司里做一个中台,业界并没有太多的成熟经验可以参考;

其次,各条业务线里烟囱式的教学系统已经分开跑了很久了,在这个基础上搭建中台,就好像在给飞行中的飞机换引擎(当然,并不是每条业务跑得同样快,这也是中台能够在各个业务产品间周旋的基础)。

17 年阿里出版的那本书「阿里中台战略」是我们当时唯一找到的理论基础,阿里大中台几年的实践,以及 17 年我们通过一支几个人的别动队在内部对可行性的探索,最终让我们在申请立项时说明这事可以做成。

中台项目正式立项,我成为立项后第一个产品经理,Sunner 是负责人和产品架构师。我们计划用两年时间把中台搭建好,让爱多思能够支撑各条业务线的发展,并且能快速孵化出新的业务。然而一年过后,2019 年 2 月底,因为公司战略和组织架构调整,中台项目被停止了。

我依然清晰的记得那天,大家在会议室里讨论已经在线上跑的中台服务未来何去何从,想想在云端本地无数的代码库中有一套打着那天的 tag,然后就没有再更新过,让人唏嘘不已。

这一年的收获

回顾这一年,我们做成了这几件事儿:

  1. 完成了多个教学服务的中台化改造。其中包括一些底层的基础服务,如账号、权限、点播、直播等;也包括一些具备教学逻辑的模块,如直播课、题库、问卷等等。每个服务都可以单独拿出来做成可直接给终端用户使用的产品,类似于 CCtalk、问卷星,并且这些服务和模块都已经支持各业务产品了。
  2. 总结出来中台化产品设计、产品研发、项目管理的一些标准规范和套路。依照这些标准和套路,没有中台经验的产品和技术人员也可以快速的开发出中台服务,并被业务产品接入使用。

当然我们也还有一些没做完的事儿,包括:

  1. 中台教学系统的闭环。我们做了一些独立的教学模块,但还没能够用中台化的标准把这些教学模块完全组合起来,形成一个可以系统化学习的课程。
  2. 中台价值的量化体系。只有做好了价值量化这一点,我们才算完成了中台在商业世界里的实践,并且经验可以被推广到公司内外。
  3. 中台商业化的探索。我个人一直希望能够把中台做成一个可商业化的企业服务,不仅仅只是一个内部支持型的产品。中台项目停止后,我也依然在教育 ToB 行业。时常在想:如果有了成熟的中台能够对现在的问题有什么帮助?现在看起来,在国内目前的商业环境下,一个好的中台,其对内服务产生的价值还是远远高于对外直接输出的价值,庆幸当年 Sunner 压制住了我想快速商业化的诉求。

假如我们能有两年时间,不知道能否达成最初的目标,也不知道未来是否还有机会继续?但我几乎肯定的是:中台会在接下来互联网和传统产业深度结合时,变得越来越重要。名字除了叫中台外,还可能会被称为平台中间件、共享服务等等。

此外对于我个人而言,应该说我这一年的收获良多:

  1. 进入互联网行业后,小步快跑成了常态。而在中台的这段时间,我难得能够暂时放开业务的压力,按照近乎理想化的标准去进行产品架构设计、做抽象、画 UML、花时间仔细思考。我本不是这样的人,这也算是在刻意练习了。
  2. 作为一个在线教育行业的新人,通过中台我能有机会参与整个事业部涉及的所有教育业务,包括 K12、成人、ToC、ToB,让我对在线教育行业有了一个更全面的认识,从中寻找兴趣所在。
  3. 结识了一帮优秀而有趣的小伙伴,大家一起做有挑战的事情,也才有了这篇文章里的回忆。

都在谈中台,究竟什么是中台?

中台的概念

中台是近年来 IT 行业非常火的概念(这里最好加上一个”IT 行业“的限定,因为曾经有商务同事以为我是研究两岸关系的,哈哈),有很多的文章从产品、技术、组织等多个角度来解释什么是中台。对于一个快速变化的新概念而言,很难有标准定义,阿里中台某高管都提到过现在阿里所做到的离他所定义的中台还有一段距离。

给定义是非常谨慎的事情,但很多时候不给定义又没办法继续聊,所以我也曾经在一个内部分享上给中台做了定义「服务于某个垂直领域的工具平台」。在做这个定义时,我是参考了云计算的概念的。云计算是一种通用服务,那么中台和云计算有什么差别呢?按照 IaaS/PaaS/SaaS 来划分,服务的领域越来越垂直。参考这种方式,我会定义中台在 PaaS 和 SaaS 之间,主要原因如下:

  1. PaaS 提供了一种服务,客户的程序员通过二次开发使用 PaaS 服务,最终完成某个功能给最终用户。PaaS 的通用性需要非常强,这样才能获得足够大的市场,比如 IM、视频云服务等,也因此 PaaS 往往是没有界面的。
  2. SaaS 提供的服务不需要客户进行开发,只需要开通服务,在管理后台上配置一下就可以直接使用。但 SaaS 服务往往针对的是一个细分领域,其定制化能力也相对弱很多。即使是像钉钉,钉钉选择 IM 这种企业中最通用化的服务,同时做成企业服务的开放生态,目标客户也主要是覆盖中小企业。定制化需求强的大客户,也往往会需要希望借助 IM PaaS 服务来自主研发内部 IM 工具。
  3. PaaS 和 SaaS 定位在服务外部客户,必须具备很低的使用成本。即使是需要通过技术来接入的 PaaS 服务,接入成本也一定要足够低,接口清晰,文档完善。
  4. 中台首先是定位在服务公司内部客户,由于这个范围的限定,导致中台的通用性可以在很多约束条件下来实现,可服务的领域比 SaaS 广。比如即使同样是电商,淘宝、天猫、聚划算、闲鱼、飞猪的站点都是不一样的,而阿里共享事业部就在中台层服务多个业务。此外,由于中台的用户是公司内部的程序员,大家有相似的背景,也可以频繁沟通,所以服务接入的成本可以做得比面向外部客户的 PaaS 要高。

中台 vs 第一性原理

很多资料在介绍中台概念时都会引用这样两个例子:

  1. 美军的特种部队加航空母舰的策略:10 人以内的一支特种部队在战斗的最前沿侦查,独立决策,一旦发现目标,迅速呼叫强大的中台航母群对其进行毁灭性打击。
  2. 芬兰著名的游戏公司 SuperCell,开发了部落冲突、皇室战争等现象级的手游。整个公司才 200 多号人,就被腾讯以 86 亿美金收购。在 SuperCell 里,一个游戏开发团队平均是 3-7 个人,但有一个强大的游戏中台在做支撑,可以并行开发 50 款游戏,然后通过“内部赛马”产生出一到两款经典。据说马云在带领阿里众多高官参观了 SuperCell 后,回来就在阿里整个集团层面启动了大中台战略。

同时我想要对比的是另一个概念「第一性原理」。第一性原理也是近几年很火的一个词,基本已经成为完成颠覆式创新的大杀器。最典型的例子之一就是 Elon Musk 了。这个同时掌管 Solar、SpaceX 和 Tesla 的硅谷钢铁侠,从最基础的物理学原理出发,重新设计和制造的猎鹰火箭,正带领着人类飞向火星。

在上述例子中,第一性原理和中台都带来了创新和成功,但其实这两者在某种程度上是矛盾的。第一性原理往往是打破原有的经验,跳出舒适圈,从最底层逻辑去进行思考。而中台是将通用的能力进行抽象和共享,将成功的经验固化下来,将有限的人力投入到创新中去。

第一性原理是物理世界运转的本质,在没有时间条件的约束下,可以推导出整个世界。假如地球要灭亡了,只有一张纸上的信息能够保留下来,写在这张纸上的就是地球文明的第一性原理。基于这些可以重塑地球文明,但可能需要几千万年。

但人类社会的运转往往是有明确时间约束的,如果我只知道 1+1=2 时就要完成微积分,可能要穷尽一生。因此,依靠前人和自己的经验做事才是人类社会能够高效运转的基本要素,放在 IT 行业,这些经验就叫中台。经验往往能带来效率提升、成本提升、质量提高,同时也能带来偏见、惯性思维模式,中台也一样。

我们为什么要做中台?

随着「阿里中台服务」那本书在 17 年的出版,中台开始走进更多人的视野,并且在 18 年逐渐热门起来。但那时网上介绍中台的文章和分享还不多,记得我在准备公司内中台分享时,没有花多大功夫就看完了几乎所有相关内容。

而到了 2019 年,中台的热度迅速攀升,火爆程度有点类似 16 年的 VR、18 年的区块链。同时我也听说有创业公司连核心业务的商业模式还没摸清楚,上来就要搞中台。这其实是没搞清楚为什么要建中台、中台要解决什么问题:

首先,中台是支撑公司多个业务产品的共享服务,如果你的公司只有一个业务产品,能做的最多只能是良好的架构设计没有多个业务产品的实际场景输入,是难以直接做出中台的。

其次,中台的目标是提高业务产品的研发效率,但为了达到这个目的,在一段时间内是需要以降低「效率」为代价的,时间长短取决于系统复杂度和团队能力的差距。

当公司随着业务发展,需要研发第二个、第三个产品时,在这种情况下可能会有两种方式来构建中台:

  1. 新产品和技术架构都是继承自当前产品,不断的通过优化当前产品架构来适应新的产品,让中台服务自然沉淀出来。这种情况下的前提条件是在做第一个产品时就做好了服务架构设计,即便如此,在第二个产品时很有可能还是要走弯路,不能满足新产品快速迭代和试错的渴望。但到了第三个、第四个产品时,就会变得越来越快了。
  2. 新的产品和技术架构都是重新设计,这样做每个产品的速度都差不多,灵活度也能做到最高。但每个产品都很难在技术上从前面的产品去借力。当团队人员发生变动、产品越来越多,多分支的维护和开发就凸显了人力不足的问题,这时候就需要搭建一个中台。这也是我们当时所面临的问题。

我所在的事业部发展了多年,有五条业务产品线。这五条产品线就是从一条产品线开始,随着时间的推移逐步发展起来的。和大部分研发团队的情况一样,在应对快速变化的市场环境时,我们没有能够做好系统的底层积累,而是选择了一条在当时看来是更简单的路径:从一套代码 copy 出了另一套代码来支撑新的产品。

多年后我们就有了五个独立的系统来支撑五个业务产品。我无法判断如果当时做好了底层系统架构,整个部门实际会发展成什么样。只知道当五个产品要在五套系统上快速往前跑时,研发的复杂程度和成本都太高了。为了解决这个问题,我们决定做中台。

当然我们也可以有另外的选择——砍掉大部分产品,只专注做到一、两个。但大家都知道,其实真正困难的不是决定做什么,而是决定不做什么,这种决策其实比做中台更加困难。此外,作为一家成熟的公司,一定是需要有能够形成合力的产品矩阵来支撑整个公司战略推进的,所以多产品并行是公司发展到一定阶段的必然选择,而做中台也绝不是站在其中某一个产品的角度来解决问题,而是站在多产品协同的角度来看公司的战略发展。

从公司战略来看,阿里巴巴的曾鸣在「智能商业」一书中提出了看十年、做一年的观点。在日益复杂而又快速变化的市场环境中,公司已经无法做到一个五年的准确的规划,并执行下去。而需要通过看十年的终局思维来看到行业最终会成为什么样子,从而制定公司愿景和方向。

通过做一年的方法来制定计划,快速落地一些事情,然后根据效果来迅速调整方向、更新计划,朝着终局推进。要想做到这点,基础能力的积累就非常重要,而中台也是其中非常重要的部分。

站在产品团队的角度来看,一个搭建完成的中台基础框架,能够带来的直接价值就是:

  • 成本节省。需要开发新功能时,很可能这个功能中台已经提供了,产品经理提供配置参数,研发直接接入服务就可以用起来了。
  • 效率提高。在中台上开发新功能,只需要参考标准和文档,一个新人也可以快速上手,并且这个新功能还可以被其他产品直接使用,产生复利效应。
  • 质量提升。从两方面来看:
  • 设计质量。中台团队通常会以功能模块为划分,专职负责某功能模块的团队往往会更有意愿去突破一些难点,成为最懂此功能模块的团队。比如现在教育领域最热门的授课方式就是直播课,而直播功能就是一个有较高门槛的功能模块。要想做出适合业务发展的直播功能,需要对云计算、计算机网络、直播授课方法、直播运营等多个方面都有较为深入的了解。这需要团队能够有一定程度的积累,不是从某一个业务产品研发团队里找几个人就能很快突击出来的。
  • 研发质量。中台的服务往往提供给多个业务产品使用,出现故障就会造成大面积的问题。所以质量保障往往是中台服务的生命线。每一个下沉到中台的服务不但会经过常规的测试,还会在 Code review、单元测试覆盖率等指标上有更为严格的要求,力保高质量的交付。

我们是怎么做中台的?

产品设计层面

随着中台的日益火爆,如何做一个中台产品经理也成了一个新的职业发展热点,最近也看到有了线上的中台产品经理课程。中台产品经理是 B 端产品经理的一种类型,有 B 端通用的能力要求,比如擅长做抽象建模、具备一定的研发技术功底、懂 UML 等,我在这里就不一一展开了。但就中台服务多个内部业务产品为主的特点来说,会对中台产品经理有些不一样的要求。在我个人的经历里,我认为有三点非常重要:

  1. 中台产品经理如何设计出用户体验好的功能?由于教育中台对其服务的要求是从前端到后端的完整服务(具体原因在技术部分介绍),因此教育中台的产品经理所设计的功能需要直接面对最终用户,也需要保有良好的用户体验。在上图中,业务产品经理的能力要求偏向市场侧,中台产品经理的能力要求偏向研发侧,绿色部分是两类产品经理都需要掌握的。教育中台对产品经理一直有要求,必须走到需求的源头不能只接二手需求。抛开个人能力而言,这对其提出的难度在于:必须花大量的精力去熟知不同的场景。中台产品经理是按照功能模块来划分职责的(如题库、直播),但实际的使用场景是用户使用整体产品的全流程,并不会只看某个功能模块,因此每个模块的产品经理需要了解所支持的所有业务的全部场景,才能做好相关模块的设计。同时教育行业是碎片化的,不同业务之前的场景差异性比较大,某模块的中台产品经理如何才能快速的熟知所有业务的全部场景?这是一个难题。
  2. 中台产品经理和技术的分界线在哪里?也许这不仅仅是做中台产品经理才需要考虑的问题,但在教育中台的很长一段时间内,我的疑问比以前任何时候都强烈。中台里有太多的产品设计,可以由具备产品思维的研发人员来考虑,但更多时候,还是需要向技术深入一步的产品经理来组织研发人员一起设计。举个极端的例子:为了降低各个业务产品在各个端(前端、后端、移动端)接入中台服务时的配置管理难度,我曾考虑改进中台服务里零散在各端代码中的配置管理,做到集中管理并且可灵活配置。此外还拓展出支持未来可能的中台服务付费需求。为了描述清楚需求,我写的 PRD 里除了描述各种场景和功能外,还用伪代码描述了如何使用。虽然伪代码的水平可能会被研发同事鄙视,但达到了清晰表述问题的目的。本文我无意提倡 PRD 里要写伪代码,主要想要说明的是中台产品经理不要指望能够和技术有清晰的界限,应该坚定的跨过去一步,同时也把产品思维带到技术中去,搭起一座桥。
  3. 中台产品经理如何设计一个新功能模块,让它能够满足各方需求,且推动其在各个业务产品上使用起来?除了要求产品经理有极强的专业能力外,还需要具备极强的主动性、沟通能力、甚至是商务能力,在各个业务之间想尽办法把中台的种子种下去。相关的经验在在本文的「中台策略对组织架构的挑战」部分做了介绍。

技术层面

在中台架构的设计之初,我们就定位了教育中台需要提供的不仅仅只是后端服务,一方面纯后端服务和 PaaS 服务就没太多区别;另一方面由于教育中台所希望提供的服务的业务属性非常强,提供的服务复杂程度远高于常见的 IM、视频云等常见 PaaS 服务,如果完全通过后端开放接口来使用,接口的数量会非常多,调用的逻辑关系也会很复杂,使用成本会远高于常见的 PaaS 服务。

因此我们希望教育中台提供的是前后端一体的服务,最终展现给用户的是前端模块 / 组件。理想的情况下,业务产品的前台页面只要嵌入中台某功能服务的前端模块,就可以使用该模块的完整功能。这种方式最大限度地拓展了中台服务的价值,但也给中台服务在设计中带来巨大的难度。经过一年反复的煎熬,我们也整理出了几条设计原则:

1. 数据结构的统一是底线

理想情况下,教育中台搭建完一个模块,各个业务产品一接入就能完美的用起来。但实际情况下没有产品经理和研发具备这样的能力,反复是一定要的,甚至于有时候教育中台需要去做一个需求还不明确的功能(我通常反对中台新做功能来完成业务产品的需求验证,ROI 太低了)。当面对这样的情况时,一定要坚守的底线是数据结构的统一。研发同学都知道数据迁移是一个大坑,所以只要数据结构是统一的,任何逻辑和交互的变化都是可以接受的。

2. 前台界面通用的边界

数据结构的统一、后端服务的共享,是容易在思想上达成一致的,难的部分只是在执行。但前端界面统一的观点自始至终都在激烈的辩论中。对于一个 ToC 产品的产品经理和设计师来说,往往对交互、视觉都非常敏感,这也是 ToC 产品能够在第一眼就留住用户的最重要原因。

但中台服务为了做到重用,往往很难在一些细节的交互上和视觉层上,百分之百的满足每个业务的需求。并且在这种用户体验的层面,往往没有谁能够说服谁。对于设计型的产品经理而言,不能把控自己产品界面里的设计,简直就是被亵渎,因此在前端界面统一这件事情上的争论有多激烈,可想而知,我自己在这件事情的立场上也有摇摆。在多个 case 的纠葛后,我们推动了几件事情,不敢说解决了这个冲突,至少是改善了问题:

  1. 推动更新整个事业部产品的交互视觉规范。对于建立规范大家都是没有疑问的,在交互规范不完善且没有被严格执行的情况下,很多时候产品经理都需要为了一些交互细节大伤脑筋:比如编辑框里字数超出了限制应该怎样提示?诸如此类。当交互规范完善,且做成了 Axure 组件后,普通产品经理都有了升级成产品设计师的可能,基于规范和组件就可以做出一个完成度很高的交互稿。而视觉规范是整个事业部各产品统一品牌形象的条件,也是统一前端组件的基础,设计在前端组件级达成一致是可以的。
  2. 根据用户前台和管理后台加以区别对待。用户前台是给终端用户使用的,也是大量 C 端用户直接接触产品的入口,不同业务的用户往往在交互和视觉上有不同的需求。而管理后台往往是给一些特殊用户、比如管理员使用的,这类用户首先数量相对少,后台操作也不那么频繁。且这类用户在操作管理后台时具备 B 端用户的属性,很多时候是部门内的运营,对功能是否强大的敏感度高于视觉体验。因此教育中台尽量能在管理后台的前端界面上保持统一,而用户前台页面会考虑放开让各个业务产品自己做。当然这一点很容易就可以找出反例,因此也只是在设计过程中的一个指导方向,并不是定理。
  3. 根据业务的目标用户年龄层次进行区分。事业部有面向成人、K12、年龄更小的儿童等各个不同年龄阶段用户的产品。年龄越小的用户对交互和视觉的要求越高,爱奇艺还专门推出了面向儿童的奇巴布。整个交互和视觉都做了重新设计。因此教育中台尽可能在面向成人的产品里去做到前端界面通用,不考虑和面向低龄人群的产品有任何前端界面的复用。

3. 前后端直连

教育中台的用户是部门其他业务产品线的程序员,虽然都是内部用户,但降低用户的使用成本是非常重要的,我在组织架构部分会详细介绍。要想推动教育中台在内部业务的使用,必须要最大程度的降低用户的使用成本。

第一年我们教育中台的别动队在搭建服务验证可行性时,服务的架构设计是这样的:

我的一年中台实战录

业务产品的后端从教育中台的后端获取数据后,通过业务产品的前端拼装好再传给教育中台的前端模块进行显示。这种方案其实等同于把一个模块的开发按照人头分工到两个团队来开发,理论上来说可以满足任何业务的需求。早期在需求还不那么确定、业务也比较少的时候,这样去进行探索是可行的。但当接入的业务产品多起来,这种架构会带来几个很麻烦的问题:

  • 业务产品的前端和后端都分别需要和教育中台的前端和后端直接对接,需要对教育中台的接口有很深入的了解,服务的接入成本非常高。
  • 由于教育中台后端暴露的接口太多,很容易在后续更新时发生变动,从而导致所有已经接入的业务产品都需要发生代码改动,并进行回归测试。

为了解决上述问题,我们改成了前后端直连的架构设计:

我的一年中台实战录

在这种方式下:

  • 教育中台的前后端是直接交互,可独立运行的。
  • 只需在前端层进行接入,接入成本大大降低。
  • 只要有限的接口保证稳定,教育中台的升级对于业务产品是无感知的。

直连的架构在某些特定情况下会增加功能实现的难度,比如要在教育中台前端模块里去显示其后端服务没有的数据时,会面临拿数据困难的问题。但总体来讲带来的好处远远大于增加的难度,我们也基本确定了前后端直连的架构是教育中台服务首选的方式。

中台策略对组织架构的挑战

高层的支持重要吗? 看过一篇文章「重新理解中台—中台的战略和困境」,对中台在组织架构层面的需求做了比较好的介绍,其中最关键的就是:中台是自顶向下的,中台一定需要得到高层的支持。

和绝大多数商业化的事业部一样,我所在事业部的 KPI 一直是可量化的营收数据。而中台项目在启动运转的相当长一段时间内,我们所做的很难对 KPI 有直接帮助,甚至于在局部较短时间内是阻碍当年 KPI 达成的。

大部分员工是很难站在一定的高度去做一个”看十年、做一年“的规划,特别是当一件事和眼前的 KPI 难以达成平衡时,中台的工作会受到各个方面的挑战。因此高层的坚定支持是中台战略的第一必要条件。中台的价值是有条件的,搭建完成后还得有机会来享受成果,这个判断也需要高层来完成。

此外高层还需要推动一些规范的建设,如交互规范、视觉规范、视觉配套的前端组件规范等,在这些规范的约束下,中台服务搭建的难度会大大降低。

各业务产品的支持重要吗?

高层的支持是基础,但在中台和业务产品实际工作中,无数次的碰撞都需要靠中台自己的影响力来推进。因此中台如何在各业务中获得影响力,并推动各业务接入服务也是至关重要的。那么如何推动业务产品接入中台服务呢?

  1. 直接利益就是人力成本节省。针对单个业务而言,他们最关心的就是接入中台服务能够为其节省的成本,这个计算方式在后面的「中台价值量化」部分会介绍。
  2. 理念的灌输。除了高层的直接支持外,中台的各负责人会时不时的在各种场合给业务的负责人和小伙伴「洗脑」,鼓吹共享服务的思想。首先拉动的一定是研发人员,好的研发人员是有代码洁癖的,即使他不得不在某些情况下写出恶心的代码。如果跟他们去聊抽象、架构和重用,天然就会产生亲切感。面对业务产品经理就往往需要做交换了——我可以在中台功能设计里支持你的一个偏定制需求,但你得答应要接入我的另一个服务,我甚至于可以出人力帮你接入。
  3. 形成生态系统。当 iOS 和 Android 已经成为世界上最大的两个移动端操作系统后,无论开发者多么希望按照 Windows Phone 的标准做开发,也只能选择 iOS 或 Android,这就是生态系统的力量。同理,当中台统一了各个业务的基础服务后,上层的业务功能无论有多么个性化的需求,都不能跳出基础服务的限制。而对于一个业务而言,放弃中台的底层服务、自己重新搭建一套系统也几乎是不可执行的,这太不划算了。无论该产品经理的主观意愿多么强烈,在 ROI 的压力下也很难获得支持。当然,每个产品最初都需要一批种子用户来实现冷启动,中台服务最初也需要能有种子客户来打磨产品,那么应该找谁来合作呢?大家习惯性会想去找战略型的重点业务产品,做成标杆客户。但实际上重点业务产品往往人力充足,并且跑得飞快,一个还不太完善的中台服务想要直接跟此类产品合作是非常难的。重点业务不在意成本,也不那么在意质量,他们更在意的是速度,这和中台本身的定位是有矛盾的。因此,中台服务反而应该去找潜力型的业务产品进行合作。此类业务有着表现自己、赢得关注的欲望,但又苦于资源不足了,是非常有意愿去借助中台的力量做事情的。当然,中台支持此类业务产品需要承担的风险就是:这个业务产品可能活不了多久就被老板砍掉了。因此如何选择具备潜力的产品,这就是需要中台负责人在战略上能有敏锐判断的地方了。

保留力量,去做重要不紧急的事情

由于互联网公司多年来信奉的就是「唯快不破」,团队在做优先级排序时,往往会倾向于去做业务价值最明显的事情。有很多重要不紧急的工作就被压在后面,永远没有再被提起过。但对中台产品团队需要有不同的要求,中台产品一定要保留力量始终去做一些基础的、重要不紧急的事情。

就好像公司如果想要做得长久,除了在商业上的持续投入外,一定要保留足够的人力来做基础性的研究,近期华为的海思芯片和鸿蒙操作系统就是最好的例子。

我们在做中台时,无论外部各个业务需求的压力有多大,都应该始终保持有一个小队在做基础组件和规范建设。比如在各套业务产品里的权限体系都还能跑、但某些功能始终无法完美支撑时,我们按照 RBAC 的方式进行一个新的角色权限系统的设计,并提供了数据迁移方法,也最后为新的业务模块功能开发打下了基础。

中台价值的量化

即使我们都认为一件事情是正确的,价值量化依然是最重要的事情之一。中台是一个 ToB 服务本质上是成本的节省和效率的提升,但由于中台的客户是内部业务产品的程序员,这个价值的量化就变得比一个给销售用的 CRM 系统要复杂了。

中台是提供给多个业务服务的共享服务,任意一个中台服务都可以为业务节省成本,因此被越多的接入,整体节省的成本越大。同时由于每个业务在整个事业部里都有不同的优先级,被高优先级业务接入的中台服务,能够产生的价值就更越大。这是符合直觉的,但如何去量化这样的价值呢?提供以下的计算方法:

假设:

  • 各个业务在事业部的优先级系数 = a1、a2、a3…;
  • 中台服务被某一个业务接入后给业务节省的成本(人天) = 业务自研此服务的成本 + 业务自己运维 – 业务产品接入中台服务的成本。

可以推导出每个服务开发出来后对整个部门节省的成本是:

  • 总体成本节省 = (a1* 业务 1 节省 + a2* 业务 2 节省 + …)- 中台开发成本 – 数据迁移(适配层开发))- 中台运维。

由于中台团队要同时面对多个业务的需求,根据以上公式,我们也可以得出一些判断需求优先级的基本规则:

  • 部门战略,也就是业务的优先级的系数。显然来自战略级业务的需求优先级是高于其他普通业务的;
  • 需求靠谱程度。这里面包括两个层次:是否是核心的需求?是否是伪需求?提出需求的业务是否靠谱?是否可能很快就被干掉了?
  • 与中台自身目标的契合程度。这也很好理解:中台不是业务的外包团队,中台需要有自己的思想和规划。

需要说明的是,虽然有了这样的计算公式,但我们在实际工作中并没有直接去量化每一个功能。主要原因在于教育中台正式立项一年的过程中,团队一直在探索中台设计的套路,比如如何才能较好的满足需求,快速的被接入,并且在运维层面对业务做到无感知。只有在搞清楚这些讨论之后,中台服务才有可能会对节省成本有明显的帮助。因此量化只是少数几个团队核心成员做规划时的参考,而没有直接做为产生的价值而公布出来。

青山绿水,江湖再见

从教育中台组的解散到今天,已经过去差不多五个月了。在写此文时回忆起中台这一年,感慨万千。

感谢两位主管 Sunner 和 Genify,Sunner 作为中台业务负责人和产品架构师,亲手搭建了整个教育中台的底层基础和业务抽象,包括「爱多思」在内的很多特有的名字都是他取的。他是我直接的老师,他对爱多思能够成功的信念、是我在多次迷惑中能坚持到最后的最主要原因。Genify 是研发负责人和技术架构师,从抽象的业务模型到实际可执行的技术方案,再到技术规范的形成,中间依然需要有一条靠经验和想象力来架设的桥梁,而他就是这座桥梁。

感谢一起战斗过的和没来得及深入合作的同事,这些思考和追忆属于我们每一个人。

感谢部门老大,没有他的支持,中台根本不可能立项。

青山不改,绿水长流,他日江湖再见,自当把酒言欢,就此别过。

Java自动化测试工具汇总

xUnit frameworks 单元测试框架

TDD \ ATDD \ BDD

  • 工具
    • JBehave – Behaviour-Driven Development (BDD)测试框架. BDD是从 test-driven development (TDD) 和 acceptance-test演进而来, 让用例的编写对新手更加友好和直觉化
    • Cucumber-JVM – 纯 java的Cucumber实现,支持大部分流行的jvm语言
    • JGiven – 开发者友好且实用的BDD工具. 开发者使用纯java及流利API编写测试场景, JGiven负责生成领域专家可读的报告
    • easyb – Java平台的BDD框架. 通过使用Domain Specific Language(DSL), easyb致力于让文档可读可执行
    • robotframework – 最有名的acceptance test-driven development (ATDD)测试框架
    Spectrum – BDD-style test runner,支持Java 8. 灵感来自于Jasmine, RSpec和Cucumber.

Model-Based Testing

  • GraphWalker – Model-Based测试框架. 这个工具可以从 graphml, dot 或 json文件中读取model,然后自动创建测试用例

Code analysis and coverage 代码扫描和代码覆盖率

  • SonarQube – 管理代码质量的开源工具
  • Gradle Quality Plugin – 静态代码分析工具,支持Java和Groovy,使用 Checkstyle, PMD, FindBugs 和CodeNarc. 插件使用了统一的控制台输出并简化了开发者的工作流: 查看不规范的错误时只需要留意控制台就好,并且控制台输出的体验跟java编译错误的输入体验一致
  • Qulice – Qulice是Java项目的代码扫描和质量控制工具. 包含了最好的静态代码扫描工具和预配置选项。你不需要单独再对这些工具进行配置了。
  • JaCoCo – JaCoCo是免费的代码覆盖率统计工具,应该也是应用最广泛的覆盖率工具了。

Web UI test automation web ui自动化工具

  • libraries
    • Selenium – 浏览器自动化工具
    • SikuliX – 基于OpenCV的 GUI 测试框架, 使用图片识别技术,支持windows/linux/mac系统
  • frameworks and wrappers 框架及封装
    • Selenide – 简洁的Selenium封装,让 UI用例的编写更容易
    • Selenified – 开源的测试框架,目的是让selenium测试更加简单,提供了简单的接口去添加测试报告,错误处理以及线程安全的运行模式。可以运行在本机或云端(Grid or SauceLabs).
    • Serenity BDD (Thucydides) – 创新的开源库,让你可以更高效的编写用户验收用例, 并可以根据用例去生成项目文档及测试报告
    • htmlelements – 让web测试时元素交互更加简单的java库
    • atlassian-selenium – 让开发者可以更高效的编写Selenium/WebDriver功能测试的开源库
    • stevia – Persado出品的开源自动化测试框架
    • darcy – 开源的测试框架,支持java 8,提供了具有表意性以及使用简单的API
    • Satisfy – 基于Thucydides和Jbehave的开源测试框架。支持WebUI, SOAP, REST, emails, files,并支持创建随机数据,开箱即用
    • JDI UI Test Automation Framework – UI自动化测试框架。扩展了Page Object设计模式,并加入了一些常用的元素
    • Geb Framework – 基于groovy自动化测试框架。专为Webdriver Page Object设计模式以及Spock Framework(BDD)的集成而设计。
    • FluentLenium – FluentLenium可以帮助你写出可读性好, 可重用, 可靠且灵活的Web UI功能测试用例. FluentLenium 提供了为Selenium实现的流利api,并为selenium用户的一些常见问题提供了解决方案。
    • Selion – 基于TestNG和Selenium提供了一系列的功能,让你可以在短时间内搞定webdriver. 支持web和移动端测试
  • extensions 扩展
    • BrowserMob Proxy -从浏览器获取性能数据的简单工具, 一般跟自动化工具,比如Selenium和Watir配合使用
    • Selenium-Grid-Extras – 让Selenium Grid 节点的管理更加简单, 并通过清理测试环境的方式让节点更加稳定
    • Selenium Grid Extensions – 扩展了Selenium grid,以及可以在执行selenium用例的同时执行Sikuli用例
    • Selenium Grid Router 轻量级的server,作用是把Selenium Wedriver的请求分发到多个Selenium hub。
    • Docker Selenium Grid – 提供了native的视频录制功能,支持Selenium Grid,最初被设计为跟docker-selenium一同使用。
    • Video Recorder Java – 使用自动化测试用例来录制视频的java库
    • Zalenium – 提供一次性的灵活的Docker-based Selenium Grid视频录制功能, 支持实时预览和online/offline控制面板。
    • SikuliFactory – 为SikuliX提供了PageFactory实现。
    • Mailosaur – 邮件自动化测试工具,基于Mailosaur。

Mobile test automation 移动自动化测试

  • Appium – 开源的自动化测试框架,可以测试native/hybrid/mobile web应用。核心是基于webdriver协议进行了扩展
  • Calabash – 跨平台的自动化测试框架,支持Android和iOS的原生应用以及hybrid应用。 Calabash的语法非常容易理解,甚至可以让非技术人员编写和执行基于上述平台的自动化测试用例。
  • Robotium – 安卓自动化测试框架,支持原生及hybrid应用. Robotium让我们可以非常方便的编写强大和稳定的黑盒UI测试用例。 有了Robotium的支持, 测试开发工程师可以编写安卓应用的功能用例系统用例以及用户验收用例。
  • UIautomator – 提供了高效的测试UI的方式。 可以创建支持真机及模拟器运行的自动化测试用例,并包含了可以查看和分析安卓UI的viewer。
  • Espresso – 比较新的开源自动化测试框架, 让开发者和测试人员都可以编写UI用例。 Espresso的api简单且易学,你可以非常快的使用这个框架上手安卓自动化测试

API test automation 接口自动化测试

  • Karate-DSL – Karate是BDD风格的使用javascript实现的测试框架。可以让你调用任何web-service类型的接口并对响应进行断言。

Windows UI test automation windows ui自动化测试工具

  • SikuliX – 基于OpenCV的 GUI 测试框架, 使用图片识别技术,支持多操作系统
  • Winium.Desktop – 测试Windows应用(主要是基于WinForms和WPF平台)的自动化测试工具. 实现了Selenium Remote WebDriver协议

Unix \ Linux UI test automation Unix \ Linux ui自动化工具

  • SikuliX – 基于OpenCV的 GUI 测试框架, 使用图片识别技术,支持多操作系统

MacOS UI test automation mac ui自动化工具

  • SikuliX – 基于OpenCV的 GUI 测试框架, 使用图片识别技术,支持多操作系统

Server side test automation 服务端自动化测试工具

  • Citrus – Javas实现的测试框架,支持企业级SOA应用的e2e服务测试, 支持 HTTP, JMS, TCP/IP, FTP, SOAP协议,以及XML和JSON.

Kong基础

介绍

技术特性适用场景说明
Kong– 基于OpenResty编写
– 高可用
– 易扩展
– 支持Cassandra存储
– 支持PostgreSQL存储
– restfull 方式 管理admin api
– 插件化支持
– 集群中的节点通过gossip协议自动发现其它节点
网关– 官网
– Kong Community Edition (CE)
– Github
– Docs
– Install Kong Community Edition
– Quickstart
– 入门
– FAQS
– Kong插件
konga– 多用户管理
– 管理多个Kong节点
– 电子邮件异常信息通知
– 管理所有Kong Admin API
– 使用快照备份,还原和迁移Kong节点
– 使用运行状况检查监控节点和API状态
– 轻松的数据库集成(MySQL、postgresSQL、MongoDB)
Kong GUI– 官网
– Github
– Doc
Kong dashboard– 基于node.jsKong GUI官方推荐UI管理工具
– Github
kongdash– 支持windows、MacOS、Ubuntu、Fedoradesktop client for Kong Admin API– 官网
– Github
– 下载

基本概念

名称说明
serviceupstream services的抽象。
Service的主要属性是它的URL。
服务与路由相关联(服务可以有许多与之关联的路由)
Route路由是进入Kong的入口点,并为要匹配的请求定义规则,并路由到给定的Service。
Route定义匹配客户端请求的规则。每个Route与一个服务相关联,一个服务可能有多个与之关联的路由。匹配给定路由的每个请求都将代理到其关联的服务。
upstream service这是指位于Kong后面的您自己的API /服务,转发客户端请求。
target
consumerAPI可能没有用户概念,会出现随意调用的情况。为此Kong提供了一种consumer对象(全局共用),如某API启用了key-auth,没有身份的访问者将无法调用该API。
api用于表示上游服务的旧实体。不推荐使用。

安装Kong

先决条件

安装PostgreSQL

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
$ yum -y install https://download.postgresql.org/pub/repos/yum/11/redhat/rhel-7-x86_64/pgdg-centos11-11-2.noarch.rpm
$ yum -y install postgresql11
$ yum -y install postgresql11-server
$ /usr/pgsql-11/bin/postgresql-11-setup initdb
$ systemctl enable postgresql-11
$ systemctl start postgresql-11
$ su – postgres
$ psql
$ CREATE USER kong;
$ CREATE DATABASE kong OWNER kong;
$ alter user kong with encrypted password ‘123456’;
\q
$ find / -name “postgresql.conf”
$ vi /var/lib/pgsql/11/data/postgresql.conf
# 修改 listen_addresses项值设定为“*”
$ find / -name “pg_hba.conf”
$ vi /var/lib/pgsql/11/data/pg_hba.conf
# 添加以下内容
host all all 0.0.0.0/0 md5
$ systemctl restart postgresql-11

CentOS7中RPM方式安装

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# 下载
$ cd /usr/local
$ wget https://bintray.com/kong/kong-rpm/download_file?file_path=centos/7/kong-1.3.0.el7.amd64.rpm

# 安装
$ yum localinstall kong-1.3.0.el7.amd64.rpm

# 配置kong的配置文件
$ whereis kong
$ cd /etc/kong/
$ cp kong.conf.default kong.conf
##### 内容开始 #####
database = postgres
pg_host = 10.10.1.179
pg_port = 5432
pg_user = kong
pg_password = 123456
pg_database = kong
##### 内容结束 #####

# 初始化kong的数据库
$ kong migrations up -c /etc/kong/kong.conf

# 启动kong
$ kong start -c /etc/kong/kong.conf

# 测试是否安装成功
$ curl -i http://localhost:8001/

kong的默认值

说明
kong默认的代理地址proxy_listen = 0.0.0.0:8000, 0.0.0.0:8443
默认的管理地址admin_listen = 127.0.0.1:8001, 127.0.0.1:8444 ssl

安装kong-dashboard

npm安装

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 安装npm
$ yum -y install npm

# 安装kong-dashboard
$ npm install -g kong-dashboard

##### 启动kong-dashboard #####
# 查看启动选项的完整列表
$ kong-dashboard start –help
# 启动Kong Dashboard
$ kong-dashboard start –kong-url http://127.0.0.1:8001
# 在自定义端口上启动Kong Dashboard
kong-dashboard start \
–kong-url http://kong:8001 \
–port [port]
# 使用基本身份验证启动Kong Dashboard
kong-dashboard start \
–kong-url http://kong:8001 \
–basic-auth user1=password1 user2=password2
##### 启动kong-dashboard #####

# 访问kong-dashboard
$ curl http://127.0.0.1:8080

Docker安装

1
2
3
4
5
6
7
8
9
10
11
12
13
# 查看启动选项的完整列表
docker run –rm -p 8080:8080 pgbi / kong-dashboard start –help

# 启动Kong Dashboard
$ docker run –rm -p 8080:8080 pgbi / kong-dashboard start –kong-url http:// kong:8001

# 在自定义端口上启动Kong Dashboard
$ docker run –rm -p [port]:8080 pgbi / kong-dashboard start –kong-url http:// kong:8001

# 使用基本身份验证启动Kong Dashboard
$ docker run –rm -p 8080:8080 pgbi / kong-dashboard start \
–kong-url http:// kong:8001
–basic-auth user1 = password1 user2 = password2

安装konga

先决条件

  • 安装node.js 8.0+
  • 安装npm

CentOS7安装

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
##### 安装node.js 8+ ######
$ cd /usr/local
$ wget https://nodejs.org/dist/v10.14.2/node-v10.14.2-linux-x64.tar.xz
$ tar -xJvf node-v10.14.2-linux-x64.tar.xz
$ mv node-v10.14.2-linux-x64 node
$ vi /etc/profile
##### 在末尾处添加如下
export NODEJS_HOME=/usr/local/node
export PATH=$NODEJS_HOME:$PATH
##### 使环境变量生效
$ source /etc/profile
$ node –version
##### 安装bower ######
$ npm install bower -g
$ bower -v
##### 安装gulp ######
$ npm install gulp -g
$ gulp -v
##### 安装grunt ######
$
##### 安装konga ######
$ git clone https://github.com/pantsel/konga.git
$ cd konga
$ npm i

# 启动
$ npm start
$ curl http://localhost:1337

Admin API

节点信息

查询节点信息

1
$ curl http://localhost:8001
字段说明
node_id正在运行的kong节点的uuid,当kong启动时随机生成,每次kong重启时这个uuid都会变
availabel_on_serverkong节点上安装的plugins的名称
enabled_in_clusterkong节点中启用的插件,即在数据库中生成了对应存储表

查询节点状态

1
$ curl http://localhost:8001/status
字段说明
total_requests客户端请求总数
connections_active包括等待连接的活动客户端连接的当前数量
connections_accepted接受的客户端连接的总数
connections_handled处理连接的总数。一般来说,除非达到一定的资源限制,否则参数值与接受值相同
connections_reading当前Kong正在读取请求头的连接数
connections_writingNGINX将响应写入客户端的连接的当前数量
connections_waiting等待请求的空闲客户端连接的当前数量
reachable反映数据库连接状态的布尔值。注意,此标志不反映数据库本身的健康状况。

Service

添加Service

1
2
$ curl -i -X POST http://localhost:8001/services -d “name=test.service” -d “url=http://后端服务域名/api”
$ curl -i -X POST http://localhost:8001/services -d “name=test.service” -d “protocol=http” -d “host=hxonline.hxsd.cn” -d “path=/api”
字段说明
name服务名称
protocol协议:http or https 默认是 http
host后端服务域名
port后端服务端口
path后端服务子路径;没有就填 ‘/‘
retries重试次数:默认 5次
connect_timeout请求后端服务的超时时间:默认60000 ms
write_timeout写超时时间:默认60000 ms
read_timeout读超时时间:默认60000 ms
url后端服务url地址

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
$ curl -i -X POST \
–url http://localhost:8001/services/ \
–data ‘name=example-service’ \
–data ‘url=http://mockbin.org’
HTTP/1.1 201 Created
Date: Thu, 13 Dec 2018 07:36:57 GMT
Content-Type: application/json; charset=utf-8
Connection: keep-alive
Access-Control-Allow-Origin: *
Server: kong/0.14.1
Content-Length: 259

{“host”:”mockbin.org”,”created_at”:1544704617,”connect_timeout”:60000,”id”:”c7be1fbd-c8dc-42a5-9397-a494514db290″,”protocol”:”http”,”name”:”example-service”,”read_timeout”:60000,”port”:80,”path”:null,”updated_at”:1544704617,”retries”:5,”write_timeout”:60000}

查询所有Service

1
$ curl -i -X GET http://localhost:8001/services

示例:

1
2
3
4
5
6
7
8
9
10
$ curl -i -X GET http://localhost:8001/services/
HTTP/1.1 200 OK
Date: Thu, 13 Dec 2018 07:42:02 GMT
Content-Type: application/json; charset=utf-8
Connection: keep-alive
Access-Control-Allow-Origin: *
Server: kong/0.14.1
Content-Length: 282

{“next”:null,”data”:[{“host”:”mockbin.org”,”created_at”:1544704617,”connect_timeout”:60000,”id”:”c7be1fbd-c8dc-42a5-9397-a494514db290″,”protocol”:”http”,”name”:”example-service”,”read_timeout”:60000,”port”:80,”path”:null,”updated_at”:1544704617,”retries”:5,”write_timeout”:60000}]}

查询某个Service

1
$ curl -i -X GET http://localhost:8001/services/{服务名称 or 服务id}

示例:

1
2
3
4
5
6
7
8
9
10
$ curl -i -X GET http://localhost:8001/services/example-service
HTTP/1.1 200 OK
Date: Thu, 13 Dec 2018 08:02:24 GMT
Content-Type: application/json; charset=utf-8
Connection: keep-alive
Access-Control-Allow-Origin: *
Server: kong/0.14.1
Content-Length: 247

{“host”:”mockbin.org”,”created_at”:1544704617,”connect_timeout”:60000,”id”:”c7be1fbd-c8dc-42a5-9397-a494514db290″,”protocol”:”http”,”name”:”example-service”,”read_timeout”:60000,”port”:80,”updated_at”:1544704617,”retries”:5,”write_timeout”:60000}

获取某个路由下的Service

1
2
$ curl -i -X GET http://localhost:8001/routes/{路由ID}/service
$ curl -i -X GET http://localhost:8001/routes/xxxx-xxx-xxx-xx/service

更新Service

1
$ curl -i -X PUT http://localhost:8001/services/{服务名称或ID} -d “name=test.service” -d “protocol=http” -d “host=hxonline.hxsd.cn” -d “path=/api”

删除Service

1
2
$ curl -i -X DELETE http://localhost:8001/services/{服务名称或ID}
$ curl -i -X DELETE http://localhost:8001/services/test.service

Route

添加Route

1
2
3
4
$ curl -i -X POST –url http://localhost:8001/routes/ \
-d ‘protocols[]=http&protocols[]=https’ \
-d ‘paths=/test’ \
-d ‘service.id=xxx-xxxx-xxxx-xx’

示例:

1
2
3
4
5
6
7
8
9
10
11
12
$ curl -i -X POST \
–url http://localhost:8001/services/example-service/routes \
–data ‘hosts[]=example.com’
HTTP/1.1 201 Created
Date: Thu, 13 Dec 2018 08:06:10 GMT
Content-Type: application/json; charset=utf-8
Connection: keep-alive
Access-Control-Allow-Origin: *
Server: kong/0.14.1
Content-Length: 290

{“created_at”:1544688370,”strip_path”:true,”hosts”:[“example.com”],”preserve_host”:false,”regex_priority”:0,”updated_at”:1544688370,”paths”:null,”service”:{“id”:”c7be1fbd-c8dc-42a5-9397-a494514db290″},”methods”:null,”protocols”:[“http”,”https”],”id”:”076d68f8-47fd-47aa-af48-40e71677aa9c”}

访问接口:

1
2
3
$ curl -i -X GET \
–url http://localhost:8000/ \
–header ‘Host: example.com’

可以在/etc/hostsname中将example.com地址配置为kong所在的机器的地址:

1
10.10.192.35 example.com

然后就可以通过example.com:8000打开http://mockbin.org。

字段是否必填说明
protocols必填协议列表,http、https。设置:protocols[]=http&protocols[]=https
methods半选填
默认是二者都行
接受请求的方法:GET 或 POST ,二者都行。设置 methods[]=GET&methods[]=POST
hosts半选填与此路由匹配的域名列表。例如:example.com。用作form-encode, 设置:hosts[]= Foo.com和hosts[]= BAR.com
paths必填与此路由匹配的路径列表。
例如:/test
strip_path选填
preserve_host选填
service必填与此路由绑定的服务。
设置:service.id=

获取全部Route

1
$ curl -i -X GET http://localhost:8001/routes/

获取某个Route

1
2
# xxx-xxx-xxx 路由ID
$ curl -i -X GET http://localhost:8001/routes/xxx-xxx-xxx

获取某Service下的Route

1
$ curl -i -X GET http://localhost:8001/services/{服务名或服务ID}/routes

更新Route

可以用 PATCH 和 PUT,PATCH可以修改已存在的路由,PUT 如果路由不存在则新建一个。

1
2
3
4
# xxx-xxx-xxx 路由ID
$ curl -i -X PUT http://localhost:8001/routes/xxx-xxx-xxx \
-d ‘protocols[]=http&protocols[]=https’ \
-d ‘paths=test’ \

删除Route

1
2
# xxx-xxx-xxx 路由ID
$ curl -i -X DELETE http://localhost:8001/routes/xxx-xxx-xxx

配置upstream

1
$ curl -X POST http://localhost:8001/upstreams –data “name=helloUpstream”

配置 target

1
$ curl -X POST http://localhost:8001/upstreams/hello/targets –data “target=localhost:3000” –data “weight=100”

配置Consumer

添加Consumer

1
2
3
4
5
6
7
8
# 创建一个consumer
$ curl -X POST \
–data “username=oauthadmin” \
–data “custom_id=personapi” \
http://127.0.0.1:8001/consumers/
# 在key-auth插件中为此consumer生成key
$ curl -X POST \
http://127.0.0.1:8001/consumers/oauthadmin/key-auth

配置插件

为 hello 服务添加50次/秒的限流

1
2
3
$ curl -X POST http://localhost:8001/services/hello/plugins \
–data “name=rate-limiting” \
–data “config.second=50”

为 hello 服务添加 jwt 插件

1
2
$ curl -X POST http://localhost:8001/services/login/plugins \
–data “name=jwt”

将插件安装在路由上

1
2
3
4
5
6
$ curl -X POST http://localhost:8001/routes/{routeId}/plugins \
–data “name=rate-limiting” \
–data “config.second=50”

$ curl -X POST http://localhost:8001/routes/{routeId}/plugins \
–data “name=jwt”

配置Certificates

1
$

kong与Consul集成

您可以通过指定dns_resolver属性(在kong.conf配置文件中)指向Consul服务器(或通过设置KONG_DNS_RESOLVER=环境变量)使Kong与Consul一起使用。 通过这样做,迫使Kong使用Consul来解析upstream_url API 中的主机名地址。 参考

运维

管理kong

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 查看版本
$ kong version

# 启动
$ kong start -c /etc/kong/kong.conf

# 停止
$ kong stop

# 重新加载
$ kong reload

# 重启
$ kong restart

# 验证配置
$ kong check /etc/kong/kong.conf

# 健康检查
$ kong health

# 初始化数据库配置
$ kong migrations up -c /etc/kong/kong.conf

配置集群

配置健康检查

配置断路器

配置监控

插件

认证

Basic Authentication

JWT

Key Authentication

LDAP Authentication

OAuth 2.0 Authentication

OKTA

Upstream HTTP Basic Authentication

安全

Bot Detection

CORS

IP Restriction

Cleafy plugin for Kong

Kong Spec Expose

Kong Upstream JWT

Signal Sciences

Wallarm

流控

ACL

Rate Limiting

Request Termination

Response Rate Limiting

Kong Response Size Limiting

Kong Service Virtualization

Request Size Limiting

分析和监控

Datadog

Prometheus

Zipkin

Moesif API Insights

SignalFx

日志

TCP、UDP、HTTP、File、Syslog、StatsD、Loggly等

常见问题

附录

数据字典

acls

字段名类型默认值说明
iduuid
consumer_iduuid
grouptext

apis

字段名类型默认值说明
iduuid
nametext
upstream_urltext
preserve_hostbool
created_attimestamp(6)
retriesint25
https_onlybool
http_if_terminatedbool
hoststext
uristext
methodstext
strip_uribool
upstream_connect_timeoutint4
upstream_send_timeoutint4
upstream_read_timeoutint4

basicauth_credentials

字段名类型默认值说明
iduuid
consumer_iduuid
usernametext
passwordtext
created_attimestamp(6)

certificates

id | uuid | | |
cert | text | | |
key | text | | |
created_at | timestamptz | | – |

cluster_events

字段名类型默认值说明
iduuid
node_iduuid
attimestamp(6)
nbftimestamp(6)
expire_attimestamp(6)
channeltext
datatext

consumers

字段名类型默认值说明
iduuid
custom_idtext
usernametext
created_at

hmacauth_credentials

字段名类型默认值说明
iduuid
consumer_iduuid
usernametext
secrettext
created_attimestamp(6)

jwt_secrets

字段名类型默认值说明
iduuid
consumer_iduuid
keytext
secrettext
created_attimestamp(6)
algorithmtext
rsa_public_keytext

keyauth_credentials

字段名类型默认值说明
iduuid
consumer_iduuid
keytext
created_attimestamp(6)

oauth2_authorization_codes

字段名类型默认值说明
iduuid
codetext
authenticated_useridtext
scopetext
created_attimestamp(6)
credential_iduuid
api_iduuid
service_iduuid

oauth2_credentials

字段名类型默认值说明
iduuid
nametext
consumer_id
client_idtext
client_secrettext
redirect_uritext
created_attimestamp(6)

oauth2_tokens

字段名类型默认值说明
iduuid
credential_iduuid
access_tokentext
token_typetext
refresh_tokentext
expires_inint4
authenticated_useridtext
scopetext
created_attimestamp(6)
api_iduuid
service_iduuid

plugins

字段名类型默认值说明
iduuid
nametext
api_iduuid
consumer_iduuid
configjson
enabledbool
created_attimestamp(6)
route_iduuid
service_iduuid

ratelimiting_metrics

字段名类型默认值说明
iduuid
identifiertext
periodtext
period_datetimestamp(6)
valueint4
route_iduuid
service_iduuid

response_ratelimiting_metrics

字段名类型默认值说明
iduuid
identifiertext
periodtext
period_datetimestamp(6)
valueint4
route_iduuid
service_iduuid

routes

字段名类型默认值说明
iduuid
created_attimestamp(6)
updated_attimestamp(6)
protocolstext[]
methodstext[]
hoststext[]
pathstext[]
regex_priorityint8
strip_pathbool
preserve_hostbool
service_iduuid

schema_migrations

字段名类型默认值说明
iduuid
migrationsvarchar(100)[]

services

字段名类型默认值说明
iduuid
created_attimestamp(6)创建时间
updated_attimestamp(6)更新时间
nametextServiceName
retriesint8代理失败时要执行的重试次数。默认值为5。
protocoltext协议。http/https
hosttextThe host of the upstream server
portint8The upstream server port. Defaults to 80
pathtextThe path to be used in requests to the upstream server. Empty by default.
connect_timeoutint8The timeout in milliseconds for establishing a connection to the upstream server. Defaults to 60000.
write_timeoutint8The timeout in milliseconds between two successive write operations for transmitting a request to the upstream server. Defaults to 60000.
read_timeoutint8The timeout in milliseconds between two successive read operations for transmitting a request to the upstream server. Defaults to 60000.

snis

字段名类型默认值说明
iduuid
nametext
certificate_iduuid
created_attimestamp(6)

targets

字段名类型默认值说明
iduuid
targettext
weightint4
upstream_iduuid
created_attimestamp(6)

ttls

字段名类型默认值说明
primary_key_valuetext
primary_uuid_valueuuid
table_nametext
primary_key_nametext
expire_attimestamp(6)

upstreams

字段名类型默认值说明
iduuid
nametext
slotsint4
created_attimestamp(6)
healthchecksjson
hash_ontext
hash_fallbacktext
hash_on_headertext
hash_fallback_headertext
hash_on_cookietext
hash_on_cookie_pathtext

参考

开源

文档

Kong系列

文章