<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>架构 on 元闰子的邀请</title>
    <link>https://yrunz.com/tags/%E6%9E%B6%E6%9E%84/</link>
    <description>Recent content in 架构 on 元闰子的邀请</description>
    <generator>Hugo -- gohugo.io</generator>
    <language>en-us</language>
    <lastBuildDate>Sun, 03 Aug 2025 00:00:00 +0000</lastBuildDate><atom:link href="https://yrunz.com/tags/%E6%9E%B6%E6%9E%84/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>分离还是统一，这是个问题</title>
      <link>https://yrunz.com/p/%E5%88%86%E7%A6%BB%E8%BF%98%E6%98%AF%E7%BB%9F%E4%B8%80%E8%BF%99%E6%98%AF%E4%B8%AA%E9%97%AE%E9%A2%98/</link>
      <pubDate>Sun, 03 Aug 2025 00:00:00 +0000</pubDate>
      
      <guid>https://yrunz.com/p/%E5%88%86%E7%A6%BB%E8%BF%98%E6%98%AF%E7%BB%9F%E4%B8%80%E8%BF%99%E6%98%AF%E4%B8%AA%E9%97%AE%E9%A2%98/</guid>
      <description>To be, or not to be, that is the question. —— 莎士比亚
 前言 “高内聚，低耦合”是软件工程领域广为流传的一个原则，在此原则的驱使下，应用系统逐渐从单体架构演进成微服务架构，甚至进一步按函数粒度拆分成 FaaS（函数即服务），呈分离趋势。
然而，我们又能看到很多软件系统呈统一的演进趋势，比如数据系统从批处理和流处理分离的 Lambda 架构[1]演进到流批一体架构、从数据仓库和数据湖分离演进到湖仓一体；比如大模型，从文本大模型演进到多模态大模型。
再往下到硬件基础设施，随着 AI 时代的到来，CPU 已难以招架新的 AI 负载，各类加速芯片纷纷出现，如 GPU、TPU、DPU，整体呈从通用到专用的分离趋势；但包含 36 个 CPU 和 72 个 GPU 的 NVIDIA GB200 NVL72[2]、包含 192 个 CPU 和 384 个 NPU 的 HUAWEI CloudMatrix384[3]等超节点的出现，似乎又呈现统一趋势。
那么，从顶层业务到底层硬件，整个系统到底是趋于分离，还是统一呢？
这是个问题。
本文将从业务、模型、平台、基础设施四个视角出发，探讨各类系统走向分离或者统一的原因，并尝试从中总结出趋势。
业务视角 大多数业务始于单体架构，它架构和技术栈简单，非常利于初创团队进行原型验证。然而，随着业务的增长，代码量和用户量变得更多，单体架构逐渐显现出它的局限性：
 可维护性差。某个模块新增或修改一个特性，就必须重新升级整个系统。 可用性差。某个模块出现一个 BUG 可能会导致整个系统宕机，对外无法提供服务。  这些局限性背后的原因是系统模块间耦合太深，导致牵一发而动全身。
我们可以根据业务属性，将模块拆分出来成为独立的服务，于是就有了服务化架构。更近一步，再将数据解耦，让每个服务拥有独立的数据库实例，就有了微服务架构[4]。
 更详细的从单体架构演进到微服务架构的内容，参考《软件架构，一切尽在权衡[4]》。
 虽然微服务架构也有缺点，比如服务间通信导致的性能下降，但人们更倾向于在平台或基础设施层去解决它们，而不是走回单体架构的路。
AI 时代的应用也是如此，虽然目前 Multi-Agent 系统的失败率普遍在 60% 以上 [5]，存在 Agent 间无效通信等诸多问题，但大多人都不会怀疑从 Single-Agent 到 Multi-Agent 的演进趋势。Multi-Agent 系统下，各个 Agent 各司其职，在复杂任务的执行上对比 Single-Agent 系统有着绝对的优势。</description>
    </item>
    
    <item>
      <title>深入理解Lambda架构</title>
      <link>https://yrunz.com/p/%E6%B7%B1%E5%85%A5%E7%90%86%E8%A7%A3lambda%E6%9E%B6%E6%9E%84/</link>
      <pubDate>Sun, 21 Jul 2024 00:00:00 +0000</pubDate>
      
      <guid>https://yrunz.com/p/%E6%B7%B1%E5%85%A5%E7%90%86%E8%A7%A3lambda%E6%9E%B6%E6%9E%84/</guid>
      <description>荐语 本文要介绍的是 2015 年 O&amp;rsquo;Reilly 出版的书籍 Big Data: Principles and best practices of scalable realtime data systems，书中主要介绍一种常见的大数据架构 —— Lambda 架构。Lambda 架构通过批处理和流处理相结合的方法，有效处理了大数据场景下的工作负载。相比以关系型数据库为核心的传统数据系统，它在可扩展性、容错性等方面有了更进一步的提升。
虽然 Lambda 架构是十年前被提出的数据架构，但 Lambda 架构的以不可变数据作为核心、历史和实时数据分层处理的架构思想仍值得我们学习。
传统数据系统的局限 考虑有这么一个系统，根据用户的浏览记录，统计用户访问网页的次数：
在上述系统中，每次用户浏览一个 url，应用就会向数据库更新一行数据。随着用户量的不断上升，并发量急剧上涨，数据库最先达到瓶颈，频繁出现更新超时的现象。针对这种情况，我们可以引入消息队列，将单次写改成批量写，以此降低数据库的并发数。
但这并不是长久之计，随着用户量的再次上涨，单实例的数据库还是会成为单点瓶颈。为此，可以对数据库进行水平扩展，分库分表，将原本集中在单个数据库实例上的请求，负载均衡到多个实例之上。
分库分表虽能消除数据库单点瓶颈，但也会带来其他的一些问题：
 应用逻辑更复杂。应用层需要根据数据的 key 找到其所在的数据库实例，以便进行数据更新。 维护成本更高。为了避免因数据库实例宕机导致的服务不可用，需要通过增加数据副本、缓冲队列等手段提升可用性。另外，随着数据量的增大，可能因数据库实例间负载不均衡，而产生对数据进行重分区的操作。这些都会导致维护成本的急剧增加。 容错性问题。一旦开发者在更新数据库时出现人为错误，比如在增加 pageviews 字段时多乘了 2，那么数据将难以恢复，因为很难溯源。  上述例子中，从简单的 CURD，到增加消息队列、分库分表、副本、重分区等，系统也越来越复杂。导致这一切的原因是，传统数据库系统并非为分布式而生，它无法很好地适用于大数据场景。
