googo 邀请码 lbVOQjeS

googo 可以让你更好的浏览外文网站,比如meta、youtube,google等。 它之前的网址是googo.in,现在已经不用访问。是一款非常好用的科学上网工具,使用简单。 使用这个链接可以获取优惠:https://cn.googo.us/#/register?code=lbVOQjeS 或者使用这个邀请码:lbVOQjeS

March 18, 2023

Hobert读书笔记

第一章 并发编程的挑战第2章 InnoDB存储引擎第1章 Java的I/O演进之路第1章 Spring框架的由来第2章 Tomcat总体架构三、Paxos的工程实践序第1章 概述第6章 深入分析ClassLoader工作机制第8章 虚拟机字节码执行系统

June 8, 2021

三、Paxos的工程实践

2.2.3 Paxos算法详解 假设有一组可以提出提案的进程集合,那么对于一个一致性算法来说需要保证以下几点: 在这些被提出的提案中,只有一个会被选定。 如果没有填被提出,那么就不会有被选定的提案。 当一个填被选定后,进程应该可以获取被选定的提案信息。 对于一致性来说,安全性(Safety)需求如下: 只有被提出的提案才能被选定。 只有一个值被选定。 如果某么进程认为某个提案被选定了,那么这个提案必须是真的被选定的那个。 提案的选定 大多数。 推导过程 P1:一个Accptor必须批准它收到的第一个提案 P1a:一个Acceptor只要尚未响应过任何编号大于Mn的Prepare请求,那么它就可以接收这个编号为Mn的提案。(优化:可以忽略已批准过的提案的Prepare请求) P2:如果编号为M0,Value值为V0的提案(即[M0,V0])被选定了,那么所有比编号M0更高的,且被选定的提案,其Value值必须为V0。 P2a:如果编号为M0,Value值为V0的提案(即[M0,V0])被选定了,那么所有比编号M0更高的,且被Acceptor批准的提案,其Value值必须为V0。 P2b:如果一个提案[M0,V0]被选定后,那么之后任何Proposer产生的编号更高的提案,其Value值都为V0。 P2c:对于任意的Mn和Vn,如果提案[Mn,Vn]被提出,那么肯定存在一个由半数以上的Acceptor组成的集合S,满足以下两个条件中的任意一个: S中不存在任何批准过编号小于Mn的提案的Acceptor。 选取S中所有Acceptor批准的编号小于Mn的提案,其中编号最大的那个提案其Value值是Vn。 推导过程为第二数学归纳法。略 Proposer生成提案 对于Proposer,获取被通过的提案比预测可能会被通过的提案简单。 Proposer选择一个新的提案编号Mn,然后向某个Acceptor集合的成员发送请求,要求该集合中的Acceptor做出如下回应。 像Proposer承诺,保证不再批准任何编号小于Mn的提案。 如果Acceptor已经批准过任何提案,那么其就向Proposer反馈当前该Acceptor已经批准的编号小于Mn但为最大编号的那个提案的值。 如果Proposer收到了来自半数以上的Acceptor的响应结果,那么它就可以产生编号为Mn、Value值为Vn的提案,这里的Vn是所有响应中编号最大的提案Value值。当然还存在一种情况,就是半数以上的Acceptor都没有批准过任何提案,即响应中不包含任何的提案,那么此时Vn值就可以由Proposer任意选择。 Acceptor批准提案 一个Acceptor可能会收到来自Proposer的两种请求,分别是Prepare请求和Accept请求,分别相应如下: Prepare请求:Acceptor可以在任何时候响应一个Prepare请求。 Accept请求:在不违背Accept现有承诺的前提下,可以任意响应Accept请求。 算法陈述 阶段一: Proposer发送提案编号Mn; Acceptor根据约束接收提案,如果接收过返回接收最大值Vn; 阶段二: 如果Proposer收到大多数A的响应,发送[Mn,Vn]; Acceptor根据约束接收提案; 提案的获取 通知全部Learner 选取主Learner 将主Learner改为Learner集合 通过选取主Proposer保证算法的活性 三、Paxos的工程实践 3.1 Chubby 一个分布式锁服务。解决分布式协作,元数据存储,Master选举等一系列与分布式锁服务相关的问题。 底层为Paxos算法。 3.1.1 概述

June 8, 2021

