架构师训练营第10周作业 微服务

根据微服务框架 Dubbo 的架构图,画出 Dubbo 进行一次微服务调用的时序图。

架构师训练营第10周作业 微服务_第1张图片
架构师训练营第10周作业 微服务_第2张图片
服务提供者.png
架构师训练营第10周作业 微服务_第3张图片
服务调用者.png

关于微服务架构(中台架构、领域驱动设计、组件设计原则),你有什么样的思考和认识?

注:以下内容写于2018年3月28日,结合了本人所在行业的实际现状

互联网行业无疑是软件开发技术最领先的行业,无数前人的经验积累足以提供极为丰富的技术资源和最佳实践。跟随互联网的脚步,可以减少在开发过程中采坑的几率,并显著提高开发效率、提升产品质量。

国外已有成熟的视效公司开始接受并使用互联网技术,例如工业光魔正在开发基于Amazon AWS的云端制作平台,MPC借助微服务架构实现了自动化程度极高的资产管理系统,越来越多的视效公司招聘需求中也体现出了对互联网、云平台技术的重视。

但是,视效行业所固有的一些特点使得互联网的诸多知识和经验没法直接拿来参照,同时视效公司的技术团队也缺乏互联网行业那种高屋建瓴的全局观和技术领导力,这使得先进技术的落地变得十分困难,因此更需要对视效行业技术架构的现状进行深入分析,并与互联网行业做全面的对比,了解两者之间的异同,才能掌握促成先进技术落地所需的各种条件。

视效制作流程的目的是用计算机、代码和工具来代替部分人力操作,提高效率、减少沟通成本和人为错误,从本质上讲就是对文件流和信息流在不同制作环节进行跟踪、处理和监管;在具体实现的层面,会通过代码整合一系列第三方平台和软件,并开发一些相应的工具,使得文件流和信息流可以在这些平台和软件之间顺利流通,最终得到符合要求的输出结果。

视效行业的平台和软件大多会提供Python语言开发接口,Python语言本身就极为擅长处理文件流和信息流,并且拥有可读性强、上手快、无需编译(运行部署方便)等特点,因此成为了视效流程开发所使用的主力语言,由此也影响着视效行业的技术选型和架构模式。除了Python,视效公司使用比较普遍的就是C++,后者更偏向于高级算法和底层操作,多被用于研发包含算法和图形图像处理的插件以及底层核心系统。

从部署和运行方式来看,视效制作流程所面对的用户以公司内部人员为主,同时涉及到与客户方、外包商和其他影视生产机构的技术衔接和数据互通,大型跨国公司还需考虑跨网络异地制作的能力。但是总体上讲,影视生产过程的高度保密性和生产机构之间的技术竞争导致了视效制作流程的用户群是严格受控的,规模从几十人到几千人不等,整个流程也不会向公众开放。因此,对于不存在跨地区协同的公司来说,代码的部署基本上是在本地局域网内完成的,而对于需要多地部署的公司,每个分部也都会通过Git、SVN等同步机制获得完整的代码,并部署到各自本地网络。

现代互联网行业所使用的最主流的语言是Java,绝大多数新技术也是以Java作为主要的实现方式,Java的部署环境比Python的部署环境配置起来要复杂一些(MacOS和Linux系统默认是可以直接运行python程序的),并且互联网应用是通过公网提供给用户,需要更多地考虑网络传输所带来的问题,例如高可用、数据一致性等,并且互联网应用用户量级经常在百万级以上,对低延时、低错误率、弹性扩容等具有极高的要求,而这些要求在视效公司通常是可以忽略的。

Python代码易于运行的特点和本地网络的部署模式使得视效公司在代码部署和运行方面常常会采取一种较为特殊的形式:用户直接运行源代码(或.pyc后缀的python字节码)。开发团队通常会专门准备一块存储区来存放供用户使用的代码,用户本机只需安装Python,运行该存储区中的代码即可。

这种部署模式,其实相当于用户每次在运行代码的时候,都从代码库拉取到本地内存,并由用户本机执行,这是典型的“胖客户端”模式,即绝大多数的代码逻辑均在用户端执行,用户拿到的应用十分臃肿。在大多数视效公司,甚至没有所谓的服务端(除了数据库之外),所有的代码全部都是客户端代码,即便是某些第三方软件之间的协同使用到了TCP通信,也都是在用户本地完成的。

