3月26日,51CTO训练营邀请滴滴出行的首席架构师李令辉和大家分享《架构设计之大道至简》。他主要跟大家聊了聊他眼中架构师的道和术,以及滴滴出行所经历的一些坎坷和宝贵经验。
李令辉 滴滴出行 首席架构师
李令辉,滴滴出行首席架构师,移动互联网资深从业者,对移动互联网技术发展趋势以及技术团队的组建有独道见解。他具有多年互联网架构的设计经验,擅长高性能高并发高可用的架构设计工作,主导了滴滴打车技术迭代中的核心服务架构升级。
李令辉认为架构师这个行业有道和术,通过对道的理解,自然衍生出术。作为一个架构师,每次面临设计,面临问题的时候,你会有很多选择,这些选择是术的选择,比如是用Java来实现还是用PHP来实现,具体采用那个框架等等。其实这些细节真的没有那么重要,架构师真正要进行道的选择,那就是如何让整个事情的实现越来越复杂,还是越来越简单。
作为一个架构师,他最重要的工作是让事情的实现越来越简单, 大道至简。 如果你有了大道至简这个思维方式后,你看很多事情的视角就会变化。你会发现它们有共同的特点,通过一个方法可以解决一类问题。一个事情变简单是非常有价值的,简单意味着容易被别人读懂,你不容易忘记它,甚至忘记了,重新学习也不难,改造起来很容易,发现问题的时候也一针见血,一眼就会看穿所有的问题。
做架构,什么叫做架构?架构本身是为了满足我们人类的需求,人的脑子没有想象的那么好使,我们只能面对一个相对来说简单和单一的问题。架构的特点就是让我们每个脆弱的人在处理问题的时候永远是面临一个或几个简单的问题,而不是很复杂的问题, 也就是分而治之,降低管理的复杂度。
架构服务于什么?组织结构设计了软件系统,同时架构服务于组织结构。这个听着有点怪,但是事实就是这样,有什么样的组织结构,就会设计出什么样的软件系统。你会发现,如果两个人属于两个团队,他们同时在开发一个系统的时候,时间长了之后,他们自然会把它拆成两个系统,A系统和B系统,或者是A模块和B模块的依赖关系。你会发现,在BAT这种大的公司里,每个团队和团队的接口之间还是蛮清晰的,很少有两个团队之间说,我们都负责一个东西,我提交的时候要通知你,你的团队不要碰我的团队的代码。所以说,如果你想让一个系统越来越内聚,那么你就把维护这个系统的人放到一个团队里,让他们向一个老板汇报,让他们的KPI目标一致,因为这将符合你的最终目的。
当然,如果你希望他们把这两件事情作为两个相对独立的事情来做,好处是可以一部分人专注于某一个事情,另一部分人专注于另一个事情,那么你就把他们拆成两个团队,你都不需要在架构上做太多的事情。所以架构设计是服务于组织结构的,如果你希望你的组织结构向一个你想要发展的方向去走。你就应该让你设计的软件架构和你的组织架构,和你未来想要的组织架构,尽量的一致。
架构师这个职业,严格来说,不止是一个知识,你要摔过很多坑,你要见识过很多组织的情况,你要做过很复杂的系统,最主要的是失败的经验,然后对应的解决方案,特别是其中纠结的经验。只有这样,你才知道在实际的生产环境和开发环境中,你该如何取舍。
架构师的工作职责,第一个是解决问题。你必须得解决所有的技术问题。第二个是提高效率,面对快速的迭代和市场的快速变化,如何低成本的去追上发展趋势,去满足用户需求,是一门大学问。工作中, 懂得如何提高研发的效率,提高组织结构的效率,提高产品迭代的速度,将是你打败竞争对手的核心竞争力 。第三是架构师要不断地应对变化。因为市场是变化的,组织机构和业务形态是变化的,用户的需求是变化的,所以架构师要不断跟着这个产品发展思考,我们下一步该往哪个方向走,该怎么去组织我们的团队,该怎么组织软件的结构等。
下面是李令辉分享的两个实际的案例,是 滴滴在2014年面临的两个挑战 。第一个挑战是level-0的服务,这个服务非常重要,如果挂了,整个滴滴就会停服。无论你是司机还是乘客,你的APP就用不了了。
第二个挑战是滴滴一上线,就是一个关系到生活,国计民生的应用,它从一上来就帮助大家省钱,给大家创造财富。所以,它一开始就是一个带钱的应用,对大部分互联网人来说,相对是一个比较陌生的领域。
level-0服务面临的第一个问题是没有任何安全测试,也没有集成测试,全是拿用户的操作测试。第二问题是性能特别差。第三个问题是已有代码没有人维护了。更可怕的是,它里面用到了很多奇怪的,现在不太常见的技术,比如说阿帕奇。
作为架构师解决问题,不喜欢只解决一个问题,而是希望解决一类问题,考虑的核心诉求是什么呢?就是第一别挂,第二是要能工作。所以当MySQL堵塞的时候,或者任何东西堵塞的时候,程序都不再请求他,直接返回一个值,因为程序大部分请求是不依赖于存储的。
还有一个比较讨厌的地方,它依赖于B源的存储,这个存储既不是开源的,也不是公司内部开发的,这个存储不太可控,经常网络一抖动,就发生写入失败。往往对同一个写,ABC写三次,A失败了,B成功了,C又失败了。最后读出的值,是随机的,很可怕。
下面,我们看看滴滴怎么解决写入失败的问题,他们是保持无状态。这是怎么实现的呢?用户登录的时候,程序拿时间窗、手机号、一个密钥(这个密钥是根据你的手机号找到的唯一密钥)放在一起,签个名,然后算出一段验证码发给用户,只要用户这一分钟返回该验证码,程序就可以允许该用户登录,不依赖任何存储。后来说这个也不行,用户可能没有那么快登录回来。变通的方法是用户试过去五分钟的验证码,只要任何一次成功,用户就可以登录。
做完上述这些调整后的战绩如何呢?第一个,QPS到九千的时候还没有任何感觉,到一万的时候,稍微有点延时提高,从平均不到5毫秒的延时到10毫秒左右,其实也没什么太大关系。因为100毫秒也是可以接受的。
再就是部署,部署变的非常容易,一个binary,它是镜像的,然后加一个配置文件就可以上线了。可用性到了100%。99%的请求不依赖任何存储,日志收集通过网络实现。
这是李令辉跟大家分享的第一个案例,滴滴是怎么解决遇到的问题和取得哪些成果。下面谈下实施过程中,大家需要注意的细节。
首先,领导者要定一个比较疯狂的目标,给大家一些士气和冲的动力。团队管理者定目标切忌不要定长目标,因为人的士气,一鼓作气,再而衰,三而竭。第二是不要太相信大团队。做任何突击的事情,最好是两三个人,超过这个数字,效率肯定很低。第三不要太盲目相信一些权威的意见,其实那些专家都不是你,不是那么值得相信,你可以借鉴,你可以听取,但你要盲目的完全照搬没有任何意义,因为你的团队只有你自己清楚,你自己的能力,你所面临的问题,也只有你自己清楚。
再就是在一些适当的环境中,动态语言的优势没有那么明显。一个相对来说灵活性差一些,约束比较强的工程性语言会给你更多的帮助。虽然程序员的个体效率可能会有下降,但就团队的整体效率来说,会有明显的提升。
下面我们谈下滴滴支付面临的第二个问题,滴滴的支付系统有两套代码,两个系统。我们来说说它有什么问题。第一个问题是存在多套代码。多套代码有什么问题呢?最大问题是出了问题,工程师不知道出在哪里。第二是工程师解决了一个问题,也不知道有没有全部解决。第三个是工程师的测试量都要大一倍,不止一倍。第四是这两套代码中一定有一套有问题,甚至两套都有。
第二个问题是和业务耦合在一起。在早期的时候,滴滴并没有人专职去开发支付,因为刚上线的时候,只是一个叫车平台,不带支付,用户拿现金支付就可以。但是后来支付就作为一个普通的应用来开发了,这就有一个很大的问题。你作为一个工程师,不会把它当做一个单独的系统来对待,这本身并没有什么问题,问题就在于因为它是一个人开发的,或者是一伙人开发的,当它需要改动一个功能的时候,并没有按照系统的边界去划分,而是怎么省事怎么来。比如说要对这些用户进行一个补贴,或者是当金额超过多少去补贴,正常来说,你不应该在支付系统做,为什么?因为支付系统就是收钱付钱的。这个地方改动越少越好,因为不能有错,但你想,代码那么多处,你哪知道哪是汇聚点?大家都会改到共同的出口和入口,那就是支付模块,因为所有的订单都会流转到这里。运行一段时间,你会发现支付系统在原有的系统中非常的冗余和庞大,所有的功能都耦合在里面,甚至还有免单逻辑。
这个问题你说是工程师的问题吗?不是,从工程师的角度来说,任务很多,他永远要选择最快的路,最好的方式去解决,能改一处,不改多处,这都是从软件工程学到的,没有错。所以,工程师在这处做修改,很快就生效,他就觉得非常完美,但是过了很长时间再看这个支付代码呢?就会发现这哪是支付系统呀!什么逻辑都有,这里面还要判断车型,判断这个人,还要判断他有没有券,他的券是什么时候的,他有没有免单,各种逻辑,是一个面条型的代码。更可怕的是再加新的功能或者是冗余判断的时候,你甚至不知道在哪里加,也不知道该删掉哪些,因为好像都有用。为什么会这样?就是因为你的组织结构的边界没有划分好。
第三个问题是,支付对一致性的要求要远远高于其他逻辑。解决办法是架构师需要了解事务隔离的级别,增加对一致性的理解。
第四个问题是扩展性非常差。日常代码的实现需要能够捕捉异常、处理事务。
第五个问题是一个系统性的问题了,依赖于单库事务。后来滴滴把单库事务去掉了,不再依赖事务。
最后就不说什么性能差,代码脏乱差了,这个是互联网长期存在的问题。
滴滴的支付团队尝试怎么解决上述问题的呢?第一,他们做了最重要的事情,给这个项目命了一个名,叫Phoenix。因为滴滴要涅槃重生,因为不重生可能就烧没了,他们必须要重生,所以项目名称是Phoenix。他们定了Phoenix要做的事情,互联网公司讲究的是快,什么都要快,其实想清楚比部署要快更重要,因为经常会发现想清楚是不用做,想的快比做的快更重要。正确的沟通组织结构,不是一个简简单单的技术问题,要顺势而为。
所以,他们当时抽象了一套支付原语,什么叫支付原语呢?李令辉设计了像MySQL那样协议类的东西,没有那么复杂。在云服务PASS的时代,合作方会提供给你一整套解决方案,你调用对方的接口就可以。后面的一套东西太复杂了,滴滴做起来不值得。
第二是独立的团队。李令辉当时就意识到,组织结构决定了实现的方式,所以他们用独立的团队去做支付,不掺在原来的业务团队里。
第三,使用Java来做开发。为什么要用新语言呢?PHP真的招不到合适靠谱的人。当时滴滴还没有和快的合并,主要是PHP架构,但是最后这个服务是用Java做的,因为Java在支付领域是非常成熟的实践,世界上非常大的支付,包括支付宝、京东支付、银联的网银在线,包括PayPal,都是Java开发的,你可以确定Java一定能解决这个问题。然后,支付团队就孵化,保证团队间的隔离,去事务,这是他们的前提。
通过几番的改动和艰苦的努力,滴滴实现了用很少的机器,其实不到十台,撑住全公司的业务。第二个是全年无宕机,没有任何一次主动的事故。除去外部系统,微信支付挂掉和支付宝挂掉不算在内。第三,无宕机无事故,事故和宕机不太一样,滴滴单台宕掉过,但是不影响服务。第四没有回滚,这个非常重要,在2015年,他们没有一次上线的代码出现问题回滚。第五就是接入了滴滴公司所有的产品线。
了解更多训练营内容:http://x.51cto.com