去年,我曾写过一篇 #架构设计与开发、运维之间关系# 的文章,大体对在架构设计时如何权衡面向运维与面向开发进行过一通理论化梳理。也许是因为当时才刚刚开始写作,无论是案例,还是措词,都显得极其平庸,总感觉有一肚子话无法倾囊抖出。
又是一年过去了,时间是否虚度?虽然这一年的工作场景略显单调,但是却很充实,帮助我取得了更大的长进。
今天,我以中间件为载体,再次探讨一下 “如何与中间件的各种利益相关者相互协调?他们的关注点一般又会有哪些?” 的相关话题,也许你在曾经想到过,或思绪一闪而过,却没有探究其深层的根由。
就这个问题,我觉得是开发与运维对中间件需求不一致引起的,这其实没什么大不了,因为跟架构有关系的任何话题,如果权衡不好这两者之间的权重,那你的方案基本上也就告吹了。那么,作为一名应用研发,你一般会有哪些疑问?或作为一名系统运维,你会如何理解“面向开发”与“面向运维”之间的关联呢?
设计之初的常见疑问
对于应用研发来说,即学即用,且接入成本低廉的中间件是最好的,因此在中间件设计之前,他们通常会提出一些疑问。
疑问1:当架构师提出缓存自主研发方案时,他们会问 “为什么非要自主研发?下载个Redis用起来就行了呀,集群及高可用方案官方都提供了,很成熟呀”
其实不然,如果站在 “比数据库快,满足Request/Response就行” 的角度看当然没问题,但实际中间件需解决 “应用交互之间的技术解耦”,如某业务线因‘特殊原因’提出要使用HBase作为介质时,你只能再为他提供一个客户端,常此以往,运维侧需要投入的时间、人力及风险制衡将增大,更何况同时管理那么多SDK(或协议)也是个头痛的问题。
疑问2:当架构师提出通过添加代理层,解决客户端与服务端之间的解耦问题,他们又会问 “为什么要增加代理层?将SDK再次封装下,并把配置外移,便于维护就可以了呀。”
也不尽然,增加代理层的目的是将所有现有与将来可能出现的处理逻辑与规则(如路由控制、服务降级、协议转换)尽可能的放在代理层来实现,并在发布、维护及弹性伸缩、出现故障、冗余时,能够更快更灵活的变更,不仅达到技术解耦的效果,而且整个过程对于应用无感知。
值得一提的是,增加代理层,也方便在多机房场景中进行来回切换。
聊完了应用研发,再来说说系统运维,也许分布式或微服务架构,对于运维来说不是什么好事,不仅管理成本高,而且治理难度大,所以在设定中间件的目标过程中,他们似乎也有话要说。
疑问3:为了满足用户增长,当架构师提出利用分布式架构来提升扩展性,他们会说 “为什么要采用分布式架构?集群化架构也能扛住啊,我们的流量有那么大吗?你们有考虑过运维的感受吗?”
通过实际案例说明下,某调度中间件系统采用集群化架构,把几万个调度逻辑都放在一个WAR包里部署,然后运行在几十个同构的虚拟机上。过了不久,因为A调度出现阻塞,导致实例异常挂起,尽管几十个实例在运行,但由于资源互通,最终触发雪崩效应。
设想一下,如果这个场景使用的是分布式切片服务,每个切片服务于不同的应用,那影响的也只是有A调度和其他相关联的服务,而不会导致整个系统都不能使用。
这些疑问和见解,在许多公司的系统演变过程中或多或少都出现过,当然,纯靠 “堆人+堆机器” 打通关的也不在少数,甚至一年之间系统重构过2-3次的也屡见不鲜,在我看来,大部分原因都是前期没有调研,尤其没有在设计目标上做出权衡(如开发与运维之间的资源投入倾向),进入研发周期时才发现,对于快速发展的创业型公司来说无疑是沉重打击。
怎么理解 “面向开发” 与 “面向运维”?
我们先来看看网上热门多年的话题,在绝大多数人的印象中 “开发工程师与运维工程师的区别”
开发工程师:能够使用一种(或多种)编程语言,完成某个产品(或项目),通常更关注制造过程,不关心运营生命周期。
运维工程师:管理(或维护)系统、主机及产品,通常更关心运营生命周期,不关心制造过程,相比之下,心理素质较高;
从客观的叙述可以看到,由于岗位职责的不同与视角上的差异,无论是架构设计还是技术选型,在目标设定之初就容易引起开发与运维之间的博弈,尤其在使用者日趋大众化的今天,许多系统在设计之初并未明确技术的原则与目标,导致利益相关者(如开发和运维)之间失衡,引发复杂性向后倾斜,使得运维的工作越来越沉重,而且系统也越改越复杂,最终增大了事故发生率,甚至推倒重来、互相推诿。
简而言之,从中间件设计的原则与目标来看,我们可以基本得出以下公式:
如果设计目标 = 遵守轻量级接入原则(如SDK或Socket),通过代理层处理逻辑与规则的实现,并支持服务的编排部署、弹性伸缩,在出现故障、冗余或性能需求时,能够更快、更灵活的变更规则与逻辑,整个过程应用无感知。
那么,我认为这个中间件的设计目标 = 面向运维。
如果设计目标 = 采用轻量级开发框架处理逻辑与规则的实现,利用客户端直连的方式提供服务,无代理层,缩短应用开发时间,屏蔽应用升级风险,最终帮助实现敏捷式开发,而编排部署、弹性伸缩等高可用方案由应用架构本身提供。
那么,我认为这个中间件的设计目标 = 面向开发。
这样得结论已很明确,这两者之间并非互斥关系,而是一种互补关系。
通过个案例来说明一下
我来通过一个案例,说明一下当遇到 “如何权衡中间件在面向运维与面向开发的设计目标” 时,该如何做出选择。
先来介绍下故事情节,假设我们公司的业务在近几年突飞猛进,从业务视角来看,从‘单业务’发展为‘事业部(多条业务)’,从技术视角看,传统关系型数据库已无法承受持续增长的性能需求,这个时候我们就需要一个缓存系统来解决当前的性能痛点。
在十万火急的前置下,必须快速满足业务要求,所以没有仔细考虑,简单实现Jedis封装,并支持主动感知Master切换,匆忙上线了V1.0版本。
图1. 我们的分布式缓存V1.0
又过了一段时间,随着接入的应用系统越来越多,版本混乱、维护成本等现象频发,今天这头告急,明天那头起火,对于中间件运营及运维而言,真是苦不堪言。
当遇到这些问题时,我们对原因进行了复盘整理:
业务增长,服务拆分:从项目切换至产品,而每种业务产品都以服务的形式提供;
资源成本,测试手段:由功能迭代或BUG修复引发的SDK变更,无论如何说明、解释,应用侧都认为“基础组建必须全量回归测试”,但自动化测试又是一个长期工程,而传统黑盒测试又人力与时间资源都紧靠业务需求而难以实施,常此以往,污染的速度大大的超过治理的速度;
单一职责,技术债务:虽然都使用JAVA作为主要编程语言,但每个团队都有自己的开发手法,有的有开发框架,有的没有,连用的JDK&第三方包都不一样;
冷部署、扩容与排障:当出现故障、扩容或部署时,在这个时候就会有人跳出来问“你们没有热切换的方式吗?”,显然除了停机操作这种暴力手段之外,我们几乎没有更好的选择;
从复盘整理可以看出,在无法实施轻量级java框架的客观条件之下,为了能在面向运维与面向开发间做出权衡,我们发布了V2.0版本。
图2. 我们的分布式缓存V2.0
又过了一段时间,随着接入的应用系统越来越多,版本混乱、维护成本等现象频发,今天这头告急,明天那头起火,对于中间件运营及运维而言,真是苦不堪言。
通过增加代理层、支持分片化、协议私有化等功能,基本已达到“主体面向运维,且兼顾开发”的设计目标:
实现技术解耦:轻量级SDK,把更多的逻辑与规则放到代理层实现,避免当需功能迭代或BUG修复时,规避测试与运维的双重压力
独立切片部署:每个系统都能够独立使用自己的缓存分组,就算出现故障,也只影响自己;
热部署、扩容:当出现故障、冗余或性能需求时,可以通过中间层的路由控制、灰度功能更快、更灵活的处理;
通过上面的陈述,我们可以看到 既面向运维,又面向开发,是中间件设计过程中始终追求的核心准则,但有时却会因为客观场景、技术债务、硬件环境等原因使其难以兼顾,而我们需要保证的,是在设计目标时做出合理的权衡,以保障系统的持续发展。
小 结
现在只要一谈起架构策略,相信许多人的脑海中会浮现出中台战略、基础服务下沉,以及大平台与小团队等一系列高大上的词汇,中间件作为企业应用级基础建设,在目标设计时的盲目往往会引发后期的故障发生与成本损失,所以我们必须思考 “如何与中间件的各种利益相关者相互协调,并合理满足他们的关注点”,希望对你有所帮助。
相信你在设计目标时,应该有遇到过开发与运维不一致的情况吧?最后你又是如何权衡的呢?欢迎你把答案写到评论区,和我一起讨论。
来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/69901778/viewspace-2295472/,如需转载,请注明出处,否则将追究法律责任。
转载于:http://blog.itpub.net/69901778/viewspace-2295472/