而互联网应用由于需要跨网络提供给用户,并且不可能让用户直接接触源代码,因此部署都是在专门的服务器上完成。服务器上的应用是持续运行的,通过网络释放特定的接口、功能和内容。除此之外,需要为每个用户分发额外的客户端(对于纯Web应用来说,客户端就是浏览器),这时候客户端仅仅起了内容呈现和交互的功能,而业务逻辑甚至页面的生成全部都放在了服务端来执行。

互联网应用的升级扩容如果不涉及到用户界面的更改,对用户来说通常是无感知的,通过蓝绿部署、灰度发布等策略也不会造成服务的停顿。而视效公司的部署模式,由于代码运行在用户本地内存,因此代码库更新后用户必须重新运行代码才能使用新功能(除非代码使用了reload,但很多动态加载的功能是没有办法reload的),这与互联网应用更新客户端时的情况是一样的。这会对代码发布效率和用户体验产生显著的影响,如果用户没有重新启动最新版的代码,还可能因为代码版本的不统一而出现更多问题。

本地运行的胖客户端通常也被称为单体应用,即所有的代码都包含在一个或少数几个开发项目中,即便是不少开发人员使用了经典的MVC分层方式,以及多线程、多进程,但是代码在运行时终归还是融为一体的,整个应用的运行时间周期是相同的。

单体应用的好处是显而易见的:

  1. 开发简单快捷,所有的IDE都是针对单体应用开发而设计的,可以大幅度提高开发效率
  2. 无需学习过多知识,上手快,所有需要的功能都可以在同一个项目中找到,并直接导入使用
  3. 易于测试,所有代码同时启动,可以立即进行功能测试和系统集成测试
  4. 针对视效公司运行方式来说几乎没有部署成本,用户直接从指定存储区读取代码并运行

但是今天这篇文章要来梳理一下单体应用的弊端。关于这个话题,网上能搜到铺天盖地的文章,不过讲的都是互联网环境下的单体应用,视效公司在单体应用开发方面的弊端,似乎还没有人很完整地梳理过。

首先,单体应用会限制技术选型。由于所有的代码都一起运行,因此代码之间通常会直接使用模块导入、函数调用的形式进行整合,这就要求代码使用同样的语言进行开发,有时候甚至还会对开发语言和第三方库的版本有要求。

例如,Python2和Python3是不兼容的,目前绝大多数视效公司仍使用Python2进行开发,主要原因是制作软件提供的API以及常用的第三方库都是基于Python2的,但Python官方已经宣布2020年停止对Python2的支持,这意味着现有的代码库迟早要向Python3迁移。单体应用实现语言版本的渐进式升级会很麻烦,因为用户要么用Python2来运行,要么用Python3来运行,因此必须整个代码库都改完了才能顺利运行。

同理,在单体应用架构环境下引入新技术是很困难的,做不到用最合适的技术解决特定的问题,功能的实现方式被特定的语言所限定。

其次,代码耦合性极高,难以掌握。这点在只有一两个开发人员的中小型视效公司可能体现得还不明显,因为工具基本上是一个个做的,彼此较为独立,但是随着工具越来越多、代码规模越来越大、流程越来越规范,一些常用的功能会被提取出来,成为公共库,并逐渐演化为流程开发的核心框架。这一框架通常包括数据库操作、项目管理平台操作、路径转换与解析、自定义GUI控件、文件版本和命名控制等等,所有的工具都是以函数调用的方式来使用核心框架所提供的功能。从代码复用的角度讲,这种方式虽然是把通用功能提取了出来,很大程度上减少了代码重复度,确保了核心功能的一致性,但是随之而来的是代码的相关性极强、耦合度极高。

高耦合系统的依赖关系和代码逻辑极其复杂,往往只有原编写者才能完全了解代码结构和逻辑。由于每个人的编码风格是不一样的,而代码又是直接通过函数调用的形式组合在一起,因此缺乏统一的设计模式和接口规范,学习成本高,上手时间较长,重构困难,灵活性差,修改起来很不方便,经常出现牵一发而动全身的情况,一段代码的错误会导致整个应用打不开。另外,虽然代码中功能和函数逻辑在不断更新,但代码本身的调用结构一旦确定了就很难改动,所以视效公司的代码架构全面翻新这种事情,基本上几年才会发生一次。

再者,单体架构难以应对业务需求的多样性。由于代码本身耦合性高,因此代码的逻辑结构(即调用链和功能设计)基本上也是固定的,然而技术部门几乎随时都在迎接新需求,经常需要对既有的功能做出修改。

