QCon全球软件开发大会8折优惠倒计时,购票立减¥1760!了解详情 >>> 了解详情
写点什么

虚拟研讨会:软件架构文档

2010 年 1 月 11 日

软件架构文档是企业应用开发过程中的重要一环,理解一个项目中的架构文档的关键是理解它在项目生命周期中所扮演的角色。一个项目产生架构文档的根本原因是为了交流、分析、记录和保存(比如,跟踪决策过程使之不会随着时间而流失)。一个项目产生的架构文档的数量和类型应当反映该项目为了创造产品所需的交流及分析。

架构文档在项目中用于在上至其管理团队,从架构师或首席设计师到开发者,以及随着时间的流逝,未来的维护者和开发者之间进行沟通所用。仅这个简单的声明就引出了三个问题,它们的答案有助于决定文档的数量和类型。项目中出现了多少疏漏? 有多少开发人员在开发产品,他们的水平如何?以及该产品将使用多久?疏漏越多,说明需要更多的文档用来交流管理。开发者越多和他们的开发水平越低,就意味着开发者需要更多的指导。而产品使用时间越长,则需要越多用于和未来该产品的开发者们交流的文档。

架构文档的交流部分包括与管理层,开发者们沟通和在软件生命周期中的交流。分析需求可能来自为了决定产品的质量(包括性能,安全,可靠性等)的内部原因或来自如兼容某些规则或标准的需求。

在这个虚拟研讨会中,InfoQ 希望能从顶级的软件架构专家们那里找到软件架构文档的重要性和如何记录架构,特别是在敏捷软件开发环境中。

回答我们问题的小组成员有:

Len Bass,SEI 的高级技术成员,《 Software Architecture in Practice 》和即将面世的《 Documenting Software Architectures: Views and Beyond 》的第二版 的作者之一。

Grady Booch,IBM 荣誉科学家,《 Handbook of Software Architecture 》的作者。

Paulo Merson,SEI 的高级技术成员,《 Documenting Software Architectures: Views and Beyond 》的作者之一。

Eoin Woods , Barclays Global Investors 应用程序架构组的领导人,《 Software Systems Architecture: Working With Stakeholders Using Viewpoints and Perspectives 》作者之一。

在使用敏捷和精益开发方法(如 SCRUM,极限编程,看板等)的开发团队中,软件架构文档的作用是什么?

Len:架构文档的作用并不因为开发方法的不同而变化。 文档并不是唯一的交流方法,在敏捷中如 SCRUM 或 XP 面对面交流等方法取代了一部分文档的需求。但并没有替代对于设计决策的详细交流的文档需求。面对面的会议很难正确表达它们。敏捷方法并不能处理跨时间的交流或向管理层的交流。 在敏捷方法中,所减少的文档是面对面交流能够解决的那部分沟通需求,但不会有更多减少。

Grady:对于那些使用更形式化的方法的团队来说其角色也一样。简单来说,记录一个软件密集型系统的架构有如下几个目的:让团队推敲重要决定,去记录组织的重要事件,对于每一次迭代的专门管理提供一些基本资料。对于这些轻量级的方法,架构文档更多地用于反映实际建造的系统,而不是将要设计的系统。

Paulo:软件架构的角色通常来说是为了在不精确且动态变化的需求与可运行的代码实现之间搭起桥梁。 架构文档捕捉设计决策,它是个指导实现(最主要)的部件,能够在早期用于评估所设想的解决方案的方向是否正确,还有助于任务分配和跟踪。但是对于敏捷呢,如果说敏捷开发是架构文档的对立面显然不对。敏捷并不是为了避免设计,而是避免预先大量设计(BDUF)。更深远广泛的架构策略是预先制定的,但许多其他的决策会推迟直到需要时才做。比如说,数据模型经常会被充分的预先讨论以避免之后的大变更。另一方面,在对于那些在下一个版本发布中的要开发的特性,它们的序列图和接口定义文档可以在实现前几天来完成。

敏捷开发者应该是有设计能力的,而敏捷架构师应该是有编写代码的技能的。 因此设计决策的交流就会更短而且更容易聚焦。实践来看是什么样的呢?

  • 多一些图表,少一些散文
  • 设计只要足够编程即可
  • 图表中多使参考已有的架构(或设计模式的应用方式)。具有相似特性或流程的设计可以用很少的文档,因为解决方案能够从参考的设计那里推断出来
  • 使用概图而不是正式的全面的图
  • 设计图被划掉而不是更新,这样好过保留过时的设计。如下图是我经常使用的一个可视化标记

