我在系统设计上犯过的14个错

在上篇《架构师画像》的文章中提到了自己在系统设计上犯过的一些错,觉得还挺有意义的,这篇文章就来回顾下自己近八年来所做的一些系统设计,看看犯的一些比较大的血淋淋的错误(很多都是推倒重来),这八年来主要做了三个基础技术产品,三个横跨三年的大的技术项目(其中有两个还在进行中),发现大的错误基本集中在前面几年,从这个点看起来能比较自豪的说在最近的几年在系统设计的掌控上确实比以前成熟了很多。

第1个错
在设计服务框架时,我期望服务框架对使用者完全不侵入,于是做了一个在外部放一个.xml文件来描述spring里的哪些bean发布为服务的设计,这个版本发布后,第一个小白鼠的用户勉强在用,但觉得用的很别扭,不过还是忍着用下去了,到了发布的时候,发现出现了两个问题,一是这个xml文件研发也不知道放哪好,所以到了发布的时候都不知道去哪拿这个xml文件。
这个设计的关键错误就在于在设计时没考虑过这个设计方式对研发阶段、运维阶段的影响,后来纠正这个错误的方法是去掉了这个xml文件,改为写了一个Spring FactoryBean,用户在spring的bean配置文件中配置下就可以。
因此对于一个架构师来说,设计时在全面性上要充分考虑。

第2个错
服务框架在核心应用上线时,出现了前端web应用负载高,处理线程数不够用的现象,当时处理这个故障的方式是回滚了服务框架的上线,这个故障排查了比较长的时间后,查到的原因是服务框架用的JBoss Remoting在通信时默认时间是60s,导致一些处理速度慢的请求占据了前端web应用的处理线程池。
上面这里故障的原因简单来说是分布式调用中超时时间太长的问题,但更深层次来思考,问题是犯在了设计服务框架时的技术选型,在选择JBoss-Remoting时没有充分的掌握它的运行细节,这个设计的错误导致的是后来决定放弃JBoss-Remoting,改为基于Mina重写了服务框架的通信部分,这里造成了服务框架的可用版本发布推迟了两个多月。
因此对于一个架构师来说,在技术选型上对技术细节是要有很强的掌控力的。

第3个错
在服务框架大概演进到第4个版本时,通信协议上需要做一些改造,突然发现一个问题是以前的通信协议上是没有版本号的,于是悲催的只能在代码上做一个很龌蹉的处理来判断是新版本还是老版本。
这个设计的错误非常明显,这个其实只要在最早设计通信协议时参考下现有的很多的通信协议就可以避免了,因此这个错误纠正也非常简单,就是参考一些经典的协议重新设计了下。
因此对于一个架构师来说,知识面的广是非常重要的,或者是在设计时对未来有一定的考虑也是非常重要的。

说到协议,就顺带说下,当时在设计通信协议和选择序列化/反序列化上没充分考虑到将来多语言的问题,导致了后来在多语言场景非常的被动,这也是由于设计时前瞻性的缺失,所谓的前瞻性不是说一定要一开始就把未来可能会出现的问题就解掉,而是应该留下不需要整个改造就可以解掉的方法,这点对于架构师来说也是非常重要的。

第4个错
在服务框架切换为Mina的版本上线后,发布服务的应用重启时出现一个问题,就是发现重启后集群中的机器负载严重不均,排查发现是由于这个版本采用是服务的调用方会通过硬件负载均衡去建立到服务发布方的连接,而且是单个的长连接,由于是通过硬件负载均衡建连,意味着服务调用方其实看到的都是同一个地址,这也就导致了当服务发布方重启时,服务调用方重连就会集中的连到存活的机器上,连接还是长连,因此就导致了负载的不均衡现象。
这个设计的错误主要在于没有考虑生产环境中走硬件负载均衡后,这种单个长连接方式带来的问题,这个错误呢还真不太好纠正,当时临时用的一个方法是服务调用方的连接每发送了1w个请求后,就把连接自动断开重建,最终的解决方法是去掉了负载均衡设备这个中间点。
因此对于一个架构师来说,设计时的全面性要非常的好,我现在一般更多采用的方式是推演上线后的状况,一般来说在脑海里过一遍会比较容易考虑到这些问题。

第5个错
服务框架在做了一年多以后,某个版本中出现了一个严重bug,然后我们就希望能通知到用了这个版本的应用紧急升级,在这个时候悲催的发现一个问题是我们压根就不知道生产环境中哪些应用和机器部署了这个版本,当时只好用一个临时的扫全网机器的方法来解决。
这个问题后来纠正的方法是在服务发布和调用者在连接我们的一个点时,顺带把用的服务框架的版本号带上,于是就可以很简单的知道全网的服务框架目前在运行的版本号了。
因此对于一个架构师来说,设计时的全面性是非常重要的,推演能起到很大的帮助作用。