比较糟糕的情况是,视效公司需要面对大量的“一次性”需求:虽然流程和制作规范总体上是不怎么变化的,但是在实际应用中经常会碰到用户跑过来说“我能不能不走这套流程”,“我现在碰到个特殊的情况”,而这种一次性需求往往会破坏现有的代码逻辑结构,导致原本不该做出修改的底层代码为了满足特殊需求而不得不加上if else。

流程本身之所以存在,原本的用意就是消除不确定性,让一切都在程序的掌控之中,让影片制作过程有规矩可循、有记录可跟踪、有固定的操作模式可以遵守。但是谁都无法否认一次性需求的存在,甚至一次性需求的优先级往往要高于其他长远的需求。

另外,视效公司往往是多个项目制作并行推进,每个项目都可能有不同的制作规范和流程,如何使用同一套工具满足不同项目的需求,也是技术人员所必须解决的难题。

为了在应对需求和保护现有流程之间寻找平衡,技术人员可谓是煞费苦心,经常导致的一种后果就是,原本清清爽爽的流程,被塞入了大量为满足个性化需求而设计的功能,并且这些功能一旦加上了就很难再拿掉。

最后,单体应用的测试难度大,交付周期长。视效公司的技术团队通常不具备互联网公司完善的自动化测试和持续交付流水线,新功能的测试多是由开发人员自行完成。但是,开发人员所能考虑的测试覆盖面是有限的,并且由于代码逻辑关系复杂,Debug的过程会非常痛苦,需要反复检查多个模块、多个函数,即便这样也无法自信地发布新版本,代码的Bug往往需要由使用者来发现并反馈,从而拖慢新功能交付的速度,削弱了使用者和开发者之间的信任关系。并且,由于所有开发人员在同一个项目中进行开发,为了尽可能减少代码冲突的情况,还需在开发中照顾到其他人的代码变更,保留其所需的功能。

总而言之,在单体架构模式下进行开发,代码库只会变得越来越臃肿,越来越难以理解,技术升级和演进也越来越困难,为新业务提供价值的效率也会越来越差,这些问题逐渐累积,就会形成所谓的“技术债务”。

开发人员的组织架构形式和沟通方式同样会显著影响到代码的架构。中小型视效公司只有一两个开发人员,基本上不存在分工的情况,所有的开发工作都得一起做,这时候还不存在什么沟通上的问题,当然,代码本身的规模和架构也是完全限制在一两个人可以掌控的范围内,只需考虑自己编写起来顺手就够了。

随着公司规模的扩大,技术团队成员数量增多,逐渐出现了职能上的划分。分而治之的做法本身是没有问题的,互联网开发中也会分为基础架构团队、后端团队、前端团队等等,这样有助于开发者更专注自己所熟悉的领域,只需做好自己分内的事情。但这样做得有一个大前提,那就是统一的需求分析、交互设计、接口规范以及高效的沟通协作机制,而这正是视效公司所缺乏的。

视效公司的技术团队往往缺少统一的开发规范和指导方针,甚至连文档都没有,这就会给开发人员之间的沟通协作带来极大的困难,因为彼此都很难理解对方的工作。事实上,需求分析、交互设计、接口设计、代码实现都是由各个开发人员自行完成,在缺乏统一的代码风格和契约的情况下,从语法、设计模式、接口规范到界面操作都因人而异。开发者在需要进行跨领域开发时,不得不反复询问并学习其他人负责的代码,从而添加自己需要的功能,这样显然是增加了沟通成本和学习成本,对新人来说是十分不利的,事实上面对复杂的流程系统还没有谁能够方方面面都掌握。

另外,过分强调职能划分反而会阻碍开发者的学习进程,因为没有人愿意免费做原本该是别人负责的事情,这会进一步导致团队成员之间沟通协作的减少,有的开发者会选择放弃使用其他人的代码,自行实现所需的功能,从而增加了代码的冗余度和错误率。这两种情况都会拖慢新功能上线的速度,削弱重构的可能性,同时给用户造成不必要的困扰。随着技术人员的离职更迭,代码会变得更加难以维护,技术债务也会越来越严重,到了“不坏不修”的地步。

那么,有没有办法能够解决这些问题呢?答案当然是肯定的。互联网行业早就深刻意识到了这些问题,并逐步演化出各种解决方案,而这些方案发展到今天,便成为了互联网应用架构最主流的一个趋势,那就是微服务架构

