如何成为一个技术全面的架构师

本文首发于InfoQ:http://www.infoq.com/cn/articles/the-well-rounded-architect

架构师是一个充满挑战的职业,需要关注很多维度和技术。只专注于单一领域的架构师并不是优秀的架构师。Pat Kua(原ThoughtWorks咨询师)是一位经验丰富的技术人员,他指出,一个好的架构师需要是技术全面的架构师,他还在文中探讨了成为一个技术全面的架构师所必须具备的六个特质。

  • 作为技术领导者
  • 作为开发人员
  • 聚焦系统
  • 具备企业家思维
  • 具备权衡策略思维与战术思维
  • 能够进行良好的沟通

作为技术领导者

一名好的软件架构师需要明白,作为领导者并不一定要告诉开发人员做什么。相反,好的架构师就像一个导师,能够带领开发团队向同一个技术愿景前进。好的架构师会借助于讲故事、影响力、引导冲突、构建信任等领导技能,将他们的架构愿景变成现实。一个好的领导者,同时也是一个好的架构师。他/她会仔细听取每个参与者的意见,通过与团队的反馈互动调整他们的愿景。

作为开发人员

一个架构师同时又是一个好的开发人员。通常,做出一个良好的架构选择需要权衡理想的架构状态与软件系统的当前状态。例如,如果一个问题更适合采用关系型数据库来解决,那么将文档数据库引入到系统中的做法是毫无道理的。一个架构师如果不考虑技术选型与问题域之间的匹配度,那么会很容易受到各种技术的诱惑——这也就是常见的“象牙塔式架构师”行为模式。

缓解这种情况的最佳方式是让架构师多与开发人员待在一起,花一些时间在代码上。了解系统的构建方式及系统的约束,这将帮助架构师在当下环境做出正确的选择。

聚焦系统

经验丰富的开发人员明白代码只是软件的一个部分。为了让代码可运行,他们还需要了解代码在生产环境中运行良好所需的其他重要质量属性。他们需要考虑部署过程、自动化测试、性能、安全和可支持性等方面。开发人员可能以临时的方式来实现这些质量属性,而架构师不仅需要专注于了解代码,还要了解并满足不同利益相关者(如支持、安全和运营人员)的需求。一个好的架构师需要专注于寻找那些能够满足不同利益相关者需求的解决方案,而不是选择针对某一个参与者的偏好或风格进行优化的工具或方法。

所有的技术选型都有相关的成本和收益,一个好的架构师需要从这两个角度考虑新的技术选型。成功的企业家愿意承担风险,不过也会寻求快速学习和快速失败的方法。架构师也可以用类似的方式做出技术选型,收集真实世界中有关短期和长期成本的信息,以及他们可能意识到的好处。

这方面一个很好的例子是,架构师避免承诺立即使用一个在阅读新文章时看到的工具或某一会议上听过的工具。相反,他们试图通过架构调研来了解工具在其环境中的相关性,以收集更多信息。他们对于工具的选择不是基于销售量,而是考虑他们需要什么、以及这个工具所提供的价值。他们还会寻找这些工具背后的隐性成本,例如工具的支持情况(如文档化程度、社区使用情况),工具可能带来的约束或长期来看可能引入的额外风险。

权衡策略思维与战术思维

许多团队由一些独立的开发人员一起构建软件,而每个人都倾向于选择自己最舒适或最有经验的工具和技术。好的架构师会持续关注可能有用的新技术、工具或方法,但不一定立即采用它们。技术采用往往需要长期的考量。架构师将在团队和组织层面寻求敏捷度(允许团队快速采取行动)和对齐(保持足够的一致性)之间的良好平衡。建立自己的技术雷达这样的练习是用战略思维探索技术的一个有用工具。

良好的沟通

架构师需要知道,有效沟通是建立信任和影响团队以外成员的关键技能。他们知道不同群体使用不同的词汇,而使用技术术语和描述与业务人员沟通将会变得比较困难。与其谈论模式、工具和编程概念,架构师需要使用听众熟悉的词汇与之交流,诸如风险回报、成本和收益等。这比单纯使用技术词汇进行沟通来得更好。架构师还需要认识到团队内部沟通与外部沟通同样重要,可以使用图表和小组讨论的方式来建立和完善技术愿景,并进行书面记录(如架构决策日志或Wiki等),从而为将来留下可追溯的历史。

结论

做一个技术全面的架构师并不容易,因为有很多方面需要我们关注,而每个方面都有很多作为开发人员经常不会专注并练习的技能。其实最重要的不一定是一个架构师的能力,而是他们在每个不同的领域都有足够的专业知识。仅仅掌握上述某个领域的架构师不如在六个方面都有良好专业知识的架构师有价值。

原文:The Well Rounded Architect

Pat Kua/文 禚娴静/译

Share

给CxO的微服务指南

文/Jim Highsmith & Neal Ford 译者/禚娴静

这是技术雷达系列文章的第四篇。在这一系列的文章中,技术雷达的作者们向企业领导者分享了他们对于那些推动业务差异化的技术问题和解决方案的洞见和经验。ThoughtWorks技术雷达由ThoughtWorks全球技术专家咨询委员会创建,它包含了对软件开发和业务战略有显著影响的技术趋势组合,2016年是技术雷达发布的第七年。

未来已经在这里,它只是分布不均。—— 威廉·吉布森

数字时代就在我们身边。将软件开发视为高成本开销而不是竞争力的企业将会举步维艰。为了参与并在这个数字世界中繁荣兴旺,企业必须学会适应我们这个时代的不确定性—更快地将新产品推向市场,快速而有效地改进当前的产品,并为客户提供有意义的数字体验。

为了实现这些目标,企业需要将敏捷性/适应性集成到三个领域:人、流程和技术。

人们需要适应试验和改变。以“迭代”的形式推进这一过程,并加强学习。技术,包括技术架构,需要支持快速地交付产品与服务。

技术的敏捷性不能被忽视——敏捷的团队和流程并不能弥补笨重的技术所带来的缺陷。

但作为一个高级管理人员,在这样一个充满了新技术的世界,你应该将注意力投注在哪里呢?

这个问题的答案被那些不停地讨论着看似神秘话题的技术人员们所掩盖。哪些神秘的话题需要得到管理层的注意呢?其中最重要的话题之一是微服务。原因如下所述:

微服务影响人、流程和技术:包括团队组织结构,流程和实践(如DevOps),以及重新调整架构以适应我们要解决的问题,而并非纯技术层面。微服务促进了每个领域的适应性。它值得管理层花时间去了解其潜在的贡献。

技术敏捷度

微服务架构风格的特性是由一组极小的服务组成,它们彼此独立且单独部署。微服务由Netflix这样的公司推广开来。每个服务包含一个离散的业务功能,它在技术上与其他服务相隔离,产生了类似乐高积木的效果:开发人员可以将服务切换为一个新的服务,而无需破坏其他服务。就像巨型的乐高模型可以由75,000块乐高组成,大型的数字应用程序也可以由这些受乐高思想启发的服务所构建。

这种架构有一些明显的好处。首先,每个服务与其他服务高度解耦,这意味着它们是自包含的。因此,一个服务中的技术更改(例如数据结构更改)不会影响另一个服务。服务仍然可以通过消息传递进行通信,但是任何一个服务都不允许修改另一个服务的实现细节。

第二,因为开发人员不需要共享基础设施,他们可以选用适合于该问题复杂度的技术栈来实现每一个服务。考虑到当今技术栈的复杂性,在同一应用程序中,使用简单工具处理简单问题和使用复杂工具处理复杂问题的能力使开发团队在增加了灵活性的同时也降低了成本。领导者重视能将复杂问题简化的技术专家。

第三,每个服务封装了业务功能,它更容易促成围绕特定问题域的团队,而不是通过作业功能分割的团队。例如,服务团队通常包括开发人员、业务分析师、DBA、运维人员和QA—即构建和部署服务所需的一切角色。这样一来便降低了协调成本。

“从功能性组织结构向产品或服务结构转变”是敏捷企业转型的一个日益增长的趋势。而微服务架构支持了这种趋势的变化。

最后,因为每个服务是相互隔离的,所以以服务组成的架构既快速又灵活。同时因为服务的业务范围很小,对服务的更改可以快速地发生,这为开发人员提供了新的高级功能。一旦架构师设计了一个小型独立服务的系统,其中应用程序由部署的多个服务之间的消息传递组成,多变量测试等操作就变得容易了。

例如,企业可能对他们网站的未来发展方向并不确定。因此他们设计了具有相似性但功能不同的两种服务,并向不同的用户组部署不同的版本,以获取结果从而推动未来的发展。像Facebook这样的公司也是通过使用这种类型的A / B测试进行试验来分析他们的用户。

标准化一直是IT组织降低成本的方式之一。不幸的是,它也降低了灵活性—标准化越多,灵活性越少。通过采纳微服务架构,架构师和开发人员可以使用更加多样化且能够紧密反映问题复杂度的技术栈来设计应用程序。

微服务架构风格与许多企业部署软件和分配IT资源的方式相反。许多架构风格的主要目标之一是有效地利用共享资源(操作系统、数据库服务器、应用程序服务器等)。由于资源的成本影响了底线,因此这些公司建立了软件架构以最大化共享资源。

然而,共享资源有一个缺点。无论开发人员如何有效地构建与这些服务器的隔离,都会出现对这些资源的竞争。有时组件由于依赖管理而互相干扰,有时由于两个组件争用某些资源(例如CPU)而产生问题。这就不可避免地会导致共享组件以并不期望的方式进行交互。

容器和解耦

在软件交付中,有两个关键的技术“环境”:开发人员工作的开发环境,以及IT运维人员管辖范围的部署环境。传统情况下,在这两个环境之间移动代码充满了技术错误,冗长的时间延迟以及组织层面的沟通不畅。

几年前,一些有趣的事情发生了:Linux对于大多数企业足够友好,Linux的变体在商业上免费—但是这不足以影响技术架构。

接下来,开源的创新与敏捷开发技术的结合鼓励了开发人员创建各种工具,将许多繁琐的运维手工操作自动化。这被许多人称为DevOps革命。

这场革命使得开发团队和IT运维人员通过使用Puppet、Chef和Docker等高级工具更加紧密地联系在一起。意外地,Linux的变体允许免费操作,开发人员可以在不受干扰的情况下将其组件部署到一个原始的操作系统。一整个可能的错误类别就突然消失了,因为组件之间能够相互解耦。

如果开发人员可以构建他们自己的现实环境,他们必须减少与运维部门的协调,也就减少了组织间的摩擦。用程序启动类生产环境的能力消除了测试、集成、共享环境下的资源竞争、以及一系列其他问题。

21世纪的架构敏捷度

在治理方面,微服务架构风格有其他的好处。传统的做法是,企业架构师定义了组织的共享技术栈,以最大化项目间的资源使用,同时最大程度地减少支撑成本。例如,一个大型企业可能将Java和Oracle标准化以作为其主要开发平台。如果每个人都使用Oracle,那么该企业的一切都可以存储在一个工业强度的数据库中。

规范化治理有一个缺点—通常,为了标准化的某一目的,团队必须使用并不太理想的工具。与此同时,还有一个潜在的更微妙的缺点。例如,考虑一个已经选择在特定消息队列上标准化的大型企业。在评估需要此共享基础设施的所有项目时,企业架构师会找出最复杂的场景,并选择一个适合这种复杂度的工具来处理它们。

然而,许多项目并不具备这个最复杂的场景。但因为每个项目必须共享相同的基础设施,所以每个团队都得承担其他团队的最大复杂度。这种情况也发生在数据库层。许多项目只需要几个记录的简单数据存储,并不需要复杂的查询功能,但最终都使用了具有工业强度的数据库服务器,只因为这个企业的标准如此。

容器化技术解决了这个问题,因为它远离了共享基础设施—每个项目都部署在自己原始的、容器化的环境中。因为不存在共享的动力去选择工具,所以每个项目刻意选择更适合自己的工具。

当然,如果企业架构师允许每个项目选择自己的技术栈,那么会存在一些严重的缺点。我们经常看到一个称之为““Goldilocks治理”(企业架构师选择几个技术栈—简单、中等和复杂,并根据规模分配新项目)的实践,它适用于高度解耦的环境。这些知识是可迁移的,该公司仍然可以从中受益,但是却可以远离那些由于疏忽大意而将问题过于复杂化的行为。

为什么我们会谈到这儿?

Eric Evans的《领域驱动设计》一书对微服务架构发展产生了巨大的影响。它介绍了一种将大问题空间分解为领域或重要实体(如客户和订单)及其关系(客户下订单)和行为的技术。领域定义的一部分是有关边界上下文的概念:每个领域形成一个围绕实现细节的封装层。

例如,如果分析人员识别了一个Customer领域,那么它存在于自己的边界上下文中。在Customer的上下文中,开发人员知道所有的实现细节:类结构,数据库模式等。

但是,其他边界上下文(如Orders)不能看到这些实现细节。虽然领域可以为了协调的目的互相发送消息,但是任何一个领域都不能穿透另一个领域的边界上下文。因此,在一个边界上下文中的开发人员可以自由地更改实现,而不必担心破坏其他领域。

微服务中的容器是领域驱动设计(DDD)中边界上下文的物理表现:每个容器代表了一层封装,以防止实现细节干扰系统的其他部分。边界上下文提供的隔离展示了结构化软件架构的不同方式。

在过去,设计架构主要围绕技术架构:架构模式、库、框架等。例如,分层架构在许多组织中是很常见的:

[图1]传统的分层架构

在图1中,架构师沿技术层进行分离,使之在需要时可以很容易地替换一整层的内容。例如,如果开发人员需要更改持久机制,所有相关代码都会出现在持久层中。

那么开发人员多久更换一次整个持久层呢?几乎从不。但你的团队多长时间基于像Customer这样的概念工作呢?每天!

在图1分层架构中,Customer处于哪一层呢?其中部分在表示层、业务层、持久层等等。因此,项目架构上通用单元的变化在分层架构中并没有得到很好的支持,原因是通用的变更影响到了每一层。

如果不同层代表了开发团队的不同部分,其影响会更严重。例如,对Customer的更改可能涉及到用户界面、业务逻辑、持久层和其他特性。如果你的组织由开发人员、DBA、用户界面设计师和运维人员组成,而他们在相互隔离的HR部门下,那么进行常见更改的协调成本是巨大的。

微服务强调解耦和容器化,翻转了图1中的传统做法,使领域成为架构的主要元素,如图2所示。

图2:以领域为中心的架构

在图2中,分层结构仍然存在,但是其耦合边界是领域的边界,它将技术架构归入实现细节,并用领域对其进行封装。以技术为中心的组织单元中,员工与员工彼此隔离,要想在这样的组织中构建以领域为中心的架构是很难的。

许多技术人员在构建数字化企业中会遇到这样的问题:想要支持如移动应用等新举措,却被那些需要拆分的遗留系统所拖累。如今,这些企业越来越多地引入微服务作为这种拆分过程的关键战略。

Greenfield项目得益于DDD实践。通过DDD理解了他们的问题领域以及重要组件之间的接口所在。对于现有项目,更加模块化的系统会促使开发者解开事务性的泥球,并且可以在那些属于一起的组件和偶然耦合的组件之间做更清楚地区分。

团队

你还将遇到微服务对团队影响:一个架构风格是如何推动开发团队重组的。

1968年,梅尔文·康威对软件开发做了一个很有预见性的观察,被称为康威定律

设计系统的组织,其产生的设计等价于这些组织间的沟通结构。

康威定律对软件开发的意义是什么呢?你的设计反映了你的团队结构。企业常见的团队结构是由人力资源部门推动的,他们将职能类似的技术专家组织在一起。虽然这是一种符合逻辑的排序算法,但这种结构在设计自包含服务时效果不佳。

如我们认为的康威逆定律,现在许多公司在围绕业务领域组织跨职能团队,而不是围绕技术分层构建。例如,一个Customer服务可能包括一个业务分析师、开发人员、QA、UX、DBA和运维人员。

团队会在准备好之后再部署服务,而不是先构建一些东西传递到运维人员,使之包含在下一个巨大的发布中。许多选择微服务的公司使用持续部署,以尽快将变更投入生产环境中。

总结

企业高管可能会认为微服务是最新流行词而不予考虑,但那些了解这种架构影响的人可以获得实实在在的好处。微服务可以提高交付速度,增强灵活性,并提高效率。他们将工作进行重组,并与业务问题域保持一致,而不是技术域;从一组独立,更易于开发和维护的服务中创建业务应用程序;更好地匹配技术解决方案与业务问题的复杂程度;构建可以帮助重组现有遗留系统以及创建能够快速响应不断变化的业务条件的新产品和服务的自适应架构。

威廉·吉布森是正确的—许多公司已经将IT竞争力看作鲁棒性一个新的度量。对于这些企业来说,未来已经在这里了。其他还没有意识到这一点的公司可能会发现他们的未来已经受到了影响。

原文链接:https://www.thoughtworks.com/cn/insights/blog/cxo-guide-microservices


更多精彩洞见,请关注微信公众号:思特沃克

Share

服务拆分与架构演进

本文首发于InfoQ:http://www.infoq.com/cn/articles/service-split-and-architecture-evolution

领域驱动设计和服务自演进能力是内功。

前言

《微服务的团队应对之道》提到,微服务帮助企业提升其响应力,而企业需要从DevOps、服务构建、团队和文化四点入手,应对微服务带来的复杂度和各种挑战,从而真正获益。如果说运维能力是微服务的加油站,服务则是其核心。

gas

企业想要实施微服务架构,经常问到的第一个问题是,怎么拆?如何从单体到服务化的结构?第二个问题是拆完后业务变了增加了怎么办?另外,我们想要改变的系统往往已经成功上线,并有着活跃的用户。那么对其拆分还需要考虑现有的系统运行,如何以安全最快最低成本的方式拆分也是在这个过程中需要回答的问题。

