工作了几年,辗转了几个公司,最近一直在思考一个问题:如何借力用力,一两拨千斤。程序员,总是有一种自负:总感觉自己做的系统比他人好,自己写的代码比人优雅,然后自己重新实现类似的系统、编写类似的代码,遇到了类型的问题、甚至比别人多踩了一些大坑,才恍然大悟原来他人为何要那么做。系统设计、软件实现,其实是一个权衡、妥协的过程。
各种设计,各种实现都有实现者的权衡考虑,一个有责任、有思考力的设计者都会有自己的设计、实现初衷、取舍。我们不应该先入为主地做判断。这种程序员的自负情节是要不得的。
这跟借力用力、一两拨千斤的思考貌似没有半毛钱的关系,其实这是有关系的。如果不摒弃自负的心理,是无法借力用力、一两拨千斤。但是程序员如何借力用力,一两拨千斤呢?这里,我就谈谈我的工作感悟吧。
我,20008年至2012年在国内一家著名的存储软件公司从事存储软件开发设计。在这四年里,我发现了这个公司的一个习惯,比如我们部门内要从事某个项目的软件开发,老同事,总会去问其他组的同事,是否有可以复用的代码,是否有可以复用的系统设计,然后从其他部门拿来可以复用的代码、系统设计,或者从其他部门拿来设计经验:比如设计类似的系统会踩到哪些坑,然后在早期的系统设计考虑解决方案。
这种复用策略、借鉴只是一些小的,从更大的就是这个公司提倡的归一化设计。什么是归一化设计呢?由于是从事存储系统设计,存储分为刀片、阵列、服务器。不同类型的存储,其实可以用领域模型来思考,除了CPU、内存、硬盘参数不同,其他的都是一样的。比如驱动、接口、插槽。因此归一化设计的思路就是利用这个特点,对于刀片、阵列、服务器分别设计了不同的平台,各种类型的平台,装上不同的CPU、内存、硬盘,就形成了同一产品系列面向高、中、低端的存储设备。这种归一化设计除了能够节约成本、快速出货,而且从用户的角度来说:为用户提供了统一的操作界面、使用手册,降低用户后继新设备的学习、使用成本。
归一设计,在手机行业比较著名的例子,我觉得就是MTK(联发科),K910(华为海思)。归一设计的核心理念,就是软件、科技成熟的阶段进行的模块化、组件化、平台化的系统设计,更高级的是方案设计。
但是,我在12年末离开了这家公司,到了国内著名的互联网公司,进了这个公司的E部门。在E部门,我学习了这个部门XXPlatform,这个是基于RPC机制快速应用服务开发的平台,使用这个平台可以快速开发基于服务(SOA)的应用,并有了统一的配置中心进行配置下发、应用部署,用起来还是相当的方便,但是要形成这样的平台是要花一些成本的,而且还有一些问题是没有解决的:容灾、故障切换。后来,我也从内部渠道发现了一个现象:不同的部门总是喜欢搞一个类似的平台,O部门、S部门、W部门、I部门都有自己类似平台,后来,我到C部门,发现C部门更搞的是也实现类似的平台,而且特烂:没有统一的公共库,各种坑,容灾、故障自动切换还计划中,性能用机器堆出来的,最近在搞一个项目,弄得几乎所有人的都在加班加点奋战。其实,众所周知这个公司S部门能够同时处理2亿加用户在线,容灾、故障切换是最成熟的,而且也有十几年的历史了,按照道理应该成为公司的一个归一化方案,不同的部门就利用该方案快速推动业务,结果恰恰相反,不同的部门总是重复造轮子,然后踩坑、填坑,进行所谓的技术挑战。
其实,个人感觉,大家的能力都是差不多,别人从事那么久的,形成的方案、平台能用,就拿来用,快速推出业务才是王道,再挑战,也不会超越前辈多少。除非,你有更新、突破性的原创能力,但在中国的软件开发环境,我觉得现在还没有这种情况。
比如就拿分布式设计的CAP特点,一致性(C)、可用性(A)、分区容错性(可靠性)P,这三者在一个分布式系统中,是无法完全满足的。那么有一个部门对于设计了一个可以复用分布式系统,你是拿来用,还是重新挑战一把、再搞一个?
个人觉得,能够复用,就复用,快速推动业务才是硬道理,除非该部门设计的系统确实够烂的,不然程序员何必为难自己呢?我们先来看下分布式系统的整体视图:
所有的分布式系统,其完整的视图,应该都是类似的,需要解决以下的几个问题,以及当前常用的解决方案:
1:域名到IP地址的解析,这个是通过DNS服务器来解决。
2:内部系统对外部的隔离,解决安全隐患。常用的做法是通过网关类型模式,实现一些应用网关,暴露在公网上,从而隔离内部核心系统、数据。
3:数据容灾问题,尤其是跨区域容灾问题,实现数据安全性、可靠性,解决突发事件导致的服务不可用、不可恢复问题。
4:同一类服务之间的负载均衡,采用的是客户端前端调用智能选择、LVS、负载均衡协调器。不同的系统根据的系统特点选择,当采用LVS已经是一种范例。
5:应用、交换机的可用性,常用的做法当然是冗余热备:standby 或者activity。standby 模式下,需要发现master 可用性,常用的做法就是心跳,然后通过配置中心更新配置,提醒前端调用改变访问IP和端口。
6:服务之间访问模式:原始socket 调用,现在成熟的做法是采用RPC,然后实现平台、框架快速推进业务开发。开源的RPC就有zero c 的ICE,还有ACE 的DAO(?TAO)。
网络通信从客户端到服务端的访问模式,以及服务端的模式都可以组件化、平台化了。
7:解决数据库访问性能的问题,采用分库分表,实在不行,采用类似memcached 用加快访问,以及采用这种方案数据的一致性解决方法。
从整个视图,由上而下,我们可以看到脱离具体的业务外,不同的业务从安全、性能、可靠性、容灾等问题的解决其实是一致的、无差异的,基本上可以组件化、平台化、方案化,供其他组、部门借力用力,一两拨千斤,快速业务推进。
但是一个刚开始的小公司是不能这么搞一个大而全的系统,这样会搞死小公司。但是,有一点小公司无论如何得组件化的是数据:DB、文件系统。数据安全性、可靠性是无法突破的底线。早点组件化、平台化数据为后来的业务安全、可靠带来根本、有力的保障。