微服务架构虽然不能说是救命稻草,但在互联网行业已然呈现出遍地开花的态势,Netflix、Amazon、阿里巴巴、美团点评、京东、微博等一线互联网企业都是微服务架构的践行者、推动者和受益者。那么,到底什么是微服务架构?这种架构又是否适合视效行业呢?

实际上,微服务架构不是一个全新的概念,它是从分布式架构面向服务的架构进化而来的。这里就要先解释清楚什么是分布式,什么是服务。

分布式简单来说就是由多台服务器组成的网络,也就是把代码分别部署到不同的服务器上,并通过网络进行互联。在互联网行业,分布式的概念是针对服务端的,因为随着应用代码的规模越来越大、用户数量越来越多,单台服务器已经无法满足需求,这时候就需要进行水平扩容,用多台服务器同时运行代码,然后由负载均衡器来决定特定的请求由哪台服务器来响应,这就是最简单的分布式服务。当然,除了水平扩容之外,还有垂直扩容的方式,即对服务端进行拆分,将数据存储、文件存储、业务逻辑代码、网关代理等等分别由不同的服务器来执行,这样会进一步扩大分布式网络的规模。

其实,在视效公司里也早就存在分布式架构了,那就是渲染农场,渲染农场的多台节点通过网络与存储服务器和渲染管理服务器相连,用户使用客户端访问渲染管理服务器并提交任务,渲染管理服务器将任务分发给各台节点并发执行,从而成本缩减渲染时间。

而对于单体胖客户端而言,将其业务代码与界面代码分开,部署到不同的机器上,改造成服务端+瘦客户端的模式,并通过网络互连,这就是分布式架构。

服务是一个抽象概念,是代码所提供的业务能力,而非代码所包含的函数功能本身。如果说模块、包是对代码进行划分的方式,那么服务就是对业务进行划分的方式。因此,站在代码的角度,我们会将代码分成数据访问层、业务逻辑层和界面表示层,会按照Shotgun模块、文件路径模块、配置模块等进行划分,但是站在服务的角度,用户关心的并不是底层代码的划分方式,而是它能够从代码中得到的功能,例如用户认证服务、文件上传服务、查询服务、消息通知服务等。服务通常具有松耦合、可复用和自治性的特点,即服务应当是可以独立部署的,用户可以通过网络访问服务,服务间也可以通过网络互相连接,显然,这也是一种分布式架构。

在面向服务的架构基础上,微服务架构应运而生。微服务架构汲取了敏捷开发、持续集成/持续交付、自动化部署等新技术的优势,解决了传统的面向服务的架构体量过大、服务契约复杂、集中式管理、技术栈单一等缺点,为面向服务的架构提供了更完善的进化形态。

微服务架构其实并没有一个准确的定义,就如同NoSQL、函数式编程等概念一样,只能从实践的角度来解释。通常认为微服务架构的提出者是ThoughtWorks首席科学家、世界知名软件架构大师Martin Fowler,他于2014年在自己的博客上发表了名为《微服务架构》的博文,对这一架构理念进行了系统的梳理。

这篇博文中对微服务的定义如下:

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

首先,“微”这个字强调的是服务的小型化、单一职能化,但具体多微才算微并没有一个确切的准则,更多的是因人而异,并且微服务架构并不是一次成型的架构,而是一个逐渐渐进演化的架构,因此对于“微”的定义也会随着架构规模和团队发展而有所变化。

服务的划分应当围绕业务进行,服务与服务之间应当是相互独立的。微服务架构是一种分布式架构,服务间应当通过语言无关、平台无关、技术无关的网络通信方式来互通,各个微服务本身可以选择适合业务特性的语言和技术来实现。

微服务架构可以很好地解决本文提出的单体架构所存在的问题:

  1. 服务可以使用不同的语言来实现,并且由于服务之间是通过语言无关的通信机制来沟通,因此可以很容易地实现多版本、多需求的服务共存

  2. 通过独立的机制随时进行切换,可以快速而灵活地满足各类业务需求。使用微服务的过程可以形象地看作是搭积木,通过将不同业务功能的微服务按照特定的方式进行组合,就能得到不同的业务流程

  3. 各个微服务彼此独立,开发和部署过程互不影响,可以快速上手开发新服务,并能在有限的用例集下快速地对代码进行修改、测试和升级,而无需影响其他服务的正常运行

你可能感兴趣的:(架构师训练营第10周作业 微服务)