在做中间件设计时,你是如何权衡好利益相关者的?| 是面向运维,还是面向开发?

2020-01-15 11:24:39 浏览数 (1)

去年,我曾写过一篇 #架构设计与

又是一年过去了,时间是否虚度?虽然这一年的工作场景略显单调,但是却很充实,帮助我取得了更大的长进。

今天,我以中间件为载体,再次探讨一下 “如何与中间件的各种利益相关者相互协调?他们的关注点一般又会有哪些?” 的相关话题,也许你在曾经想到过,或思绪一闪而过,却没有探究其深层的根由。

就这个问题,我觉得是开发与运维对中间件需求不一致引起的,这其实没什么大不了,因为跟架构有关系的任何话题,如果权衡不好这两者之间的权重,那你的方案基本上也就告吹了。那么,作为一名应用研发,你一般会有哪些疑问?或作为一名系统运维,你会如何理解“面向开发”与“面向运维”之间的关联呢?

设计之初的常见疑问

对于应用研发来说,即学即用,且接入成本低廉的中间件是最好的,因此在中间件设计之前,他们通常会提出一些疑问。

疑问1:当架构师提出缓存自主研发方案时,他们会问 “为什么非要自主研发?下载个Redis用起来就行了呀,集群及高可用方案官方都提供了,很成熟呀”

其实不然,如果站在 “比数据库快,满足Request/Response就行” 的角度看当然没问题,但实际中间件需解决 “应用交互之间的技术解耦”,如某业务线因‘特殊原因’提出要使用HBase作为介质时,你只能再为他提供一个客户端,常此以往,运维侧需要投入的时间、人力及风险制衡将增大,更何况同时管理那么多SDK(或协议)也是个头痛的问题。

疑问2:当架构师提出通过添加代理层,解决客户端与服务端之间的解耦问题,他们又会问 “为什么要增加代理层?将SDK再次封装下,并把配置外移,便于维护就可以了呀。”

也不尽然,增加代理层的目的是将所有现有与将来可能出现的处理逻辑与规则(如路由控制、服务降级、协议转换)尽可能的放在代理层来实现,并在发布、维护及弹性伸缩、出现故障、冗余时,能够更快更灵活的变更,不仅达到技术解耦的效果,而且整个过程对于应用无感知。

值得一提的是,增加代理层,也方便在多机房场景中进行来回切换。

聊完了应用研发,再来说说系统运维,也许分布式或微服务架构,对于运维来说不是什么好事,不仅管理成本高,而且治理难度大,所以在设定中间件的目标过程中,他们似乎也有话要说。

疑问3:为了满足用户增长,当架构师提出利用分布式架构来提升扩展性,他们会说 “为什么要采用分布式架构?集群化架构也能扛住啊,我们的流量有那么大吗?你们有考虑过运维的感受吗?”

通过实际案例说明下,某调度中间件系统采用集群化架构,把几万个调度逻辑都放在一个WAR包里部署,然后运行在几十个同构的虚拟机上。过了不久,因为A调度出现阻塞,导致实例异常挂起,尽管几十个实例在运行,但由于资源互通,最终触发雪崩效应。

设想一下,如果这个场景使用的是分布式切片服务,每个切片服务于不同的应用,那影响的也只是有A调度和其他相关联的服务,而不会导致整个系统都不能使用。

这些疑问和见解,在许多公司的系统演变过程中或多或少都出现过,当然,纯靠 “堆人 堆机器” 打通关的也不在少数,甚至一年之间系统重构过2-3次的也屡见不鲜,在我看来,大部分原因都是前期没有调研,尤其没有在设计目标上做出权衡(如开发与运维之间的资源投入倾向),进入研发周期时才发现,对于快速发展的创业型公司来说无疑是沉重打击。

怎么理解 “面向开发” 与 “面向运维”?

我们先来看看网上热门多年的话题,在绝大多数人的印象中 “开发工程师与运维工程师的区别”

  • 开发工程师:能够使用一种(或多种)编程语言,完成某个产品(或项目),通常更关注制造过程,不关心运营生命周期。
  • 运维工程师:管理(或维护)系统、主机及产品,通常更关心运营生命周期,不关心制造过程,相比之下,心理素质较高;

从客观的叙述可以看到,由于岗位职责的不同与视角上的差异,无论是架构设计还是技术选型,在目标设定之初就容易引起开发与运维之间的博弈,尤其在使用者日趋大众化的今天,许多系统在设计之初并未明确技术的原则与目标,导致利益相关者(如开发和运维)之间失衡,引发复杂性向后倾斜,使得运维的工作越来越沉重,而且系统也越改越复杂,最终增大了事故发生率,甚至推倒重来、互相推诿。

简而言之,从中间件设计的原则与目标来看,我们可以基本得出以下公式:

  1. 如果设计目标 = 遵守轻量级接入原则(如SDK或Socket),通过代理层处理逻辑与规则的实现,并支持服务的编排部署、弹性伸缩,在出现故障、冗余或性能需求时,能够更快、更灵活的变更规则与逻辑,整个过程应用无感知。
  2. 那么,我认为这个中间件的设计目标 = 面向运维。
  3. 如果设计目标 = 采用轻量级开发框架处理逻辑与规则的实现,利用客户端直连的方式提供服务,无代理层,缩短应用开发时间,屏蔽应用升级风险,最终帮助实现敏捷式开发,而编排部署、弹性伸缩等高可用方案由应用架构本身提供。
  4. 那么,我认为这个中间件的设计目标 = 面向开发。

这样得结论已很明确,这两者之间并非互斥关系,而是一种互补关系。

通过个案例来说明一下

我来通过一个案例,说明一下当遇到 “如何权衡中间件在面向运维与面向开发的设计目标” 时,该如何做出选择。

先来介绍下故事情节,假设我们公司的业务在近几年突飞猛进,从业务视角来看,从‘单业务’发展为‘事业部(多条业务)’,从技术视角看,传统关系型数据库已无法承受持续增长的性能需求,这个时候我们就需要一个缓存系统来解决当前的性能痛点。

在十万火急的前置下,必须快速满足业务要求,所以没有仔细考虑,简单实现Jedis封装,并支持主动感知Master切换,匆忙上线了V1.0版本。

图1. 我们的分布式缓存V1.0

又过了一段时间,随着接入的应用系统越来越多,版本混乱、维护成本等现象频发,今天这头告急,明天那头起火,对于中间件运营及运维而言,真是苦不堪言。

当遇到这些问题时,我们对原因进行了复盘整理:

  • 业务增长,服务拆分:从项目切换至产品,而每种业务产品都以服务的形式提供;
  • 资源成本,测试手段:由功能迭代或BUG修复引发的SDK变更,无论如何说明、解释,应用侧都认为“基础组建必须全量回归测试”,但自动化测试又是一个长期工程,而传统黑盒测试又人力与时间资源都紧靠业务需求而难以实施,常此以往,污染的速度大大的超过治理的速度;
  • 单一职责,技术债务:虽然都使用JAVA作为主要编程语言,但每个团队都有自己的开发手法,有的有开发框架,有的没有,连用的JDK&第三方包都不一样;
  • 冷部署、扩容与排障:当出现故障、扩容或部署时,在这个时候就会有人跳出来问“你们没有热切换的方式吗?”,显然除了停机操作这种暴力手段之外,我们几乎没有更好的选择;

从复盘整理可以看出,在无法实施轻量级java框架的客观条件之下,为了能在面向运维与面向开发间做出权衡,我们发布了V2.0版本。

图2. 我们的分布式缓存V2.0

又过了一段时间,随着接入的应用系统越来越多,版本混乱、维护成本等现象频发,今天这头告急,明天那头起火,对于中间件运营及运维而言,真是苦不堪言。

通过增加代理层、支持分片化、协议私有化等功能,基本已达到“主体面向运维,且兼顾开发”的设计目标:

  • 实现技术解耦:轻量级SDK,把更多的逻辑与规则放到代理层实现,避免当需功能迭代或BUG修复时,规避测试与运维的双重压力
  • 独立切片部署:每个系统都能够独立使用自己的缓存分组,就算出现故障,也只影响自己;
  • 热部署、扩容:当出现故障、冗余或性能需求时,可以通过中间层的路由控制、灰度功能更快、更灵活的处理;

通过上面的陈述,我们可以看到 既面向运维,又面向开发,是中间件设计过程中始终追求的核心准则,但有时却会因为客观场景、技术债务、硬件环境等原因使其难以兼顾,而我们需要保证的,是在设计目标时做出合理的权衡,以保障系统的持续发展。

小 结

现在只要一谈起架构策略,相信许多人的脑海中会浮现出中台战略、基础服务下沉,以及大平台与小团队等一系列高大上的词汇,中间件作为企业应用级基础建设,在目标设计时的盲目往往会引发后期的故障发生与成本损失,所以我们必须思考 “如何与中间件的各种利益相关者相互协调,并合理满足他们的关注点”,希望对你有所帮助。

相信你在设计目标时,应该有遇到过开发与运维不一致的情况吧?最后你又是如何权衡的呢?欢迎你把答案写到评论区,和我一起讨论。

0 人点赞