喻轩Blog - 架构
type: channel
link: https://blog.yuxuan.io/tags/%E6%9E%B6%E6%9E%84
items: Kafka架构与实战
items: RabbitMQ架构与实战
items: FastDFS系统架构和功能原理
items: MongoDB架构
items: 微服务架构

Kafka架构与实战
type: item
date: 2020-10-21T15:09:28+08:00
description: 概念和基本架构Kafka介绍Kafka是最初由Linkedin公司开发，是一个分布式、分区的、多副本的、多生产者、多订阅者，基于zookeeper协调的分布式日志系统（也可以当做MQ系统），常见可以用于web/nginx日志、访问日志，消息服务等等，Linkedin于2010年贡献给了Apache基金会并成为顶级开源项目。主要应用场景是：日志收集系统和消息系统。Kafka主要设计目标如下：以时间复杂度为O(1)的方式提供消息持久化能力，即使对TB级以上数据也能保证常数时间的访问性能。高吞吐率。即使在非常廉价的商用机器上也能做到单机支持每秒100K条消息的传输。支持Kafka Server间的消息分区，及分布式消费，同时保证每个partition内的消息顺序传输。同时支持离线数据处理和实时数据处理。支持在线水平扩展有两种主要的消息传递模式：点对点传递模式、发布-订阅模式。大部分的消息系统选用发布-订阅模式。Kafka就是一种发布-订阅模式。对于消息中间件，消息分推拉两种模式。Kafka只有消息的拉取，没有推送，可以通过轮询实现消息的推送。Kafka在一个或多个可以跨越多个数据中心的服务器上作为集群运行。Kafka集群中按照主题分类管理，一个主题可以有多个分区，一个分区可以有多个副本分区。每个记录由一个键，一个值和一个时间戳组成。Kafka具有四个核心API：Producer API：允许应用程序将记录流发布到一个或多个Kafka主题。Consumer API：允许应用程序订阅一个或多个主题并处理为其生成的记录流。Streams API：允许应用程序充当流处理器，使用一个或多个主题的输入流，并生成一个或多个输出主题的输出流，从而有效地将输入流转换为输出流。Connector API：允许构建和运行将Kafka主题连接到现有应用程序或数据系统的可重用生产者或使用者。例如，关系数据库的连接器可能会捕获对表的所有更改。Kafka优势高吞吐量：单机每秒处理几十上百万的消息量。即使存储了许多TB的消息，它也保持稳定的性能。高性能：单节点支持上千个客户端，并保证零停机和零数据丢失。持久化数据存储：将消息持久化到磁盘。通过将数据持久化到硬盘以及replication防止数据丢失。零拷贝顺序读，顺序写利用Linux的页缓存分布式系统，易于向外扩展。所有的Producer、Broker和Consumer都会有多个，均为分布式的。无需停机即可扩展机器。多个Producer、Consumer可能是不同的应用。可靠性 - Kafka是分布式，分区，复制和容错的。客户端状态维护：消息被处理的状态是在Consumer端维护，而不是由server端维护。当失败时能自动平衡。支持online和offline的场景。支持多种客户端语言。Kafka支持Java、.NET、PHP、Python等多种语言。Kafka应用场景日志收集：一个公司可以用Kafka可以收集各种服务的Log，通过Kafka以统一接口服务的方式开放给各种Consumer；消息系统：解耦生产者和消费者、缓存消息等；用户活动跟踪：Kafka经常被用来记录Web用户或者App用户的各种活动，如浏览网页、搜索、点击等活动，这些活动信息被各个服务器发布到Kafka的Topic中，然后消费者通过订阅这些Topic来做实时的监控分析，亦可保存到数据库；运营指标：Kafka也经常用来记录运营监控数据。包括收集各种分布式应用的数据，生产各种操作的集中反馈，比如报警和报告；流式处理：比如Spark Streaming和Storm。基本架构消息和批次Kafka的数据单元称为消息。可以把消息看成是数据库里的一个“数据行”或一条“记录”。消息由字节数组组成。消息有键，键也是一个字节数组。当消息以一种可控的方式写入不同的分区时，会用到键。为了提高效率，消息被分批写入Kafka。批次就是一组消息，这些消息属于同一个主题和分区。把消息分成批次可以减少网络开销。批次越大，单位时间内处理的消息就越多，单个消息的传输时间就越长。批次数据会被压缩，这样可以提升数据的传输和存储能力，但是需要更多的计算处理。模式消息模式（schema）有许多可用的选项，以便于理解。如JSON和XML，但是它们缺乏强类型处理能力。Kafka的许多开发者喜欢使用Apache Avro。Avro提供了一种紧凑的序列化格式，模式和消息体分开。当模式发生变化时，不需要重新生成代码，它还支持强类型和模式进化，其版本既向前兼容，也向后兼容。数据格式的一致性对Kafka很重要，因为它消除了消息读写操作之间的耦合性。主题和分区Kafka的消息通过主题进行分类。主题可比是数据库的表或者文件系统里的文件夹。主题可以被分为若干分区，一个主题通过分区分布于Kafka集群中，提供了横…