本文会针对以上问题,介绍我们团队在服务拆分和演进过程中的实践和经验总结。

我们项目架构的演化历程

1-evolution-course

该项目始于2009年,到现在已有7年的时间。在这7年中覆盖的业务线不断扩大,从工单、差旅、计费、文件、报表、增值业务等;业务流程从部分节点到用户端的全线延伸;7年间打造多个产品,架构经历了多次调整,从单体架构、RPC、服务化、规模化到微服务。

主要架构变迁如下图所示:

2-process

在这7年架构演进路上,我们遇到的主要挑战如下:

  • 如何拆?即如何正确理解业务,将单体结构拆分为服务化架构?
  • 拆完后业务变了增加了怎么办?即在业务需求不断发展变化的前提下,如何持续快速地演进?
  • 如何安全地持续地拆?即如何在不影响当下系统运行状态的前提下,持续安全地演进?
  • 如何保证拆对了?
  • 拆完了怎么保证不被破坏?

问题1:如何将单体结构拆分为服务化架构?

就如庖丁解牛一样,拆分需要摸清内部的构造脉络,在筋骨缝隙处下刀。那么微服务架构中,我们认为服务是业务能力的代表,需要围绕业务进行组织。拆分的关键在于正确理解业务,识别单体内部的业务领域及其边界,并按边界进行拆分。

1. 识别业务领域及边界。

首先需要将客户、体验设计师、业务分析师、技术人员集结在一起对业务需求进行沟通,随后对其进行领域划分,确定限界上下文(Boundary Context),也称战略建模。

以下我们经常使用的方法和参考的红蓝宝书:

  • Inception-> User Journey | Scenarios,用于梳理业务流程,由粗粒度到细粒度逐一场景分析。
  • 四色建模,用于提取核心概念、关键数据项和业务约束。
  • 领域驱动设计-战略设计,用于划分领域及边界、进行技术验证。
  • Eventstorming,用于提取领域中的业务事件,便于正确建模。

3-modeling

Inception与DDD战略设计的对比:

4-inception

一个业务领域或子域是一个企业中的业务范围以及在其中进行的活动,核心子域指业务成功的主要促成因素,是企业的核心竞争力;通用子域不是核心,但被整个业务系统所使用;支撑子域不是核心,不被整个系统使用,该能力可从外部购买。一个业务领域和子域可以包括多个业务能力,一个业务能力对应一个服务。领域的边界即限界上下文,也是服务的边界,它封装了一系列的领域模型。

一个业务流程代表了企业的一个业务领域,业务流程所涉及的数据或角色或是通用子域,或是支撑子域,由其在企业的核心竞争力的角色所决定。比如企业有统一身份认证,决策不同部门负责不同的流程任务,那么身份认证子域并不产生业务价值,不是业务成功的促成因素,但是所有流程的入口,因而为通用子域,可为单独服务;而部门负责的业务则为核心子域。

举个例子

工单业务流程:

某企业为服务人员提供工单服务的业务流程简化如下。首先搜索服务人员,选取服务人员购买的服务,基于目标国家的工单流程,向服务人员收取资料,对其进行审计,最后发送结果。

5-work-order识别的领域:

其中服务为其核心竞争能力,包括该企业对全球各国的政策理解,即法律流程,服务资料(问卷),计算服务,资料审计服务,相比其他竞争对手的服务(价位/效率等),这些都为改企业提供核心的业务价值,自然也是核心子域。而其他用于统计改企业员工工作的工单,组织结构和员工为支撑子域,并不直接产生业务价值

6-recognition

领域划分的原则

在划分的过程中,经常纠结的一个问题是:这个模型(概念或数据)看起来放这个领域合适,放另一个也合适,如何抉择呢?

  • 第一,依据该模型与边界内其他模型或角色关系的紧密程度。比如,是否当该模型变化时,其他模型也需要进行变化;该数据是否通常由当前上下文中的角色在当前活动范围内使用。
  • 第二,服务边界内的业务能力职责应单一,不是完成同一业务能力的模型不放在同一个上下文中。
  • 第三,划分的子域和服务需满足正交原则。领域名字代表的自然语言上下文保持互相独立。
  • 第四,读写分离的原则。例如报表需有单独报表子域。核心子域的划分更多基于来自业务价值的产生方,而非不产生价值的报表系统。
  • 第五,模型在很多业务操作中同时被修改和更新。
  • 第六,组织中业务部分的划分也是一种参考,一个业务部门的存在往往有其独特的业务价值。

简单打个比方,同一个领域上下文中的模型要保持近亲关系,五福以内,同一血统(业务)。

领域划分的误区和建议

  • 业务能力还是计算能力?在划分一些貌似通用的领域时,其实只是用到了通用的计算能力而不是业务能力,只需采用通用库的方式进行封装,而无需使用服务的方式。如我们系统的模板服务,是构建通用的模板服务,服务于整个平台的服务;还是每个服务拥有独立的模板模块?
  • 尽早识别剥离通用领域。如身份认证与鉴权领域,是企业系统中最复杂、有相对多变的领域,需要及早隔离它对核心业务的干扰。
  • 时刻促成技术人员与客户、业务人员的对话。业务领域的划分离不开对业务意图的真正理解。而需求人员和体验设计师对于User Journey的使用更熟悉,而技术人员、架构师对领域驱动设计、Eventstorming更熟悉。不管哪种方法都要求跨角色的群体协同工作,即客户人员、业务分析师、体验设计师与技术人员、架构师。而现实的情况中,User Journey更多的在Inception,在需求阶段进行,而领域驱动设计、Eventstorming更多的在开发设计阶段被使用,故而需求阶段经常缺失技术人员,而开发设计阶段经常缺失客户、业务人员的参与。 另一个常见的现象是,Inception的参与人员和真正的开发团队有可能不是同一个群体,那么Inception中的业务沟通往往以UI的方式作为传递,因此在开发中经常只能通过UI设计来理解业务的真正意图。 所以要想将正确的理解业务,做对软件,需要时刻促成技术人员与客户、业务人员的对话。

识别了被拆对象的结构和边界,下一步需要决定拆分的策略和拆分的步骤。