序 大、快、多样性只是表象,大数据的真正价值在于生命性和生态性。(活数据) 第1 章 总述 如果不能对数据进行有序、有结构地分类组织和存储,如果不能有效利用并发掘它,继而产生价值,那么它同时也成为一场“灾难”。无需、无结构的数据犹如堆积如山的垃圾,给企业带来的是有令人咋舌的高额成本。 要求: 如何建设高效的数据模型和体系,是数据易用,避免重复建设和数据不一致性; 如何提供高效易用的数据开发工具; 如何做好数据质量保障; 如何有效管理和控制日益增长的存储和计算消耗; 如何保证数据服务的稳定,保证其性能; 如何设计有效的数据产品高效赋能于外部客户和内部员工 数据采集层 日志采集体系方案包括两大体系:Aplus.JS是Web端日志采集技术方案;UserTrack是App端日记采集技术方案。 在采集技术基础上面向各个场景的埋点规范。 在传输方面采用TimeTunel(TT),它既包括数据库的增量数据传输,也包括日志数据的传输;既支持实时流式计算,也知乎此各种时间窗口的批量计算。 也通过数据同步工具(DataX和同步中心,其中同步中心是给予DataX易用性封装的)直连异构数据库(备库)来抽取各种时间窗口的数据。 数据计算层 数据只有被整合和计算,才能被用于洞察商业规律,挖掘潜在信息,从而实现大数据价值,达到赋能于商业和创造价值的目的。 阿里巴巴的数据计算层包括两大体系:数据存储及计算云平台(离线计算平台MaxCompute和实时计算平台StreamCompute)和数据整合及管理体系(内部称之为“ OneData ”) 。 MaxCompute是阿里巴巴自主研发的离线大数据平台。 StreamCompute是阿里巴巴自主研发的流式大数据平台。 OneData是数据整合及管理的方法体系和工具。 借助此体系,构建了数据公共层。 从数据计算频率角度来看,阿里数据仓库可以分为离线数据仓库(传统的数据仓库概念)和实时数据仓库(典型应用:双11实时数据)。 阿里数据仓库的数据加工链路也是遵循业界的分层理念,包括: 操作数据层(Operational Data Store,ODS); 明细数据层(Data WareHouse Detail,DWD); 应用数据层(Application Data Store,ADS)。 通过数据仓库不同层次之间的加工过程实现从数据资产向信息资产的转化,并且对整个过程进行有效的元数据管理及数据质量处理。 元数据模型整合及应用是一个重要的组成部分,主要包含: 数据源元数据 数据仓库元数据 数据链路元数据 工具类元数据 数据质量类元数据 元数据应用主要面向数据发现、数据管理等,如用于存储、计算和成本管理。 数据服务层 当数据已被整合和计算好之后,需要提供给产品和应用进行数据消费。 针对不同的需求,数据服务层的数据源架构在多种数据库之上,如Mysql和HBase。 数据服务层主要考虑性能、稳定性、扩展性。 OneService(数据服务平台)一数据仓库整合计算好的数据作为数据源,对外通过接口的方式提供数据服务,主要提供简单数据查询服务、复杂数据查询服务(用户识别、用户画像等)和实时数据推送服务。 数据应用层 第1篇 数据技术篇 第2章 日志采集 第2篇 数据模型篇 第8章 大数据领域建模综述 8.1 为什么需要数据建模 如何将数据进行有序、有结构地分类组织和存储? 数据模型就是数据组织和存储方法,它强调从业务、数据存取和使用角度合理存储数据。有了适合业务和基础数据存储环境的模型,那么大数据就能获得以下好处: 性能:良好的数据模型能帮助我们快速查询所需要的数据,减少数据的I/O吞吐。 成本:良好的数据模型能极大地减少不必要的数据冗余,也能实现计算结果复用,极大地降低大数据系统中的存储和计算成本。 效率:良好的数据模型能极大地改善用户使用数据的体验,提高使用数据的效率。 质量:良好的数据模型能改善数据统计口径的不一致性,减少数据计算错误的可能性。 8.2 关系行数据库系统和数据仓库 大数据领域仍然使用关系型数据库,使用关系理论描述数据之间的关系,只是基于其数据存储的特点关系数据模型的范式上有了不同的选择。 8.3 从OLTP和OLAP系统特别看模型方法论的选择 OLTP系统通常面向的主要数据操作是随即读写,主要采用满足3NF的实体关系模型存储数据,从而在事务处理中解决数据的冗余和一致性问题;而OLAP系统面向的主要数据操作时批量读写,事物处理中的一致性不是OLAP所关注的,其主要关注数据的集合,以及在一次性的复杂大数据查询和处理中的性能,因此它需要采用一些不同的数据建模方法。 8.4 典型的数据仓库建模方法论 8.4.1 ER模型 数据仓库中的3NF和OLPT系统中的3NF的却别在于,它是站在企业角度面向主题的抽象,而不是针对某个具体业务流程的实体对象关系的抽象。其具有以下几个特点: 需要全面了解企业业务和数据; 事实周期非常长; 对建模人员的能力要求非常高。 采用ER模型建设数据仓库魔性的出发点是整合数据将个系统中的数据以整个企业角度按主题进项相似性组合和合并,并进行一致性处理,为数据分析决策服务,但是并不能直接用于分析决策。 其建模步骤分为三个阶段: 高层模型:一个高度抽象的模型,描述主要的主题以及主题间的关系,用语描述企业的业务总体概况。 中层模型:在高层模型的基础上,细化主题的数据项。 物理模型(也叫底层模型):在中层模型的基础上,考虑物理存储,同时基于性能和平台特点进行物理属性的设计,也可能作一些表的合并、分区的设计等。 实践典型:金融业务FS-LDM。 8.4.2 维度模型 是数据仓库工程领域最流行的数据仓库建模的经典。 维度建模从分析决策的需求出发构建模型,为分析需求服务,因此它重点关注用户如何更快速地完成需求分析,同时具有较好的大规模复杂查询的响应性能。其典型的代表是星形模型,以及在一些特殊场景下使用的雪花模型。其设计分为一下几个步骤: 选择需要进行分析决策的业务过程。业务过程可以是单个业务事件,比如交易的支付、退款等;也可以时某个时间的状态,比如当前的账户余额等;还可以是一系列相关业务时间组成的业务流程,具体需要看我们分析的是某些事件发生情况,还是当前状态,或是事件流转效率; 选择粒度。在事件分析中,我们要预判所有分析需要细分的程度,从而决定选择的粒度。粒度是维度的一个组合; 识别维表。选择好粒度之后,就需要细雨此粒度设计维表,包括维度属性,用于分析时进行分组和筛选; 选择事实。确定分析需要衡量的指标。 8.4.3 Data Vault模型 ER模型的衍生。其设计的出发点也是为了实现数据的整合,但不能直接用于数据分析决策。...