RabbitMQ架构与实战
type: item
date: 2020-10-14T16:41:54+08:00
description: RabbitMQ介绍、概念、基本架构RabbitMQ介绍RabbitMQ，俗称“兔子MQ”（可见其轻巧，敏捷），是目前非常热门的一款开源消息中间件，不管是互联网行业还是传统行业都广泛使用（最早是为了解决电信行业系统之间的可靠通信而设计）。高可靠性、易扩展、高可用、功能丰富等支持大多数（甚至冷门）的编程语言客户端。RabbitMQ遵循AMQP协议，自身采用Erlang（一种由爱立信开发的通用面向并发编程的语言）编写。RabbitMQ也支持MQTT等其他协议。RabbitMQ具有很强大的插件扩展能力，官方和社区提供了非常丰富的插件可供选择：https://www.rabbitmq.com/community-plugins.htmlRabbitMQ整体逻辑架构RabbitMQ Exchange类型RabbitMQ常用的交换器类型有： fanout 、direct 、topic 、headers 四种。Fanout会把所有发送到该交换器的消息路由到所有与该交换器绑定的队列中，如图：Directdirect类型的交换器路由规则很简单，它会把消息路由到那些BindingKey和RoutingKey完全匹配的队列中，如下图：、Topictopic类型的交换器在direct匹配规则上进行了扩展，也是将消息路由到BindingKey和RoutingKey相匹配的队列中，这里的匹配规则稍微不同，它约定：BindingKey和RoutingKey一样都是由"."分隔的字符串；BindingKey中可以存在两种特殊字符“”和“#”，用于模糊匹配，其中""用于匹配一个单词，"#"用于匹配多个单词（可以是0个）。Headersheaders类型的交换器不依赖于路由键的匹配规则来路由信息，而是根据发送的消息内容中的headers属性进行匹配。在绑定队列和交换器时指定一组键值对，当发送的消息到交换器时，RabbitMQ会获取到该消息的headers，对比其中的键值对是否完全匹配队列和交换器绑定时指定的键值对，如果匹配，消息就会路由到该队列。headers类型的交换器性能很差，不实用。RabbitMQ数据存储存储机制RabbitMQ消息有两种类型：持久化消息和非持久化消息。这两种消息都会被写入磁盘。持久化消息在到达队列时写入磁盘，同时会内存中保存一份备份，当内存吃紧时，消息从内存中清除。这会提高一定的性能。非持久化消息一般只存于内存中，当内存压力大时数据刷盘处理，以节省内存空间。RabbitMQ存储层包含两个部分：队列索引和消息存储。队列索引：rabbit_queue_index索引维护队列的落盘消息的信息，如存储地点、是否已被给消费者接收、是否已被消费者ack等。每个队列都有相对应的索引。索引使用顺序的段文件来存储，后缀为.idx，文件名从0开始累加，每个段文件中包含固定的segment_entry_count 条记录，默认值是16384。每个index从磁盘中读取消息的时候，至少要在内存中维护一个段文件，所以设置queue_index_embed_msgs_below 值得时候要格外谨慎，一点点增大也可能会引起内存爆炸式增长。消息存储：rabbit_msg_store消息以键值对的形式存储到文件中，一个虚拟主机上的所有队列使用同一块存储，每个节点只有一个。存储分为持久化存储（msg_store_persistent）和短暂存储（msg_store_transient）。持久化存储的内容在broker重启后不会丢失，短暂存储的内容在broker重启后丢失。store使用文件来存储，后缀为.rdq，经过store处理的所有消息都会以追加的方式写入到该文件中，当该文件的大小超过指定的限制（file_size_limit）后，将会关闭该文件并创建一个新的文件以供新的消息写入。文件名从0开始进行累加。在进行消息的存储时，RabbitMQ会在ETS（Erlang Term Storage）表中记录消息在文件中的位置映射和文件的相关信息。channel.queueDeclare("q.demo1", true, false, false, null); channel.exchangeDeclare("ex.demo1", BuiltinExchangeType.DIRECT, true, false, null); channel.queueBind("q.demo1", "ex.demo1", "key.demo1", null); Map<String, Object> headers = new HashMap<>(); headers.put("mykey", "myvalue"); AMQP.BasicProperties props = new AMQP…