2.拆分方法与策略

拆分方法需要根据遗留系统的状态,通常分为绞杀者与修缮者两种模式。

  • 绞杀者模式 指在遗留系统外围,将新功能用新的方式构建为新的服务。随着时间的推移,新的服务逐渐“绞杀”老的一流系统。对于那些老旧庞大难以更改的遗留系统,推荐采用绞杀者模式。
  • 修缮者模式 就如修房或修路一样,将老旧待修缮的部分进行隔离,用新的方式对其进行单独修复。修复的同时,需保证与其他部分仍能协同功能。

我们过去所做的拆分中多为修缮者模式,其基本原理来自Martin Fowler的branch by abstraction的重构方法,如下图所示:

7-branch-by-abstraction

就如我们团队所总结的16字重构箴言,我觉得十分的贴切:

“旧的不变,新的创建,一步切换,旧的再见”。

通过识别内部的被拆模块,对其增加接口层,将旧的引用改为新接口调用;随后将接口封装为API,并将对接口的引用改为本地API调用;最后将新服务部署为新进程,调用改为真正的服务API调用。

同时,拆分建议从业务相对独立、耦合度最小的地方开始。待团队获取相应经验和基础设施平台构建完善后,再进行核心应用迁移和大规模的改造。另外,核心通用服务尽量先行,如身份认证服务。

3. 拆分步骤

对于模块的拆分包括两部分:数据库与业务代码,可以先数据库后业务代码,亦可先业务代码后数据库。然而我们的项目拆分中遇到的最大挑战是数据层的拆分。在2015年的拆分中发现,数据库层由于当时系统性能调优的驱动,在代码中出现了跨模块的数据库连表查询。这导致后期服务的拆分非常的困难。因此在拆分步骤上我们更多的推荐数据库先行。

4.数据库拆分

我们借鉴了重构数据库一书中提到的方法,通过重复schema同步数据,对数据库的读写操作分别进行迁移。如下图所示:

8-TDDL

虽然技术上是可行的,然而这仍然占用了大量不必要的时间,包括大量的数据迁移。这也是导致当时的拆分无法在给定时间内完成的很大因素。

5. 我们的结果:

系统架构图:

9-system-architecture

问题2:拆分后业务变了增加了怎么办?

随着客户业务的变化,我们的服务也在持续的增加,而其中碰到了一个特大的服务。服务的大小如何衡量呢?该服务生产代码7万行+,测试代码14万行+,测试运行时间2个小时。团队中7个stream每天50%工作需要对这个服务进行更改,使得团队间的依赖非常严重,独立功能无法单独快速前行,交付速度及质量都受到了影响。

我们的总结:

客户的业务是在变化的,我们对业务的认知也是逐渐的过程,所以Martin Fowler在他的文章中提出,系统的初期建议以单体结构开始,随业务发展决定其是否被拆分或合并。那么这也意味着这样构建的服务在它的生命周期中必然会持续被拆分或合并。那么为了实现这样一个目标,使系统拥有快速的响应力,也要求这样的拆分必然是高效的低成本的。

因此,服务的设计需要满足如下的原则:

  • 服务要有明确的业务边界,以单体开始并不意味着没有边界。 服务要有边界,即使以单体开始也要定义单体时期的边界。我们系统中有一个名为“Monkey”的服务,是在中国虎年启动的,由此它并不是一个业务概念。当这个服务的名字为MonkeyAPI时,可以想象5年来它变成了什么?几乎所有和这个产品相关的功能都放入了这个服务中。脱离平台来看这一个产品的系统,其实它只是做了前后端分离而已。这个例子告诉我们,没有边界就会导致大杂烩,之后对其进行整理和重造的代价很大,可能需要花费“几代人”的努力。
  • 服务要有明确清晰的契约设计,即对外提供的业务能力。
  • 服务内部要保持高度模块化,才能够容易的被拆分。
  • 可测试。

问题3:如何安全地持续地拆?

就如前言中提到的,系统已经上线大量的用户正在使用,如何在不影响当下系统运行状态的前提下,持续安全地演进?其实持续演进就是一场架构层次的重构,在这样的路上同样需要:

  • 坏味道驱动,架构的坏味道是代码坏味道在更高层次的展现,也就意味着架构的混乱程度同样反映了该系统代码层的质量问题。
  • 安全小步的重构。
  • 有足够的测试进行保护——契约测试。
  • 持续验证演进的方向。

真正有挑战的问题4:如何保证拆对了?

拆分不能没有目标,尤其在具有风险的架构层次拆分更需谨慎。那么我们如何验证拆分的结果和收益?或许它可以提高开发效率,交付速度快,上线快,宕机时间也短,还能提高开发质量,可扩展性好,稳定,维护成本低,新人成长快,团队容易掌握等等。然而软件开发是一个复杂的事情,拆分可以引起多个维度的变化,度量的难度在于如何准确定位由拆分这一单一因素引起的价值的变化(增加或降低)。

其实要回答这个问题,还是要回到拆分之初:为什么而拆? 我所见过的案例中有因为政治原因拆的、业务发展需要的、系统集成驱动的等等;有因之而成功的,也有因之而失败的。拆并不是一件容易的事,有诸多的因素。我认为不管表象是什么,拆之前需要弄清拆分的价值所在,这也是我们可以保证拆分结果的源头。

总结

系统可由单体结构开始,不断的演进。而团队需要对业务保持敏感,与客户、业务人员进行业务对话,不断修炼领域驱动设计和重构的能力。

team

在拆分的路上,我们的经验显示其最大的障碍来自意大利面一样的系统。不管我们是什么样的架构风格,高内聚低耦合的模块化代码内部质量仍然是我们架构演进的基石。具有夯实领域驱动设计和重构功底的团队才可以应对这些挑战,持续演进,保持其生命力。而架构变迁之前需要弄清背后的变迁动因与价值,探索性前进,及时反馈验证,才是正解。那么我们如何保证架构不被破坏呢?这个问题会在后续的文章中持续探讨。

最后,勿忘初心,且行且演进。

Share

微服务的团队应对之道

这两年,微服务架构火了。在国内,从消费级互联网应用,到企业级应用;从金融领域,到电信领域;从新开发系统到已经开发了十几二十年的遗留系统;一夜之间,好像所有的团队都在谈微服务。

然而,我们为什么采用微服务呢?

让我们的系统尽可能快地响应变化“ – Rebecca Parson

是的,让我们的系统尽可能快地去响应变化。其实几十年来我们一直在尝试解决这个问题。如果一定要在前面加个限制的话,那就是低成本的快速响应变化。上世纪九十年代Kent Beck提出要拥抱变化,在同期出现了诸多轻量级开发方法(诸如 XP、Scrum);2001年敏捷宣言诞生,之后又出现了精益、看板等新的管理方式。如果说,这些是为了尽快的响应变化,在软件开发流程和实践方面提出的解决方案,那么微服务架构就是在软件技术和架构层面提出的应对之道。

微服务是如何做到的?

微服务是一种架构模式,它提倡将单一应用程序划分成一组小的服务,服务之间互相协调、互相配合,为用户提供最终价值。每个服务运行在其独立的进程中,服务与服务间采用轻量级的通信机制互相沟通(通常是基于HTTP协议的RESTful API)。每个服务都围绕着具体业务进行构建,并且能够被独立地部署到生产环境、类生产环境等。另外,应当尽量避免统一的、集中式的服务管理机制,对具体的一个服务而言,应根据业务上下文,选择合适的语言、工具对其进行构建。

— James Lewis and Martin Fowler

这是James和Martin2014年在微服务一文中提到的对于微服务的定义。相对于单体架构和SOA,它的主要特点是组件化、松耦合、自治、去中心化,体现在以下几个方面:

1

  • 一组小的服务

服务粒度要小,而每个服务是针对一个单一职责的业务能力的封装,专注做好一件事情。

  • 独立部署运行和扩展

每个服务能够独立被部署并运行在一个进程内。这种运行和部署方式能够赋予系统灵活的代码组织方式和发布节奏,使得快速交付和应对变化成为可能。

  • 独立开发和演化

技术选型灵活,不受遗留系统技术约束。合适的业务问题选择合适的技术可以独立演化。服务与服务之间采取与语言无关的API进行集成。相对于单体架构,微服务架构是更面向业务创新的一种架构模式。

  • 独立团队和自治

团队对服务的整个生命周期负责,工作在独立的上下文中,自己决策自己治理,而不需要统一的指挥中心。团队和团队之间通过松散的社区部落进行衔接。

我们可以看到整个微服务的思想就如我们现在面对信息爆炸、知识爆炸是一样的:通过解耦我们所做的事情,分而治之以减少不必要的损耗,使得整个复杂的系统和组织能够快速的应对变化。

My First Law of Distributed Object Design: Don’t distribute your objects (From P of EAA). – Martin Fowler

然而谈到微服务,我们不能忽视的一点是:微服务仍然是典型的分布式系统,它必然有分布式系统固有的问题:诸如怎么部署,出错怎么办,怎么保证数据的最后一致性;与此同时,还有服务要多微?业务变化后服务如何调整?服务规模化后怎么办?如何避免一个服务改动导致的多个级联服务的失败?如何进行测试等等问题。

“微服务不是免费的午餐”。从实战中我们清楚地认识到选择任何架构都有利有弊,服务化也是一样。企业从微服务架构获益的同时,必然要面对切换到由众多服务组成的分布式系统后所带来的挑战。我们认为只有提升团队应对这些挑战的成熟度,才能真正使企业从这种微服务架构中获益.

那么对于即将和已经开始实施微服务的团队,应该如何应对呢?结合我们项目的经验,我认为应从DevOps、服务构建、团队和文化四点入手:

  • 首先需要考虑构建DevOps能力,这是保证微服务架构在持续交付和应对复杂运维问题的动力之源;
  • 其次保持服务持续演进,使之能够快速、低成本地被拆分和合并,以快速响应业务的变化;
  • 同时要保持团队和架构对齐。微服务貌似是技术层面的变革,但它对团队结构和组织文化有很强的要求和影响。识别和构建匹配架构的团队是解决问题的另一大支柱。
  • 最后,打造持续改进的自组织文化是实施微服务的关键基石。只有持续改进,持续学习和反馈,持续打造这样一个文化氛围和团队,微服务架构才能持续发展下去,保持新鲜的生命力,进而实现我们的初衷。

在本文中会针对第一点,并以我们项目微服务的发展历程为例介绍我们遇到的各种坑和采取的相关措施,希望能够给正在准备实施和已经实施微服务的团队一些借鉴。

背景

2

我们于2012年在系统集成需求的驱动下走上了服务化之路,通过将重复的业务能力封装在几个服务之间完成了系统集成,并成功上线。最初只有8个服务支持我们的业务系统,服务与服务之间采取基于HTTP的RESTful API的方式进行通信,通过自动化脚本将应用部署到生产环境。

3

在随后的2年中我们的业务快速发展,增加到了60多个站点和服务。服务的上线周期也越来越短,从两年到两周。而这样的一套生产环境是由客户的2名运维人员手工维护的(包括基础设施构建),每次发布都会存在大量的人工干预,比如对部署结构和系统权限的修改等,给我们的整个生产部署和运维带来了较大的问题:

  • 大量的人工干预,频频出错。

手工方式始终是易错和低效的,而且会导致各种难以重现的环境问题(就如雪花服务器),这使得一些复杂点的运维工作,包括技术平台的整体升级、灾备系统搭建等很难开展,往往要提前半年甚至一年来进行筹备。当我们服务规模化和发布周期缩短之后,它带来的伤害又进一步明显起来。为了减小这种伤害,业务部门不得不降低发布频率,从两周延迟到一个月,因此也影响到了对最终用户的响应速度,并不得不采取Hotfix的方式来修正发布速度的问题,实际上又增加了问题的复杂度。

  • 缺乏有效的监控和日志管理,产品环境定位困难。

没有监控,无法及时知晓服务的运行状态。很多问题直到最终用户才能发现,问题反馈周期很长。同时,运维人员不懂开发人员的服务设计和调用流程,开发人员不懂产品环境的配置结构。当产品环境出错之后,往往要花费数小时才能定位到问题根源。

另外,负载均衡和单点服务降级无法真正开展,当一个服务出现问题时,需要关掉其所在的服务器。产品环境资源利用率低,运维成本居高不下。

2015年初,当我们遇到了Powershell的bug和服务不能正常启动的貌似随机问题时,就如遇到了那根压倒骆驼的最后一根稻草,客户对我们明确的提出“不要再添加任何一个服务了”。

残酷的事实再一次向我们证明,在这样一个复杂系统下,传统的手工运维方式必然要被淘汰,微服务的实施有一定的先决条件:基础的运维能力(如监控、快速配置、快速部署)需提前构建,否则就会陷入像我们一般如此被动的局面。而我们也看到,当服务规模化后需要更多自动化和标准化的手段来提升效能和降低成本。

服务治理

随后的一段时间,我们的团队重新梳理了最后一公里和运维的需求,如下图所示,并开始服务治理,从而构建支撑当下架构的交付和运维能力。

4

  • 基础环境的准备

环境需要能够快速的创建并启用全新的服务,能够快速对现有的环境进行自动更改等,以保持各环境的一致性。我们推荐采用基础设施及代码的实践,通过代码来描述计算和网络基础设施的方法,使得图案度i可以快速安全的搭建和处理由新的配置代替的服务器,服务器之间可以拥有更高的一致性,降低了在“我的环境工作,而你的环境不工作”的可能,也是为后续的发布策略和运维提供更好的支撑。

在技术层面,随着云平台技术的成熟,Docker和围绕Docker生态圈的形成,开发和运维团队如虎添翼。组织可以根据现状选择合适的方式逐步迁移到云平台。另外,对于目前不能采用Docker技术的Windows平台,Chef、Ansible也是不错的选择。

  • 发布部署

要求服务能够快速上线,能够快速部署到各个环境以便尽快得到验证。面对如此庞大的服务及复杂的依赖关系,我们建议分离产品、预发布环境及测试环境。采用部署流水线,由自动化脚本实现全环境的快速部署,包括配置管理、版本管理等。

  • 运行时监控与业务运营

当产品环境出错时,需要快速的定位问题,检测可能发生的意外和故障。而监控是快速定位和预防的不二选择,在微服务架构中更是至关重要。监控包括服务可用状态、请求流量、调用链、错误计数等内容,以便发现问题及时修复,实时调整系统负载,进行必要服务降级,过载保护等等,从而让系统和环境提供高效高质量的业务服务。其中健康状态页面、结构化的日志、实时服务依赖关系可视化、流量统计、事件机制等都是监控领域可采取的基础技术手段。

除此,随着服务越来越多,需要进一步考虑蓝绿部署、灰度发布、服务安全、容器编排等问题和自动化标准化的手段,从而更好的管理大规模下服务产生的运维需求。下图是最近几期ThoughtWorks技术雷达中提到的相关内容,可供不同技术栈和处于不同实施阶段的团队参考:

5

我们的选择和改进

6

针对上面的几个痛点和我们项目所处的阶段,结合项目.NET技术栈的生态系统,我们主要采用Chef自动化构建本地构建的基础设施,与运维团队一起优化部署流程,提供服务健康状态页面支持监控,使用Splunk进行集中式日志管理,并可视化服务依赖关系等来进行全面服务治理,我们的部署时间从原来的十几个小时缩短到三十分钟,成功率也大大提高。结合业务部门的需求和现状的分析,对其方面地要求,如独立部署、配置管理、服务发现、过载保护等的改进仍在继续中。

除此之外,我们还做了什么?

You build it, you own it. – Amazon CTO

打造DevOps文化,将运维作为需求提前注入到开发流程。

7

从上一节可以看出,微服务对开发人员和运维人员的工作方式和技能都有新的要求,带来很强的冲击。在2014年随后的一段时间,我们开始将运维的需求内建到整个开发流程。当新业务需求提出的时候会同时交给开发人员和运维人员,后者将整个运维环境的要求(包括监控和安全)进行分析,产出约束规范或非功能需求递给开发人员;开发人员在做技术决策和开发工作时遵循这些约束并满足这些非功能需求,最后将整个产品以及监控的实现交付给运维团队。双方同时也重建了沟通计划,互换技术架构和运维方面的知识,互相支持和深入合作。

在我们整个服务治理的过程中,花了大量的时间去理顺最后一公里和运营监控的问题,与运维团队的合作一步步从无到有,从不信任不合作到逐渐改善,才使得微服务的实施顺利进阶。可以说在企业内要成功实施微服务,最不能少的合作角色就是企业的运维团队。微服务实施要想顺利进行,必须要打破开发团队和运维团队之间的高墙,真正做到“开发自运维”,运维自开发,互生互长。我们也向其他的组织推荐这样的方式,打通DevOps的任督二脉,为微服务保驾护航。

扩展阅读:
1.http://martinfowler.com/microservices/
2.http://martinfowler.com/bliki/MicroservicePrerequisites.html
3.http://martinfowler.com/bliki/InfrastructureAsCode.html
4.https://www.thoughtworks.com/insights/blog/microservices-evolutionary-architecture
5.http://liguanglei.name/blogs/2015/04/22/devops-chinese-name/
6. https://www.thoughtworks.com/insights/blog/macro-trends-technology-industry

Share

微服务即演进式架构

作者:Neal Ford & Rebecca Parsons
译者:禚娴静

微服务架构风格正在席卷全球。去年三月,O’Reilly举办了他们的第一届软件架构大会,提交的话题中与微服务相关的占绝大部分。那么为什么这种架构风格会突然如此流行了呢?

微服务是后DevOps革命时代出现的第一种全新架构风格。它第一个全面拥抱持续交付的工程实践,也是演进式架构家族的一员。演进式架构以支持增量的、非破坏的变更作为第一原则,同时支持在应用程序结构层面的多维度变化。不过,微服务仅仅是支持某些演进行为的众多架构之一。本文分享了演进式架构风格的一些特点和原则。

演进式架构

软件行业曾经有这样一个共识,架构一旦确定,“日后很难改变”。演进式架构将支持增量式变更作为第一原则。由于历来变更都是很难预测的,改造的成本也极其昂贵,所以演进式架构听上去很吸引人。如果真的可以在架构层次做到演进式地变更,那么变更就会更容易、成本更低,也能发生在开发实践、发布实践和整体敏捷度等多个地方。

微服务满足这一定义,因为它遵循强边界上下文的原则,从而使得Evan的领域驱动设计中描述的逻辑划分变成物理上的隔离。微服务能够通过采用自动化基础设施建构、测试、自动化部署等先进的DevOps实践,获得这种分离。因为每个服务在结构层面与其他服务都是解耦的,替换服务就像替换乐高积木一般。

演进式架构的特点

不同的演进式架构展示了一些共同的特点,我们已经识别了很多特点,并收录进即将出版的《演进式架构》一书里。本文将分享其中的几个特点。

模块化和耦合

边界划分明确的组件,显然可以给希望做出非破坏性变更的开发人员以更大的便利。而毫无架构元素的大泥球架构就无法做到演进式变更,因为它缺少模块化。

0602-微服务即演进式架构-1

[在一个大泥球项目中类(图中圆边上的点)与类之间的耦合度 – 来自一个不愿透漏名字的项目]

不适当的耦合将变更导向难以预料的方向,从而阻碍演化。而演进式架构都支持一定程度的模块化,这种模块化通常体现在技术架构层面(例如经典的分层架构)。

围绕业务能力组织

受领域驱动设计的启发,越来越多的成功架构都以在领域架构层的模块化为特色。基于服务的架构与传统的SOA主要区别在于模块划分的策略,SOA是严格按照技术层进行模块划分,而基于服务的架构则倾向于按业务领域划分。

试验

试验(experimentation)是演进式架构给商业交付带来的最大价值之一。从操作角度来讲,可以采用A/B测试、金丝雀发布等常见的持续交付实践对应用进行低成本的、微小的变更。微服务架构常常是围绕服务之间的路由来定义应用程序的。通常,微服务架构围绕服务之间的路由来定义应用程序,允许同一个服务的多个版本同时运行。这反过来也使得试验和现有功能的逐步替换成为可能。最终,这使得你的业务可以花更少的时间去猜测待办故事项,从而投入到假设驱动开发中。

演进式架构的原则

思考演进式架构的另一个方法就是通过考察它的原则。原则描述了架构本身的不同特点或者设计架构的方法,其中一些原则专注在特定的架构决策的过程。

适应度函数

我们将浮现式和演进式架构区别对待,并且认为这种区分是很重要的。就像在进化计算技术中的遗传算法一样,一个架构级别的适应度函数指明了我们的目标架构是什么样子,其中一些系统关注高运行时间,而其他一些系统则会更关注吞吐量或者安全。

fitness-function

[这张雷达图展示了适用于软件系统的几个重要的适应度函数]

在项目前期思考一个特定系统的适应度函数,可以对决策的制定和决策的时机提供指导。适应度函数是对架构决策的衡量,通过适应度函数,我们可以预见架构是否在向正确的方向演进。

痛苦的事提前做

受极限编程社区的启发,许多持续交付和演进式架构的实践都体现了“痛苦的事提前做”的原则。在做一个项目的时候,如果一些事情可能会很痛苦,那么你需要强迫自己更早更频繁地去做这些事情。这反过来会鼓励你用自动化的手段消除这些痛苦并能提前识别问题。那些基础的持续交付实践,如部署流水线、自动化基础设施建构、数据库迁移,就是这一原则的应用,它们会提早解决变更带来的常规问题,从而使架构的演进更容易。

最后责任时刻

决策的时机是传统架构和演进式架构的最主要区别。这些决策可以是围绕应用程序的结构、技术栈、特定的工具或通信模式。在传统的架构中,这些决策发生在早期写代码之前。而在演进式的架构中,它们发生在最后责任时刻。延迟决策的好处是有更多可用的信息来辅助决策。其成本是在做出决定后任何可能出现的重复工作,这些工作可以通过合适的抽象得到减轻,但成本仍然是实打实存在的。相比而言,决定太早的成本也是很明显的。比如一个通讯工具的选择,不同的工具有不同的特性。如果我们选择一个可能最终也不会用到的重量级工具,那就是在项目中引入了技术债。这个技术债是由于使用了错误的工具导致开发进度延缓而出现的。你不能拿事先“抽象一切”作为借口。我们仍然支持敏捷YAGNI(You Ain’t Gonna Need It,你不会需要它)的原则,但会更倾向于在合适的时候做出决策。

当然,我们在考虑最后责任时刻的一个最直接的问题就是,什么时刻是最后责任时刻。适应度函数对这个问题提供了指导建议。那些会对架构、设计选型或者项目的关键成功有重大影响的决策,都必须尽早做出,延缓这些决策往往得不偿失。

总结

软件架构师常常用画图的方式来解释系统之间的适配,然而太多的架构师并没有意识到,一个静态的二维架构图的生命是很短暂的。软件世界是不断变化的,它是动态的而不是静态的存在。而架构也并不是一个等式,它是持续过程的快照。

持续交付和DevOps运动都诠释了一个问题,即由于忽略实现架构并使之保持常新所要付出的精力。其实架构建模和付出这些精力本没有什么问题,但是架构的实现仅仅是第一步。架构只是抽象,直到真正投入运维。换句话说,如果你没有实现并升级过一个架构,甚至帮它度过难关,那么你就无法真正判断架构的长期生命力。

架构师的运维意识是演进式架构的关键。演进会影响具体的实现,因此这些实现细节是不能忽略的。持续交付在架构层面的要求,使得实现更可视化,演进更简单化。因此,持续交付是所有演进式架构重要的助力。

其他资源:

在这期ThoughtWorks技术领袖播客中,Rebecca和Neal讨论了演进式架构的含义,以及组织如何将它作为商业优势加以使用。

原文链接:Microservices as an Evolutionary Architecture

Share