June 8, 2021

第1章 Java的I/O演进之路

第1章 Java的I/O演进之路 1.1 I/O基础入门 1.1.1 Linux网络I/O模型简介 UNIX提供了5种I/O模型: (1)阻塞I/O模型 (2)非阻塞I/O模型 (3)I/O复用模型 (4)信号驱动I/O模型 (5)异步I/O 1.1.2 I/O多路复用技术 把多个I/O阻塞复用到同一个select的阻塞上,从而是的系统在单线程的情况下可以同时处理多个客户端请求。比多线程有性能优势,节约资源。 支持I/O多路复用的系统调用select/pselect/poll/epoll。 epoll的优点: 支持一个进程打开的socket描述符(FD)不受限制(仅受限于操作系统的最大文件句柄数(内存))。 I/O效率不会随着FD数目的增加而线性下降。 使用mmap加速内核与用户空间的消息传递。 epoll的API更加简单。 1.2 Java的I/O演进 历史题,略。 第2章 NIO入门 2.1 传统的BIO编程 C/S模型,客户端发起连接请求,三次握手,通过Socket进行通信。 2.1.1 BIO通信模型图 一请求一应答模型:每次接收到连接请求都创建一个新的线程进行链路处理。处理完成后通过输出流返回应答给客户端,线程销毁。 该模型最大的问题就是:缺乏弹性伸缩能力,当客户端并发访问量增加后,服务端的线程个数和客户端并发访问数呈1:1的正比关系。 2.1.2 同步阻塞式I/O创建的TimeServer源码分析 import java.io.IOException; import java.net.ServerSocket; import java.net.Socket; /** * <p>Description: 同步阻塞式I/O创建的TimeServer</p> * * @author 李宏博 * @version xxx * @create 2019/8/14 17:58 */ public class TimeServer { /** * * @param args */ public static void main(String[] args) throws IOException { int port = 8080; if (args != null && args.length > 0) { try { port = Integer.valueOf(args[0]); } catch (NumberFormatException e) { e.printStackTrace(); } } ServerSocket server = null; try { server = new ServerSocket(port); System....

June 8, 2021

第1章 Spring框架的由来

第1章 Spring框架的由来 1.1 Spring之崛起 1.2 Spring框架概述 基于POJO(Plain Old Java Object,简单Java对象)的轻量级开发理念。 Spring总体架构: 1.3 Spring大观园 1.4 小结 第2章 Spring的IOC容器 2.1 我们的理念是:让别人为你服务 2.2 手语,呼喊,还是心有灵犀 2.2.1 构造方法注入 IoC Service Provider会检查被注入对象的构造方法,取得它所需要的依赖对象列表,进而为其注 入相应的对象。同一个对象是不可能被构造两次的,因此,被注入对象的构造乃至其整个生命周期, 应该是由IoC Service Provider来管理的。 2.2.2 setter方法注入 setter方法注入虽不像构造方法注入那样,让对象构造完成后即可使用,但相对来说更宽松一些, 可以在对象构造完成后再注入。 2.2.3 接口注入 对于前两种注入方式来说,接口注入没有那么简单明了。被注入对象如果想要IoC Service Provider为其注入依赖对象,就必须实现某个接口。这个接口提供一个方法,用来为其注入依赖对象。 IoC Service Provider最终通过这些接口来了解应该为被注入对象注入什么依赖对象。 示例: 2.2.4 三种注入方式的比较 接口注入。不提倡,带有侵入性 构造方法注入。这种注入方式的优点就是,对象在构造完成之后,即已进入就绪状态,可以马上使用。缺点就是,当依赖对象比较多的时候,构造方法的参数列表会比较长。而通过反射构造对象的时候,对相同类型的参数的处理会比较困难,维护和使用上也比较麻烦。而且在Java中,构造方法无法被继承,无法设置默认值。对于非必须的依赖处理,可能需要引入多个构造方法,而参数数量的变动可能造成维护上的不便。 setter方法注入。因为方法可以命名, 所以setter方法注入在描述性上要比构造方法注入好一些。另外, setter方法可以被继承,允许设置默认值,而且有良好的IDE支持。缺点当然就是对象无法在构造完成后马上进入就绪状态。 2.3 IOC的附加值

June 8, 2021

第1章 概述

第1章 概述 互联网公司的分布式两个特点:规模大、成本低。 1.1 分布式存储概念 定义:分布式存储系统是大量普通PC服务器通过Internet互联,对外作为一个整体提供存储服务。 特性: 可扩展。集群,性能随规模线性增长。 低成本。有自动容错,自动负载均衡机制。所以可以构建在普通PC机器。 高性能。 易用。提供易用的对外接口。 主要挑战:数据、状态信息的持久化,要求在自动迁移、自动容错、并发读写的过程中保证数据的一致性。 主要技术来自于分布式系统和数据库: 数据分布:如何保证均匀?如何实现跨服务器读写? 一致性:如何复制?如何保证出现异常也一致? 容错:如何检测?出错如何迁移? 负载均衡:如何实现?如何做到迁移时不影响其他业务? 事务与并发控制:如何实现分布式事务?如何实现MVCC? 易用性:如何易用?如何方便运维? 压缩/解压缩:如何根据数据特点设计压缩/解压缩算法?如何平衡节省的存储空间和消耗的CPU资源浪费? 1.2 分布式存储分类 数据大致分为三类: 非结构化数据:包括所有格式的办公文档、文本、图片、图像、音频和视频信息等。 结构化数据:一般存储在关系数据库中,可用二位关系表结构来表示。 半结构化数据:与结构化的区别在于,半结构化数据的模式结构和内容混在一起,没有明显的区分,也不需要预先定义数据的模式结构。 存储系统分为四类: 分布式文件系统 图片、照片、视频等非结构化数据对象,以对象组织,对象之间没有关联,称为Blob(Binary Large Object,二进制大对象)数据。 总体上看:分布式文件系统存储三种类型的数据:Blob对象、定长块以及大文件。在系统层面,分布式文件系统内部按照数据块来组织数据,每个数据块的大小大致相同,每个数据块可以包含多个Blob对象或者定长块,一个大文件也可以拆分成多个数据块。 分布式键值系统 用于存储关系简单的半结构化数据,它只提供基于主键的CRUD功能,即根据主键创建、读取、更新或者删除一条键值记录。 与传统哈希表相似,但是支持将数据分布到多个存储节点。 分布式键值系统是分布式表格系统的一种简化实现,一般用作缓存。 分布式表格系统 用于存储较为复杂的半结构化数据。不仅仅支持简单的CRUD操作,而且支持扫面某个主键范围。 借鉴了许多关系型数据库的技术,例如支持某种程度上的事务,比如单行事务,某个实体组下的多行事务。 与分布式数据库先比,仅支持针对单张表格的操作,不支持复杂操作。 分布式数据库 一般从单机数据库扩展而来,用于存储结构化数据。 第2章 单机存储系统 单机存储引擎就是哈希表、B树等数据结构在机械磁盘、SSD等持久化介质上的实现。 2.1 硬件基础 2.1.1 CPU架构 经典的多CPU架构为对称多处理结构(Symmetric Multi-Processing,SMP),即在一个计算机上汇集了一组处理器,它们之间对称工作,无主次或从属关系,共享相同的物理内存及总线。 2.1.2 IO总线 存储系统的性能瓶颈一般在与IO。 2.1.3 网络拓扑 传统的数据中心网络拓扑,分三层。最下面是接入层,中间是汇聚层,上面是汇聚层。存在的问题:大量下层接入,导致同一个接入层下的服务器之间的带宽减小。 2.1.4 性能参数 2.1.5 存储层次架构 存储系统的性能主要包括两个维度:吞吐量以及访问延时。 2.2 单机存储引擎 2.2.1 哈希存储引擎 Bitcask是一个基于哈希表结构的键值寸尺系统,它仅支持追加操作(Append-only),即所有的写操作只追加而不修改老的数据。 在Bitcask系统中,每个文件有一定的大小限制,当文件增加到相应的大小时,就会产生一个新的文件,老的文件只读不写。在任意时刻,只有一个文件市可写的,用于数据追加,称为活跃数据文件。而其他已经达到大小限制的文件,称为老数据文件。 数据结构 一条一条写入,每条记录的数据项分别为:主键(key),value内容(value),主键长度(key_sz),value长度(value_sz),时间戳(timetamp)以及crc校验值。(删除不会删除旧的条目,而是将value设定为一个特殊的之作标识)。内存中的结构是hash表。 定期合并 为解决垃圾文件问题。将所有老数据文件中的数据扫描一遍生成一个新的数据文件。对用一个key的多个操作以保留最新的一个原则进行删除。 快速恢复 每次合并时,将内存中的哈希索引表转储到磁盘中,生成一个索引文件。这个索引文件只存储value的位置。 2.2.2 B树存储引擎 不仅支持随机读取,还支持范围扫描。 数据结构 InnoDB按照页面(Page)来组织数据,每个页面对用B+树的一个节点。 B+树的根节点是常驻内存的。修改操作首先需要记录提交日志,接着修改内存中的B+树。 缓冲区管理 LRU、LIRS 2.2.3 LSM树存储引擎 将对数据的修改增量保持在内存中,达到指定的大小先之后将这些修改操作批量写入磁盘,读取时需要合并磁盘中的历史数据和内存中最近的修改操作。

June 8, 2021

第2章 InnoDB存储引擎

第2章 InnoDB存储引擎 事务安全。 2.1 InnoDB存储引擎概述 Mysql5.5开始是默认的表存储引擎(之前尽在Window下是)。第一个完整支持ACID事务的MySQL存储引擎。其特点是行锁设计、支持MVCC、支持外键、提供一致性非锁定读,同时被设计用来最有效地利用以及使用内存和CUP。 高性能、高可用、高扩展。 2.2 InnoDB存储引擎的版本 略 2.3 InnoDB体系结构 后台线程的主要作用是负责刷新内存池中的数据,保证缓冲池中的内存缓存的是最近的数据。 此外将已修改的数据文件刷新到磁盘文件,同时保证在数据库发生异常的情况下InnoDB能恢复到正常运行状态。 2.3.1 后台线程 多线程模型。不同的后台线程处理不同的任务。 Master Thread 将缓冲池的数据异步刷新到磁盘,保证数据一致性,包括脏页的刷新、合并插入缓冲(INSERT BUFFER)、UNDO页的回收。 IO Thread 大量使用AIO(Async IO),极大提高性能。IO Thread主要负责这些IO请求的回调处理。 第3章 文件 参数文件:告诉MySQL启动时查找文件的地址,指定初始化参数; 日志文件:错误日志文件、二进制文件日志文件、慢查询日志文件、查询日志文件; socket:当用UNIX域套接字方式进行连接时需要的文件 pid文件:MySQL实例的进程ID文件 MySQL表结构文件:用来存放MySQL表结构定义文件 存储引擎文件:每个存储引擎都会有自己的文件来保存各种数据,这些存储引擎真正存储了记录和索引等数据。 3.1 参数文件 MySQL实例也可以不需要参数文件,这是编译MySQL时指定的默认值。但是如果在默认的数据库目录下找不到mysql架构,则会启动失败。 3.1.1 什么是参数 数据库中的参数是键值对。MySQL中无类似Oracle中的隐藏参数。 3.1.2 参数类型 动态参数:运行时可修改 静态参数:在整个实例的生命周期都不能修改。 3.2 日志文件 日志文件记录了MySQL数据库的各种类型活动。 3.2.1 错误日志 对MySQL的启动、运行、关闭过程进行了记录。当出现MySQL数据库不能正常启动时,第一个必须查找的文件应该就是错误日志文件。 3.2.2 慢查询日志 帮助DBA定位可能存在问题的SOL语句,从而进行SQL语句层面的优化。设置一个阈值(最小精度是微秒),超过(必须是大于,等于不行)时将该SQL语句记录到慢查询日志中。 默认情况下并不启动慢查询日志。 如果没有使用索引也会被记录。 3.2.3 查询日志 记录了所有对MySQL数据库请求的信息,无论这些请求是否得到了正确的执行。 3.2.4 二进制文件 记录了对MySQL数据库执行更改的所有操作,但是不包括SELEC和SHOW这类操作。 作用: 恢复:某些数据的恢复需要二进制日志。 复制:主从复制 审计:通过审计二进制文件,判断是否有数据库进行注入的攻击。 二进制日志文件在默认情况下并没有启动,需要手动指定参数来启动。会影响性能,但仅仅1%。 缓冲写宕机问题,无缓冲写宕机问题 3.3 套接字文件 3.4 pid文件 3.5 表结构定义文件 记录该存储引擎对应的表结构 3.6 InnoDB存储引擎文件 InnoDB独有的文件 3.6.1 表空间文件 InnoDB采用将存储的数据按表空间进行存放的设计。 还可设置独立表空间,用户不用将所有数据都存放于默认的表空间中。 单独的表空间仅存储表的数据、索引和插入缓冲BITMAP等信息,其余的信息存放到默认表空间。 3.6.2 重做日志文件 宕机问题。 重做日志文件与二进制文件的区别: 记录范围,二进制日志会记录所有与MySQL数据库有关的日志记录,而InnoDB仅记录该存储引擎的事务日志。 记录内容,二进制日志记录具体操作(逻辑日志),而InnoDB的重做日志文件是关于每个Page的更改的物理情况 写入时间,二进制日志文件只在事务提交前进行一次写入,而重做日志文件在事务的进行过程中,一直会被写入。 3.7 小结 重做日志文件使得InnoDB存储引擎可以提供可靠的事务。 第4章 表 4.1 索引组织表 在InnoDB存储引擎中,表都是根据主键顺序组织存放。 在InnoDB存储引擎中,每张表都有个主键,如果没有显示定义,则按如下方式创建: 首先判断表中是否含有唯一非空索引(Unique NOT NULL),如果有,则该列即为主键。(按定义索引的顺序选择) 如果不符合上述条件,InnoDB存储引擎自动创建一个6字节大小的指针。 _rowid仅适用于单个列为主键的情况。...

June 8, 2021

第2章 Tomcat总体架构

第2章 Tomcat总体架构 系统设计及中间件设计时的参考:生命周期管理、可扩展的容器组件设计、类加载方式。 2.1 总体设计 如何设计一个应用服务器? 2.1.1 Server 最基本的功能:接收请求,业务处理,返回响应。 两个方法: start():启动服务器,打开Socket链接,监听端口,负责接收请求,处理及返回。 stop():停止服务器并释放网络资源。 作为嵌入在应用系统中的远程请求处理方案,且访问量低时可行。但作为应用服务器不可行。 2.1.2 Connector和Container 请求监听与请求处理放到一起扩展性差。 Connector负责监听,返回。 Container负责处理请求。 均分别拥有自己的start()和stop()方法来加载和释放自己维护的资源。 明显的缺陷:如何让Connector与Container对应?可以维护一个复杂关系映射,但是并不必需。Container设计足够灵活。 引入Service,负责维护多个Connector和一个Container。 在Tomcat中,Container是一个更加通用的概念。为了与Tomcat中的组件命名一致,所以重新命名为Engine,用以表示整个Servlet引擎。 Engine表示整个Servlet引擎。Server表示整个Servlet容器。 2.1.3 Container设计 应用服务器是用来部署并运行Web应用的,是一个运行环境,而不是独立的业务处理系统。因此需要在Engine容器中支持管理Web应用,当接收到Connector的处理请求时,Engine容器能够找到一个合适的Web应用来处理。 使用一个Context来表示一个Web应用,并且一个Engine可以包含多个Context。 虚拟主机,加入Host。一个Host可以包含多个Context。 Tomcat的设计中Engine可以包含Host也可以包含Context,这是由具体的Engine实现确定的。Tomcat提供的默认实现StandardEngine只能包含Host。 一个Web应用可以包含多个Servlet实例。在Tomcat中,Servlet定义被称为Wrapper。 “容器”的作用都是处理请求并返回响应数据。所以引入一个Container接口:addchild()添加子容器,backgroundProcess()实现文件变更的扫描。 2.1.4 Lifecycle 所有组件均存在启动、停止这两个生命周期方法,可在此基础上扩展生命周期管理的方法,即对于生命周期管理进行一次接口抽象。 将Server接口替换为Lifecycle接口: Init():初始化组件 start():启动组件 stop():停止组件 destory():销毁组件 addLifecycleListener:添加事件监听器(用于监听组件的状态变化) removeLifecycleListener:删除 Tomcat核心组件的默认实现均继承自LifecycleBeanBase抽象类,该类不但负责组件各个状态的转换和事件处理,还将组件自身注册为MBean,以便通过Tomcat的管理工具进行动态维护。 2.1.5 Pipeline和Valve 以上设计以保证核心架构的了可伸缩性和可扩展性。但是还要考虑各个组件的灵活性,使其同样可扩展。 责任链模式是一种比较好的选择。Tomcat即采用该模式来实现客户端请求的处理。在Tomcat中每个Container组件通过执行一个责任链来完成具体的请求处理。 Pipeline(管道)用于构造责任链,Valve(阀)代表责任链上的每个处理器。Pipeline中维护了一个基础的Valve(位于末端,最后执行)。 Tomcat的每个层级的容器(Engine、Host、Context、Wrapper)均有对应的基础Valve实现,同时维护一个Pipeline实例。即任何层级的容器都可以对请求处理进行可扩展。 2.1.6 Connector设计 基本功能: 监听服务器端口,读取来自客户端的请求。 将请求数据按照指定协议进行解析。 根据请求地址匹配正确的容器进行处理。 将响应返回客户端。 Tomcat支持多协议,默认支持HTTP和AJP。同时支持多种I/O方式,包括BIO(8.5之后移除)、NIO、APR。而且在Tomcat8之后新增了对NIO2和HTTP/2协议的支持。因此对协议和I/O进行抽象和建模时需要关注的重点。 在Tomcat中,ProtocolHandler表示一个协议处理器,其包含一个Endpoint(无此接口,仅有AbstractEndpoint抽象类)用于启动Socket监听,还包含一个Processor用于按照指定协议读取数据,并将请求交由容器处理。 在Connector启动时,Endpoint会启动线程来监听,并在接收到请求后调用Processor进行数据读取。 当Processor读取客户端请求后,需要按照请求地址映射到具体的容器进行处理,这个过程即为请求映射。由于Tomcat各个组件采用通用的生命周期管理,而且可以通过管理工具进行状态变更,因此请求映射除考虑映射规则的实现外,还要考虑容器组件的注册与销毁。 Tomcat通过Mapper和MapperListener两个类实现上述功能。前者用于维护容器映射信息,同时按照映射规则(Servlet规范)查找容器。后者实现了ContainerListener和LifecycleListener,用于在容器组件状态发生变更时,注册或者取消对应的容器映射信息。为了实现上述功能,MapperListener实现了Lifecycle接口,当其启动时(在Service启动时启动),会自动作为监听器注册到各个容器组件上,同时将已创建的容器注册到Mapper。 Tomcat通过适配器模式(Adapter)实现了Connector与Mapper、Container的解耦。实现自己的Adapter可以脱离Servlet容器又使用Tomcat链接器。 2.1.7 Excutor 并发问题的解决方案。采用线程池(默认采用JDK5的线程池,继承自Lifecycle,当作通用组件进行管理)对线程进行统一管理。 在Tomcat中Excutor由Service维护,因此同一个Service中的组件可以共享一个线程池。 如果没有定义任何线程池,相关组件(Endpoint)会自动创建线程池,此时线程池不再共享。 在Tomcat中,Endpoint会启动一组线程来监听Socket端口,当接收到客户端请求后,会创建请求处理对象,并交由线程池处理,由此支持并发处理客户端请求。 2.1.8 Bootstrap和Catalina 除开前面的核心组件外,还需要提供一套配置环境来支持系统的可配置性,便于通过修改配置来优化应用。 集群、安全等组件同样重要,但不属于通用概念。 Tomcat通过类Catalina提供了一个Shell程序,用于解析server.xml创建各种组件,同时,负责启动、停止应用服务器(只需要启动Tomcat顶层组件Server)。 Tomcat使用Digester解析XML文件,包括server.xml以及web.xml等。 最后,Tomcat提供了Bootstrap作为应用服务器启动入口。Bootstrap负责创建Catalina实例,根据执行参数调用Catalina相关方法完成针对应用服务器的操作(启动、停止)。 Bootstrap与Tomcat应用服务器完全松耦合(通过反射调用Catalina实例),它可以直接依赖JRE运行并为Tomcat应用服务器创建共享类加载器,用于构造Catalina实例以及整个Tomcat服务器。 上述是Tomcat标准的启动方式。但是Server及其子组件代表了应用服务器本身,那么我们可以不通过Bootstrap和Catalina来启动服务器。 Tomcat组件说明: 组件名称 说明 Server 表示整个Servlet容器,因此Tomcat运行环境中只有唯一一个Server实例 Service Service表示一个或者多个Connector的集合,这些Connector共享同一个Container来处理其请求。在同一个Tomcat实例内可以包含任意多个Service实例,它们彼此独立 Connector 即Tomcat链接器,用于监听并转化Socket请求,同时将读取的Socket请求交由Container处理,支持不同协议以及不同的I/O方式 Container Container表示能够执行客户端请求并返回响应的一类对象。在Tomcat中存在不同级别的容器:Engine、Host、Context、Warpper Engine Engine表示整个Servlet引擎。在Tomcat中,Engine为最高层级的容器对象。尽管Engine不是直接处理请求的容器,确实获取目标容器的入口 Host Hostz作为一类容器,表示Servlet容器中的虚拟机,与一个服务器的网络名有关,如域名等。客户端可以使用这个网络名连接服务器,这个名称必须在DNS服务器上注册。 Context Context作为一类容器,用于表示ServletContext,在Servlet规范中,一个ServletContext即表示一个独立的Web应用 Wrapper Wrapper作为一类容器,用于表示Web应用中动议的Servlet Executor 表示Tomcat组件可以共享的线程池 2.2 Tomcat启动 Tomcat默认实现在相关概念的基础上结合生命周期管理监听器完成了大量的启动工作。...

June 8, 2021

第6章 深入分析ClassLoader工作机制

第6章 深入分析ClassLoader工作机制 三个作用: 将Class加载到JVM中; 审查每个类应该由谁加载(双亲委派); 将CLass字节码重新解析成JVM统一要求的格式。 6.1 ClassLoader类结构解析 defineClass方法:将byte字节流解析成JVM能够识别的Class对象。意味着不仅仅可以通过class文件实例化对象。调用此方法生成的Class对象还没有resolve,resolve会在对象真正实例化时才进行。 findClass方法:通过直接覆盖ClassLoader父类的findClass方法来实现类的加载规则,从而取得要加载的字节码。然后调用defineClass方法生成类的Class对象。如果想在类被加载到JVM的时候就被链接(Link),那么可以接着调用另外一个resolveClass方法。 如果想实现自己的ClassLader,一般都会继承URLClassLoader。 6.2 ClassLoader的等级加载机制 双亲委派。 (1)Bootstrap ClassLoader,主要加载JVM自身工作需要的类,完全由JVM自己控制。(既没有更高一级的父加载器,也没有子加载器)。 (2)ExtClassLoader,并不是JVM亲自实现,加载System.getProperty(“java.ext.dirs”)目录下的类。 (3)AppClassLoader,父类是ExtClassLoader。加载System.getProperty(“java.class.path”)目录下的类都可以被其加载。 实现自己的类加载器,都必须最终调用getSystemClassLoader()作为父加载器。而此方法获取到的就是AppClassLoader。 注意Bootstrap ClassLoader并不是如其他文章所说,而是其并无子类也无父类。ExtClassLoader并没有父类加载器。 ExtClassLoader和AppClassLoader都继承了URLClassloader类,而URLClassLoader又实现了抽象类ClassLoader,在创建Launcher对象时会首先创建ExtClassLoader,然后讲ExtClassLoader对象作为父加载器创建AppClassLoader对象。所以如果在Java应用中没有定义其他的ClassLoader,那么除了System.getProperty(“java.ext.dirs”)目录下的类是由ExtClassloader加载,其他类都是由AppClassLoader加载。 加载class文件到内存的两种方式:隐式,显式。 6.3 如何加载class文件 加载、验证、准备、解析、初始化。 第13章 Spring框架的设计理念与设计模式分析 13.1 Spring的骨骼架构 三个核心组件:Core、Context和Bean。 13.1.1 Spring的设计理念 最核心:Bean。(面向Bean编程) 解决了一个关键问题:把对象之间的依赖关系转而用配置文件来管理(依赖注入)。 Spring通过把对象包装在Bean中,从而达到管理这些对象及做一系列额外操作的目的。 这种设计策略完全类似于OOP的设计理念。构建一个数据结构,然后根据这个数据结构设计它的生存环境,并让它在这个环境中按照一定的规律不停地运动,在它们地不停运动中设计一个系列于环境或者与其他各地完成信息交换。(同时也是大多数框架地设计理念) 13.1.12 核心组件如何协同工作 Context负责发现每个Bean之间的关系,建立关系并且维护关系。所以Context就是一个Bean关系的集合,也叫Ioc容器。 Core就是发现、建立和维护每个Bean之间的关系所需要的一系列工具。(也就是一些Util) 13.2 核心组件详解 13.2.1 Bean组件 包:org.springframework.beans。这个包下的类主要解决三件事:Bean的定义、Bean的创建及对Bean的解析。(使用者只需关心创建) Spring是典型的工厂模式,工厂的继承层次关系图如下: 顶级接口BeanFactory有3个子接口:ListableBeanFactory、HierarchicalBeanFantory和AutowireCapableBeanFactory。 DefaultListableBeanFactory实现了所有的结构。 定义多接口的原因:每个接口有不同的使用场景,主要是为了区分Spring内部对象的传递和转化过程中,对对象的数据访问所作的限制。例如,ListableBeanFactory接口标识这些Bean是可列表的,HierarchicalBeanFactory表示这些Bean是有继承关系的,AutowireCapableBeanFactory接口定义Bean的自动装配规则。4个接口共同定义了Bean的集合、Bean之间的关系和Bean的行为。 Bean的定义主要有Beandefinition描述: Bean是配置文件信息中<bean/>节点信息的转化。Spring解析完成后,内部就是一个BeanDefinition对象。 Bean的解析过程过于繁琐,不赘述。 13.2.2 Context组件 ApplicationContext继承了BeanFactory。 ApplicationContext的子类主要包含两个方面: ConfigurableApplicationContext表示该Context是可修改的,也就是构建Context中,用户可以动态添加或修改已有的配置信息。 WebApplication,用于Web,可以直接访问Servletcontext。 ApplicationContext必须完成的事情: 标识一个应用环境 利用BeanFactory创建Bean对象 保存对象关系表 能够捕获各种事件 13.2.3 Core组件 其中有很多关键类,一个重要的组成部分就是定义了资源的访问方式。 Resource类相关:封装了各种可能的资源类型,也就是说对使用者来说屏蔽了文件类型的不同。通过继承InputStreamSource接口,在这个接口中有个getInputStream方法,返回InputStream类,所有资源都可以通过InputStream来获取,及屏蔽了资源的提供者。 Context把资源的加载、解析和描述工作委托给了ResourcePatternResolver类来完成。 13.2.4 Ioc容器如何工作 如何创建BeanFactory工厂 refresh方法。源码已阅就不贴了。步骤如下: (1)构建BeanFactory (2)注册可能感兴趣的事件 (3)创建Bean实例对象 (4)出发被监听的事件 如何创建Bean实例并构建Bean的关系网 详见源码。 Ioc容器的扩展点 BeanFactoryPostProcessor和BeanPostProcessor,分别在构建BeanFactory和构建Be’an对象时调用。还有就是InitPostProcessor和DisposableBean,它们分别在Bean实例创建和销毁时被调用。用户可以实现在这些接口中定义的方法,Spring会在适当的时候调用他们。还有一个是FactoryBean。(会扩展是精通Spring的第一步) Ioc容器如何为我所用 扩展点。通过扩展点来改变Spring的通用行为。(AOP是一个例子,可以作为参考) 13.3 Spring中AOP的特性详解 13.3.1 动态代理的实现原理 java.lang.reflect.Proxy。 重点看公有方法。 阅读源码部分略。 13.2.2 Spring AOP如何实现 13.4 设计模式解析之代理模式 给某一个对象创建一个代理对象,有代理对象控制对原对象的引用,而创建代理对象之后可以再调用时增加一些额外的操作。 13.5 设计模式解析之策略模式 CGLIB与JDK动态代理的选择,就是策略模式的一种实现。

June 8, 2021