zoukankan      html  css  js  c++  java
  • 第二章 演化式架构师

     
    2.3分区
     
    前面我们将架构师比作城市规划师,那么在这个比喻里面,区域的概念对应的是什么呢? 它们应该是我们的服务边界,或者是一些粗粒度的服务群组。作为架构师,不应该过多关 注每个区域内发生的事情,而应该多关注区域之间的事情。这意味着我们应该考虑不同的 服务之间如何交互,或者说保证我们能够对整个系统的健康状态进行监控。至于多大程度 地介人区域内部事务,在不同的情况下则有所不词。很多组织采用微服务是为了使团队的 自治性最大化,第10章会对这个话题做更多讨论。如果你就处在这样的组织中,那么你 会更多地依靠团队来做出正确的局部决定。
     
    但是在区域之间,或者说传统架构图中的框图之间,我们需要非常小心,因为在这些地方 犯的错误会很难纠正。
     
    代码架构师
    如果想确保我们创造的系统对开发人员足够友好,那么架构师需要理解他们的决定对 系统会造成怎样的影响。最低的要求是:架构师需要花时间和团队在一起工作,理想 情况下他们应该一起进行编码。对于实施结对编程的团队来说,架构师很容易花一定 的时间和团队成员进行结对。理想情况下,你应该参与普通的工作,这样才能真正理 解普通的工作是什么样子。架构师和团队真正坐在一起,这件事情再怎么强调也不过 分!相比通过电话进行沟通或者只看看团队的代码,一起和团队工作的这种方式会更 加有效。至于和团队在一起工作的频率可以取决于团队的大小,关键是它必须成为日 常工作的一部分。如果你和四个团队在一起工作,那么每四周和每个团队都工作半天, 可以帮助你有效地和团队进行沟通,并了解他们都在做什么。
     
    2.4 一个原则性的方法
     
    做系统设计方面的决定通常都是在做取舍,而在微服务架构中,你要做很多取舍!当选择 一个数据存储技术时,你会选择不太熟悉但能够带来更好可伸缩性的技术吗?在系统中存 在两种技术栈是否可接受?那三种呢?做某些决策所需要的信息很容易获取,这些还算是 容易的。但是有些决策所需要的信息难以完全获取,那又该怎么办呢?
     
    基于要达到的目标去定义一些原则和实践对做设计来说非常有好处。接下来让我们对它们 做一些讨论。
     
    2.4.1战略目标
     
    2.4.2原则
    为了和更大的目标保持一致,我们会制定一些具体的规则,并称之为原则,它不是一成不 变的。举个例羊,如果组织的一个战略目标是缩短新功能上线的周期,那么一个可能的原 则是,交付团队应该对整个软件生命周期有完全的控制权,这样他们就可以及时交付任何 就绪的功能,而不受其他团队的限制。如果组织的另一个目标是在其他国家快速增长业 务,你需要使用的原则可能就是,整个系统必须能够方便地部署到相应的国家,从而符合 该国家对数据存储地理位置方面的要求。
     
    很有可能这些原则并不适合你的组织。一般来讲,原则最好不要超过10个,或者能够写 在一张海报上,不然大家会很难记住。而且原则越多,它们发生重叠和冲突的可能性就 越大。
     
    Heroku 的 12 Factors (http://www12factor.net)就是一组能够帮助你在 Heroku 平台上创建 应用的设计原则,当然它们在其他的上下文中可能也有用。其中,有些原则实际上是为了让你的应用程序可以适应Heroku这个平台而引入的约束。约束是很难(或者说不可能) 改变的,但原则也是我们自己决定的。你应该显式地指出哪些是原则,哪些是约束,这样 用户就会很清楚哪些是不能变的。从个人角度来讲,我认为把原则和约束放在同一个列表 中是有好处的,这样我们就可以不时地回顾一下这些约束是否真的不可改变。
     
    2.4.3实践
     
    我们通过相应的实践来保证原则能够得到实施,这些实践能够指导我们如何完成任务。通 常这些实践是技术相关的,而且是比较底层的,所以任何一个开发人员都能够理解。这些 实践包括代码规范、日志数据集中捕获或者HTTP/REST作为标准集成风格等。由于实践 比较偏技术层面,所以其改变的频率会髙于原则。
     
    就像原则那样,有时候实践也会反映出组织内的一些限制。比如,如果你只支持CemOS, 那么相应的实践就应该考虑这个因素。
     
    实践应该巩固原则。比如前面我们提过一个原则是开发团队应该可以对软件开发全流程有 控制权,相应的实践就是所有的服务都部署在不同的AWS账户中,从而可以提供资源的 自助管理和与其他团队的隔离。
     
    2.4.4将原则和实践相结合
     
    有些东西对一些人来说是原则,对另一些人来说则可能是实践。比如,你可能会把使用 HTTP/REST作为原则,而不是实践。这也没什么问题,关键是要有一些重要的原则来指 导系统的演化,同时也要有一些细节来指导如何实现这些原则。对于一个足够小的群组, 比如单个团队来说,将原则和实践进行结合是没问题的。但是在一个大型组织中,技术和 工作实践可能不一样,在不同的地方需要的实践可能也不同。不过这也没关系,只要它们 都能够映射到相同的原则即可。比如一个.NET团队可能有一套实践,一个Java团队有另 一套实践,但背后的原则是相同的。
     
    2.4.5真实世界的例子
    我的同事Evan Bottchei•帮一个客户画出了如图2-1所示的图表。该图很清楚地显示了目 标、原则和实践之间的相互影响。几年间,实践改动得很频繁,而原则基本上没怎么变。 可以把这样一个图表打印出来并共享给相关人员,其中每个条目都很简单,所以开发人员 应该很容易记住它们。尽管每条实践背后还有很多细节,但仅仅能把它们总结表述出来也 是非常有用的。
    图2-1:原则和实践的真实例子
    上面提到的一些项可以使用文档来支撑,但大多数情况下我喜欢给出一些示例代码供人阅 读、研究和运行,从而传递上面涉及的那些信息。更好的方式是,创造一些工具来保证我 们所做事情的正确性。后面马上就会对这个话题做深入的讨论。
    战略目标(业务)->架构原则(系统设计)->技术实践(技术选型)
     
    2.5要求的标准(原则)
     
    当你浏览这些实践,并思考你需要做的取舍时,需要注意一个很重要的因素:系统允i午多 少可变性。我们需要识別出各个服务需要遵守的通用规则,-种方法是,给出一个好服务 的例子来阐释好服务的特点。在系统中什么是好服务“公民”呢?它需要有什么样的能力 才能保证整个系统是可控的,并且一个有问题的服务不会导致整个系统瘫痪?这些问题很 难回答,因为就像人一样,在某种上下文中是一个好公民不代表在其他上下文中也是,但 我们还是可以观察到各个服务中一些通用的优秀实践。一些关键领域有太多的变化方向, 而这可能会导致很多问题。就像1^{^的Ben Christensen说的那样,当我们在考虑一个更 大的全景图时,“系统应该由很多小的但有自治生命周期的组件构成,而且这些组件之间 有着紧密的关联”。所以在优化单个服务自治性的同时,也要兼顾全局。一种能帮助我们 实现平衡的方法就是,清楚地定义出一个好服务应有的属性。
     
    下面从几个方面定义出好服务应该具有的哪些属性:
    2.5.1 监控
    能够清晰地描绘出跨服务系统的健康状态非常关键。这必须在系统级别而非单个服务级别 进行考虑。在第8章会讲到,往往在需要诊断一个跨服务的问题或者想要了解更大的趋势 时,你才需要知道每个服务的健康状态。简单起见,我建汶确保所有的服务使用同样的方 式报告健康状态及其与监控相关的数据。
     
    你可能会选择使用推送机制,也就是说,每个服务主动把数据推送到某个集中的位置。你 可以使用Graphite来收集指标数据,使用Nagios来检测健康状态,或者使用轮询系统来 从各个节点收集数据,但无论你的选择是什么,都应尽量保持标准化。每个服务内的技术 应该对外不透明,并且不要为了服务的具体实现而改变监控系统。日志功能和监控情况类 似:也需要集中式管理。
     
    2.5.2 接口
    选用少数几种明确的接口技术有助干新消费者的集成。使用一种标准方式很好,两种也不 太坏,但是20种不同的集成技术就太糟糕了。这里说的不仅仅是关于接口的技术和协议。 举个例子,如果你选用了 HTTP/REST,在URL中你会使用动词还是名词?你会如何处理 资源的分页?你会如何处理不同版本的API ?
     
    2.5.3架构安全性
    一个运行异常的服务可能会毁了整个系统,而这种后果是我们无法承担的,所以,必须保 证每个服务都可以应对下游服务的错误请求。没有很好处理下游错误请求的服务越多,我 们的系统就会越脆弱。你可以至少让每个下游服务使用它们自己的连接池,进一步让每个 服务使用一个断路器。在第11章中讨论规模化微服务时,会就这个话题做更深入的讨论。
     
    返回码也应该遵守一定的规则。如果你的断路器依赖于HTTP返回码,并且一个服务 决定使用2XX作为错误码,或者把4XX和5XX混用,那么这种安全措施就没什么意 义了。即使你使用的不是HTTP,也应该注意类似的问题。对以下几种请求做不同的处 理可以帮助系统及时失败,并且也很容易追溯问题:(1)正常并且被正确处理的请求; (2)错误请求,并且服务识别出了它是错误的,但什么也没做;(3)被访问的服务宕机 了,所以无法判断请求是否正常。如果我们的服务没有很好地遵守这些规则,那么整个 系统就会更加脆弱。
     
    2.6代码治理
     
    聚在一起,就如何做事情达成共识是一个好主意。但是,花时间保证人们按照这个共识来 做事情就没那么有趣了,因为在各个服务中使用这些标准做法会成为开发人员的负担。我坚信应该使用简单的方式把事情做对。我见过的比较奏效的两种方式是,提供范例和服务 代码模板。
     
    2.6.1 范例
     
    编写文档是有用的。我很清楚这样做的价值,这也正是我写这本书的原因。但是开发人员 更喜欢可以查看和运行的代码。如果你有一些很好的实践希望别人采纳,那么给出一系列 的代码范例会很有帮助。这样做的一个初衷是:如果在系统中人们有比较好的代码范例可 以模仿,那么他们也就不会错得很离谱。
     
    理想情况下,你提供的优秀范例应该来自真实项目,而不是专门实现的一个完美的例子。 因为如果你的范例来自真正运行的代码,那么就可以保证其中所体现的那些原则都是合 理的。
     
    2.6.2裁剪服务代码模板
    如果能够让所有的开发人员很容易地遵守大部分的指导原则,那就太棒了。一种"了能的 方式是,当开发人员想要实现一个新服务时,所有实现核心属性的那些代码都应该是现 成的。
     
    Dropwizard (http://dropwizard.io)和 Karyon (https://github.com/Netflix/karyon)是两个基干 JVM的开源微容器。它们的运行模式相似,会自动下载一系列第三方库,这些库可以提供 一些特性,比如健康检查、HTTP服务、提供指标数据接口等。这样你就有了一个可以从 命令行启动的嵌入式servlet容器。这是一个很好的开始,但是你可以做得更多。在实际工 作中,你可以使用Dropwizard和Karyon作为基础,然后根据自己的上下文加入更多的定 制化特性。Dropwizard和Karyon是类似于spring容器的一个东西。
     
    举个例子,如果你想要断路器的规范化使用,那么就可以将Hystrix (https://github.com/ Netflix/Hystrix)这个库集成进来。或者,你想要把所有的指标数据都发送到中心Graphite服务器,那么就可以使用像 Dropwizard’s Metrics (https://github.com/dropwizard/metrics)这
     
    样的开源库,只需要在此基础上做一些配置,响应时间和错误率等信息就会自动被推送到 某个已知的服务器上。
     
    针对自己的开发实践裁剪出一个服务代码模板,不但可以提高幵发速度,还可以保证服务 的质量。
     
    当然,如果你的姐织使用多种不同的技术栈,那么针对每种技术栈都需要这一个服务代 码模板。你也可以把它当作一种在团队中巧妙地限制语言选择的方式。如果只存在基于 Java的服务代码模板,那么选用其他技术栈就意味着开发人员需要自己做很多额外的工 作。Netflix非常在意服务的容错性,因为它们不希望一个服务停止工作造成整个系统都无法正常工作。Netflix提供了一个基于JVM的库来处理这些问题。任何一个新技术栈的 引入都意味着要把这部分工作重新做一遍。相对于做这些事情的代价,Netflix更关心的 是,开发这些库时可能会引入的错误。如果某个新实现的服务的容错处理机制出错,其对 系统带来严重影响的风险也会增加。Netflix使用挎斗(sidebar)服务来降低这种风险。挎 斗服务会和-JVM进行本地通信,而为了完成这种通信,该JVM需要使用某些特定的第三 方库。
     
    有一点需要注意的是,创建服务代码模板不是某个中心化工具的职贵,也不是指导(即使 是通过代码)我们应怎样工作的架构团队的职责。应该通过合作的方式定义出这些实践, 所以你的团队也需要负责更新这个模板(内部开源的方式能够很好地完成这项工作)。
     
    我也见过一个团队的士气和生产力是如何被强制使用的框架给毁掉的。基于代码重用的目 的,越来越多的功能被加到一个中心化的框架中,直至把这个框架变成一个不堪重负的怪 兽。如果你决定要使用一个裁剪的服务代码模板,一定要想清楚它的职责是什么。理想情 况下,应该可以选择是否使用服务代码模板,但是如果你强制团队使用它,一定要确保它能够简化开发人员的工作,而不是使其复杂化。
    你还需要知道,重用代码可能引入的危险。在重用代码的驱动下,我们可能会引人服务之 间的耦合。有一个我接触过的组织非常担心这个问题,所以他们会手动把服务代码模板复 制到各个服务中。这样做的问题是,如果核心服务代码模板升级了,那么需要花很长时间 把这些升级应用到整个系统中。但相对干耦合的危险而言,这个问题倒没那么严重。还有 一些我接触过的团队,把服务代码模板简单地做成了一个共享的库依赖,这时他们就要 非常小心地防止对DRY (Don't Repeat Yourself,避免重复代码)的追求导致系统过度辋 合!这是一个很微妙的话题,所以第4章会做更深入的讨论。
     
    2.7技术债务
     
    有时候可能无法完全遵守技术愿景,比如为了发布一些紧急的特性,你可能会忽略一些约 束。其实这仅仅是另一个需要做的取舍而已。我们的技术愿景有其本身的道理,所以偏离 了这个愿景短期可能会带来利益,但是长期来看是要付出代价的。可以使用技术债务的概 念来帮助我们理解这个取舍,就像在真实世界中欠的债务需要偿还一样,累积的技术债务 也是如此。
     
    不光走捷径会引入技术债务。有时候系统的目标会发生改变,并且与现有的实现不符,这 种情况也会产生技术债务。
     
    架构师的职责就是从更高的层次出发,理解如何做权衡。理解债务的层次及其对系统的影 响非常重要。对于某些组织来说,架构师应该能够提供一些温和的指导,然后让团队自行 决定如何偿还这歧柃米倩务„而其他的组织就需要更加结构化的方式,比如维护一个债务
     
    2.8例外菅理
     
    原则和实践可以指导我们如何构建系统。那么,如果系统偏离了这些指导又会发生什么 呢?有时候我们会决定针对某个规则破一次例,然后把它记录下来。如果这样的例外出 现了很多次,就可以通过修改原则和实践的方式把我们的理解固化下来。举个例子,可 能我们有一个实践论述应该总是使用MySQL做数据存储,但是后来有足够的证明表明在 海量存储的场景下应使用Cassandra,这时就可以对实践进行修改:“在大多数场景下使用 MySQL做存储,如果是数据快速增长的场景,可以使用Cassandra。”
     
    2.9集中治理和领导
     
    架构师的部分职责是治理。那么治理又是什么意思呢? COBIT (Control Objectives for Information and Related Technology,信息和相关技术的控制目标)给出了一个很好的定义:
     
    治理通过评估干系人的需求、当前情况及下一步的可能性来确保企业目标的达成, 通过排优先级和做决策来设定方向。对于已经达成一致的方向和目标进行监督、
     
    ——COBIT 5
    在IT的上下文中有很多事情需要治理,而架构师会承担技术治理这部分的职责。如果说, 架构师的一个职责是确保有一个技术愿景,那么治理就是要确保我们构建的系统符合这个 愿景,而且在需要的时候还应对愿景进行演化。
    架构师会对很多事情负责。他们需要确保有一组可以指导开发的原则,并且这些原则要与 组织的战略相符。他们还需要确保,以这些原则为指导衍生出来的实践不会给开发人员带 来痛苦。他们需要了解新技术,需要知道在什么时候做怎样的取舍。上述这些职责已经相 当多了,但是他们还需要让同事也理解这些决定和取舍,并执行下去。对了,还有前面提 到的:他们还需要花时间和团队一起工作,甚至是编码,从而了解所做的决定对团队造成 了怎样的影响。
     
    要求很高,是吗?没错。但是我坚定地认为他们不应该独自做这些事情,可以由一个治理小组来做这个工作,并确定愿景。
    -般来讲,治理是一个小组活动。它可以是与一个足够小的团队进行非正式聊天,也可以 是在比较大的范围内,与一个有着正式成员的小组进行结构化例会。在这些会议上,可以 讨论前面提到的那些原则,有必要的话也可以对其进行修改。这个小组应该由技术专家领 导,并且要宥一线人员的参与。这个小组也应该负责跟踪和管理技术风险。
     
    我很喜欢的一种模式是,由架构师领导这个小组,但是每个交付团队都有人参加。架构师 负责确保该组织的正常运作,整个小组都要对治理负责。这样职责就得到了分担,并且保 证有来自高层的支持。这也可以保证信息从开发团队顺畅地流入这个小组,从而保证小组 做出更合理的决定。
     
    有时候架构师可能不认同小组做的决定,这时应该怎么办?我曾经面对过这样的场景,我 认为这是架构师需要面对的最富有挑战性的场景之一。事实上,大多数情况下我会认同小 组的决定。我曾经尝试说服大家,但事实证明这很难做到。一个小组通常会比单个人更加聪明,而且我也不止一次被证明是错误的!如果你给一个小组权力去做决定,但在最后又 忽略了这个决定,那这个小组就毫无意义可言了。有时候我也会对小组施加影响。那么我 为什么这么做,我会在什么时候做,又会怎么说呢?
     
    类比一下教小孩儿骑自行车的过程。你没法替代他们去骑车。你会看着他们摇摇晃晃地前 行,但是,如果每次你看到他们要跌倒就上去扶一把,他们永远都学不会。而且无论如 何,他们真正跌倒的次数会比你想象的要少!但是,如果他们马上就要驶入车流繁忙的大 马路,或者附近的鸭子池塘,你就必须站出来了。类似地,作为一名架构师,你必须要在 团队驶向类似鸭子池塘这样的地方时抓紧他们。还有一点要注意的是,即使你很清楚什么 是对的,然后尝试去控制团队,也可能会破坏和团队的关系,并且会使团队感觉他们没有 话语权。有时候按照一个你不同意的决定走下去反而是正确的,知道什么时候可以这么 做,什么时候不要这么做是很困难的,但有时也很关键。
    2.10建设团队
     
    对于一个系统技术愿景的主要负责人来说,执行愿景不仅仅等同于做技术决定,和你一起 工作的那些人自然会做这些决定。对于技术领导人来说,更重要的事情是帮助你的队友成 长,帮助他们理解这个愿景,并保证他们可以积极地参与到愿景的实现和调整中来。
     
    我坚定地相信,伟大的软件来自于伟大的人。所以如果你只担心技术问题,那么恐怕你看 到的问题远远不及一半。
     
    2.11 小结
     
    总结一下本章,下面是我认为的一个演进式架构师应该承担的职责。
     
    •愿景
     
    确保在系统级有一个经过充分沟通的技术愿景,这个愿景应该可以帮助你满足客户和组 织的需求。
     
    •同理心
     
    理解你所做的决定对客户和同事带来的影响。
     
    •合作
     
    和尽量多的同事进行沟通,从而更好地对愿景进行定义、修订及执行。
    •适应性
     
    确保在你的客户和组织需要的时候调整技术愿景。
     
    •自治性
     
    在标准化和团队自治之间寻找一个正确的平衡点。
     
    • 治理
     
    确保系统按照技术愿景的要求实现。
     
    演进式架构师应该理解,成功要靠不断地取舍来实现。总会存在一些原因需要你改变工作 的方式,但是具体做哪些改变就只能依赖于自己的经验了。而僵化地固守自己的想法无疑 是最糟糕的做法。
     
    虽然本章的大部分建议对任何一个系统架构师来说都适用,但是在微服务系统中,架构师 需要做更多的决定,因此,能更好地平衡这些取舍是非常关键的。
     
     
     
     
     
  • 相关阅读:
    golang垃圾回收和SetFinalizer
    读《我编程,我快乐--程序员职业规划之道》
    golang cache--go-cache
    golang web framework--Martini
    golang http proxy反向代理
    php 设计模式之简单工厂模式
    php 设计模式之责任链模式
    什么是反向索引
    ip地址二进制转十进制
    架构师之路
  • 原文地址:https://www.cnblogs.com/mongotea/p/11992020.html
Copyright © 2011-2022 走看看