Eoin:可能听起来很明显,但是这个问题的回答实际上取决于团队,项目和相关的背景。 一个架构描述,如同任何可交付的产品,是需要为了某个目标和受众而创建的,否则只是浪费时间。有时候读者就是创建者本人(为了帮助分析或只是一个备忘录),但是有时候通常会是更广泛的人群,他们对任何重大的交付都有兴趣。

我个人来说,我从来没有发现在敏捷开发和软件架构中有什么巨大的矛盾。即使我知道这并不是每个人的经验……也许只是我比较幸运。我的经验很清楚,不管是敏捷还是其他,简明的架构描述,并随着项目的进度进行增量开发能给开发团队提供一个有用的背景环境。

一个好的项目描述捕获设计的那些从代码看来并不明显的方面(比如部署设计,外部接口或系统监视方法)并且解释这个系统是如何满足它的非功能性需求的(典型的包括硬件,安全和性能的需求)。如果没有人对这些感兴趣,那么就没有必要存在架构描述,但是我发现这很少见。

还有一点值得提到的是架构描述会有很多种形式,在敏捷团队中厚厚的文档常常是有效性最低的那个。重要的是在创建架构描述时你不断收集的信息及进行的分析而不是你所用的文档模版。一个架构描述必须是一个有效沟通的媒介,包括 wiki,模型,数据库,电子表格以及一组短小且重点明确的文档都可能是比重量级大型文档更适合捕捉和交流架构设计信息的方式。

当基于 DSL(领域特定语言)来记录应用程序的架构文档时有没有特殊的模式可遵从?

Eoin:我还没有在某个生产系统上用过 DSL,但是我不觉得实现技术的不同会对我记录架构文档的方式有很大影响。你仍然需要去定义系统环境、功能结构、部署环境、信息结构及运营环境等,而且还要解释非功能性需求是如何达到的。你需要在每个部分(或视图)描述你的设计的基本元素。可能因为实用技术不同而有所不同,但你仍然需要描述一些相同的内容。

Len:如果扩展 DSL 语言,概要文件和原型,额外的建模语言以及元模型等定义了符号(有的有,有的没有),那么他们会有助于使文档更加简明。如果读者和作者都理解这些额外符号的话就能够减少成本。

Grady:不是的,任何描述软件架构文档的方式的本质是为了让相关者明确他们关注内容的视图。确认视图上的内容取决于领域和开发文化。

Paulo:架构文档包括多个体现系统结构的视图。 用 DSL 并不影响这些视图,比如 部署视图或运行时视图(又叫组件和连接器或进程)。使用 DSL 使得架构中的模块 (或者叫代码) 视图更明显,这个视图显示了实现单元,它们如何在模块和子模块中组织的,以及它们之间不同的关联和依赖。在任何一个视图中,标识出描述各种元素的类型很重要。DSL 规定了良好定义的的一组元素类型和关系。特别是当 DSL 和其他语言混用在一个完整解决方案中的情况,在架构文档中标识出元素类型非常重要。在实际中的大部分情况下,概览图就是你需要的全部。因此你使用不同的形状或颜色标识不同的元素类型的时候也标明了在文档中一个长方形和一个椭圆形分别代表不同的意思。而且别忘了也要区分线条和箭头。

DSL 经常和自动化工具,类库或代码生成器一同出现。因此明确实现的范围及哪些是由工具实现的也很重要。上下文结构图会有所帮助。当使用代码生成器的时候,要确保生成了哪一类的运行元素以及它们的属性。找出是否单进程 / 线程,使用何种通信协议,使用什么数据存储机制以及访问质量相关方面,比如安全,性能和互操作性等。我曾经参加用 Microsoft Visual Studio 定义 DSL 模型的演示 ,很难忘。但即使你用 DSL 你也可以用一个通用的建模语言如 UML 来记录设计。UML 概要文件经常用于定义从 UML 符号衍生来的定义领域相关的符号。

新的动态 Java 模块规范 (比如 OSGi) 的哪些方面符合软件架构文档的工作?

Paulo:软件架构师们通常关注在架构文档中的模块视图(也叫代码视图)。他们花时间解释在程序包和子程序包中实现单元是如何组织的,模块间如何相互依赖,它们如何按水平或按层次组织的,以及特殊事务的消息流程。创建架构的这个代码视图固然重要,但架构师们常常忽略运行时视图和部署视图。记述这些视图需要对运行环境、平台结构、组建模型很好的理解。运行时视图体现了实现单元如何被转换为线程,进程,Servlet,DLL 等其它组件,还体现了数据存储、基础结构服务、外部服务、通信机制(如 JMS,SOAP,http,本地或远程方法调用)等方面。运行时视图允许你来推算运行时特性,如性能、安全及可用性等难以看着程序包和类图就能估计出的部分。部署视图不仅体现了硬件基础架构,而且解释了部件如何打包到一起并部署的。这些视图有助于能够帮助对服务能力,性能,安全性和其他属性的估计。

现在让我来解释清楚你的问题。如果你在架构一个 OSGi 应用程序,你应该理解在 OSGi 运行环境和它们的生命周期中都有哪些种组件。我的唯一 OSGi 经验是开发一个 Eclipse 插件,它有点隐藏 OSGi 框架的意思,你只需要创建声明(manifest)文件。 但让我问 OSGi 应用程序架构师们一些问题:

  1. 你的架构文档体现了每个部署包里都有什么 java 包 / 类吗?
  2. 在运行时,对于每一个包都有对应的线程吗?或者每一个包的服务都有一个独立的线程吗?
  3. 对于以上任何一种方式,你的文档都列明了这些线程吗?
  4. 每一次服务调用都创建一个新的线程实例吗,类似于每一个 http 请求同一个 servlet 的多线程实例?
  5. 如果我想要部署一个被多个包调用的 jar 文件,文档里能知道我应该把它放哪吗?
  6. OSGi 的发布 - 订阅机制发送的事件是同步的还是异步的?
  7. 你的运行视图区分了调用的类型和使用的协议(比如 http)吗?
  8. 文档体现了服务注册的交互吗?这肯定会影响可用性,性能和安全。

第一部分的答案是创建架构的运行和部署视图来反映运行环境的组件类型,通信机制和基础服务。

第二部分的答案是关于易变性。我们的结构文档模版中有专门关于它的一节。在变更指导部分我们描述了让系统变得可配置的解决方案的机制。以下是一些变化机制例子:

  • 替换提供同样接口的组件
  • 组件复制
  • 组件可选的包含(插件和附件)
  • 构建时组装配置

类似于 OSGi 的框架给了你这些开箱即用的变化机制。架构的运行时情况可能会随组件的不同而变化。 架构文档应该在变更指导中记录什么组件会变化,有什么可选项或配置及其影响,什么条件下可能会触发特殊的选项,和每个选项的绑定时间(对于 OSGi 可能是运行时,但也可能是构建或部署时)。

Eoin:任何让一个系统的组件结构在运行时(就设计时而言)更加可见是一件很好的事情,现在当我们从设计进行到代码时,我们丢弃了很多这样的信息(因此需要一个架构描述–参见前述答案!)我最近在学习 OSGi 并且看起来它解决了这个高度关注的问题,帮助系统功能结构更容易被理解。同样的,像 OSGi 这样的模块技术只是帮助展示了架构结构的一种,而在实践中,还需要考虑其他种类。因此即便它们可能提供了很多其他技术上的好处来证明他们自己,它们依然不允许我们放弃我们的架构描述。

Len:一个标准的视图软件架构是模块分解视图。 每一个视图中的模块都代表了一个封装范围。OSGi 是指定封装范围的一种方式,它很适合模块分解视图。

Grady:OSGi 主要关注 Kruchten 所说的实现视图,也就是与组件的物理打包及在随后为部署视图做准备相关的事情。它们处理了这些组件生存的物理拓扑结构。这样,从一个架构的角度来,OSGi 只是一个特殊的封装打包技术。

UML 的特性如”配置文件” 和 “模版” 是如何帮助记录软件架构的?

Grady:这些是主要的 UML 可扩展性机制……但是以我的经验来看,基本的 UML 语言已经足够记录大多数软件集约型系统的架构核心部分了。