第6个错
服务框架这种基础类型的产品,在发布时会碰到个很大的问题,就是需要通知到使用者去发布,导致了整个发布周期会相当的长,当时做了一个决定,投入资源去实现完全动态化的发布,就是不需要重启,等到做的时候才发现这完全就是个超级大坑,最终这件事在投入两个人做了接近半年后,才终于决定放弃,而且最终来看其实升级的问题也没那么大。
这个问题最大的错误在于对细节把握不力,而且决策太慢。
因此对于一个架构师来说,技术细节的掌控非常重要,同时决策力也是非常重要的。

第7个错
服务发布方经常会碰到一个问题,就是一个服务里的某些方法是比较耗资源的,另外的一些可能是不太耗资源,但对业务非常重要的方法,有些场景下会出现由于耗资源的方法被请求的多了些导致不太耗资源的方法受影响,这种场景下如果要去拆成多个服务,会导致开发阶段还是挺痛苦的,因此服务框架这边决定提供一个按方法做七层路由的功能,服务的发布方可以在一个地方编写一个规则文件,这个规则文件允许按照方法将生产环境的机器划分为不同组,这样当服务调用方调用时就可以做到不同方法调用到不同的机器。
这个功能对有些场景来说用的很爽,但随着时间的演进和人员的更换,能维护那个文件的人越来越少了,也成为了问题。
这个功能到现在为止我自己其实觉得也是一直处于争议中,我也不知道到底是好还是不好…
因此对于一个架构师来说,设计时的全面性是非常重要的。

第8个错
服务框架在用的越来越广后,碰到了一个比较突出的问题,服务框架依赖的jar版本和应用依赖的jar版本冲突,服务框架作为一个通用技术产品,基本上没办法为了一个应用改变服务框架自己依赖的jar版本,这个问题到底怎么去解,当时思考了比较久。
可能是由于我以前OSGi这块背景的原因,在设计上我做了一个决定,引入OSGi,将服务框架的一堆jar处于一个独立的classloader,和应用本身的分开,这样就可以避免掉jar冲突的问题,在我做了引入OSGi这个决定后,团队的1个资深的同学就去做了,结果是折腾了近两个月整个匹配OSGi的maven开发环境都没完全搭好,后来我自己决定进去搞这件事,即使是我对OSGi比较熟,也折腾了差不多1个多月才把整个开发的环境,工程的结构,以及之前的代码基本迁移为OSGi结构,这件事当时折腾好上线后,效果看起来是不错的,达到了预期。
但这件事后来随着加入服务框架的新的研发人员越来越多,发现多数的新人都在学习OSGi模式的开发这件事上投入了不少的时间,就是比较难适应,所以后来有其他业务问是不是要引入OSGi的时候,我基本都会建议不要引入,主要的原因是OSGi模式对大家熟悉的开发模式、排查问题的冲击,除非是明确需要classloader隔离、动态化这两个点。
让我重新做一个决策的话,我会去掉对OSGi的引入,自己做一个简单的classloader隔离策略来解决jar版本冲突的问题,保持大家都很熟悉的开发模式。
因此对于一个架构师来说,设计时的全面性是非常重要的。

第9个错
服务框架在用的非常广了后,团队经常会被一个问题困扰和折腾,就是业务经常会碰到调用服务出错或超时的现象,这种情况通常会让服务框架这边的研发来帮助排查,这个现象之所以查起来会比较复杂,是因为服务调用通常是多层的关系,并不是简单的A–>B的问题,很多时候都会出现A–>B–>C–>D或者更多层的调用,超时或者出错都有可能是在其中某个环节,因此排查起来非常麻烦。
在这个问题越来越麻烦后,这个时候才想起在09年左右团队里有同学看过G家的一篇叫dapper的论文,并且做了一个类似的东西,只是当时上线后我们一直想不明白这东西拿来做什么,到了排查问题这个暴露的越来越严重后,终于逐渐想起这东西貌似可以对排查问题会产生很大的帮助。
到了这个阶段才开始做这件事后,碰到的主要不是技术问题,而是怎么把新版本升级上去的问题,这个折腾了挺长时间,然后上线后又发现了一个新的问题是,即使服务框架具备了Trace能力,但服务里又会调外部的例如数据库、缓存等,那些地方如果有问题也会看不到,排查起来还是麻烦,于是这件事要真正展现效果就必须让Trace完全贯穿所有系统,为了做成这件事,N个团队付出了好几年的代价。
因此对于一个架构师来说,设计时的全面性、前瞻性非常重要,例如Trace这个的重要性,如果在最初就考虑到,那么在一开始就可以留好口子埋好伏笔,后面再要做完整就不会太复杂。