FastDFS系统架构和功能原理
type: item
date: 2020-09-09T21:48:41+08:00
description: 架构详解storage server：存储服务器（又称存储节点或数据服务器），文件和文件属性（meta data）都保存到存储服务器上。Storage server直接利用OS的文件系统调用管理文件。Storage server（后简称storage）以组（卷，group或volume）为单位组织，一个group内包含多台storage机器，数据互为备份，存储空间以group内容量最小的storage为准，所以建议group内的多个storage尽量配置相同，以免造成存储空间的浪费。以group为单位组织存储能方便的进行应用隔离、负载均衡、副本数定制（group内storage server数量即为该group的副本数），比如将不同应用数据存到不同的group就能隔离应用数据，同时还可根据应用的访问特性来将应用分配到不同的group来做负载均衡；缺点是group的容量受单机存储容量的限制，同时当group内有机器坏掉时，数据恢复只能依赖group内的其它机器，使得恢复时间会很长。group内每个storage的存储依赖于本地文件系统，storage可配置多个数据存储目录，比如有10块磁盘，分别挂载在/data/disk1-/data/disk10 ，则可将这10个目录都配置为storage的数据存储目录。storage接受到写文件请求时，会根据配置好的规则，选择其中一个存储目录来存储文件。为了避免单个目录下的文件数太多，在storage第一次启动时，会在每个数据存储目录里创建2级子目录，默认每级256个，总共65536个文件，新写的文件会以hash的方式被路由到其中某个子目录下，然后将文件数据直接作为一个本地文件存储到该目录中。group：组，也可称为卷。同组内服务器上的文件是完全相同的，同一组内的storage server之间是对等的，文件上传、删除等操作可以在任意一台storage server上进行。meta data：文件相关属性，键值对（ Key Value Pair）方式，如：width: 1118。tracker server：跟踪服务器，主要做调度工作，起负载均衡的作用。在内存中记录集群中所有存储组和存储服务器的状态信息，是客户端和数据服务器交互的枢纽。因为不记录文件索引信息，所以占用的内存量很少。Tracker是FastDFS的协调者，负责管理所有的storage server和group，每个storage在启动后会连接Tracker，告知自己所属的group等信息，并保持周期性的心跳，tracker根据storage的心跳信息，建立group==>[storage server list]的映射表。Tracker需要管理的元信息很少，会全部存储在内存中；另外tracker上的元信息都是由storage汇报的信息生成的，本身不需要持久化任何数据，这样使得tracker非常容易扩展，直接增加tracker机器即可扩展为tracker cluster来服务，cluster里每个tracker之间是完全对等的，所有的tracker都接受stroage的心跳信息，生成元数据信息来提供读写服务。client：客户端，作为业务请求的发起方，通过专有接口，使用TCP/IP协议与跟踪器服务器或存储节点进行数据交互。FastDFS向使用者提供基本文件访问接口，比如upload、download、append、delete等，以客户端库的方式提供给用户使用。设计理念轻量级FastDFS服务端只有两个角色：Tracker server和Storage server。Tracker server在内存中记录分组和Storage server的状态等信息，不记录文件索引信息，占用的内存量很少。另外，客户端（应用）和Storage server访问Tracker server时，Tracker server扫描内存中的分组和Storage server状态信息，然后给出应答。由此可以看出Tracker server非常轻量化，不会成为系统瓶颈。FastDFS中的Storage server直接利用OS的文件系统存储文件。FastDFS不会对文件进行分块存储，客户端上传的文件和Storage server上的文件一一对应。对于互联网应用，文件分块存储没有多大的必要。它既没有带来多大的好处，又增加了系统的复杂性。FastDFS不对文件进行分块存储，与支持文件分块存储的DFS相比，更加简洁高效，并且完全能满足绝大多数互联网应用的实际需要。在FastDFS中，客户端上传文件时，文件ID不是由客户端指定，而是由Storage server生成后返回给客户端的。文件ID中包含了组名、文件相对路径和文件名，Storage server可以根据文件…