Paulo:UML 最初是为了给面向对象系统建模而形成的,现在被看作通用的建模语言。 意味着同样的符号能够被用来代表很多不同的事物。比如,一个叫 Dispatcher 的 UML 组件可以很好地代表 Java EE 中一个 Servlet 或在 OS 内核的一个线程。 你可以定义模版来使 UML 符号专门化,如果你知道你的一些组件将会是 servlet,就把它们模版化为 <>。

如果使用正确的话,模版会自动的把你的 UML 图表达得更清楚。UML 自带一些标准模版,但我们也可随意创建自己的模版。一个 UML 配置文件是一组有机组合的 UML 模版。UML 中有一个 Java EE 的配置文件,一个.NET 的配置文件,一个 SOA 的,一个系统工程的以及更多其他的配置文件。 重用或重新创建模版及配置文件都是个很好的主意。

Eoin:一句话,我认为他们是救生圈。 UML2 的扩展特性是很突出的特点之一,并且该特点也让 UML 可用于很多不同的领域。 很好利用它的人很少,但是现代 UML 工具已经最终支持了语言的扩展能力并且能用模版和配置文件来调整 UML 使之适合更手头的工作所使用特定的语言。只需要很少工作,你就能定制一个定制化 UML 版本专门用于表达你的架构中的那些结构。其结果是有了一个基于 UML 的架构描述语言和现成的工具支持,因此能克服多数基于研究的 ADL 语言(如 xADL,Acme 或 Darwin)引进(采用)时的主要障碍之一。

有一些新规范描述了在 SOA 中进行服务设计时的 UML 概要文件和元模型,比如面向服务架构的建模语言(SoaML)。在使用 SOA 和云计算架构模型的应用程序中架构文档应该如何来做呢?

Eoin:我的观点是 SOA 和云计算是相对独立的技术(即使一个能让另一个更容易)。SOA 是一种架构风格,它指导你将系统设计成一组松耦合的通信服务;云计算是一个部署模型,它通过运行时平台的虚拟化来简化系统所驻存服务器的提供,运维和管理。他们都是与位置独立相关的技术,但是从不同角度解决该问题。 比如 DSL,我并不认为这些技术从根本上改变了你需要从架构描述中捕获的内容或者最多需要捕获多少。然而,就像我们上面所讨论的,定制型的建模语言(如 SoaML)肯定有助于让记录某些架构类型的过程更加直截了当。

Paulo:SOA 是一个架构风格,能够用很多技术来实现,比如 SOAP Web 服务,REST,消息系统,BPEL 和 ESB。我认为在实际中当你创建一个基于 SOA 的架构时候,很快你就要明确你将要使用的技术。

比如,我认为如果在 SOA 设计中一个服务提供商组件被模板化为 <> 或者 <> 而不是通常的 < >,对于读者来说这就更加一针见血。 想一想,软件开发者们不得不不断跟进新的编程语言、脚本语言、技术、开发框架、设计模式、IDE 和插件、建模工具以及开发技术的增长速度。

作为一个软件开发者,如果 UML 概要文件本身很简单,且包含了我已经熟悉的解决方案的元素模板,那我就愿意去学习它。 我认为一个技术无关的 UML 概要文件(比如 SoaML)受到广大架构师的欢迎是很难的。如果你用的建模工具支持它的话,熟悉这个配置文件当然非常重要。不管有没有 UML 概要文件,要正确记录采用 SOA、云计算、OSGi、P2P 或其他架构方式的应用程序,关键是要包含一个用于明确表达该方式的特定的元素类型及关系类型的架构视图。

Grady:没错,它关系到哪些是重要的设计决策的决定。在选择使用服务的系统中,消息本身的设计(不只是点对点的服务)是非常重要的(但也经常被忘记)。在云计算的场合(顺便提一下,这个词汇在当下更迎合市场而不是技术),更多关注的是部署视图的元素。

哪些是软件架构师们在记录他们的软件架构时需要牢记的最佳实践和“陷阱”(gotchas)?

Len

  • 软件架构师们需要牢记生成文档的目的,过度或不足都是对文档目的错误判断的表现。
  • 项目并不是使用文档作为经年沟通的最佳决定因素,因为随着时间的消逝,大部分项目组成员将不再担任维护和升级的工作,文档是否存在对于他们没有什么利害关系。
  • Wiki 被证明为维护架构文档的一种有效的媒介。