大数据技术的局限 那么，大数据技术就能解决这些问题吗？答案是，否！
像 Hadoop、MapReduce、HBase 等这类大数据技术，虽然能够天然就处理海量数据，但它们都有各种局限，比如，MapReduce 是批处理系统，计算时延高；HBase 键值查询快，但无法满足复杂的 SQL 查询场景。
为了找到解决方法，我们还需从大数据系统的本质入手。
大数据系统的本质 作者对数据系统进行了如下定义：
 A data system answers questions based on information that was acquired in the past up to the present.</description>
    </item>
    
    <item>
      <title>软件架构，一切尽在权衡</title>
      <link>https://yrunz.com/p/%E8%BD%AF%E4%BB%B6%E6%9E%B6%E6%9E%84%E4%B8%80%E5%88%87%E5%B0%BD%E5%9C%A8%E6%9D%83%E8%A1%A1/</link>
      <pubDate>Sun, 16 Jun 2024 00:00:00 +0000</pubDate>
      
      <guid>https://yrunz.com/p/%E8%BD%AF%E4%BB%B6%E6%9E%B6%E6%9E%84%E4%B8%80%E5%88%87%E5%B0%BD%E5%9C%A8%E6%9D%83%E8%A1%A1/</guid>
      <description>荐语 本文要介绍的是 2021 年 O&amp;rsquo;Reilly 出版的书籍 Software Architecture: The Hard Parts（后文简称 SAHP），某种程度上，它是 Fundamentals of Software Architecture（后文简称 FSA）的延续，两本书的主要作者相同。FSA 是基础篇，重点介绍了常见的 8 种架构模式；SAHP 是进阶篇，重点介绍了如何从单体架构演进到分布式架构。
软件架构，特别是分布式架构，是很复杂的。这世上不存在所谓的通用架构模式，针对特定的应用场景，架构师需要在数据一致性、可扩展性、性能等多个因素上进行权衡，这也是本书标题强调的 The Hard Parts。
FSA 里有一句名言，“Everything in software architecture is a trade-off.”。本书的重点就是要教会你，在单体架构演进到分布式架构的过程中，如何众多因素上进行权衡分析，以此来选择出一个符合实际业务场景的软件架构。
为什么叫 “The Hard Part”？ 书的名字叫 Software Architecture: The Hard Parts，其中，Hard 有两层含义。
第一，软件架构很难。设计软件架构需要考虑的因素很多，架构师也必然会遇到各式各样的难题，而且它们往往没有通用的解决方法。软件工程的问题，很多时候可以通过 Google 搜索来解决；而软件架构的问题，只能根据实际的业务场景，在众多的考虑因素上进行权衡。所以，在软件架构上，不要试图找到最好的设计，而是要努力实现最不糟糕的权衡组合。
 Don’t try to find the best design in software architecture; instead, strive for the least worst combination of trade-offs.
 第二，软件架构是稳定的。Hard 取自于硬件的英文单词 Hardware，相比软件 Software，硬件是更稳定的一个，它是软件的基础。同理，软件架构是一款软件的架子、基础，好的架构通常都是稳定的，不轻易随软件的变更而变化。</description>
    </item>
    
    <item>
      <title>从分层架构到微服务架构（五）之服务化架构</title>
      <link>https://yrunz.com/p/%E4%BB%8E%E5%88%86%E5%B1%82%E6%9E%B6%E6%9E%84%E5%88%B0%E5%BE%AE%E6%9C%8D%E5%8A%A1%E6%9E%B6%E6%9E%84%E4%BA%94%E4%B9%8B%E6%9C%8D%E5%8A%A1%E5%8C%96%E6%9E%B6%E6%9E%84/</link>
      <pubDate>Fri, 08 Apr 2022 00:00:00 +0000</pubDate>
      
      <guid>https://yrunz.com/p/%E4%BB%8E%E5%88%86%E5%B1%82%E6%9E%B6%E6%9E%84%E5%88%B0%E5%BE%AE%E6%9C%8D%E5%8A%A1%E6%9E%B6%E6%9E%84%E4%BA%94%E4%B9%8B%E6%9C%8D%E5%8A%A1%E5%8C%96%E6%9E%B6%E6%9E%84/</guid>
      <description>《从分层架构到微服务架构》是一系列介绍《Fundamentals of Software Architecture》中提到的8种架构模式的文章，这里不会事无巨细地介绍所有的细节，而是会挑选其中关键内容，更多详情请阅读原书。
往期精彩：
 从分层架构到微服务架构（一） 从分层架构到微服务架构（二）之分层架构 从分层架构到微服务架构（三）之管道架构 从分层架构到微服务架构（四）之微内核架构   前言 从本文开始，我们进入了《从分层架构到微服务架构》系列中分布式架构的介绍，本文要介绍的是服务化架构（Service-Based Architecture，SBA）。
SBA 可以看成是单体架构和微服务架构之间的一个折中方案，它也是按照业务领域进行服务划分，但服务划分的粒度相比微服务要更粗。SBA 与微服务架构一大不同是，它允许各个服务间共享同一个数据库实例，这也使得 SBA 在架构上既有单体架构的特点，也有分布式架构的特点，显得更加的灵活。因此，从单体架构演进到 SBA，会比直接演进到微服务架构更加容易。
架构视图 基础视图 SBA 的基础架构视图分成 3 部分：
 User Interface，作为系统的接入口，接收客户端的请求，并转发到业务服务。。 Domain Services，业务服务按照领域进行划分，分开部署、业务独立。 Database，服务间共享的数据库实例，因为数据库实例只有一个，所以可以支持 ACID 事务。  使用 SBA 的系统通常只会划分 4 ～ 12 个服务，避免产生过多的数据库连接。服务数量不多，也决定了 SBA 中的服务相比微服务架构中的服务有着更粗的粒度。User Interface 与服务间通过远程通信协议来完成业务往来，常见的通信方式有REST、RPC、消息队列等。需要注意的是，SBA 是不允许服务间通信的，这与微服务架构有着本质的区别。