MongoDB架构
type: item
date: 2020-08-30T20:38:14+08:00
description: MongoDB逻辑结构MongoDB 与 MySQL 中的架构相差不多，底层都使用了可插拔的存储引擎以满足用户的不同需要。用户可以根据程序的数据特征选择不同的存储引擎,在最新版本的 MongoDB 中使用了 WiredTiger 作为默认的存储引擎，WiredTiger 提供了不同粒度的并发控制和压缩机制，能够为不同种类的应用提供了最好的性能和存储率。在存储引擎上层的就是 MongoDB 的数据模型和查询语言了，由于 MongoDB 对数据的存储与 RDBMS 有较大的差异，所以它创建了一套不同的数据模型和查询语言。MongoDB的数据模型描述数据模型内嵌内嵌的方式指的是把相关联的数据保存在同一个文档结构之中。MongoDB的文档结构允许一个字段或者一个数组内的值作为一个嵌套的文档。引用引用方式通过存储数据引用信息来实现两个不同文档之间的关联,应用程序可以通过解析这些数据引用来访问相关数据。如何选择数据模型选择内嵌:1）数据对象之间有包含关系 ,一般是数据对象之间有一对多或者一对一的关系 。2）需要经常一起读取的数据。3）有 map-reduce/aggregation 需求的数据放在一起，这些操作都只能操作单个 collection。选择引用:1）当内嵌数据会导致很多数据的重复，并且读性能的优势又不足于覆盖数据重复的弊端 。2）需要表达比较复杂的多对多关系的时候 。3）大型层次结果数据集 嵌套不要太深。MongoDB存储引擎存储引擎概述存储引擎是MongoDB的核心组件，负责管理数据如何存储在硬盘和内存上。MongoDB支持的存储引擎有MMAPv1 ,WiredTiger和InMemory。InMemory存储引擎用于将数据只存储在内存中，只将少量的元数据(meta-data)和诊断日志（Diagnostic）存储到硬盘文件中，由于不需要Disk的IO操作，就能获取所需的数据，InMemory存储引擎大幅度降低了数据查询的延迟（Latency）。从mongodb3.2开始默认的存储引擎是WiredTiger,3.2版本之前的默认存储引擎是MMAPv1，mongodb4.x版本不再支持MMAPv1存储引擎。storage: journal: enabled: true dbPath: /data/mongo/ ##是否一个库一个文件夹 directoryPerDB: true ##数据引擎 engine: wiredTiger ##WT引擎配置 WiredTiger: engineConfig: ##WT最大使用cache（根据服务器实际情况调节） cacheSizeGB: 2 ##是否将索引也按数据库名单独存储 directoryForIndexes: true journalCompressor:none （默认snappy） ##表压缩配置 collectionConfig: blockCompressor: zlib (默认snappy,还可选none、zlib) ##索引配置 indexConfig: prefixCompression: trueWiredTiger存储引擎优势文档空间分配方式WiredTiger使用的是BTree存储，MMAPV1是线性存储，需要Padding并发级别WiredTiger文档级别锁，MMAPV1引擎使用表级锁数据压缩snappy (默认) 和 zlib，相比MMAPV1(无压缩) 空间节省数倍。内存使用WiredTiger 可以指定内存的使用大小。Cache使用WT引擎使用了二阶缓存WiredTiger Cache，File System Cache来保证Disk上的数据的最终一致性。而 MMAPv1 只有 journal 日志。WiredTiger引擎包含的文件和作用WiredTiger.basecfg: 存储基本配置信息，与 ConfigServer有关系WiredTiger.lock: 定义锁操作table*.wt: 存储各张表的数据WiredTiger.wt: 存储table* 的元数据WiredTiger.turtle: 存储WiredTiger.wt的元数据journal: 存储WAL(Write Ahead Log)WiredTiger存储引擎实现原理写请求WiredTiger的写操作会默认写入Cache ,并持久化到WAL (Write Ahead Log)，每60s或Log文件达到2G做一次checkpoint (当然我们也可以通过在写入时传入 j: true 的参数强制 journal 文件的同步，writeConcern{ w: , j: , wtimeout: }) 产生快照文件。WiredTiger初始化时，恢复至最新的快照状态，然后再根据WAL恢复数据，…

