为了提升系统的可扩展性(scalability),分布式数据库或分布式存储系统通常支持数据分区(partitioning)或分片(sharding),即将完整的数据拆分存放在多个服务器节点上,拆分后的部分数据称为“partition”或“shard”。数据被拆分后多个服务器节点能分摊负载压力,从而提升系统性能。“分区”和分片”,这两个术语,在很多情况下不区分,可以混用。如果严格区分的话,分片拆分的数据分布在多个服务器节点上,而分区拆分的数据在单个服务器节点。另外,复制(replication)也典型的分布式技术,多个数据副本能实现读请求的负载均衡,提升系统性能。同时复制也提供了冗余容错的能力,提升系统的可用性(availability)。本文关注消息中间件的消息存储系统,解析并对比 RocketMQ 和 Kafka 的消息数据的分片和复制的具体实现策略。
大型网站的稳定性、可靠性和韧性
为了应对负载的增长,提升系统性能,目前大型网站普遍都是分布式架构,采用微服务架构风格。分布式系统的最重要的架构特性是伸缩性(scalability),伸缩性的系统具备应对增长的工作负载的能力。关于性能和伸缩性,可以参阅笔者的文章《大型网站的性能和可伸缩性》。相对于采用单体架构的系统,分布式系统中有大量的服务器及设备,各服务之间存在错综复杂的依赖关系,存在更多的不确定性。整个系统的故障率会随服务节点的增加而呈指数级增加,单一节点问题可能会被无限放大,日常运行过程中一定会伴随故障发生。所以构建分布式系统需要关注的另外一个重要架构特性是稳定性(stability)。有关减少系统故障以及快速从故障中恢复的工程实践,国内通常称为“稳定性建设”,而国外类似的工程实践更多称为“站点可靠性工程”(SRE, Site reliability engineering)。稳定性(stability)、可靠性(reliability)、韧性(resilience)、可用性(availability)等架构特性,相似并且相关,虽然严格区分的话,含义并不相同,但是很多时候在探讨这些架构特性时往往涵盖的是类似的内容。本文的内容主要是总结稳定性、可靠性、韧性这些架构特性的内涵,以及如何建设分布式系统的这些特性。
大型网站的性能和可伸缩性
互联网网站在初期用户量和访问量一般都很小,往往只需要采用最简单的技术架构就能对外提供稳定服务。最简单的架构通常采用的是单机应用服务器、单机数据库服务器这样的单体架构。成功的互联网网站,比如电商平台,流量、用户量、交易量等核心指标是呈指数增长的,所以就需要提升网站系统的性能,来应对更大的负载。通过向系统中增加资源来提升系统性能的能力,被称为可伸缩性(scalability)。为了提升系统的可伸缩性,典型的大型网站,比如 eBay、Amazon 和淘宝等,几乎都经历过从单体架构向分布式架构演进的过程。本文主要关注大型网站或 Web 服务这类系统,解释系统的性能和可伸缩性相关的核心概念,并介绍系统的性能指标、系统的扩展策略和分布式架构风格,同时也总结分析典型大型网站的可扩展性架构演进案例,案例包括 eBay、Amazon、淘宝等。
流行互联网网站技术栈整理(万字长文)
本文整理总结主要的流行互联网网站技术栈,以及这些网站的技术栈和架构的历史演进过程。涉及的网站大部分都是当前或曾经访问量或月活用户量 Top 的网站(参见 Similarweb 网站的统计[1],或访问量 Top 10 网站的历史演变[2],或 wiki 整理的至少 1 亿月活用户量的社交平台[3])。国内网站或 APP 涵盖了主流[4]的阿里、腾讯、百度、美团、字节、京东等大厂的互联网产品。整理的技术栈主要是流行网站的服务端业务系统的技术栈,包括编程语言、数据库、RPC 框架等,同时也简单整理了大数据技术栈,前端和客户端技术栈等不涉及。除了对大部分流行网站的技术栈做系统性梳理外,本文还挑选部分有代表性的网站,对这些网站的技术栈和架构的历史演进做详细解析。
亚马逊网站架构演进
SOA 与微服务
Amazon,1994 年创立,早期网站是单服务、单数据库的单体架构的系统[1][2][3],全部代码由 C++ 编写,编译成单个二进制文件,整个代码仓库被命名为 Obidos。Obidos 是底层是一个 Web 页面渲染引擎,是一个框架,业务逻辑基于这个框架开发,Obidos 渲染引擎和业务逻辑共同组成整个代码仓库。随着时间的推移,Obidos 变得越来越复杂,编译 Obidos 整个代码库耗时 12 小时,开发调试效率低下[2:1]。另外,全部业务逻辑在单个二进制文件中,导致紧耦合,新功能特性无法快速发布上线。1995 年,Amazon 网站的技术架构,如下图所示[3:1]:
可靠性工程概述
为了应对负载的增长,目前大型网站普遍都采用分布式架构。相对于采用单体架构的系统,分布式系统中有大量的服务器及设备,各模块之间存在错综复杂的依赖关系,存在更多的不确定性。整个系统的故障率会随设备的增加而呈指数级增加,单一节点问题可能会被无限放大,日常运行过程中一定会伴随故障发生。所以,可靠性开始成为大型网站关注的最重要的质量属性之一,并因此发展出了站点可靠性工程(Site reliability engineering,SRE)。站点可靠性工程,是从可靠性工程发展而来的,从可靠性工程中借鉴了概念和成果。本文溯本求源,内容主要是总结概括,可靠性工程的历史演进和核心概念,软件可靠性工程的核心概念,以及可靠性设计的方法。
I/O 多路复用与网络服务器并发策略
目前主流的网络服务器,网络 I/O 相关的底层最核心的技术都是 I/O 多路复用(I/O Multiplexing),比如 Apache HTTP Server、Nginx、Redis 等。本文尝试解释各种 I/O 模型,包括解释什么是 I/O 多路复用,同时也总结 I/O 多路复用底层的系统调用 select、poll、kqueue 和 epoll 的演进和区别,并编写了使用这些函数的示例代码。另外,本文还总结了各种基于 I/O 多路复用实现的网络服务器的并发策略的三种模式,包括对 Apache HTTP Server、Nginx 和 Redis 等网络服务器的并发策略的具体案例的解析。
InnoDB 的并发控制:锁与 MVCC
Java 线程同步实现原理深度解析
本文内容主要是,以官方文档资料和 Java 8 的 JDK 和 HotSpot 源码为依据,对 Java 的基于 synchronized
和 ReentrantLock
线程同步的底层实现原理进行总结梳理和深度解析。同时,也在广度上对线程同步相关的基础概念做总结概括。
基础概念
在并发编程模型中,需要处理的两个最关键的问题就是通信(communication)和同步(synchronization)[1]。通信指线程可用于获得其他线程产生的信息的各种机制。通信机制通常都基于共享内存(shared memory)或消息传递(message passing)。在共享内存的编程模型中,某些或全部程序变量可以由多个线程访问。如果一对线程之间需要通信,只要一个线程将值写入某个变量,另一线程来读它即可。在消息传递编程模型中,不同线程没有公共的状态。当一对线程之间需要通信时,其中的一个必须执行一次明确的 send 操作,将数据传送给另一个线程。
同步[1:1]是控制不同线程之间操作发生的相对顺序的各种机制,用以排除导致不正确结果的交错。消息传递模型中的同步通常是隐式的,消息的发送必须在接收之前。如果某个线程企图接收一个尚未发送的消息,那么它就必须等到发送方赶上来。在共享内存编程模型中,同步通常不是隐式的,除非我们做了某些特殊的事情,否则“接收方”就可能在某个变量被“发送方”修改之前读到其中的“老”值。
微服务 API 网关 Kong 实践
MySQL 5.7 的 JSON 类型
2015 年 8 月,MySQL 5.7.8 开始提供对 JSON 的原生支持[1][2]。MySQL 对 JSON 的支持可以说是千呼万唤始出来。2009 年开始 NoSQL 逐渐流行起来,相继出现了键值对数据库、文档数据库、列族数据库、图数据库等各类 NoSQL,解决经典关系型数据库无法解决的痛点。其中,对灵活存储半结构化数据的需求,使得类似 MongoDB 这类文档数据库涌现出来。各大主流关系型数据库也在响应趋势,开始支持半结构化数据。早在 2012 年,PostgreSQL 9.2 就已经添加了 JSON 数据类型[3]。Oracle 也在 2014 年 7 月发布 12c Release 1 后开始支持 JSON[4][5]。Facebook 在 MySQL 5.7 没发布之前,对 5.6 版本的 MySQL 添加了存储 JSON 功能,这个特性被 Facebook 命名为 DocStore (Document Database for MySQL at Facebook)[6][7]。另外,SQL 标准组织行动也很快,在 2014 年 3 月已经完成了 SQL/JSON 标准草案(SQL/JSON Proposals)[8][9][10]。完整的草案在 2016 年 12 月正式被采纳为标准,即 SQL:2016。
Elastic Stack 日志分析平台搭建笔记
Elastic Stack(旧称 ELK Stack)是最受欢迎的开源日志平台 [ ref ]。Elastic Stack 由 Elasticsearch、Logstash、Kibana 和 Beats 四个组件组成:
- Beats,是轻量型采集器的平台,从边缘机器向 Logstash 和 Elasticsearch 发送数据。
- Logstash,集中、转换和存储数据,是动态数据收集管道,拥有可扩展的插件生态系统,能够与 Elasticsearch 产生强大的协同作用。
- Elasticsearch,搜索、分析和存储您的数据,是基于 JSON 的分布式搜索和分析引擎,专为实现水平扩展、高可靠性和管理便捷性而设计。
- Kibana,实现数据可视化,导览 Elastic Stack。能够以图表的形式呈现数据,并且具有可扩展的用户界面,供您全方位配置和管理 Elastic Stack。
Java Agent 学习笔记
Java 从 1.5 开始提供了 java.lang.instrument
(doc)包,该包为检测(instrument) Java 程序提供 API,比如用于监控、收集性能信息、诊断问题。通过 java.lang.instrument
实现工具被称为 Java Agent。Java Agent 可以修改类文件的字节码,通常是,在字节码方法插入额外的字节码来完成检测。关于如何使用 java.lang.instrument
包,可以参考 javadoc 的包描述(en, zh)。
MySQL binlog:格式、增量恢复、闪回、Java 解析
栈帧与调用惯例
Java 外部函数接口:JNI, JNA, JNR
遇到的问题
前段时间开发的时候,遇到一个问题,就是如何用 Java 实现 chdir
?网上搜索一番,发现了 JNR-POSIX
项目 [ stackoverflow ]。俗话说,好记性不如烂笔头。现在将涉及到的相关知识点总结成笔记。
ZooKeeper 学习笔记
Java 运行时获取方法参数名
本文整理 Java 运行时获取方法参数名的两种方法,Java 8 的最新的方法和 Java 8 之前的方法。
Java 8 的新特性
翻阅 Java 8 的新特性,可以看到有这么一条“JEP 118: Access to Parameter Names at Runtime”。这个特性就是为了能运行时获取参数名新加的。这个 JEP 只是功能增强的提案,并没有最终实现的 JDK 相关的 API 的介绍。查看“Enhancements to the Reflection API” 会看到如下介绍: