0%

本文内容主要是,以官方文档资料和 Java 8 的 JDK 和 HotSpot 源码为依据,对 Java 的基于 synchronizedReentrantLock 线程同步的底层实现原理进行总结梳理和深度解析。同时,也在广度上对线程同步相关的基础概念做总结概括。

基础概念

在并发编程模型中,需要处理的两个最关键的问题就是通信(communication)和同步(synchronization)[1]。通信指线程可用于获得其他线程产生的信息的各种机制。通信机制通常都基于共享内存shared memory)或消息传递message passing)。在共享内存的编程模型中,某些或全部程序变量可以由多个线程访问。如果一对线程之间需要通信,只要一个线程将值写入某个变量,另一线程来读它即可。在消息传递编程模型中,不同线程没有公共的状态。当一对线程之间需要通信时,其中的一个必须执行一次明确的 send 操作,将数据传送给另一个线程。

同步[1:1]是控制不同线程之间操作发生的相对顺序的各种机制,用以排除导致不正确结果的交错。消息传递模型中的同步通常是隐式的,消息的发送必须在接收之前。如果某个线程企图接收一个尚未发送的消息,那么它就必须等到发送方赶上来。在共享内存编程模型中,同步通常不是隐式的,除非我们做了某些特殊的事情,否则“接收方”就可能在某个变量被“发送方”修改之前读到其中的“老”值。

阅读全文 »

为了提升系统的可扩展性(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 Multiplexing),比如 Apache HTTP Server、Nginx、Redis 等。本文尝试解释各种 I/O 模型,包括解释什么是 I/O 多路复用,同时也总结 I/O 多路复用底层的系统调用 select、poll、kqueue 和 epoll 的演进和区别,并编写了使用这些函数的示例代码。另外,本文还总结了各种基于 I/O 多路复用实现的网络服务器的并发策略的三种模式,包括对 Apache HTTP Server、Nginx 和 Redis 等网络服务器的并发策略的具体案例的解析。

阅读全文 »

目前主流数据库事务的并发控制实现,如 MySQL InnoDB、PostgreSQL、Oracle,都使用两阶段封锁 2PLMVCC 技术,但具体实现细节上存在差异。InnoDB 是在以封锁技术为主体的情况下,用 MVCC 技术辅助实现读-写、写-读操作的并发。PostgreSQL 的并发控制技术是以 MVCC 技术为主,封锁技术为辅。本文主要关注 InnoDB 事务的并发控制实现。

阅读全文 »

Kong 简介

Kong 是云原生、高效、可扩展、分布式的微服务抽象层,被称为 API 网关,或者 API 中间件。Kong 在 2015 年 4 月由 Mashape 公司开源,基于 OpenResty 和 Apache Cassandra/PostgreSQL 构建,提供易于使用的 RESTful API 来操作和配置 API 系统[1][2]

阅读全文 »

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(旧称 ELK Stack)是最受欢迎的开源日志平台 [ ref ]。Elastic Stack 由 Elasticsearch、Logstash、Kibana 和 Beats 四个组件组成:

  • Beats,是轻量型采集器的平台,从边缘机器向 Logstash 和 Elasticsearch 发送数据。
  • Logstash,集中、转换和存储数据,是动态数据收集管道,拥有可扩展的插件生态系统,能够与 Elasticsearch 产生强大的协同作用。
  • Elasticsearch,搜索、分析和存储您的数据,是基于 JSON 的分布式搜索和分析引擎,专为实现水平扩展、高可靠性和管理便捷性而设计。
  • Kibana,实现数据可视化,导览 Elastic Stack。能够以图表的形式呈现数据,并且具有可扩展的用户界面,供您全方位配置和管理 Elastic Stack。
阅读全文 »

Java 从 1.5 开始提供了 java.lang.instrumentdoc)包,该包为检测(instrument) Java 程序提供 API,比如用于监控、收集性能信息、诊断问题。通过 java.lang.instrument 实现工具被称为 Java Agent。Java Agent 可以修改类文件的字节码,通常是,在字节码方法插入额外的字节码来完成检测。关于如何使用 java.lang.instrument 包,可以参考 javadoc 的包描述(en, zh)。

阅读全文 »

MySQL 的 binlog 日志文件,记录了数据库表的全部修改操作。本文简单整理 MySQL binlog 相关知识,以及如何使用 binlog 恢复或闪回数据库数据。

STATEMENT 格式的 binlog

要想开启 binlog,需要在启动 MySQL 时传入 --log-bin 参数。或者也可以在 MySQL 配置文件 /etc/my.cnf,设置 log_bin 开启 binlog。MySQL 5.7 开始,开启 binlog 后,--server-id 参数也必须指定,否则 MySQL 服务器会启动失败。

阅读全文 »

栈与栈帧

要想知道函数是怎么被调用的,需要了解栈帧和调用惯例相关知识。俞甲子2009 的“第10章 内存: 栈与堆”对相关概念有很好的介绍。本文是对相关知识的学习笔记。

阅读全文 »

遇到的问题

前段时间开发的时候,遇到一个问题,就是如何用 Java 实现 chdir?网上搜索一番,发现了 JNR-POSIX 项目 [ stackoverflow ]。俗话说,好记性不如烂笔头。现在将涉及到的相关知识点总结成笔记。

阅读全文 »

ZooKeeper 介绍

ZooKeeper(wikihomegithub) 是用于分布式应用的开源的分布式协调服务。通过暴露简单的原语,分布式应用能在之上构建更高层的服务,如同步、配置管理和组成员管理等。在设计上易于编程开发,并且数据模型使用了熟知的文件系统目录树结构 [ doc ]。

阅读全文 »

本文整理 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” 会看到如下介绍:

阅读全文 »

javac 是 Java 代码的编译器[1][2],初学 Java 的时候就应该接触过。本文整理一些 javac 相关的高级用法。Lombok 库,大家平常一直在使用,但可能并不知道实现原理解析,其实 Lombok 实现上依赖的是 Java 编译器的注解处理 API(JSR-296[3],本文同时尝试解析 Lombok 的实现原理。

阅读全文 »

本文整理 Java 并发框架 Executor 的用法,并对结合 JDK 相关的实现源码作简单分析。

任务与线程池

先来看下 Executor 框架的 javadoc 描述 [ ref1 ref2 ]

阅读全文 »