微服务架构
type: item
date: 2020-07-16T17:47:33+08:00
description: 互联网应用架构发展随着用户群体逐渐扩大，网站的流量成倍增长，常规的单体架构已无法满足请求压力和业务的快速迭代，下面我们以一个招聘系统为例，从最开始的单体架构分析，一步步演变到现在的微服务架构。单体应用架构最初，用户量、数据量规模都比较小，项目所有的功能模块都放在一个工程中编码、编译、打包并且部署在一个Tomcat容器中，这种架构模式就是单体应用架构，这样的架构既简单实用、便于维护，成本又低，成为了当时的主流架构方式。优点：项目前期开发节奏快，团队成员少的时候能够快速迭代。架构简单：MVC架构，只需要借助IDE开发、调试即可。易于测试：只需要通过单元测试或者浏览器完成。易于部署：打包成单一可执行的jar或者打成war包放到容器内启动。缺点：随着不断的功能迭代，单个项目过大，代码杂乱，耦合严重，开发团队逐渐壮大以后，沟通成本变高，如：代码从编译到启动耗时达到3-5分钟。新增业务困难：在冗杂的系统中增加新业务，维护旧功能，全是不可预测的问题。新人来了以后很难接手任务，学习成本高，需要大概一周时间才能上手开发。核心业务与边缘业务混合在一块，出现问题互相影响，如：一个临时活动流量猛涨，机器负载升高就会影响正常的业务服务。业务量上涨之后，单体应用架构进一步丰富变化，比如应用集群部署、使用Nginx进行负载均衡、增加缓存服务器、增加文件服务器、数据库集群并做读写分离等，通过以上措施增强应对高并发的能力、应对一定的复杂业务场景，但依然属于单体应用架构。垂直应用架构为了避免上面提到的那些问题，开始做模块的垂直划分，做垂直划分的原则是基于现有的业务特性来做，第一个核心目标是为了业务之间互不影响，第二个核心目标是在研发团队壮大后提高效率，减少之间的依赖。优点：系统拆分实现了流量分担，解决了并发问题。可以针对不同模块进行优化。方便水平扩展，负载均衡，容错率提高。系统间相互独立，互不影响，新的业务迭代时更加高效。缺点：服务之间相互调用，如果某个服务的端口或者ip地址发生改变，调用的系统得手动修改配置文件。搭建集群之后，实现负载均衡比较复杂，如：内网负载，在迁移机器时会影响调用方的路由，导致线上故障。服务之间调用方式不统一，基于httpclient、webservice，接口协议不统一。服务监控不到位：除了依靠端口、进程的监控，调用的成功率、失败率、总耗时等这些监控指标是没有的。SOA应用架构在做了垂直划分以后，模块随之增多，维护的成本在也变高，一些通用的业务和模块重复的越来越多，为了解决上面提到的接口协议不统一、服务无法监控、服务的负载均衡，引入了阿里巴巴开源的Dubbo ，一款高性能、轻量级的开源Java RPC框架，它提供了三大核心能力：面向接口的远程方法调用，智能容错和负载均衡，以及服务自动注册和发现。SOA (Service-Oriented Architecture)，即面向服务的架构。根据实际业务，把系统拆分成合适的、独立部署的模块，模块之间相互独立（通过Webservice/Dubbo等技术进行通信）。优点： 分布式、松耦合、扩展灵活、可重用。缺点： 服务抽取粒度较大、服务调用方和提供方耦合度较高（接口耦合度）。微服务应用架构微服务架构可以说是SOA架构的一种拓展，这种架构模式下它拆分粒度更小、服务更独立。把应用拆分成为一个个微小的服务，不同的服务可以使用不同的开发语言和存储，服务之间往往通过Restful等轻量级通信。微服务架构关键在于微小、独立、轻量级通信。微服务是在SOA上做的升华，粒度更加细致，微服务架构强调的一个重点是“业务需要彻底的组件化和服务化”。微服务架构和SOA架构相似又不同：微服务架构和SOA架构很明显的一个区别就是服务拆分粒度的不同，但是对于一些架构发展来说，我们所看到的SOA阶段服务拆分粒度相对来说已经比较细了，所以上述SOA到微服务，从服务拆分上来说变化并不大，只是引入了相对完整的新一代的Spring Cloud微服务技术。所以，上述我们看到的都是架构演变的阶段结果，每一个阶段其实都经历了很多变化，服务拆分其实也是经过了从粗到细，并非绝对的一步到位。例：我们在SOA架构的初期，“简历投递模块”和“人才搜索模块”都有简历内容展示的需求，只不过说可能略有区别，一开始在两个模块中各维护了一套简历查询和展示的代码；后期我们将服务更细粒度拆分，拆分出简历基础服务，那么不同模块调用这个基础服务即可。微服务架构体现的思想及优缺点微服务架构设计的核心思想就是“微”，拆分的粒度相对比较小，这样的话每个服务具有单一职责，开发的耦合度就会降低，微小的功能可以独立部署扩展、灵活性强，升级改造影响范围小（比如升级JDK）。微服务架构的优点:微服务很小，便于特定业务功能的聚焦.微服务很小，每个微服务都可以被一个小团队单独实施（开发、测试、部…