大多数情况下，SBA 中的服务只有一个或者少量实例，与微服务动辄成百上千个实例有着很大的区别。主要是因为 SBA 服务粒度更粗，无法做到像微服务那样精准的按需扩容，扩容太多反而会导致资源的浪费。
SBA 的另一大特点是允许所有服务共享同一数据库实例，使得它能够直接将传统单体架构的那一套 SQL 查询逻辑、ACID 事务搬过来，让架构的演进更加的平滑。不过，共享数据也会带来一些问题，比如数据模型变更的影响范围更大，后面会在“**数据拆分”**一节详细讲述。
拆分 User Interface 在大型系统中，单一的 User Interface 可能导致代码耦合、性能瓶颈等问题，这时候我们可以进一步对它进行拆分。拆分的方法可以是基于业务领域的拆分，业务相关的几个服务使用同一个 User Interface；或者基于服务的拆分，为每个服务都配备一个 User Interface。
拆分 Database 类似地，我们也可以对数据库进行拆分，可以拆分成几个服务共享一个实例；也可以像微服务架构中那样，每个服务独享一个实例。数据库拆分的原则就是：确保数据是解耦的，不会被其他服务所依赖，避免出现跨库查询或服务间通信。</description>
    </item>
    
    <item>
      <title>实现DCI架构</title>
      <link>https://yrunz.com/p/%E5%AE%9E%E7%8E%B0dci%E6%9E%B6%E6%9E%84/</link>
      <pubDate>Sun, 10 Oct 2021 00:00:00 +0000</pubDate>
      
      <guid>https://yrunz.com/p/%E5%AE%9E%E7%8E%B0dci%E6%9E%B6%E6%9E%84/</guid>
      <description>前言 在面向对象编程的理念里，应用程序是对现实世界的抽象，我们经常会将现实中的事物建模为编程语言中的类/对象（“是什么”），而事物的行为则建模为方法（“做什么”）。面向对象编程有三大基本特性（封装、继承/组合、多态）和五大基本原则（单一职责原则、开放封闭原则、里氏替换原则、依赖倒置原则、接口分离原则），但知道这些还并不足以让我们设计出好的程序，于是很多方法论就涌现了出来。
近来最火的当属领域驱动设计（DDD），其中战术建模提出的实体、值对象、聚合等建模方法，能够很好的指导我们设计出符合现实世界的领域模型。但DDD也不是万能的，在某些应用场景下，按照传统的战术建模/面向对象方法设计出来的程序，也会存在可维护性差、违反单一职责原则等问题。
本文介绍的DCI建模方法可以看成是战术建模的一种辅助，在某些场景下，它可以很好的弥补DDD战术建模的一些缺点。接下来，我们将会通过一个案例来介绍DCI是如何解决DDD战术建模的这些缺点的。
 本文涉及的代码归档在github项目：https://github.com/ruanrunxue/DCI-Architecture-Implementation
 案例 考虑一个普通人的生活日常，他会在学校上课，也会趁着暑假去公司工作，在工作之余去公园游玩，也会像普通人一样在家吃喝玩乐。当然，一个人的生活还远不止这些，为了讲解方便，本文只针对这几个典型的场景进行建模示例。
使用DDD建模 按照DDD战术建模的思路，首先，我们会列出该案例的通用语言：
 人、身份证、银行卡、家、吃饭、睡觉、玩游戏、学校、学生卡、学习、考试、公司、工卡、上班、下班、公园、购票、游玩
 接着，我们使用战术建模技术（值对象、实体、聚合、领域服务、资源库）对通用语言进行领域建模。
DDD建模后的代码目录结构如下：
- aggregate:聚合- company.go- home.go- park.go- school.go- entity:实体- people.go- vo:值对象- account.go- identity_card.go- student_card.go- work_card.go我们将身份证、学生卡、工卡、银行卡这几个概念，建模为值对象（Value Object）：
package vo // 身份证 type IdentityCard struct { Id uint32 Name string } // 学生卡 type StudentCard struct { Id uint32 Name string School string } // 工卡 type WorkCard struct { Id uint32 Name string Company string } // 银行卡 type Account struct { Id uint32 Balance int } .</description>
    </item>
    
    <item>
      <title>从分层架构到微服务架构（四）之微内核架构</title>
      <link>https://yrunz.com/p/%E4%BB%8E%E5%88%86%E5%B1%82%E6%9E%B6%E6%9E%84%E5%88%B0%E5%BE%AE%E6%9C%8D%E5%8A%A1%E6%9E%B6%E6%9E%84%E5%9B%9B%E4%B9%8B%E5%BE%AE%E5%86%85%E6%A0%B8%E6%9E%B6%E6%9E%84/</link>
      <pubDate>Fri, 23 Apr 2021 00:00:00 +0000</pubDate>
      
      <guid>https://yrunz.com/p/%E4%BB%8E%E5%88%86%E5%B1%82%E6%9E%B6%E6%9E%84%E5%88%B0%E5%BE%AE%E6%9C%8D%E5%8A%A1%E6%9E%B6%E6%9E%84%E5%9B%9B%E4%B9%8B%E5%BE%AE%E5%86%85%E6%A0%B8%E6%9E%B6%E6%9E%84/</guid>
      <description>《从分层架构到微服务架构》是一系列介绍《Fundamentals of Software Architecture》中提到的8种架构模式的文章，这里不会事无巨细地介绍所有的细节，而是会挑选其中关键内容，更多详情请阅读原书。