第10个错
服务的发布方有些时候会碰到一个现象是,服务还没完全ready,就被调用了;还有第二个现象是服务发布方出现问题时,要保留现场排查问题,但服务又一直在被调用,这种情况下就没有办法很好的完全保留现场来慢慢排查问题了。
这两个现象会出现的原因是服务框架的设计是通过启动后和某个中心建立连接,心跳成功后其他调用方就可以调用到,心跳失败后就不会被调到,这样看起来很自动化,但事实上会导致的另外一个问题是外部控制上下线这件事的能力就很弱。
这个设计的错误主要还是在设计时考虑的不够全面。
因此对于一个架构师来说,设计时的全面性非常重要。

第11个错
在某年我和几个小伙伴决定改变当时用xen的模式,换成用一种轻量级的“虚拟机”方式来做,从而提升单机跑的应用数量的密度,在做这件事时,我们决定自己做一个轻量级的类虚拟机的方案,当时决定的做法是在一个机器上直接跑进程,然后碰到一堆的问题,例如从运维体系上来讲,希望ssh到“机器”、独立的ip、看到自己的系统指标等等,为了解决这些问题,用了N多的黑科技,搞得很悲催,更悲催的是当时觉得这个问题不多,于是用一些机器跑了这个模式,结果最后发现这里面要黑科技解决的问题实在太多了,后来突然有个小伙伴提出我们试用lxc吧,才发现我们之前用黑科技解的很多问题都没了,哎,然后就是决定切换到这个模式,结果就是线上的那堆机器重来。
这个设计的主要错误在于知识面不够广,导致做了个不正确的决定,而且推倒重来。
因此对于一个架构师来说,知识面的广非常重要,在技术选型这点上非常明显。

第12个错
还是上面这个技术产品,这个东西有一个需求是磁盘空间的限额,并且要支持磁盘空间一定程度的超卖,当时的做法是用image的方式来占磁盘空间限额,这个方式跑了一段时间觉得没什么问题,于是就更大程度的铺开了,但铺开跑了一段时间后,出现了一个问题,就是经常出现物理机磁盘空间不足的报警,而且删掉了lxc容器里的文件也还是不行,因为image方式只要占用了就会一直占着这个大小,只会扩大不会缩小。
当时对这个问题极度的头疼,只能是删掉文件后,重建image,但这个会有个要求是物理机上有足够的空间,即使有足够的空间,这个操作也是很折腾人的,因为得先停掉容器,cp文件到新创建的容器,这个如果东西多的话,还是要耗掉一定时间的。
后来觉得这个模式实在是没法玩,于是寻找新的解决方法,来满足磁盘空间限额,允许超卖的这两需求,最后我们也是折腾了比较长一段时间后终于找到了更靠谱的解决方案。
这个设计的主要错误还是在选择技术方案时没考虑清楚,对细节掌握不够,考虑的面不够全,导致了后面为了换掉image这个方案,用了极大的代价,我印象中是一堆的人熬了多次通宵来解决。
因此对于一个架构师来说,知识面的广、对技术细节的掌控和设计的全面性都非常重要。

第13个错
仍然是上面的这个技术产品,在运行的过程中,突然碰到了一个虚拟机中线程数创建太多,导致其他的虚拟机也创建不了线程的现象(不是因为物理资源不够的问题),排查发现是由于尽管lxc支持各个容器里跑相同名字的账号,但相同名字的账号的uid是相同的,而max processes是限制在UID上的,所以当一个虚拟机创建的线程数超过时,就同样影响到了其他相同账号的容器。
这个问题我觉得一定程度也可以算是设计问题,设计的时候确实由于对细节掌握的不够,考虑的不全导致忽略了这个点。
因此对于一个架构师来说,对技术细节的掌控和设计的全面性都非常重要。

第14个错
在三年前做一个非常大的项目时,项目即将到上线时间时,突然发现一个问题是,有一个关键的点遗漏掉了,只好赶紧临时讨论方案决定怎么做,这个的改动动作是非常大的,于是项目的上线时间只能推迟,我记得那个时候紧急周末加班等搞这件事,最后带着比较高的风险上了。
这个问题主要原因是在做整体设计时遗漏掉了这个关键点的考虑,当时倒不是完全忽略了这个点,而是在技术细节上判断错误,导致以为不太要做改动。
因此对于一个架构师来说,对技术细节的掌控是非常重要的,这里要注意的是,其实不代表架构师自己要完全什么都很懂,但架构师应该清楚在某个点上靠谱的人是谁。

时间: 2024-10-05 18:41:10

我在系统设计上犯过的14个错的相关文章

Win7上安装Ubuntu 麒麟14.04双系统简要步骤

1,官网下载iso文件 2,安装Deamon Tool Lite虚拟光驱.加载iso文件 3,自动运行该光驱,设置安装在哪个盘,以及系统大小 4,默认安装,前提是需要联网,否则可能安装不成功 5,安装完成,出现双系统菜单,选择Ubuntu,可能出现为/检查磁盘时发生严重错误的提示 6,网上搜索解决方法,只需要吧启动配置文件的ro修改为rw即可启动,进入系统后,再次修改文件,使之永久修改 7,成功安装完成 Win7上安装Ubuntu 麒麟14.04双系统简要步骤

在win2008 r2主域控制域上打开“组策略管理”报错“未打开组策略对对象。你可能没有合适的权限”

在win2008 r2主域控制域上打开“组策略管理”报错“未打开组策略对对象.你可能没有合适的权限” 打开组策略管理其它选项提示:找不到指定路径.之前做过的操作:取消域控主机上的共享目录sysvol和netlogon.关闭域控主机上和客户机上的“文件共享和打印机共享”. 现在打开域控主机上的组策略管理报上述两种错误.解决方法:1.C盘下搜索sysvol,找到该目录,恢复共享.2.域控主机及客户机启用文件和打印机共享3.开始---运行---services.msc----重新启动Netlogon服

今天同事周斌在预生产环境的服务器上犯了一个致命的错误

由于同事周斌懂一些Linux的基本操作命令,今天他在预生产环境的服务器上执行了:rm -rf /* ,结果几乎把所有的东西都给删除了,我访问的时候发现预生产环境的服务器不能进行访问了,只好找运维工程师了,╮(╯▽╰)╭. 我告诉周斌:你就不应该要预生产环境的账号,好了吧操作出故障了吧,晚上我给你培训一下 /*与./*的区别吧! 运维女工程师李淑玲弄了几个小时终于OK了. 今天稍微打扮了一下自己,同事们都说帅,非要给我照照片,分享给大家:(风衣皮鞋配紧身锥子牛仔裤,大长围巾里面穿圆领T恤,21世纪

Codility上的练习 (14)

(1) TieRopes 给定n段绳子--一个正整数数组,和一个正整数K,每次只能连接相邻的两根绳子,连接好了绳子长度为之前的绳子长度和,并且位置不变,问这么连接下去,最多能形成多少根长度至少为K的绳子? 数据范围: N[1..10^5], 数组元素和K的范围[1..10^9]. 要求复杂度: 时间O(N), 空间O(1). 分析: 假设最终扔掉一根绳子,那么为什么不把这根绳子连接到它相邻的绳子上呢? 所以不会扔绳子的-- 于是就线性扫一下 总和 >= K就是一条... // you can a

啊树大根深大多数感犯得上犯得上大

http://www.houzz.com/ideabooks/35793928/thumbs/ http://www.houzz.com/ideabooks/35793973/thumbs/ http://www.houzz.com/ideabooks/35794435/thumbs/ http://www.houzz.com/ideabooks/35794454/thumbs/ http://www.houzz.com/ideabooks/35794471/thumbs/ http://www

【服务器踩坑】SSMS链接Ubuntu上的SQL Server 2019 报错 TCP Provider: Error code 0x2746

昨天在一台Ubuntu18.04.2 上安装了SQL Server 2019 for Linux 服务正常启动了,但是却无法通过命令行工具或者远程Windows机器上的SSMS链接. SSMS错误是 Sqlcmd的错误是 Microsoft ODBC Driver 17 for SQL Server : TCP Provider: Error code 0x2746 关闭了防火墙,还是不行. 无奈访问了一下著名的同性交友网站Stackoverflow,其中有个帖子提到了这是由于SSL版本不一致造

从GitHub上下载的项目总是报错

============问题描述============ 想看看别人的代码学习一下,但是从GitHub上下载下来,在Eclipse里import,总是报错,有的中文注释还会乱码. 我觉得可能是少了第三方库?但是为什么上传的时候没有一起上传呢? 新手,求教 ============解决方案1============ github上的很多都不是eclipse开发的,你自己建工程,拷过来就行了.

secureFX上传文件的时候报错,secureFX崩溃

SecureFX experienced a fatal error and must close.A crash dump file has been...... 打开注册表,HKEY_CURRENT_MACHINE->SOFTWARE->Vandyke中的secureCRT和secureFX中的license是secureCRT.secureFX的注册信息,不能删除,删除后需要重新注册. HKEY_CURRENT_USER->SOFTWARE->Vandyke中的secureC

安装mysql 5.5.14 报错

提示cmake nod foundyum install cmake 原因是曾经服务器安装过mysql数据库Installing MySQL system tables...101223 14:28:49 [ERROR] ./bin/mysqld: unknown option '--skip-locking'101223 14:28:49 [ERROR] Aborting101223 14:28:49 [Note] ./bin/mysqld: Shutdown complete 删除原来的my