Grady:我想到三件事情:不要过度文档,对每一个迭代保证文档的新鲜度,一定要注意那些不易从代码本身抽取出来的视图及横切关注点。

Paulo

  • 用多个视图来记录架构。
  • 总是记得在你的图表中做记号。
  • 如果你使用了一个设计或架构的模式,请让你的读者知道。
  • 遵循模版。
  • 只有当某些信息对相关的人有用的时候才花时间记录。
  • 记录重要的设计决策的理由。
  • 记得检查你所设计的内容。

Eoin:我想起的第一件事就是 SEI 的《记录软件架构》(Documenting Software Architectures)一书中有一个很好的列表,七个“优秀文档规则”,目前也是他们在网站上公开的技术报告之一(SEI-2000-SR4)。

我认为对这个问题的全面回答可能需要一本书,但我认为记录软件架构时必须牢记的一些更重要的以下几点:

  • 记录时有明确的目的。
  • 对明确的受众来创建架构描述。
  • 描述精确,即使你希望抽象。
  • 谨慎定义你使用的符号,这样其他人能够清楚地明白他们的意思。
  • 使用一组软件架构观点定义作为辅助备忘录来指导描述的内容。
  • 不要让文档成为关注的焦点,你所从中获得的过程,内容和交流才是最重要的事情。
  • 迭代地描述架构,经常检查并确保它的正确性及有效性。

在当前的经济和市场情况下,企业中的软件架构文档如何能对该公司有所帮助?

Grady:这是一个把架构像工艺品一样看待的精髓之处,在 IEEE 软件的架构部分,在我的专栏里我刚写了一篇关于这个主题的好几页的文章。

Paulo:Steve McConnell、Barry Boehm 和其他一些人已经发现源自需求或架构的一些缺陷很难修复。 换句话说,一个稳健的架构是有回报的。你的软件架构师有很多经验并且对模式、目前的技术、建模工具和语言非常了解并不重要,因为如果这个家伙创建了一个非常独特巧妙地设计但是开发者们并不理解它,这个项目并不会从这个好架构中受益。软件文档用于交流想法时很容易失败。一个原因是软件工程师通常没有受过写技术文档培训。好的架构文档可以推动:

  • 架构评估
  • 软件重用
  • 成功的采购计划或征求书
  • 项目管理任务(估计,分配作业,跟踪)

总之,详细程度恰到好处的好文档会提高软件项目成功的几率。

Eoin:如果能被很好的开发及使用,架构描述和一直进行的架构检查能提供给组织更多的在建系统(以及那些已经存在的系统)的可见性来解答以下类似的疑问:

  • 我们的系统有正确的功能吗(个别地或共同地)?
  • 我们的系统之间有重复吗?
  • 我们的系统是持续一致的方式来开发的吗?
  • 我们有没有过度的复杂、灵活、协作、安全或其他潜在的昂贵系统的特性?
  • 我们是不是建立了一个不够灵活的系统,它会导致我们接下来会花费很多来改进它?
  • 我们有没有犯成本方面的错误?

Len:软件架构文档是为以上的目的服务的。他们帮助一个企业实现它所期望的对项目监管、强化开发者们交流,强化经年年的交流以及对设计的分析等目标。

你认为软件架构文档的未来会是什么样?

Eoin:我希望未来我们需要较少的软件架构文档因为我们将能够在代码和运行时系统中看到架构!今天我们需要很多架构文档的原因之一是我们手头的技术还不能直接地表示架构结构。我希望能看到我们的架构结构体作为一流的实现结构,而且架构文档能够发展并用于捕捉决策,逻辑和分析,而不只是捕获结构。在达到这一理想的过程中,我希望在 DSL 和 ADL(架构描述语言)领域的工作能更直接指引前进的方向,当我们改善描述语言的时候,我们也在致力于寻找如何把信息准确嵌入运行时系统中的方法。

Paulo:软件架构原则还是个很新的概念,当一个架构师创建出架构文档后,它能很容易地被一个从来没有和这个架构师合作过的一个开发者所理解,实现这一目标还有很长的路要走。达到这个目标的方法是让新的架构师在学校学习软件架构而不是在实战中不断的尝试和出错。这种教育包括软件架构的正确表现形式以让其他人更好地消化和理解。通向良好的软件架构的教育这个方向的重要工作包括: Grady Booch 在软件架构手册方面的工作以及 SEI 开发的课程和出版物等。

Grady:当下在架构框架相关方面: TOGAF 、NEA、 DoDAF MoDAF FSAM Zachman 等已经花了很多精力。 好消息是有一个这些框架和方法相关的充满活力的对话在进行中—但是我希望随着时间他们之间会有洗牌或变得更简化。

Len:理想的开发环境是文档只需要按一下按钮就可得到。这需要一个集成的开发、需求管理及项目管理的环境。尽管离它的到来还需很长时间,它依然是一个值得我们努力的目标。

查看英文原文 Virtual Panel on Software Architecture Documentation


感谢马国耀对本文的审校。

给InfoQ 中文站投稿或者参与内容翻译工作,请邮件至 editors@cn.infoq.com 。也欢迎大家加入到 InfoQ 中文站用户讨论组中与我们的编辑和其他读者朋友交流。

2010 年 1 月 11 日 02:377147
用户头像

发布了 42 篇内容, 共 14.6 次阅读, 收获喜欢 1 次。

关注

评论

发布
暂无评论
发现更多内容

LeetCode题解:155. 最小栈,单个栈存储入栈元素与最小值之差,JavaScript,详细注释

Lee Chen

LeetCode 前端进阶训练营

甲方日常4

句子

工作 随笔杂谈 日常

DockerHub 镜像仓库的使用

哈喽沃德先生

Docker 容器 微服务 镜像 容器技术

java安全编码指南之:对象构建

程序那些事

Java 安全 安全编码指南 对象构建

SpreadJS 纯前端表格控件应用案例:表格数据管理平台

Geek_Willie

SpreadJS 纯前端表格控件应用案例:MHT-CP数据填报采集平台

Geek_Willie

week 12 作业

Geek_2e7dd7

性能相关 磁盘I/O子系统

Linuxer

拖延症竟然是自己给自己的一种奖励?如何干掉它?

非著名程序员

个人成长 拖延症 番茄土豆工作法

anyRTC Native 4.1.0.1与Web SDK 4.0.11上线

anyRTC开发者

学习 WebRTC 语音 直播 sdk

Cassandra Gossip协议的二三事儿

华为云开发者社区

源码 三次握手 开发者 Cassandra Gossip协议

MAC系统初始化

焦振清

macos 重装系统

你问我答:现有的应用有必要做微服务改造吗?

BoCloud博云

DevOps 微服务 容器云 云平台 博云

揭开链表的真面目

Java旅途

Java 数据结构 链表

C语言内存泄露很严重,如何应对?

华为云开发者社区

c 内存泄露 内存 代码 函数

云原生如何来进行HTTPS升级

soolaugust

架构 云原生 设计模式

话题讨论 | 当你敲代码累了时,一般喜欢吃点什么补充能量?

InfoQ写作平台官方

加班 写作平台 代码 话题讨论

挽救你的视频号:能够把PPT转换成视频,把备注转换成语音的开源项目

陈磊@Criss

3种双集群系统方案设计模式详解

华为云开发者社区

数据库 数据仓库 数据 双集群系统 双ETL模式

关于显性知识和隐性知识

Tanmer

知识管理 知识产权

Golang写算法

卒迹

golang 算法

1. 不吹不擂,第一篇就能提升你对Bean Validation数据校验的认知

YourBatman

Hibernate-Validator Bean Validation 数据校验 JSR380

SpreadJS 纯前端表格控件应用案例:雨诺订单管理系统(雨诺OMS)

Geek_Willie

技术分享:即构互动白板音视频同步、多端有序协作技术实践

ZEGO即构

音视频 在线教育 SVG

新时代背景下的Java语法特性

九叔

Java java 14 java 14 新特性 Java 分布式

Jenkins持续集成「编译打包、代码检查、单元测试、环境部署、软件测试​」

清菡

jenkins

oeasy教您玩转linux-010110内容回顾

o

Spring Bean处理器

TinyKing

Spring Framework

week 12 学习总结

Geek_2e7dd7

自己做的 PPT 总被批「缺少干货」?试试先回答这三个问题

Tony Wu

效率工具 方法论 PPT

面试必备知识点:悲观锁和乐观锁的那些事儿

鄙人薛某

面试 乐观锁 悲观锁 CAS 并发控制

移动应用开发的下一站

移动应用开发的下一站

虚拟研讨会:软件架构文档-InfoQ