往期精彩：
 从分层架构到微服务架构（一） 从分层架构到微服务架构（二）之分层架构 从分层架构到微服务架构（三）之管道架构   前言 微内核架构（Microkernel Architecture），也被称为插件式架构（plug-in architecture），作为一个在几十年前就被创建出来的架构模式，它如今仍然被广泛应用在各个领域中。比如在Web浏览器领域，谷歌的Chrome浏览器之所以被认为功能强大，一个很重要的原因是它有着丰富的插件类型；在开发工具领域，微软的VS Code初始安装后还只是个简单的文本编辑器，但用户可以安装各种插件，从而让它摇身一变成为功能强大的IDE。
Chrome和VS Code都是微内核架构的典型应用例子，它们提供一个具备最基础能力的核心系统，并定义好插件的开发接口。至于需要开发或安装哪种类型的插件，则完全由普通开发者和用户决定，这样的设计让系统具备了极强的可定制化和可扩展能力。
架构视图 微内核架构由以下两部分组成：核心系统（core system）和插件（plug-in component），将应用系统的业务逻辑拆分成核心系统和插件，能够提供很好的可扩展性和灵活性，极大地方便了后续需求的新增和修改。
核心系统 核心系统通常只需提供能够支撑整个系统正常运行的基本功能，比如前文所举的VS Code例子，用户初始安装的是VS Code的核心系统，它只是一个提供了打开文件、编辑文件内容和保存文件等基本功能的文本编辑器，其他的扩展功能（如语法检查）都是通过安装插件集成的。将复杂的业务逻辑从核心系统中剥离出来，并通过插件实现，能够提升系统的可扩展性和可维护性。同时，因为复杂的功能都成了互不干扰的插件，系统的可测性也得到了提高。
考虑现在需要实现一个电子设备回收系统，在回收之前，每种型号的手机设备的回收流程都不一样，那么我们可以这样去实现：
public void assessDevice(String deviceID) { if (deviceID.equals(&amp;#34;iPhone6s&amp;#34;)) { assessiPhone6s(); } else if (deviceID.equals(&amp;#34;iPad1&amp;#34;)) assessiPad1(); } else if (deviceID.equals(&amp;#34;Galaxy5&amp;#34;)) assessGalaxy5(); } else ... ... } } 如果我们把assessDevice看成是核心系统，那么后面每次新增一个型号的手机，都需要新增一个if分支，也即对核心系统进行了改动。这样的设计会导致核心系统非常地脆弱，正所谓改的越多，出问题的概率也越大。
比起这种将所有的可定制业务逻辑放在核心系统上的设计，更好的应该是将它们实现为插件的形式，这样不仅每个设备回收逻辑都解耦了，还提供了强大的可扩展性：添加一个新的回收设备类型，只需新增一种插件即可，核心系统无需变动。
public void assessDevice(String deviceID) { String plugin = pluginRegistry.get(deviceID); DevicePlugin devicePlugin = (DevicePlugin)constructor.newInstance(); DevicePlugin.assess(); } 微内核架构在实现时通常都结合了其他架构模式，这主要体现在核心系统的设计上，比如根据具体的业务特点，我们可以将核心系统设计成technically partitioned的分层架构，或者是domain partitioned的模块化架构。</description>
    </item>
    
    <item>
      <title>从分层架构到微服务架构（三）之管道架构</title>
      <link>https://yrunz.com/p/%E4%BB%8E%E5%88%86%E5%B1%82%E6%9E%B6%E6%9E%84%E5%88%B0%E5%BE%AE%E6%9C%8D%E5%8A%A1%E6%9E%B6%E6%9E%84%E4%B8%89%E4%B9%8B%E7%AE%A1%E9%81%93%E6%9E%B6%E6%9E%84/</link>
      <pubDate>Mon, 05 Apr 2021 00:00:00 +0000</pubDate>
      
      <guid>https://yrunz.com/p/%E4%BB%8E%E5%88%86%E5%B1%82%E6%9E%B6%E6%9E%84%E5%88%B0%E5%BE%AE%E6%9C%8D%E5%8A%A1%E6%9E%B6%E6%9E%84%E4%B8%89%E4%B9%8B%E7%AE%A1%E9%81%93%E6%9E%B6%E6%9E%84/</guid>
      <description>《从分层架构到微服务架构》是一系列介绍《Fundamentals of Software Architecture》中提到的8种架构模式的文章，这里不会事无巨细地介绍所有的细节，而是会挑选其中关键内容，更多详情请阅读原书。
往期精彩：
 从分层架构到微服务架构（一） 从分层架构到微服务架构（二）之分层架构   前言 管道架构（Pipeline Architecture），通常也被称为管道-过滤器架构（Pipes and Filter Architecture），是最常用的架构模式之一。大部分软件工程师都是通过Unix终端初次接触到该架构模式，Unix终端的Shell语言，对管道-过滤器有着原生的支持。
比如，现在需要实现这样的一个功能：读取一个文本文件的内容，找到使用频率最高的5个单词，并按照使用频率的大小顺序打印出单词及其使用频率。
那么，使用Shell可以这样来实现：
cat content.txt | # step1: 读取文件内容 tr -cs A-Za-z &amp;#39;\n&amp;#39; | # step2: 将单词按行输出 tr A-Z a-z | # step3: 将所有单词转换为 sort | # step4: 对单词进行排序 uniq -c | # step5: 计算出单词的频率 sort -rn | # step6: 按照频率对单词进行排序 head -n 5 # step7: 获取排序前5的单词 # 输出结果示例： 4 to 4 and 3 the 3 networks 3 linux 这段Shell代码就是一个简单的管道架构实现，其中|表示管道pipe，每一个step就相当于一个过滤器filter。每个filter都将上一个filter的输出结果作为输入数据，对数据进行处理后再将结果输出到管道中。</description>
    </item>
    
    <item>
      <title>从分层架构到微服务架构（二）之分层架构</title>
      <link>https://yrunz.com/p/%E4%BB%8E%E5%88%86%E5%B1%82%E6%9E%B6%E6%9E%84%E5%88%B0%E5%BE%AE%E6%9C%8D%E5%8A%A1%E6%9E%B6%E6%9E%84%E4%BA%8C%E4%B9%8B%E5%88%86%E5%B1%82%E6%9E%B6%E6%9E%84/</link>
      <pubDate>Sat, 30 Jan 2021 00:00:00 +0000</pubDate>
      
      <guid>https://yrunz.com/p/%E4%BB%8E%E5%88%86%E5%B1%82%E6%9E%B6%E6%9E%84%E5%88%B0%E5%BE%AE%E6%9C%8D%E5%8A%A1%E6%9E%B6%E6%9E%84%E4%BA%8C%E4%B9%8B%E5%88%86%E5%B1%82%E6%9E%B6%E6%9E%84/</guid>
      <description>《从分层架构到微服务架构》是一系列介绍《Fundamentals of Software Architecture》中提到的8种架构模式的文章，这里不会事无巨细地介绍所有的细节，而是会挑选其中关键内容，更多详情请阅读原书。
往期精彩：
 从分层架构到微服务架构（一）   前言 软件刚出现的时候，还是大型计算机的年代，一个软件系统一般都只会运行在一台机器上。随着软硬件技术的革新，计算机体积和成本逐渐变小，此时工程师们发现一个软件系统只运行在单台机器上会存在各种瓶颈。如果将系统按照功能划分成前端和后端，分别部署在两台服务器上，问题得到了缓解，于是便有了Client/Server架构的出现。
随后，个人电脑的兴起带动了众多富桌面应用（rich desktop application）的出现，它们基于操作系统上的user interface开发，数据则是存储在单独部署的database server上，通过标准的网络协议进行数据通信。这种Desktop + Database Server的架构和C/S架构一样，同属两层架构（two-tier architecture）。
随着90年代互联网的迅速崛起，Browser + Web Server + Database Server的组合也渐渐风靡。Browser为表现层，提供用户交互界面；Web Server为业务层，处理具体的业务逻辑；Database Server为数据层，存储系统数据。三个层次各司其职，这也是大家最熟悉的三层架构（three-tier architecture）。
上述的几种架构模式都属于分层架构（layered architecture）的范畴，分层架构并没有限定一定得有多少个层次，层次的数量可以根据应用场景灵活控制，因此也被称为n-tier architecture。它结构简单，基于此架构进行系统开发成本也很低（很多公司在组织结构上划分为前端工程师、后端工程师、DBA，根据康威定律，这天然就具备了分层架构开发的良好条件），因此它在业界备受欢迎。如果你的团队还不确定选择什么样的架构，又或者为了践行敏捷宣言中的“just starts coding“，那么分层架构会是一个不错的选择。
架构视图 在分层架构中，组件根据功能被划分在不同的层次上，虽然层次的数量和类型并没有被限制，但大多数的分层架构都由以下4层组成：表现层（presentation）、业务层（business）、持久层（persistence）和数据层（database），如下图所示。在一些简单的系统中，持久层的逻辑（如SQL）被嵌入到业务层中，形成了经典的三层架构；而在一些复杂的系统中，也会根据具体的业务划分为五层甚至更多的层次。
前文所述的表现层等4个层次都是逻辑的划分方法，在实际部署时，一般会有下图所示的几种部署形态。形态1中，表现层、业务层和持久层为一个部署单元，而数据层则单独部署，具体表现为一个独立部署的数据库或文件系统；形态2中，表现层被分离出单独部署，业务层和持久层组成一个部署单元，数据层依旧是单独部署的数据库或文件系统；形态3中，包括数据层在内的4层全都在同一个部署单元内，常见于业务简单的系统，它们往往使用的是嵌入式数据库或内存数据库。
分层架构中的每一层都扮演着各自的角色，比如表现层负责处理所有的用户请求和浏览器交互，而业务层则负责执行每次请求下的特定业务逻辑；表现层无需担心从哪里获取用户数据，它只需要将数据以特定的格式在浏览器上显示即可。同样地，业务层也无需关心用户数据从何而来以及如何呈现，它只需从持久层中取出数据，执行特定的业务逻辑（比如聚合数据），然后将结果返回给表现层。
每一层都是特定行为的抽象，这样的职责划分，使得组织能够快速高效地创建出责任模型，围绕各层打造开发团队。
层间隔离 分层架构中的每一层可以是封闭的或者开放的，封闭意味着当一个请求自顶向下在层间传递时，它不能跳过任意的一层。比如，当表现层接收到请求之后，它必须先后经过业务层和持久层才能到达数据层，如下图所示。
对于简单的数据获取类请求，如果让表现层能够直接访问数据层获取数据，无疑是最简单高效的。也即是让业务层和持久层变成开放状态，允许请求在层间传递时跳过此层。那么，究竟是封闭好，还是开放好呢？要解答这个问题，就要回到层间隔离的出发点上。
所谓的层间隔离，旨在降低一个层次上的变化对其他层次的组件的影响，简单来说，就是每个层次对其他层次的功能知道的越少越好。为了达到层间隔离的目的，就需要将每个层次置为封闭的状态。假设表现层能够直接访问持久层，那么持久层的变化将会直接影响到业务层和表现层，这加剧了层间的耦合，导致系统变化的代价高昂。
层间隔离可以降低层次变化对系统的影响，凡事没有绝对，在某些的场景，将特定的层次置为开放的状态也不失为一件好事。考虑以下例子，业务层中存在着一些共享组件承载着业务层公共的功能（比如日志类、审计类、日期和字符串工具类等）。现在有一项架构决策要求表现层不能直接访问这些共享组件，但矛盾的是，原则上表现层是可以直接访问业务层的，这种需要违反原则的决策将会很难落地。
一种解决方法是，新增一个服务层，该层包含了业务层的这些共享组件。因为业务层是关闭的状态，故表现层也就不能访问到这些共享组件了。然而，新增的服务层必须置为开放状态，否则业务层将无法直接访问持久层。新增一个服务层并置为开放状态，这样既落地了架构决策，也不会影响到原有的功能，一举两得。
注意事项 在使用分层架构时，需要注意以下两点：
1、做好模块的划分
为分层架构做好模块划分主要是为后续的架构演进做好准备，比如在业务复杂到一定程度后演进为微服务架构时，各个模块可以很自然地演进为微服务。为此，应该避免出现类的继承层次过深的现象，这会导致代码严重的耦合，不利于后续的架构演进。
2、避免掉进sinkhole反模式的陷阱
所谓sinkhole反模式指的是请求只是简单地路过各个层次，并没有做一些业务处理。
比如，表现层接收到一个获取基本用户数据（姓名、地址等）的请求后将它传递到业务层；然而，业务层并没有做任何的业务处理，直接将请求传递到持久层；持久层也仅仅是构造了一个简单的SQL语句，向数据层查询用户数据；最后，数据按照原路返回到表现层，中途没有经过任何的数据汇聚、转换等操作。
sinkhole反模式会导致很多不必要的对象实例化开销，从而增大了系统的内存消耗，并且影响了性能。
然而，一个系统多多少少都会存在一些sinkhole反模式场景，要判断一个系统是否已经彻底掉进sinkhole反模式的陷阱，主要还是看这类业务请求所占的百分比。根据20-80法则，当系统中有超过80%的业务请求是sinkhole类请求时，表示系统已经掉进sinkhole反模式的陷阱，这从侧面也说明该系统已经不再适合分层架构，是时候考虑架构演进了。
架构评分 从综合得分上看，分层架构的Overall cost和Simplicity得分很高，这很大程度上得益于分层架构本身是单体架构，少了很多分布式系统才有的复杂性。但这样导致Deployability得分很低，因为3行代码的改动就足以造成整个系统的重新部署。Testability得分不高也是这个原因，整系统的重新上线通常都需要将测试用例全部执行一遍，多了不少额外的工作量。
Elasticity、Fault tolerance、Scalability这些都是单体架构天然的劣势，自然地，分层架构在这些方面得分都很低。另外，sinkhole反模式的存在也拉低了分层架构在Performance上的得分。
总结 分层架构简单而高效，业界已经有很多成熟的应用，对那些项目刚刚起步，架构师们还没想好要采用哪种架构模式的系统而言，这是非常适合的。在实现分层架构时，我们需要合理地设置各个层次的封闭或开放状态，做好层间隔离，同时也要避免掉进sinkhole反模式陷阱。随着业务的不断扩张，分层架构在可维护性、可测试性、可扩展性等上的短板也会逐步被放大，此时就需要考虑往其他架构模式演进了。
每种架构模式都有其合适的应用场景，只有熟悉常用的几种架构模式，才能设计出更好的软件系统。下一篇文章，我们将继续介绍管道架构。</description>
    </item>
    
    <item>
      <title>从分层架构到微服务架构（一）</title>
      <link>https://yrunz.com/p/%E4%BB%8E%E5%88%86%E5%B1%82%E6%9E%B6%E6%9E%84%E5%88%B0%E5%BE%AE%E6%9C%8D%E5%8A%A1%E6%9E%B6%E6%9E%84%E4%B8%80/</link>
      <pubDate>Sat, 23 Jan 2021 00:00:00 +0000</pubDate>
      
      <guid>https://yrunz.com/p/%E4%BB%8E%E5%88%86%E5%B1%82%E6%9E%B6%E6%9E%84%E5%88%B0%E5%BE%AE%E6%9C%8D%E5%8A%A1%E6%9E%B6%E6%9E%84%E4%B8%80/</guid>
      <description>《从分层架构到微服务架构》是一系列介绍《Fundamentals of Software Architecture》中提到的8种架构模式的文章，这里不会事无巨细地介绍所有的细节，而是会挑选其中关键内容，更多详情请阅读原书。
 前言 谈到软件系统设计的方法论，在代码层面，有我们熟悉的23种设计模式（design pattern），对应到架构层面，则有所谓的架构模式（architecture pattern）。它们分别从微观和宏观的角度指导着我们设计出良好的软件系统，因此，作为一个软件工程师，我们不仅要熟悉设计模式，对常见的架构模式也要熟稔于心。正如看到一个设计模式的名字脑里就能浮现出大致的结构图，当我们看到一个架构模式的名字时，也要马上想到对应的架构图及其基本特点。比如，当谈到分层架构时，我们就应该想起它的架构图是怎样的、有哪些出色的架构特征（architecture characteristics）、系统是如何部署的、数据存储的策略是哪种、等等。
一般地，架构模式大致可以分成两类，单体架构（monolithic architecture）和分布式架构（distributed architecture）。本系列文章将会介绍以下8种常用的架构模式：
单体架构
 分层架构（Layered architecture） 管道架构（Pipeline architecture） 微内核架构（Microkernel architecture）  分布式架构
 基于服务的架构（Service-based architecture） 事件驱动架构（Event-driven architecture） 基于空间的架构（Space-based architecture） 面向服务的架构（Service-oriented architecture） 微服务架构（Microservices architecture）  软件设计中的谬误 在介绍架构模式前，我们先谈谈软件设计中的谬误（fallacy）。所谓谬误，就是在设计软件系统，特别是分布式系统时，我们先入为主地假设它们是正确，但实际上并非如此的一些观念。这些观念都是我们在设计软件时考虑不周的体现。
谬误1：网络是可靠的 很多软件工程师常常假设网络是可靠的，但实际并非如此。相比20年前，现在的网络会可靠很多，但是仍然具有很大的不确定性。如上图所述，Serivce B可能完全是正常运行的，但是因为网络的问题，Service A发出的请求无法到达Service B。一种更糟糕的场景是，Service B可以收到Service A的请求，并处理了相关的数据，但是网络问题导致了Service A无法收到Service B的响应，从而造成了数据不一致。网络的不可靠也是为什么系统中常常出现服务通信超时、服务熔断等的原因。
总而言之，如果假设网络是可靠的，那么我们设计出来的软件系统将会是不可靠的。
谬误2：时延是0 如上图所示，服务内组件间的函数/方法级别的调用，耗时是微妙，甚至是纳秒级别；但是服务间的远程调用（比如REST、消息队列、RPC），耗时会是微秒级别，甚至在异常场景会达到了秒级！在设计系统，特别是分布式系统时，时延是一个无法被忽视的因素，我们必须清楚系统的平均时延，否则设计出来的方案可能根本不可行。比如，假设系统中服务间通信时延为100ms，如果一个请求的调用链涉及到10个服务，那么该请求的时延将会是1000ms！这么高的平均时延对于一般系统来说是完全无法接受的。
进行系统设计时，考虑平均时延还不够，更重要的是95th和99th百分点。一个系统的平均时延可能仅仅只有数十毫秒，但是95th百分点的时延却达到了数百毫秒，很多时候，这也恰恰成为了拖垮整系统性能的那块“短板”。
谬误3：带宽是无限的 在单体架构中，业务流程都在单服务内闭环，消耗的带宽很少甚至为0，因此带宽并不是主要关注点。一旦将系统拆分成分布式架构，一个业务流程可能涉及多个服务间的通信，带宽就成了必须考虑的因素。带宽的不足，会导致网络变慢，从而影响系统的时延（谬误2：时延是0）和可靠性（谬误1：网络是可靠的）。
如上图所示，假设在一个Web系统中，Service A负责处理前端请求，Service B负责管理用户信息（包括姓名、性别、年龄等45个属性）。Service A每处理一个请求都需要向Service B查询用户姓名（200 bytes），而在一次请求中，Service B却返回了用户的所有信息（500 kb）。如果系统每秒处理2000次请求，每次请求消耗500 kb带宽，那么每秒消耗的总带宽会是1 Gb！如果Service B仅仅返回必须的姓名，那么同等条件下，每秒消耗的总带宽仅仅是400 kb。
此类问题就是所谓的stamp coupling，解决方法也很多，比如在请求中添加属性选择，使用GraphQL替代REST。相比于这些技术手段，更重要的是确定服务间通信所需的最小数据集，并在进行系统设计时将其作为一个重点关注的因素。
谬误4：网络是安全的 VPN、防火墙等的广泛使用，使得很多工程师在设计系统时忽略了“网络是不安全的”这一重要原则。特别是从单体架构演进到分布式架构以后，系统被攻击的概率将会大大增加。因此，在分布式系统中，每个服务都必须是安全的endpoint，这样才能确保任何未知或恶意的请求都被拦截掉。当然，安全是有代价的，这也是像微服务架构这类细服务粒度的系统，一次业务请求中调用链过长后性能极速下降的重要原因。
谬误5：网络拓扑一成不变 这里的网络拓扑指的是系统运行时所涉及到的网络设备，包括所有的路由器、防火墙、集线器、交换机等。很多工程师会假设网络拓扑是固定的，然而并非如此。</description>
    </item>
    
    <item>
      <title>在成为架构师之前所需了解的一些知识</title>
      <link>https://yrunz.com/p/%E5%9C%A8%E6%88%90%E4%B8%BA%E6%9E%B6%E6%9E%84%E5%B8%88%E4%B9%8B%E5%89%8D%E6%89%80%E9%9C%80%E4%BA%86%E8%A7%A3%E7%9A%84%E4%B8%80%E4%BA%9B%E7%9F%A5%E8%AF%86/</link>
      <pubDate>Sun, 13 Sep 2020 00:00:00 +0000</pubDate>
      
      <guid>https://yrunz.com/p/%E5%9C%A8%E6%88%90%E4%B8%BA%E6%9E%B6%E6%9E%84%E5%B8%88%E4%B9%8B%E5%89%8D%E6%89%80%E9%9C%80%E4%BA%86%E8%A7%A3%E7%9A%84%E4%B8%80%E4%BA%9B%E7%9F%A5%E8%AF%86/</guid>
      <description>前言 当你点开一个招聘APP，筛选条件选择互联网技术，在列出来的一大堆职位上，往往有那么几个带有“架构师”三个字眼的高薪职位。当你被它的高薪所吸引而点击查看职位详情时，又会被它的高要求所劝退。它们往往要求工作年限在5年以上，需要求职者有过3年以上的系统设计经验，精通各种架构模式和系统框架，反观自己却一个条件都不满足。
软件架构师就是这么一个让人向往，但又让人望洋兴叹的一个职位。就像建筑设计师总有成为总设计师的梦想，航天工作者总有成为总工程师的壮志，相信每一个软件工程师都有过成为软件架构师的想法。引用维基百科里的定义，软件架构师的职责就是在软件系统研发中，负责依据需求来确定主要的技术选择、设计系统的主体框架结构，并负责搭建实施。然而，架构师所需的技能远远不止于技术选择和系统设计。本文主要介绍软件架构的定义，以及要成为一个软件架构师所需具备的一些技能，让你对软件架构师这一职位有一个更深的了解。
 文中大部分的观点来自于《Fundamentals of Software Architecture》一书，想了解更多详情推荐阅读原书。
 软件架构的定义 对于软件架构（Software Architecture），我们通常将它看成是软件系统的蓝图（blueprint），但是如果要给出一个精确的定义，往往很难。维基百科里对软件架构的定义为，有关软件整体结构与组件的抽象描述，用于指导大型软件系统各个方面的设计。但是，这种定义也是片面的，软件架构并不仅仅是系统的整体结构和组件，光有这些还不足以指导设计出好的软件系统。
Mark Richards和Neal Ford在书中，从四个维度上对软件架构进行了描述，分别是Structure、Architecture characteristics、Architecture decisions和Design principles。
Structure Structure描述的是软件系统所使用的架构风格，比如最常见的分层架构（layered architecture）、事件驱动架构（event-driven architecture）、微核架构（microkernel architecture）、微服务架构（microservices architecture）等等。当你去问架构师一个软件系统使用什么架构时，如果他告诉你，“系统使用的是微服务架构”，那么也他仅仅阐明了系统的架构风格而已。若想了解整个系统的软件架构，对architecture characteristics、architecture decisions和design principles都要有深入的认识。
Architecture characteristics Architecture characteristics也就是我们常说的非功能需求，比如有可用性（Availability）、可扩展性（Scalability）、可靠性（Reliability）等。Architecture characteristics往往容易被软件新手所忽略，但是它对于软件系统而言却是非常的重要。如果说功能需求决定了一个软件系统的下限，那么非功能需求则决定了它的上限。
Architecture decisions Architecture decisions描述了开发软件系统时所必须遵循的规则，比如图中例子，对于一个分层架构风格的系统而言，开发工程师需要遵循以下规则：只有业务层才能直接访问服务层，表现层不能直接访问服务层。Architecture decisions更多的只是一种约束，违反了这种约束可能并不会对系统的功能造成影响，但是却是系统架构腐化的源头。
Design principles Design principles指的是系统设计的原则，用于引导开发团队选择更符合系统特点的技术方案。Design principles只会给出一个方向，而不是具体的实现方案。比如图中例子给出的系统设计原则就是：微服务之间应该尽可能通过异步通信来提升系统的性能。至于开发团队通过REST或者RPC的方式进行异步通信实现，设计原则并未进行限制。
成为架构师所需的技能 就像软件架构不仅仅是系统的整体结构和组件一样，要成为一个软件架构师，只会技术选型是远远不够的。一个合格的软件架构师应该具备以下的几种技能：
进行架构决策  An architect is expected to define the architecture decisions and design principles used to guide technology decisions within the team, the department, or across the enterprise.</description>
    </item>
    
    <item>
      <title>实践领域驱动设计（一）</title>
      <link>https://yrunz.com/p/%E5%AE%9E%E8%B7%B5%E9%A2%86%E5%9F%9F%E9%A9%B1%E5%8A%A8%E8%AE%BE%E8%AE%A1%E4%B8%80/</link>
      <pubDate>Sun, 08 Mar 2020 00:00:00 +0000</pubDate>
      
      <guid>https://yrunz.com/p/%E5%AE%9E%E8%B7%B5%E9%A2%86%E5%9F%9F%E9%A9%B1%E5%8A%A8%E8%AE%BE%E8%AE%A1%E4%B8%80/</guid>
      <description>最近几年，领域驱动设计（Domain-Driven Design，DDD）这个术语越来越多地出现在软件工程师的视野里。对DDD不熟悉的人可能会觉得它是软件领域里的一个新的概念，但是实际上，Eric Evans在十几年前就已经提出了这个概念。这个“古老”的概念在之所以能够重焕新生，很大程度上是因为遇上了“微服务”这个浪潮。如果把DDD里面的理念拿去和微服务架构做对比，你会发现它们有着高度的相似性——DDD里的限界上下文不正是微服务架构中的微服务吗？于是，各大公司纷纷运用DDD的方法论来构建自己的产品架构。有些团队成功地将DDD结合到了产品架构中，产生了许多优秀的实践；也有些团队反映DDD太过复杂，很难落地。那么DDD究竟是个什么样的理念？为什么大家都争先恐后地使用它，彷佛不加点DDD都不好意思说自己是微服务架构？然而又为什么那么多团队说DDD难以落地？本文将会结合简单的代码实现来谈谈笔者对DDD的理解。
什么是领域驱动设计？ 软件的核心是其为用户解决领域相关的问题的能力。
软件就是为了解决某一领域相关问题而存在的，比如一个普通的计算器软件，就是为了满足我们进行简单的加减乘除运算而存在。对于计算器这种小而简单的软件，一个普通的软件工程师可能花上几天就能过设计开发出来，而且基本不会出现Bug。但是对于一些大型而且复杂的系统，一个团队都得花上很长的时间去设计整个架构，然后经过n轮迭代才能开发出可用的版本，而且后面还有各种Bug要去处理。比如证券交易系统，里面就包括了用户系统、账户系统、订单系统、撮合系统等一系列的子系统，而且其中的调用关系和业务都非常复杂。像这样一个庞大的系统，怎样才能把它设计好呢？这正是DDD要回答的问题。
领域驱动设计（DDD）是一种软件开发的方法论，旨在帮助我们设计出高质量的软件模型。
在软件领域，解决复杂问题的方法不外乎是“分治”和“抽象”，DDD也是基于这两个理念建立起一套方法论。其中将一个系统划分成多个限界上下文，限界上下文中划分出多个子域，这是分治；然后在分别对各个子域进行领域建模，这是抽象。当你在设计一个业务复杂的系统却无从下手时，尝试一下DDD，说不定困难就会迎刃而解了。DDD中最核心的理念就是领域建模，可以说它提供的各种方法都是为了帮助我们设计出更能准确传达业务规则的领域模型。一个好的领域模型可以让一个系统更加健壮，可以让一个框架易用性更加好，可以让一段代码更加好维护。那么，什么样的模型才是好的领域模型？下面，我们通过一个例子来简单说明下。
 什么是领域模型？
领域模型是关于某个特定业务领域的软件模型。通常，领域模型通过对象模型来实现，这些对象同时包含了数据和行为，并且表达了准确的业务含义。
 日期和时间领域模型 如何设计一个日期和时间API？
首先需要对日期和时间的概念进行建模，从直觉上，我们可以将日期和时间抽象成一个对象Date。另外，时间和日期经常都需要进行格式化输出，因此我们还需要一个用于表示时间格式的对象DateFormat。为了更好地表示年月周日等概念，再抽象出一个表示日历的Calendar对象，以及表示时区的TimeZone对象。
相信到这里大家都已经知道，这正是JDK 1.1版本的日期时间API，下面我们先回顾一下它的用法：
public class TestOldDate { public static void main(String[] args) { // 获取表示当前时刻的Date对象  Date date1 = new Date(); // 通过Calendar等到指定日期时间的Date对象，采用当前的系统时区  Calendar calendar = Calendar.getInstance(TimeZone.getDefault()); calendar.set(2020, 2, 10, 0, 0, 0); Date date2 = calendar.getTime(); // 进行时间比较  System.out.println(&amp;#34;date1 is after date2: &amp;#34; + date1.after(date2)); // 进行时间的加减法，如获得昨天的这个时刻：  calendar.setTime(date1); calendar.set(Calendar.DAY_OF_MONTH, calendar.get(Calendar.DAY_OF_MONTH) - 1); Date date3 = calendar.</description>
    </item>
    
  </channel>
</rss>
