├── README.md ├── 权限系统.pdf ├── 设计模式-行为型.pdf ├── 设计模式-创建型和结构型.pdf ├── 通用对账系统介绍与设计(上).pdf ├── 202103.txt ├── 202101.txt ├── 202102.txt ├── 201611.txt ├── 202008.txt ├── 201711.txt ├── 201704.txt ├── 201707.txt ├── 201802.txt ├── 201610.txt ├── 201801.txt ├── 201706.txt └── 202010.txt /README.md: -------------------------------------------------------------------------------- 1 | 逐渐将之前的日常技术笔记整理到此处 2 | 版权申明:部分为网络摘抄,部分为自己理解。 -------------------------------------------------------------------------------- /权限系统.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangxingjian2015/monthly-technology/HEAD/权限系统.pdf -------------------------------------------------------------------------------- /设计模式-行为型.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangxingjian2015/monthly-technology/HEAD/设计模式-行为型.pdf -------------------------------------------------------------------------------- /设计模式-创建型和结构型.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangxingjian2015/monthly-technology/HEAD/设计模式-创建型和结构型.pdf -------------------------------------------------------------------------------- /通用对账系统介绍与设计(上).pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangxingjian2015/monthly-technology/HEAD/通用对账系统介绍与设计(上).pdf -------------------------------------------------------------------------------- /202103.txt: -------------------------------------------------------------------------------- 1 | 2 | Java中如果想要try代码之后,不允许再执行finally中的代码块,有两种方式: 3 | 1. 使用System.exit(1)直接退出虚拟机; 4 | 2. 把当前执行try catch finally代码的线程设置为守护线程。 5 | 6 | 在Java NIO中,运用了虚引用管理堆外内存。 7 | 8 | 业务系统(CRM、OA等)往往并不会保留历史数据。但在分析角度,我们是一定要保留这些改变的痕迹。这种随着时间可能会缓慢变化的维度,就是缓慢变化维、也就是SCD(Slowly Changing Dimensions)。在Kimball整理的处理方法一共有8种,但往往只有3种被详细使用。 9 | 10 | 缓慢变化维的几种处理方式: 11 | 1. 重写;与业务数据保持一致,直接update为最新的数据。 12 | 这种方法主要应用于以下两种情况: 13 | 1. 数据必须正确-例如用户的身份证号,如需要更新则说明之前录入错误。 14 | 2. 无需考虑历史变化的维度-例如用户的头像URL,这种数据并没有分析的价值。因此不做保留。 15 | 优点:简化ETL-直接update即可;节省存储空间-其他存储方法都占用更多空间。 16 | 缺点:无法保留历史痕迹-万一后续要分析,却没有保留数据。 17 | 2. 增加新行;更新历史数据时间戳,新增新行记录新值。这种方法主要用于仅需要保存历史数据的业务场景。具体的ETL规则如下: 18 | 1. 自然键第一次出现时,新增一行数据,created为业务系统的创建时间,updated为9999-12-31。数据规范不允许数据存在NULL值的情况,因此用9999-12-31代替。 19 | 2. 维度发生变化时。将自然键当前记录的updated更新为最新时间。新增一行记录,created为最新时间,updated为9999-12-31。 20 | 3. 增加当前值属性。在大部分的维度模式中,很多的源数据变化将产生类型1和类型2变化。有时两种技术都不能满足需求-当需要分析所有伴随着新值或旧值的变化前后记录的事实时,需要采用类型3变化。 21 | 剩下的几种处理方式基本不会采用。。 22 | 4. 不做调整。 23 | 5. 微型维度。当变化频率加快时候,并且维度表包含几百万的维度表。如果对变化的跟踪采用可靠的SCD2技术对查询性能具有负面影响-太多行且无必要。采用新的独立的维度表消除频繁分析或者频繁变化的属性,这一维度技术叫做微型维度。 24 | 6. 重写+微型维度; 25 | 7. 重写+增加新行+增加当前值属性。三者联合使用。 26 | 8. 双更新+增加新行。 27 | 28 | 维度建模: 29 | 是Kimball最先提出的,其最简单的描述就是,按照事实表、维度表来构建数据仓库、数据集市。在维度建模方法体系中,维度是描述事实的角度,如日期、客户、供应商等,事实是要度量的指标,如客户数、销售额等。维度建模还会分为星型模型、雪花模型等。维度建模以分析决策的需求出发构建模型,构建的数据模型为分析需求服务,因此它重点解决用户如何更快速完成分析需求,同时还有较好的大规模复杂查询的响应性能。 30 | 31 | 数仓分层从关系型在线交易到面向主题的数据仓库系统,从范式建模到维度建模的必经之路。 32 | 数据分层是一套让数据体系更有序的行之有效的数据组织和管理方法。 33 | 数仓分层的优点: 34 | 1. 数据结构化更清晰:每个分层都有它的作用域和职责,在使用表的时候能更方便地定位和理解。 35 | 2. 数据血缘追踪:提供给外部使用的是一张业务表,但是这张业务表可能来源很多张表。如果一张来源表出问题了,可以快速准确的定位到问题,并清楚每张表的作用范围。 36 | 3. 增强数据复用能力:减少重复开发,通过数据分层规范化,开发一些通用的中间层数据,能够减少重复计算,提高单张业务表的使用率,提升系统的执行效率。 37 | 4. 简化复杂的问题:把一个复杂的业务分成多个步骤实现,每一层只处理单一的步骤,比较简单和容易理解。而且便于维护数据的准确性,当数据出现问题之后,可以不用修复所有的数据,只需要从有问题的步骤开始修复。 38 | 5. 减少业务的影响:业务可能会经常变化,这样做就不必一次业务就需要重新接入数据。 39 | 6. 统一数据口径:通过数据分层,提供统一的数据出口,统一对外输出的数据口径。 40 | 41 | 怎么做数据质量: 42 | 1. 一致性; 43 | 2. 准确性; 44 | 3. 完整性; 45 | 4. 可审核性; 46 | 5. 整齐有序; 47 | 6. 唯一性; 48 | 7. 及时性; 49 | 50 | 什么是维度,什么是度量: 51 | 维度用来描述事实,从不同角度描述事实,也就是说维度是描述事实的角度。 52 | 度量是业务流程节点上的一个数值。比如销量、价格、成本等。 53 | 54 | MongoDB的多key索引(Multikey Index):当索引的字段为数组时,创建出的索引称为多key索引,多key索引会为数组的每个元素建立一条索引。 55 | MongoDB除了支持多种不同类型的索引,还能对索引定制一些特殊的属性。 56 | 1. 唯一索引(unique index):保证索引对应的字段不会出现相同的值,比如_id索引就是唯一索引; 57 | 2. TTL索引:可以针对某个时间段,指定文档的过期时间(经过指定时间后过期或在某个时间点过期); 58 | 3. 部分索引(partial index):只针对某个特定条件的文档建立索引,3.2版本才支持该特性; 59 | 4. 稀疏索引(sparse index):只针对存在索引字段的文档建立索引,可看作是部分索引的一种特殊情况。 60 | 61 | Class字节码文件10个主要组成部分:一个标准的ClassFile结构如下: 62 | ClassFile { 63 | u4 magic; //魔数,识别class文件格式 64 | u2 minor_version; //副版本号 65 | u2 major_version; //主版本号 66 | u2 constant_pool_count; //常量池计数器 67 | cp_info constant_pool[constant_pool_count - 1]; //常量池 68 | u2 access_flags; //访问标志 69 | u2 this_class; //类索引 70 | u2 super_class; //父类索引 71 | u2 interfaces_count; //接口计数器 72 | u2 interfaces[interfaces_count]; //接口索引集合 73 | u2 fields_count; //字段个数 74 | field_info fields[fields_count]; //字段集合 75 | u2 methods_count; //方法个数 76 | method_info methods[methods_count]; //方法集合 77 | u2 attributes_count; //附加属性个数 78 | attribute_info attributes[attributes_count]; //附加属性集合 79 | } 80 | 81 | 对于方法区,Java8之后的变化: 82 | 1. 移除了永久代(PermGen),替换为元空间(Metaspace); 83 | 2. 永久代中的class metadata(类元信息)转移到了native memory(本地内存,而不是虚拟机); 84 | 3. 永久代中的interned Strings(字符串常量池)和class static variables(类静态变量)转移到了Java heap; 85 | 4. 永久代参数(PermSize MaxPermSize)->元空间参数(MetadataspaceSize MaxMetadataspaceSize); 86 | 87 | Java8为什么要将永久代替换为Metaspace: 88 | 1. 字符串在永久代中,容易出现性能问题和内存溢出; 89 | 2. 类及方法等比较难确定其大小,因此对于永久代的大小指定比较困难,太小容易出现永久代溢出,太大则容易导致老年代溢出; 90 | 3. 永久代会为GC带来不必要的复杂度,并且回收效率偏低; 91 | 4. Oracle可能会将HotSpot与JRockit合二为一,JRockit没有所谓的永久代。 92 | 93 | -XX:+PrintGCApplicationStoppedTime 94 | -XX:+PrintSafepointStatistics 95 | -XX:PrintSafepointStatisticsCount=1 96 | 97 | 内存屏障是对CPU的一种特殊锁定指令,它禁止指令在该屏障上重新排序。 98 | 在内存-多级缓存的今天,一定存在一个层次,在这个层次下,数据的变化是同步的,即所有CPU看到的都是相同的状态,任意一个CPU的修改其他CPU都能看到。在这个层次之上,每个CPU有自己独享的缓存,这些缓存对其他CPU是不可见的。内存屏障的目的是通知CPU让其将自己的缓存和共享的缓存/内存做一次同步,以便其他CPU可以感知到这些变化。 99 | 内存屏障控制是本CPU独享缓存和共享缓存/内存的同步工作,而不关心和其他的CPU是否同步。如果其他CPU也想和共享内存同步,需要自己来触发内存屏障。 100 | 101 | 在执行程序时,为了提高性能,编译器和处理器常常会对指令做重排序。重排序分3种类型,分别为编译器优化重排序、指令级并行重排序和内存系统重排序。 102 | 103 | 编译器优化的重排序:编译器在不改变单线程程序语义的前提下,可以重新安排语句的执行顺序。 104 | 指令级并行的重排序:现在处理器采用了指令级并行技术(Instruction-Level Parallelism, ILP)来将多条指令重叠执行。如果不存在数据依赖性,处理器可以改变语句对应机器指令的执行顺序。 105 | 内存系统的重排序:由于处理器采用缓存和读写缓冲区,这使得加载和存储操作看上其可能是在乱序执行。 106 | 107 | mapreduce.map.memory.mb:一个Map Task可使用的内存上限,默认为1024。如果Map Task实际使用的内存超过该值,则会被强制杀死。 108 | mapreduce.task.io.sort.mb:map输出的环形缓冲区的大小,默认是100M,应尽量调大该值。 109 | mapreduce.map.sort.spill.percent:环形缓冲区中的值占比达到多少就溢出到磁盘,默认80%。 110 | mapreduce.task.io.sort.factor:一次合并溢出文件的数量,可以调大这个值,减少磁盘IO 111 | mapreduce.map.output.compress:是否对map端的输出进行压缩,默认关闭 112 | mapreduce.map.output.compress.codec:map端进行压缩默认使用的压缩器 113 | mapreduce.reduce.memory.mb : 114 | mapreduce.reduce.shuffle.palallelcopies: reduce去map拿数据时,默认开启的线程数 115 | mapreduce.reduce.shuffle.merge.percent: reduce的buffer中数据达到多少比例开始写入磁盘 116 | mapreduce.reduce.shuffle.input.buffer.percent: buffer大小占reducer可用内存的比例 117 | mapreduce.reduce.input.buffer.percent:指定多少比例的内存用来存放buffer中的数据,默认值是0,也就是默认从map端复制过来的数据都会持久化到reduce的磁盘上。 118 | 119 | Spark支持所有类型的join, 包括: 120 | 1. inner join; 121 | 2. left outer join; 122 | 3. right outer join; 123 | 4. full outer join; 124 | 5. left semi join; 125 | 6. left anti join; 126 | 127 | Hive调优措施: 128 | 1. hive.limit.optimize.enable=true; --开启对数据源进行采样的功能 129 | hive.limit.row.max.size --设置最小的采样容量 130 | hive.limit.optimize.limit.file --设置最大的采样样本数 131 | 2. join优化 132 | 2.1 将大表放在后头 133 | 2.2 使用相同的连接键:当对三个或者更多个表进行join连接时,如果每个on子句都使用相同的连接键的话,那么只会产生一个MapReduce Job。 134 | 2.3 尽量尽早地过滤数据:减少每个阶段的数据量,对于分区表要加分区,同时只选择使用到的字段。 135 | 2.4 尽量原子化操作:尽量避免一个SQL包含复杂逻辑,可以使用中间表来完成复杂的逻辑。 136 | 3. 本地模式; set hive.exec.mode.local.auto=true; 137 | 当一个job满足 138 | 4. 并行执行;Hive会将一个查询转化为一个或多个阶段,包括:MapReduce阶段、抽样阶段、合并阶段、limit阶段等。默认情况下,一次只执行一个阶段。不过,如果某些阶段不是互相依赖,是可以并行执行的。 139 | set hive.exec.parallel=true;可以开启并发执行 140 | set hive.exec.parallel.thread.number=16;同一个sql允许最大并行度,默认为8.会比较消耗系统资源 141 | 5. strict模式;对分区表进行查询,在where子句中没有加分区过滤的话,将禁止提交任务(默认:nonstrict). 142 | set hive.mapred.mode=strict; 143 | 注:使用严格模式可以禁止三种类型的查询: 144 | (1). 对于分区表,不加分区字段过滤条件,禁止执行; 145 | (2). 对于order by语句,必须使用limit语句; 146 | (3). 限制笛卡尔积的查询(join的时候不使用on,而使用where) 147 | 6. 调整mapper和reducer个数 148 | set hive.input.format=org.apache.hadoop.hive.ql.io.CombineHiveInputFormat;这个参数表示执行前进行小文件合并。 149 | 150 | 7. JVM重用;用于避免小文件的场景或者task特别多的场景,这类场景大多数执行时间都很短,因为hive调起mapreduce任务,JVM的启动过程会造成很大的开销,尤其是job有成千上万个task任务时,JVM重用可以使得JVM实例在同一个job中重新使用N次。 151 | set mapred.job.reuse.jvm.num.tasks=10; --10为重用个数; 152 | 8. 动态分区调整;hive.exec.dynamic.partition=true;表示开启动态分区功能; 153 | 9. 推测执行;通过加快获取单个task的结果以及进行侦测将执行慢的TaskTracker加入到黑名单的方式来提高整体的任务执行效率。 154 | set hive.mapred.reduce.tasks.speculative.execution=true; 155 | 10. 数据倾斜; 156 | 11. 其他参数调优; 157 | set hive.cli.print.current.db=true; 158 | set hive.cli.print.header=true; 159 | set mapred.job.name=***; 设置任务名称,方便查找监控 160 | set hive.map.aggr=true; 设置map端聚合操作 161 | set hive.groupby.skewindata=true; 有数据倾斜时,进行负载均衡 162 | set hive.fetch.task.conversion=true;对于简单不需要聚合,类似select * from t limit n语句,不需要发起MapReduce,直接通过Fetch task获取数据。 163 | 12. 小文件问题。 164 | 12.1 使用Sequencefile作为存储格式,不用textfile,在一定程度上减少小文件; 165 | 12.2 减少reduce的数量(可以使用参数控制); 166 | 12.3 少用动态分区,用时记得按distribute by分区; 167 | 对于已有的小文件,可以通过下面方案解决: 168 | 1. 使用hadoop archive命令对小文件进行归档; 169 | 2. 重建表,建表时减少reduce数量。 170 | 3. 通过参数进行调节,设置map/reduce端的相关参数。 171 | 172 | Hive数据倾斜解决方案: 173 | 1. 常规处理:将热点key进行单独处理,然后将结果进行合并; 174 | 2. map join; 175 | 3. Reduce阶段最容易出现数据倾斜的两个场景:join和count distinct。调整下列参数可有效缓解: 176 | set hive.map.aggr=true; --map端的Combiner 177 | set hive.groupby.skewindata=true; 178 | 解决数据倾斜首先要进行负载均衡,将上面两个参数设置为true,而MapReduce会生成两个额外的MR Job,这两个任务的主要操作如下: 179 | 第一步:MR Job中Map输出的结果集首先会随机分配到Reduce中,然后每个Reduce做局部聚合操作并输出结果,这样处理的原因是相同的group by key有可能被分发到不同的reduce job中,从而达到负载均衡的目的; 180 | 第二步:MR Job再根据处理的数据结果按照group by key分布到reduce中(这个过程可以保证相同的group by key被分布到一个reduce中),最后完成聚合操作。 181 | 4. 将小表放入子查询; 182 | 5. 关联字段去重。把字段为null的记录,在子查询中直接过滤。 183 | 6. count distinct的优化。比如select day, count(distinct id) as id_cnt from tb group by day;用如下方法替换: 184 | select day, count(1) as id_cnt from ( 185 | select day, id from tb group by day, id 186 | ) t group by day. 187 | 7. 将热点数据导入到一个临时表,每条数据都放大N倍。 188 | 189 | Hive存储文件格式: 190 | 1. TextFile; 191 | 2. RCFile; 192 | 3. ORCFile; 存储方式:数据按行分块,每块按照列存储。压缩块,可切分,快速列存取。 193 | 4. SequenceFile; 194 | 5. Parquet; 195 | 6. ORC; 196 | 7. Avro 197 | 8. 自定义格式; inputformat outputformat 198 | 199 | Hive在执行一条HQL的时候,会经过以下步骤: 200 | 1. 语法解析:Antlr定义SQL的语法规则,完成SQL词法、语法解析,将SQL转化成抽象语法树AST; 201 | 2. 语义解析:遍历AST,抽象出查询的基本组成单元QueryBlock; 202 | 3. 生成逻辑执行计划:遍历QueryBlock,翻译为执行操作树OperatorTree; 203 | 4. 优化逻辑执行计划:逻辑层优化器进行OperatorTree变换,合并不必要的ReduceSinkOperator,减少shuffle数据量; 204 | 5. 生成物理执行计划:遍历OperatorTree,翻译为MapReduce任务; 205 | 6. 优化物理执行计划:物理层优化器进行MapReduce任务的变化,生成最终的执行计划。 206 | 207 | 怎么优化websocket之间的交互数据量很大: 208 | 1. 数据压缩; 209 | 2. 本地缓存(注意数据有效期和数据一致性); 210 | 3. 如果是每次数据都在客户端和服务器来回无效的传输,可以参考Http2,使用编码节省带宽; 211 | 4. CDN; 212 | 5. 使用Http2/Http3/quic协议代替; 213 | 214 | Spark SQL中支持的Join类型主要包括Inner, FullOuter, LeftOuter, RightOuter, LeftSemi, LeftAnti和Cross共7种。对应的查询关键字如下: 215 | 查询关键字 Join类型 216 | inner Inner 217 | outer|full|fullouter FullOuter 218 | leftouter|left LeftOuter 219 | rightouter|right RightOuter 220 | leftsemi LeftSemi 221 | leftanti LeftAnti 222 | cross Cross 223 | 224 | 目前在Spark SQL中,Join的执行方式主要有BroadcastHashJoinExec, ShuffledHashJoinExec, SortMergeJoinExec, BroadcastNestedLoopJoinExec和CartesianProductExec这五种。 225 | 226 | Spark的shuffle的实现方式有两种:Hash Shuffle和Sort-based Shuffle. 227 | --------------------------------------------- 228 | 在划分stage时,最后一个stage称为FinalStage,它本质上是一个ResultStage对象,前面的所有stage被称为ShuffleMapStage。 229 | ShuffleMapStage的结束伴随着shuffle文件的写磁盘。 230 | ResultStage基本上对应代码中的action算子,即将一个函数应用在RDD的各个partition的数据集上,意味着一个job的运行结束。 231 | --------------------------------------------- 232 | 对于Shuffle Write, Spark当前有三种实现,具体分别为BypassMergeSortShuffleWriter, UnsafeShuffleWriter和SortShuffleWriter。 233 | 234 | --------------------------------------------- 235 | Spark内存模型: 236 | 在Spark 1.5及之前版本中,内存管理默认实现是StaticMemoryManager,称为静态内存管理。 237 | 从Spark 1.6.0版本开始,Spark默认采用一种新的内存管理模型UnifiedMemoryManager,称为统一内存管理,其特点是可以动态调整Execution和Storage的内存,因此又称为动态内存管理。 238 | 239 | Spark的内存使用主要分为两类:Execution Memory和Storage Memory。其中Execution Memory主要用于计算,比如shuffles, joins, sorts以及aggregations等操作。Storage Memory主要用于cache数据和在集群内部传输数据。 240 | Executor默认只使用堆内内存(On-heap Memory)。为了进一步优化内存的使用,Spark引入了堆外内存(Off-heap Memory),默认是关闭状态。 241 | 242 | Spark Executor通过spark.executor.memory或--executor-memory配置的内存为堆内内存,可以分为以下四块区域: 243 | 1. Execution Memory:主要用于shuffles、joins、sorts及aggregations等计算操作,又称为Shuffle Memory。 244 | 2. Storage Memory:主要用于cache数据、unroll数据,有时也被称为Cache Memory。 245 | 3. User Memory:用户内存,主要用于存储内部元数据、用户自定义的数据结构等,根据用户实际定义进行使用。 246 | 4. Reserved Memory:默认300M的系统预留内存,主要用于程序运行。 247 | --------------------------------------------- 248 | RDD特性: 249 | 1. A list of partitions; 250 | 2. A function for computing each split; 251 | 3. A list of dependencies on other RDDs; 252 | 4. Optionally, a Partitioner for key-value RDDs (e.g. to say that the RDD is hash-partitioned); 253 | 5. Optionally, a list of preferred locations to compute each split on (e.g.block locations for an HDFS file); 254 | 255 | Java中对象地址操作主要使用Unsafe调用了C的allocate和free两个方法,分配方法有两种: 256 | 1. 空闲链表(free list): 通过额外的存储记录空闲的地址,将随机IO变为顺序IO,但带来了额外的空间消耗。 257 | 2. 碰撞指针(bump pointer): 通过一个指针作为分界,需要分配内存时,仅需把指针往空闲的一端移动与对象大小相等的距离,分配效率较高,但使用场景有限。 258 | 259 | -XX:+PrintReferenceGC:观察系统的软引用,弱引用,虚引用等回收情况。 260 | 261 | Spark中RDD的高效与DAG图有着很大关系,在DAG调度中需要对计算过程划分stage,而划分依据就是RDD之间的依赖关系。 262 | 针对不同的转换函数,RDD之间的依赖分为窄依赖(narrow dependency)和宽依赖(wide dependency,也称为shuffle dependency)。 263 | 窄依赖是指父RDD的每个分区只被子RDD的一个分区使用,子RDD分区通常对应常数个父RDD分区(O(1),与数据规模无关)。 264 | 宽依赖是指父RDD的每个分区都可能被多个子RDD分区所使用,子RDD分区通常对应所有的父RDD分区(O(n),与数据规模有关) 265 | 266 | 相比于宽依赖,窄依赖对优化很有利,主要基于以下两点: 267 | 1. 宽依赖往往对应着shuffle操作,需要在运行过程中将同一个父RDD的分区传入到不同的子RDD分区中,中间可能涉及多个节点之间的数据传输;而窄依赖的每个父RDD的分区只会传入到一个子RDD分区中,通常可以在一个节点内完成转换。 268 | 2. 当RDD分区丢失时(某个节点故障),Spark会对数据进行重算。 269 | - 对于窄依赖,由于父RDD的一个分区只对应一个子RDD分区,这样只需要重算和子RDD分区对应的父RDD分区即可,所以这个重算对数据的利用率是100%的; 270 | - 对于宽依赖,重算的父RDD分区对应多个子RDD分区,这样实际上父RDD 中只有一部分的数据是被用于恢复这个丢失的子RDD分区的,另一部分对应子RDD的其它未丢失分区,这就造成了多余的计算;更一般的,宽依赖中子RDD分区通常来自多个父RDD分区,极端情况下,所有的父RDD分区都要进行重新计算。 271 | 272 | 首先,窄依赖允许在一个集群节点上以流水线的方式(pipeline)计算所有父分区。例如,逐个元素地执行map、然后filter操作;而宽依赖则需要首先计算好所有父分区数据,然后在节点之间进行Shuffle,这与MapReduce类似。 273 | 第二,窄依赖能够更有效地进行失效节点的恢复,即只需重新计算丢失RDD分区的父分区,而且不同节点之间可以并行计算;而对于一个宽依赖关系的Lineage图,单个节点失效可能导致这个RDD的所有祖先丢失部分分区,因而需要整体重新计算。 274 | 275 | Spark比Hive快的原因: 276 | 1. Spark的Job输出结果保存在内存中,而MapReduce输出结果保存在磁盘中; 277 | 2. Spark以多线程模式运行,而MapReduce以进程模式运行,进程的申请释放需要消耗更多资源; 278 | 3. Spark提供了更多算子,便于DAG,特别是窄依赖,形成pipeline。 279 | 4. MapReduce通常需要将计算的结果写入磁盘,然后还需要读取磁盘,从而导致频繁的磁盘IO。 280 | 281 | Spark SQL原理流程: 282 | 第一步: 原始的DataFrame, Dataset, SQL; 283 | 第二步: 经过Parser后,形成Unresolved Logical Plan; 284 | 第三步: 经过Analyzer/Catalog后,形成Resolved Logical Plan; 285 | 第四步: 经过基于RBO和CBO的Optimizer后,形成Optimized Logical Plan; 286 | 第五步: 经过Query Planner后,形成Physical Plan; 287 | 第六步: Cost Model; 288 | 第七步: Selected Physical Plan; 289 | 第八步: 经过AE之后,形成RDDs和DAG; 290 | 291 | 一条SQL提交之后会被Parser解析并转化为Unresolved Logical Plan。它的重点是Logical Plan即逻辑计划,它描述了希望做什么样的查询。Unresolved是指该查询相关的一些信息未知,比如不知道查询的目标表的Schema以及数据位置。 292 | 上述信息存于Catalog内。在生产环境中,一般由Hive Metastore提供Catalog服务。Analyzer会结合Catalog将Unresolved Logical Plan转换为 Resolved Logical Plan。 293 | 到这里还不够。不同的人写出来的SQL不一样,生成的Resolved Logical Plan也就不一样,执行效率也不一样。为了保证无论用户如何写SQL 都可以高效的执行,Spark SQL需要对Resolved Logical Plan进行优化,这个优化由Optimizer完成。Optimizer包含了一系列规则,对Resolved Logical Plan进行等价转换,最终生成Optimized Logical Plan。该Optimized Logical Plan不能保证是全局最优的,但至少是接近最优的。 294 | 上述过程只与SQL有关,与查询有关,但是与Spark无关,因此无法直接提交给Spark执行。Query Planner负责将Optimized Logical Plan转换为 Physical Plan,进而可以直接由Spark执行。 295 | 由于同一种逻辑算子可以有多种物理实现。如Join 有多种实现,ShuffledHashJoin、BroadcastHashJoin、BroadcastNestedLoopJoin、SortMergeJoin等。因此Optimized Logical Plan可被Query Planner转换为多个Physical Plan。如何选择最优的Physical Plan成为一件非常影响最终执行性能的事情。一种比较好的方式是,构建一个Cost Model,并对所有候选的Physical Plan应用该Model并挑选Cost最小的Physical Plan作为最终的Selected Physical Plan。 296 | Physical Plan可直接转换成RDD由Spark执行。我们经常说“计划赶不上变化”,在执行过程中,可能发现原计划不是最优的,后续执行计划如果能根据运行时的统计信息进行调整可能提升整体执行效率。这部分动态调整由Adaptive Execution完成。 297 | 298 | -------------------------------------------------------------------------------- /202101.txt: -------------------------------------------------------------------------------- 1 | Hive: 2 | create table tbl_3( 3 | name string, 4 | friends array, 5 | children map, 6 | address struct 7 | ) row format delimited fields terminated by ',' 8 | collection items terminated by '_' 9 | map keys terminated by ':'; 10 | 11 | Hive中的group by, distribute by, order by, sort by: 12 | 1. group by:对字段进行分区,将相同字段进行分区,后面会接聚合操作; 13 | 2. distribute by:仅对字段进行分区; 14 | 3. order by: 全局排序,只会起一个reducer; 15 | 4. sort by: 局部排序,若设置reducer个数为1,则结果和order by一致。 16 | 17 | Hive中的collect_set()函数通常搭配group by使用,将每组的数据收集(collect)起来封装成一个集合。 18 | Hive中的explode()函数将一行数据转换成列,在hive中只能用于array和map数据类型。 19 | 20 | MySQL中LRU List中young list和old list的维护: 21 | 当LRU List链表大于512(BUF_LRU_OLD_MIN_LEN)时,在逻辑上被分为两部分,前面部分存储最热的数据页,这部分链表称作young list,后面部分则存储冷数据页,这部分称作old list,一旦Free List中没有页面了,就会从冷页面中驱逐。两部分的长度由参数innodb_old_blocks_pct控制。每次加入或者驱逐一个数据页后,都要调整young list和old list的长度(buf_LRU_old_adjust_len),同时引入BUF_LRU_OLD_TOLERANCE来防止链表调整过频繁。当LRU List链表小于512,则只有old list。 新读取进来的页面默认被放在old list头,在经过innodb_old_blocks_time后,如果再次被访问了,就挪到young list头上。一个数据页被读入Buffer Pool后,在小于innodb_old_blocks_time的时间内被访问了很多次,之后就不再被访问了,这样的数据页也很快被驱逐。这个设计认为这种数据页是不健康的,应该被驱逐。 此外,如果一个数据页已经处于young list,当它再次被访问的时候,不会无条件的移动到young list头上,只有当其处于young list长度的1/4(大约值)之后,才会被移动到young list头部,这样做的目的是减少对LRU List的修改,否则每访问一个数据页就要修改链表一次,效率会很低,因为LRU List的根本目的是保证经常被访问的数据页不会被驱逐出去,因此只需要保证这些热点数据页在头部一个可控的范围内即可。相关逻辑可以参考函数buf_page_peek_if_too_old。 22 | 23 | 结合MongoDB文档类型内嵌数组、文档的支持,目前的单文档事务能满足绝大部分开发者的需求。为了让MongoDB能适应更多的应用场景,让开发变得更简单,MongoDB4.0将支持复制集内部跨一或多个集合的多文档事务,保证针对多个文档的更新的原子性。而在未来MongoDB4.2版本,还会支持分片集群的分布式事务。 24 | 25 | MongoDB中所有的写操作在单一文档层级上是原子的。 26 | 27 | HBase Features: 28 | 1. Linear and modular scalability. 29 | 2. Strictly consistent reads and writes. 30 | 3. Automatic and configurable sharding of tables. 31 | 4. Automatic failover support between RegionServers. 32 | 5. Convenient base classes for backing Hadoop MapReduce jobs with Apache HBase tables. 33 | 6. Easy to use Java API for client access. 34 | 7. Block cache and Bloom Filters for real-time queries. 35 | 8. Query predicate push down via server side Filters. 36 | 9. Thrift gateway and a REST-ful Web service that supports XML, Protobuf, and binary encoding options. 37 | 10. Extensible jruby-based(JIRB) shell. 38 | 11. Support for exporting metrics via the Hadoop metrics subsystem to files or Ganglia;or via JMX. 39 | 40 | ------------------------------------------ 41 | HBase最佳实践之write写入优化: 42 | HBase基于LSM模式,写是写HLOG及Memory的,也就是基本没有随机的IO,所以在写链路上性能高效且比较平稳。很多时候,写都是用可靠性来换取性能。 43 | 客户端优化: 44 | 1. 批量写:为了减少rpc的次数 45 | HTable.put(List); 46 | 2. Auto Flush: autoflush=false可以提升几倍的写性能,但是,直到数据超过2M(hbase.client.write.buffer决定)或用户执行了hbase.flushcommits()时才向region server提交请求。需要注意并不是写到了远端。 47 | HTable.setWriteBufferSize(writeBufferSize)可以设置buffer的大小。 48 | 服务端优化: 49 | 1. WAL Flag: 不写WAL可以成倍提升性能,因为不需要写HLog,减少3次IO,写MemStore是内存操作是以数据可靠性为代价的,在数据导入时,可以关闭WAL。 50 | 2. 增大memstore的内存:当前可用调高Memstore的数值,降低BlockCache的数,跟读优化的思路正好相反。 51 | 3. 大量的HFile产生:如果写很快,很容易带来大量的HFile,因为此时HFile合并的速度还没有写入的速度快。需要在业务低峰期做major compaction,充分利用系统资源;如果HFile降低不下来,则需要添加节点。 52 | ------------------------------------------ 53 | 54 | Linux查看负载的几个命令: top, w, uptime, vmstat; 55 | 56 | Linux查看大小最大的文件: 57 | 1. ls -lhS|head -5 58 | 2. find ./ -type f -printf '%s\t%p\n' | sort -n|tail -5 59 | 3. du -Sh|sort -rh|head -5 60 | 61 | MyBatis中的flushCache:将其设置为true后,只要语句被调用,都会导致本地缓存和二级缓存被清空。对select来说默认值:false,对insert,update和delete语句来说默认值是true。 62 | 63 | localCacheScope: MyBatis利用本地缓存机制(Local Cache)防止循环引用和加速重复的嵌套查询。默认值为SESSION,会缓存一个会话中执行的所有查询。若设置值为STATEMENT,本地缓存将仅用于执行语句,对相同SqlSession的不同查询将不会进行缓存。 64 | 65 | MyBatis关闭一级缓存的三种方法: 66 | 1. 全局关闭:设置mybatis.configuration.local-cache-scope=statement; 67 | 2. 指定mapper关闭:在mapper.xml的指定statement上标注flushCache="true"; 68 | 3. 在statement的SQL上添加一个随机数/串:select * from tbl where #{random}=#{random} 69 | 70 | RF(随机森林)与GBDT之间的区别与联系: 71 | 相同点: 72 | 1. 都是由多棵树组成,最终的结果都是由多棵树一起决定; 73 | 2. RF和GBDT在使用CART树时,可以是分类树或者回归树。 74 | 不同点: 75 | 1. 组成随机森林的树可以并行生成,而GBDT是串行生成; 76 | 2. 随机森林的结果是多数表决的,而GBDT则是多棵树累加之和; 77 | 3. 随机森林对异常值不敏感,而GBDT对异常值比较敏感; 78 | 4. 随机森林是减少模型的方差,而GBDT是减少模型的偏差; 79 | 5. 随机森林不需要进行特征归一化。而GBDT则需要进行特征归一化。 80 | 81 | 查看docker镜像分层的方式可以通过docker image inspect命令。 82 | ------------------------------------------ 83 | Spring-tx模块: 84 | AOP-based solution for declarative transaction demarcation. 85 | Builds on the AOP infrastructure in org.springframework.aop.framework. 86 | Any POJO can be transactionally advised with Spring. 87 | 88 | The TransactionFactoryProxyBean can be used to create transactional AOP proxies transparently to code that uses them. 89 | 90 | The TransactionInterceptor is the AOP Alliance MethodInterceptor that delivers transactional advice, based on the Spring transaction abstraction. 91 | This allows declarative transaction management in any environment, even without JTA if an application uses only a single database. 92 | ------------------------------------------ 93 | Eureka的服务下线的即时推送功能: 94 | 0. 先下线服务,有个控制台,直接指定下线实例; 95 | 1. ShutdownHook还有k8s的preKill,直接往Eureka Server下线; 96 | 2. Eureka Server接收到下线后,往Eureka Client发送Http Request告知已经下线; 97 | 3. Eureka Client将下线的服务临时存储在ConcurrentHashMap,在下次拉取全量后,进行清理; 98 | 4. Eureka Client扩展ZoneAwareLoadBalancer,在选择Server时,排除掉临时存储的Server; 99 | 5. 为了减少下线通知广播,设置标志,每个服务注册时,提供自己关注的服务列表,通知时,仅通知被依赖服务列表。 100 | 101 | 监控及巡检系统负责提升稳定性。 102 | 103 | Hive支持的文件类型有:TextFile, SequenceFile, RCFile, ORCFile, Parquet, Avro。 104 | --------------------------- 105 | create table student_text_lzo(id string, name string) 106 | row format delimited 107 | fields terminated by ',' 108 | lines terminated by '\n' 109 | stored as textfile; 110 | -- 设置为LZO压缩 111 | set hive.exec.compress.output=true; 112 | set mapred.output.compress=true; 113 | set mapred.output.compression.codec=com.hadoop.comresssion.lzo.LzopCodec; 114 | -- 导入数据 115 | insert overwrite table student_text_lzo select * from student; 116 | -- 查询数据 117 | select * from student_text_lzo; 118 | --------------------------- 119 | Hive中的视图和RDBMS中视图的概念一致,都是一组数据的逻辑表示,本质上就是一条select语句的结果集。视图是纯粹的逻辑对象,没有管理的存储(Hive 3.0.0引入的物化视图除外),当查询引用视图时,Hive可以将视图的定义与查询结合起来,例如将查询中的过滤器推送到视图中。 120 | --------------------------------------------- 121 | Apache-servicecomb-pack: 122 | 目前Omega支持以下形式的隐式事务上下文传递: 123 | 1. omega-transport-{dubbo, feign, resttemplate, servicecomb}。需要将相关的传输组件添加到class path中,否则相关的全局事务ID是无法在服务调用过程中传递。 124 | 2. 同线程内调用(基于OmegaContext的ThreadLocal字段); 125 | 3. 标注了@OmegaContextAware的java.util.concurrent.Executor{Service}。 126 | 如果无法隐式传递事务上下文怎么办?比如Service A使用某种RPC机制来调用Service B,无法注入或提取事务上下文信息。这个时候只能采用显式的方式把事务上下文传递出去。ServiceComb Pack从0.5.0开始提供了两个类来实现这一点。 127 | --------------------------------------------- 128 | MyBatis允许在映射语句执行过程中的某一点进行拦截调用。默认情况下,MyBatis允许使用插件来拦截方法调用包括: 129 | 1. Executor(update, query, flushStatement, commit, rollback, getTransaction, close, isClosed); 130 | 2. ParameterHandler(getParameterObject, setParameters); 131 | 3. ResultSetHandler(handleResultSets, handleOutputParameters); 132 | 4. StatementHandler(prepare, parameterize, batch, update, query); 133 | 134 | Hive中的窗口函数:NTILE,用于将分组数据按照顺序切分成n片,并返回当前切片值。如果切片不均匀,默认增加第一个切片的分布。 135 | 136 | ------------------------------------- 137 | JDK中所有的BlockingQueue的实现如下: 138 | public interface BlockingQueue extends Queue {...} 139 | 140 | public class ArrayBlockingQueue extends AbstractQueue 141 | implements BlockingQueue, java.io.Serializable {...} 142 | 143 | public class DelayQueue extends AbstractQueue 144 | implements BlockingQueue {...} 145 | 146 | ScheduledThreadPoolExecutor的内部类: 147 | public class ScheduledThreadPoolExecutor extends ThreadPoolExecutor 148 | implements ScheduledExecutorService { 149 | ... 150 | static class DelayedWorkQueue extends AbstractQueue 151 | implements BlockingQueue { 152 | ... 153 | } 154 | ... 155 | } 156 | 157 | public class LinkedBlockingQueue extends AbstractQueue 158 | implements BlockingQueue, java.io.Serializable {...} 159 | 160 | public class PriorityBlockingQueue extends AbstractQueue 161 | implements BlockingQueue, java.io.Serializable {...} 162 | 163 | public class SynchronousQueue extends AbstractQueue 164 | implements BlockingQueue, java.io.Serializable {...} 165 | 166 | public interface BlockingDeque extends BlockingQueue, Deque {...} 167 | 168 | public class LinkedBlockingDeque extends AbstractQueu 169 | implements BlockingQueue, java.io.Serializable {...} 170 | 171 | public interface TransferQueue extends BlockingQueue {...} 172 | 173 | public class LinkedTransferQueue extends AbstractQueue 174 | implements TransferQueue, java.io.Serializable {...} 175 | ------------------------------------- 176 | Reidis中的ZSet的实现数据结构:可以是ziplist或者skiplist。同时满足以下条件时使用ziplist编码: 177 | 1. 元素数量小于128个; 178 | 2. 所有member的长度都小于64子节; 179 | 不能满足上面两个条件使用skiplist编码。以上两个条件也可以通过Redis配置文件zset-max-ziplist-entries选项和zset-max-ziplist-value进行修改。 180 | 对于一个REDIS_ENCODING_ZIPLIST编码的ZSet,只要不满足以上任一条件,则会转换为REDIS_ENCODING_SKIPLIST编码。 181 | 182 | 虚拟机规范定义的属性有很多,比如Code, LineNumberTable, LocalVariableTable, SourceFile等等。 183 | 184 | 本地缓存怎么优化空间: 185 | 1. 对于地理位置信息,可使用专门的优化存储; 186 | 2. 如果存储内容重复比较多,可以考虑用哈夫曼编码; 187 | 3. 压缩存储; 188 | 4. 在保证不影响语义的前提下,尽量简化存储内容; 189 | 5. 对于某些二值的场景,可以考虑用BitSet; 190 | 6. 某些场景下的布隆过滤器; 191 | 192 | ParNew收集器是垃圾收集器的一种,它是Serial收集器的多线程版本,除了使用多线程进行垃圾收集之外,其余行为包括Serial收集器可用的所有控制参数(例如:-XX:SurivivorRatio, -XX:PretenureSizeThreshold,-XX:HandlePromotiionFailure等)、收集算法、STW、对象分配规则、回收策略等都有Serial收集器一致。 193 | ParNew是许多运行在Server模式下的虚拟机中首选的新生代收集器,除了Serial收集器外,只有它能CMS收集器配合工作。 194 | 195 | 如何减少full gc 196 | 1. 元空间增大; 197 | 2. 老年代空间增大; 198 | 3. 禁止或者少使用System.gc(); 199 | 4. 使用标记-整理算法,尽量让连续空间保持最大; 200 | 5. 排查代码中的无用大对象(内存泄漏); 201 | 6. 其他实际的措施; 202 | 203 | 服务提供方不稳定,频繁变动如何提升自身稳定性: 204 | 1. 强依赖->弱依赖->不依赖; 205 | 2. 在不影响业务的前提下,从实时交易,异步交易,批量交易; 206 | 3. 同步交易改成异步交易; 207 | 4. 流控/降级/兜底方案; 208 | 209 | --------------- 210 | 使用方法:hadoop fs -setrep [R] 211 | 说明:改变一个副本的复制份数; 212 | 示例:hadoop fs -setrep -w 3 -R /user/file 213 | --------------- 214 | 数据加工链路的业界的分层理念,包括操作数据层(Operational Data Store, ODS)、明细数据层(Data Warehouse Detail, DWD)、汇总数据层(Data WareHouse Summary, DWS)和应用数据层(Application Data Store, ADS)。通过数据仓库不同层次之间的加工过程实现从数据资产向信息资产的转化,并且对整个过程进行有效的元数据管理及数据质量处理。 215 | 216 | public class ApringApplication { 217 | public static final String DEFAULT_CONTEXT_CLASS = "org.springframework.context." 218 | + "annotation.AnnotationConfigApplicationContext"; 219 | public static final String DEFAULT_SERVELET_WEB_CONTEXT_CLASS = "org.springframework." 220 | + "boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext"; 221 | public static final String DEFAULT_REACTIVE_WEB_CONTEXT_CLASS = "org.springframework." 222 | + "boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext"; 223 | public static final String BANNER_LOCATION_PROPERTY_VALUE 224 | = SpringApplicationBannerPrinter.DEFAULT_BANNER_LOCATION;; 225 | public static final String BANNER_LOCATION_PROPERTY 226 | = SpringApplicationBannerPrinter.BANNER_LOCATION_PROPERTY; 227 | private static final String SYSTEM_PROPERTY_JAVA_AWT_HEADLESS = "java.awt.headless"; 228 | ... 229 | private Set> primarySources; 230 | private Set sources = new LinkedHashSet<>(); 231 | private Class mainApplicationClass; 232 | private Banner.Mode bannerMode = Banner.Mode.CONSOLE; 233 | private boolean logStartupInfo = true; 234 | private boolean addCommandLineProperties = true; 235 | private boolean addConversionService = true; 236 | private Banner banner; 237 | private ResourceLoader resourceLoader; 238 | private BeanNameGenerator beanNameGenerator; 239 | private ConfigurableEnvironment environment; 240 | private Class applicationContextClass; 241 | private WebApplicationType webApplicationType; 242 | private boolean headless = true; 243 | private boolean registerShutdownHook = true; 244 | private List> initializers; 245 | private List> listeners; 246 | private Map defaultProperties; 247 | private Set additionalProfiles = new HashSet<>(); 248 | private boolean allowBeanDefinitionOverriding; 249 | private boolean isCustomEnvironment = false; 250 | ... 251 | } 252 | 253 | 由于数据切分后数据join难度很大,可参考以下最佳实践: 254 | 第一原则:能不切分尽量不要切分; 255 | 第二原则:如果要切分一定要选择合适的切分规则,提前规划好。 256 | 第三原则:数据切分尽量通过数据冗余或者表分组(Table Group)来降低跨库join的可能; 257 | 第四原则:由于数据库中间件对join实现的优劣难以把握,而且实现高性能难度极大,业务读取尽量少使用多表join。 258 | 259 | The ResouceManager has two main components: Scheduler and ApplicationManager. 260 | 261 | Yarn被设计成可以允许应用程序(通过ApplicationMaster)以共享的、安全的以及多租户的方式使用集群资源。它也会感知集群的网络拓扑,以便可以有效地调度以及优化数据访问(即尽可能地为应用减少数据移动)。为了达成这些目标,位于ResourceManager内的中心调度器保存了应用程序的资源需求信息,以帮助它为集群中的所有应用作出更优的调度决策。由此引出了ResouceRequest以及由此产生的Container概念。 262 | 263 | Yarn依赖于三个主要组件来实现所有功能。第一个组件是ResourceManager(RM),是集群资源的仲裁者,它有两部分:一个可插拔的调度器和一个ApplicationManager,用于管理集群中的用户作业。第二个组件是位于每个节点上的NodeManager(NM),管理该节点上的用户作业和工作流。中央的ResourceManager和所有NodeManager创建了集群统一的计算基础设施。第三个组件是ApplicationMaster,用户作业生命周期的管理者。ApplicationMaster是用户应用程序驻留的地方。这三个组件共同提供了一个可扩展的、灵活的、高效的环境,来运行各种类型的大数据处理作业。 264 | 265 | MySQL崩溃恢复:用户修改了数据,并且收到了成功的消息,然而对数据库来说,可能这个时候修改后的数据还没有落盘,如果这个时候数据库挂了,重启后,数据库需要从日志中把这些修改后的数据给捞出来,重新写入磁盘,保证用户的数据不丢。这个从日志中捞数据的过程就是崩溃恢复的主要任务,也可以称为数据库前滚。当然,在崩溃恢复中还需要回滚没有提交的事务,提交没有提交的事务。由于回滚操作需要undo日志的支持,undo日志的完整性和可靠性需要redo日志来保证,所以崩溃恢复先做redo前滚,然后做undo回滚。 266 | ----------------------------------------------------- 267 | A:Atomicity 268 | C:Consistency 269 | I:Isolation 270 | D:Durability 271 | 在MySQL中是怎么实现ACID的: 272 | 1. 一致性:从两个层面来说,从数据库层面,数据库通过原子性、隔离性、持久性来保证一致性。也就是说ACID四大特性之中,C(一致性)是目的,AID(原子性、隔离性、持久性)是手段,是为了保证一致性数据库提供的手段。数据库必须要实现AID三大特性,才有可能实现一致性。例如,原子性无法保证,显然一致性也无法保证。 273 | 但是,如果在业务里故意写违反约束的代码,一致性还是无法保证的。例如,在转账代码里,故意不给B账户加钱,那一致性还是无法保证。因此,还必须从应用层角度考虑。 274 | 从应用层面,通过代码判断数据库数据是否有效,然后决定回滚还是提交数据。 275 | 2. 原子性:利用InnoDB的undo log。 276 | 3. 持久性:利用InnoDB的redo log。 277 | 4. 隔离性:锁和MVCC。 278 | ----------------------------------------------------- 279 | Spark中的reduceByKey和groupByKey: 280 | reduceByKey(func, [numPartitions]): 281 | When called on a dataset of (K, V) pairs, returns a dataset of (K, V) pairs where the values for each key are aggregated using the given reduce function func, which must be of type (V, V) => V. Like in groupByKey, the number of reduce tasks is configurable is configurable through an optional second argument. 282 | 283 | groupByKey([numPartitions]): 284 | When called on a dataset of (K, V) pairs, return a dataset of (K, Iterable) pairs. Note: If you are grouping in order to perform an aggregation (such as a sum of or average) over each key, using reduceByKey or aggregateByKey will yield much better performance. Note: By default, the level of parallelism in the output depends on the number of partitions of the parent RDD. You can pass an optional numPartitions argument to set a different number of tasks. 285 | ----------------------------------------------------- 286 | String类为什么是final: 287 | 1. 为了实现字符串池; 288 | 2. 为了线程安全; 289 | 3. 为了实现String可以创建HashCode不可变性; 290 | ------------------------------------------ 291 | Class#forName和ClassLoader#loadClass的区别: 292 | Class#forName执行了类加载阶段的下面几个阶段:加载(Loading)->验证(Verification)->准备(Preparation)->解析(Resolution)->初始化(Initialization); 293 | ClassLoader#loadClass执行了类加载阶段的下面几个阶段:加载(Loading)->验证(Verification)->准备(Preparation); 294 | 295 | Class#forName得到的Class是经过加载、链接和初始化三个阶段。执行类中的static块,给静态变量和静态代码块赋值。 296 | ClassLoader#loadClass得到的Class只进行了加载,并没有进行链接和初始化。不会执行类中的静态变量和静态代码块的赋值。 -------------------------------------------------------------------------------- /202102.txt: -------------------------------------------------------------------------------- 1 | 整个类的生命周期:加载(Loading)->验证(Verification)->准备(Preparation)->解析(Resolution)->初始化(Initialization)->使用(Using)->卸载(Unloading),其中验证/准备/解析统称为链接(Linking)。 2 | 3 | "加载"是"类加载"过程中的一个阶段,两个是不同的概念。 4 | 1. 加载(Loading):在加载阶段,虚拟机需要完成三件事情: 5 | 1. 通过类的全限定名获取定义此类的二进制字节流(并没有指明要从一个Class文件中获取,可以从其他渠道,譬如:网络、动态生成、数据库等); 6 | 2. 将二进制字节流所代表的静态存储结构转换成方法区中的运行时数据结构; 7 | 3. 在内存中生成一个代表这个类的java.lang.Class对象,作为方法区这个类的各种数据的访问入口。 8 | 9 | 加载阶段和链接阶段(Linking)的部分内容是交叉进行的,加载阶段尚未完成,链接阶段可能已经开始,但这些夹在加载阶段之中进行的动作,仍然属于链接阶段的内容,这两个阶段的开始时间仍然保持着固定的先后顺序。 10 | 11 | 2. 链接(Linking) 12 | a. 验证(Verification):验证是链接阶段的第一步,这一阶段的目的是为了确保Class文件的字节流中包含的信息符合当前虚拟机的要求,并且不会危害虚拟机自身的安全。 13 | 验证阶段大致会完成4个校验动作: 14 | 1. 文件格式验证; 15 | 2. 元数据验证; 16 | 3. 字节码验证:通过数据流和控制流分析,确定程序语义是合法的、符合逻辑的; 17 | 4. 符号引用验证:确保解析动作能正确执行。 18 | 验证阶段是非常重要的,但不是必须的,它对程序运行期没有影响,如果所引用的类经过反复验证,那么可以考虑采用-Xverifynone参数来关闭大部分的类验证措施,以缩短虚拟机类加载的时间。 19 | 20 | b. 准备(Preparation): 21 | 准备阶段是正式为类变量分配内存并设置变量初始值的阶段,这些变量所使用的内存都将在方法区中进行分配。这时候进行内存分配的仅包括类变量(被static修饰的变量),而不包括实例变量,实例变量将会在对象实例化时随着对象一起分配在堆中。其次,这里所说的初始值"通常情况"下是数据类型的零值。假设一个类变量的定义为: 22 | public static int value = 123; 23 | 那变量value在准备阶段过后的初始值是0而不是123。因为这时候尚未开始执行任何java方法,而把value赋值为123的putstatic指令是程序被编译后,存放在类构造器()方法之中,所以把value赋值为123的动作将在初始化阶段才会执行。 24 | 至于"特殊情况"是指:public static final int value2 = 456;即当类字段标注为final之后,value2的值在准备阶段阶段初始化为456而非0。 25 | 总结:final是在准备阶段时就赋值了,static准备阶段数据是零值,在初始化阶段才会赋值。 26 | 27 | c. 解析(Resolution): 解析阶段是虚拟机将常量池内的符号引用替换为直接引用的过程。解析动作主要针对类或接口、字段、类方法、方法类型、方法句柄和调用点限定符7类符号引用进行。 28 | 29 | 3. 初始化(Initialization): 静态(static)变量的初始化和静态Java代码块,并初始化程序设置的变量值。 30 | 31 | 类依赖分析器:jdeps。在jdk8中添加,是一个命令行工具,可以展示包层级和类层级的Java类依赖关系,以.class文件、目录或者jar文件为输入,会把依赖关系输出到控制台。 32 | ------------------------ 33 | Arrays#sort(int[]),Arrays#sort(int[], int, int),Arrays#sort(long[]),Arrays#sort(long[],int,int),Arrays#sort(short[]),Arrays#sort(short[],int,int),Arrays#sort(char[]),Arrays#sort(char[],int,int),Arrays#sort(byte[]),Arrays#sort(byte[],int,int),Arrays#sort(float[]),Arrays#sort(float[],int,int),Arrays#sort(double[]),Arrays#sort(double[],int,int)最终都会调用:DualPivotQuiksort#sort(...); 34 | 35 | public static void sort(Object[] a) { 36 | if (LegacyMergeSort.userRequested) 37 | legacyMergeSort(a); 38 | else 39 | ComparableTimSort.sort(a, 0, a.length, null, 0, 0); 40 | } 41 | 另外还有各种类型的parallelSort 42 | 比如: 43 | public static void parallelSort(int[] a) { 44 | int n = a.length, p, g; 45 | if (n <= MIN_ARRAY_SORT_GRAN || 46 | (p = ForkJoinPool.getCommonPoolParallelism()) == 1) 47 | DualPivotQuiksort.sort(a, 0, n - 1, null, 0, 0); 48 | else 49 | new ArrayParallelSortHelpers.FJInt.Sorter(null, a, 50 | new int[n], 0, n, 0, 51 | ((g = n / (p << 2)) <= MIN_ARRAY_SORT_GRAN) ? 52 | MIN_ARRAY_SORT_GRAN : g).invoke(); 53 | } 54 | 55 | Implementation note: The sorting algorithm is a Dual-Pivot Quicksort by Vladimir Yaroslavskiy, Jon Bentley, and Joshua Bloch. This algorithm offers O(n log(n)) performance on many data sets that cause other quicksorts to degrade to quadratic performance, and is typically faster than traditional (one-pivot) Quicksort implementations. 56 | ------------------------ 57 | TimSort是一个归并排序做了大量优化的版本。对归并排序排在已经反向排好序的输入时做了特别优化。对已经正向排好序的输入减少回溯。对两种情况混合(一会升序,一会降序)的输入处理比较好。 58 | 59 | org.springframework.util.ListenableFuture; 60 | /* Extend #Future with the capability to accept completion callbacks. 61 | If the future has completed when the callback is added, the callback is 62 | triggered immediately. 63 | Inspired by #com.google.common.util.concurrent.ListenableFuture. 64 | */ 65 | public interface ListenableFuture extends Future { 66 | void addCallback(ListenableFutureCallback callback); 67 | void addCallback(SuccessCallback sucessCallback, FailureCallback failureCallback); 68 | 69 | default CompletableFuture completable() { 70 | CompletableFuture completable = new DelegatingCompletableFuture<>(this); 71 | addCallback(completable::complete, completable::completeExceptionally); 72 | return completable; 73 | } 74 | } 75 | 76 | 77 | public class LinkedBlockingQueue extends AbstractQueue 78 | implements BlockingQueue, java.io.Serializable { 79 | ... 80 | /* A variant of the "two lock queue" algorithm. The putLock gates 81 | entry to put (and offer), and has an associated condition for 82 | waiting puts. Similarly for the takeLock. The "count" field 83 | that they both rely on is maintained as an atomic to avoid 84 | needing to get both locks in most cases. Also, to minimize need 85 | for puts to get takeLock and vice-versa, cascading notifies are 86 | used. When a put notices that it has enabled at least one take, 87 | it signals taker. That taker in turn signals others if more 88 | items have been entered since the signal. And symmetrically for 89 | takes signalling puts. Operations such as remove(Object) and 90 | iterators acquire both locks. 91 | 92 | Visibility between writers and readers is provided as follows: 93 | 94 | Whenever an element is enqueued, the putLock is acquired and 95 | count updated. A subsequent reader guarantees visibility to the 96 | enqueued Node by either acquiring the putLock (via fullyLock) 97 | or by acquiring the takeLock, and then reading n = count.get(); 98 | this gives visibility to the first n items. 99 | 100 | To implement weakly consistent iterators, it appears we need to 101 | keep all Nodes GC-reachable from a predecessor dequeued Node. 102 | That would cause two problems: 103 | - allow a rogue Iterator to cause unbounded memory retention 104 | - cause cross-generational linking of old Nodes to new Nodes if 105 | a Node was tenured while live, which generational GCs have a 106 | hard time dealing with, causing repeated major collections. 107 | However, only non-deleted Nodes need to be reachable from 108 | dequeued Nodes, and reachability does not necessarily have to 109 | be of the king understood by the GC. We use the trick of 110 | linking a Node that has just been dequeued to itself. Such a 111 | self-link implicitly means to advance to head.next. 112 | */ 113 | 114 | static class Node { 115 | E item; 116 | Node next; 117 | Node(E x) { item = x;} 118 | } 119 | private final int capacity; 120 | private final AtomicInteger count = new AtomicInteger(); 121 | transient Node head; 122 | private transient Node last; 123 | private final ReentrantLock takeLock = new ReentrantLock(); 124 | private final Condition notEmpty = takeLock.newCondition(); 125 | private final ReentrantLock putLock = new ReentrantLock(); 126 | private final Condition notFull = putLock.newCondition(); 127 | ... 128 | } 129 | 130 | NameNode包含哪些组件: 131 | 1. Namespace: 维护整个文件系统的目录树结构及目录树上的状态变化; 132 | 2. BlockManager: 维护整个文件系统中与数据块相关的信息及数据块的状态变化; 133 | 3. NetworkTopolgy: 维护机架及DataNode信息,机架感知的基础; 134 | 4. LeaseManager: 读写的互斥同步就是靠Lease实现,支持HDFS的write-once-read-many的核心数据结构; 135 | 5. CacheManager: Hadoop2.3.0引入的集中式缓存新特性,支持集中式缓存的管理,实现memory-locality提升读性能; 136 | 6. SnapshotManager: Hadoop2.1.0引入的Snapshot新特性,用于数据备份、回滚,以防止因用户误操作导致集群出现数据问题; 137 | 7. DelegationTokenSecretManager:管理HDFS的安全访问; 138 | 8. 其他还有临时数据信息、统计信息metrics等等; 139 | NameNode常驻内存主要被Namespace和BlockManager使用,两者使用占比分别接近50%,其他部分内存开销较小且相对固定,与Namespace和BlockManager相比基本可以忽略。 140 | 141 | HDFS无法高效存储大量小文件,如何处理好小文件: 142 | HDFS本身提供了几个解决方案:Hadoop Archive, SequenceFile和CombineFileInputFormat。 143 | 144 | public class LinkedBlockingDeque extends AbstractQueue 145 | implements BlockingQueue, java.io.Serializable { 146 | ... 147 | static final class Node { 148 | E item; 149 | Node prev; 150 | Node next; 151 | Node(E x) { 152 | item = x; 153 | } 154 | } 155 | 156 | transient Node first; 157 | transient Node last; 158 | private transient int count; 159 | private final int capacity; 160 | /* Main lock guarding all access */ 161 | final ReentrantLock lock = new ReentrantLock(); 162 | private final Condition notEmpty = lock.newCondition(); 163 | private final Condition notFull = lock.newCondition(); 164 | ... 165 | } 166 | 167 | 在存储结构上,SequenceFile主要由一个Header后跟多条Record组成,Header主要包括了Key ClassName,Value ClassName,存储压缩算法,用户自定义元数据等信息。此外,还包含了一些同步标识,用于快速定位到记录的边界。每条Record以键值对的方式进行存储,用来表示它的字符数组可以解析成:记录的长度、Key的长度、Key值和Value值,并且Value值的结构取决于该记录是否被压缩。 168 | 169 | The Guava project contains several Google's core libraries that we relay on in our Java-based projects: collections, caching, primitives support, concurrency libraries, common annotations, string processing, I/O, and so forth. 170 | 171 | Lateral View和表生成函数(例如Split,Explode等函数)结合使用,它能够将一行数据拆成多行数据,并对拆分后的数据进行聚合。 172 | 173 | 输入ping IP后敲回车,发包前会发生什么: 174 | 首先根据目的IP和路由表决定走哪个网卡,再根据网卡的子网掩码地址判断目的IP是否在子网内。如果不在则会通过arp缓存查询IP的网卡地址,不存在的话会通过广播询问目的IP的mac地址,得到后就开始发包,同时mac地址也会被arp缓存起来。 175 | 176 | 如何保障产品/功能的质量: 177 | 从代码开发、测试保障、线上质量三个方面来保障。 178 | 1. 在代码开发阶段,有单元测试、代码Review、静态代码扫描等; 179 | 2. 测试保障阶段,有功能测试、性能测试、高可用测试、稳定性测试、兼容性测试等; 180 | 3. 在线上质量方面,有灰度发布、紧急回滚、故障演练、线上监控和巡检等。 181 | 182 | public class PriorityBlockingQueue extends AbstractQueu 183 | implements BlockingQueue, java.io.Serializable { 184 | ... 185 | private static final int DEFAULT_INITIAL_CAPACITY = 11; 186 | private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8; 187 | private transient Object[] queue; 188 | private transient int size; 189 | private transient Comparator comparator; 190 | private final ReentrantLock lock; 191 | private final Condition notEmpty; 192 | /*Spinlock for allocation, acquired via CAS*/ 193 | private transient volatile int allocationSpinLock; 194 | /* 195 | A plain PriorityQueue used only for serialization, 196 | to maintain compatibility with previous versions of this class. 197 | Non-null only during serialization/deserialization. 198 | */ 199 | private PriorityQueue q; 200 | ... 201 | } 202 | 203 | 越权(或者说权限提升,Privilege Escalation)是指攻击者能够执行其本身没有资格执行的一些操作,属于访问控制的问题。越权访问分为垂直越权访问和水平越权访问。垂直越权是指不同用户级别之间的越权,如普通用户执行管理员的权限。水平越权是指相同级别用户之间的越权操作。 204 | 205 | Docker网络架构源自一种叫做容器网络模型(CNM)的方案,该方案是开源的并且支持插接式连接。 206 | Libnetwork是Docker对CNM的一种实现,提供了Docker核心网络架构的全部功能。不同的驱动可以通过插拔的方式接入Libnetwork来提供定制化的网络拓扑。 207 | 为了实现开箱即用的效果,Docker封装了一系列本地驱动,覆盖了大部分常见的网络需求。其中包括单机桥接网络(Single-Host Bridge Network)、多机覆盖网络(Multi-Host Overlay),并且支持接入现有VLAN。 208 | 209 | OAuth2.0具体来说,一共分为四种授权类型(authorization grant),即四种颁发令牌的方式,适用于不同的场景。四种授权方式如下: 210 | 1. 授权码(authorization-code); 211 | 2. 隐藏式(implicit); 212 | 3. 密码式(password); 213 | 4. 客户端凭证(client credentials); 214 | 215 | Feign原理简述: 216 | 1. Spring启动时,会扫描包下所有@FeignClient注解的类,并将这些类注入到Spring的IOC容器中。并通过JDK动态代理来生成RequestTemplate。 217 | 2. RequestTemplate中包含请求的所有信息,如请求参数,请求URL等。 218 | 3. RequestTemplate生成Request,然后将Request交给client处理,这个client默认是JDK的HttpUrlConnection,也可以是OKHttp、Apache的HttpClient等; 219 | 4. 最后client封装成LoadBalanceClient,结合Ribbon负载均衡发起调用。 220 | 221 | 雪花算法是三个缺点: 222 | 1. 时间回拨问题; 223 | 2. 机器ID的分配和回收问题; 224 | 3. 机器ID的上限问题。 225 | 226 | VSS: Virtual Set Size, 虚拟耗用内存。它是一个进程能访问的所有内存空间地址的大小。这个大小包含了一些没有驻留在RAM种的内存,就像malloc已经被分配,但还没有写入。VSS很少用来测量程序的实际使用内存。 227 | 228 | RSS: Resident Set Size,实际使用物理内存。RSS是一个进程在RAM中实际持有的内存大小。RSS可能会产生误导,因为它包含了所有该进程使用的共享库所占用的内存,一个被加载到内存中的共享库可能有很多进程会使用它。RSS不是单个进程使用内存量的精确表示。 229 | 230 | PSS: Proportional Set Size,实际使用的物理内存,它与RSS不同,它会按比例分配共享库所占用的内存。PSS是一个非常有用的数值,如果系统中所有进程的PSS相加,所得和即为系统占用内存的总和。当一个进程被杀死后,它所占用的共享库内存将会被其他仍然使用该共享库的进程所分担。在这种方式下,PSS也会带来误导,因为当一个进程被杀后,PSS并不代表系统回收的内存大小。 231 | 232 | USS:Unique Set Size, 进程独自占用的物理内存。这部分完全是该进程独享的。USS是一个非常有用的数值,因为它表明了运行一个特定进程所需的真正内存成本。当一个进程被杀死,USS就是所有系统回收的内存。USS是用来检查进程是否有内存泄漏的最好选择。 233 | 234 | HBase中系统故障恢复以及主从复制都基于HLog实现。默认情况下,所有写入操作(写入、更新以及删除)的数据都先以追加形式写入HLog,再写入MemStore。大多数情况下,HLog并不会被读取,但如果RegioinServer在某些异常情况下发生宕机,此时已经写入MemStroe中但尚未flush到磁盘的数据就会丢失,需要回放HLog补救丢失的数据。此外,HBase主从复制需要主集群将HLog日志发送给从集群,从集群在本地执行回放操作,完成集群之间的数据复制。 235 | 236 | 如果不关心RocketMQ Broker的启动时间,有一种更好的选择,就是通过"预触摸"Java堆以确保在JVM初始化期间每个页面都将被分配。不关心启动时间的可以启用: -XX:+AlwaysPreTouch,禁用偏置锁可能会减少JVM暂停:-XX:-UseBiaseLocking。 237 | 238 | 从MongoDB 3.2版本开始,MongoDB支持多数据存储引擎,MongoDB支持的存储引擎有:WiredTiger,MMAPv1和In-Memory。 239 | 从MongoDB 3.2开始默认的存储引擎是WiredTiger,3.3版本之前的默认存储引擎是MMAPv1,MongoDB4.x版本不再支持MMAPv1存储引擎。 240 | MongoDB不仅能将数据持久化存储到硬盘文件中,而且还能将数据只保存到内存中;In-Memory存储引擎用于将数据只存储在内存中,只将少量的元数据和诊断日志(Diagnostic)存储到硬盘文件中,由于不需要Disk的IO操作,就能获取索取的数据,In-Memory存储引擎大幅度降低了数据查询的延迟(Latency)。 241 | 242 | RxJava有四个基本概念:Observer(观察者),Observable(被观察者),subscribe(订阅),事件。Oberver和Observable通过subscribe()实现订阅关系,从而Obervable可以在需要的时候发出事件通知Oberver。 243 | 244 | ObservableEmitter是事件发射器,定义并且向观察者发送需要发送的事件。 245 | 246 | 如何建设数仓: 247 | 1. 在线数仓和离线数仓; 248 | 2. 数仓建设整体流程: 249 | 2.1 业务建模; 250 | 2.2 领域建模; 251 | 2.3 逻辑建模; 252 | 2.4 物理建模; 253 | 2.5 规范治理; 254 | 3. 领域建模主要指选用何种模型:Kimball模型(常用)、Inmon模型、Data Valut模型、Anchor模型。 255 | 4. Kimball模型: 面向分析/由下往上建设,适用于快速交付建设。 256 | 4.1 星型模型:一个事实表+多个维度表,不存在二级维度表; 257 | 4.2 雪花模型:星型模型扩展,维表扩展,可以连接多个子维表; 258 | 4.3 星座模型:星型模型扩展,多个事实表共用一个维表; 259 | 5. Inmon模型:面向业务/由上往下建设,全面支撑、分步实施。 260 | 1NF:列原子性,属性值唯一,不具有多义性; 261 | 2NF:基于1NF,非主键属性必须依赖于主键,而不能依赖于主键的一部分; 262 | 3NF:基于2NF,非主键列必须直接依赖于主键,不能存在传递依赖; 263 | 6. Data Valut模型:面向细节、追踪历史; 264 | Hub:中心表/记录业务主键; 265 | Link:链接表/记录业务关系; 266 | Satellite:附属表/记录业务描述; 267 | PIT:辅助表/解决多个附属表统一时点的问题; 268 | 7. Anchor模型:6NF,KV结构,只做添加扩展,不做修改; 269 | Anchors:类似于中心表/记录业务主键; 270 | Attributes:类似于Satellite/全部KV结构化,一个表只有一个Anchors描述; 271 | Ties: 类似于Link表/记录Anchors之间的描述; 272 | Knots: 公共属性表/记录共用属性信息。 273 | ---------------------------------------------- 274 | 如何构建主题域: 275 | 主题是在较高层次上将企业信息系统中的数据进行综合、归类和分析利用的一个抽象概念,每一个主题基本对应一个宏观的分析领域。在逻辑意义上,它是对应企业中某一宏观分析领域所涉及的分析对象。 276 | 划分主题域方法: 277 | 在业务调研之后,可以进行主题域的划分。划分主题域,需要分析各个业务模块中有哪些业务活动。通过按照以下方法划分主题域,可以按照用户企业的部门划分,也可以按照业务过程或者业务板块中的功能模块划分。 278 | 1. 按照系统划分:业务系统有几种,就划分为几类; 279 | 2. 按业务过程划分:比如业务系统中有商品、交易、物流等; 280 | 3. 按部门规划:比如公司内的生产、供应链、研发、销售等; 281 | 为保障整个体系的生命力,主题域需要抽象提炼,并长期维护更新,但不轻易变动。划分数据域时,需满足以下两点: 282 | 1. 能涵盖当前所有的业务需求; 283 | 2. 能在新业务进入时,无影响地被包含进已有的主题域和扩展新的主题域。 284 | ---------------------------------------------- 285 | HDFS的写流程: 286 | 1. 首先客户端调用DistributedFileSystem对象的create方法,指明要创建的文件名; 287 | 2. 通过DistributedFileSystem对象与Hadoop集群的NameNode进行一次RPC远程调用,在HDFS的Namespace中创建一个全新的文件。在创建之前,NameNode会执行各种检查以确认文件没有重名(该文件名下没有关联的块)以及客户端具有创建文件的权限。若检查通过,NameNode会为新文件生成一条记录,文件创建成功后会向客户端返回一个FSDataOutputStream中封装了一个DFSOutputStream对象,用于DataNode和NameNode的通信。 288 | 3. 当客户端开始写数据,DFSOutputStream将文件分割成很多很小的数据,然后将每个小块放进一个个包(Packet, 包中除了数据还有描述数据用的标识),Packet会被写进一个数据队列(data queue)。 289 | 3.1 当数据流入DFSOutputStream时,DFSOutputStream内会有一个chunk(512B)大小的buf,当数据写满这个buf(或遇到强制flush),会计算checksum(4B)值,然后填塞进packet; 290 | 3.2 当一个chunk填塞进入packet后,仍然不会立即发送,而是将数据累积一个packet填满后,将这个packet放入dataqueue队列,等待DataStreamer线程取出并发送到DataNode节点; 291 | 4. DataStreamer线程从dataQueue中获取数据包(packet),发送该数据包给pipeline中的第一个DataNode,然后取出该数据包添加到ackQueue。(第一个DataNode将一个packet写入成功,则传递给下一个)。DataStreamer会要求NameNode挑选出合适存储块(block,默认128M)备份的DataNode列表,这个列表会构成一个pipeline(若备份数为3,则pipeline中有3个DataNode)。 292 | 5. ResponseProcessor线程会从各个DataNode中接收ack确认消息,若从所有的DataNode中收到了ack消息。则表示一个packet发送成功,ResponseProcessor线程会将ackQueue队列中对应的Packet删除。若发生错误,ackQueue中未完成的packet将被移除(被添加到data queue首部重新发送),并建立一个新的不包含错误DataNode的pipeline,DataStreamer重新开始从data queue中获取packet进行发送。 293 | 失败情况详解: 294 | 1). pipeline被关闭,ackQueue中未完成的packet将被移除,被添加到data queue首部重新发送; 295 | 2). NameNode会在失败DataNode上还未写完的block上生成一个新的标识ID,宕机的DataNode重新启动以后,NameNode将该DataNode上的失败的block删除,并将该DataNode从pipeline中移除; 296 | 3). 然后将其他几个正常运行的DataNode生成一个新的pipeline,继续写入packet(不会重复写); 297 | 4). NameNode注意到块备份数小于规定的备份数,会安排另一个节点上创建完成备份(直接从已有的块中复制即可),一直到满足备份数(dfs.replication)。 298 | 5). 若多个节点写入失败,只要满足最小备份数(dfs.NameNode.replication.min)设置,也会写入成功,剩下的备份会被集群异步的执行,直到满足备份数(dfs.replication)。 299 | 6. 当客户端将所有的数据写入完成,会调用close()方法,该方法会将所有的packet刷新flush进pipeline中的DataNode,等这些packet都确认成,客户端告知NameNode,此时NameNode是知道文件被分成的哪些block的(因为DataStreamer向NameNode申请了block),等待最少的备份数(replica)被创建(dfs.NameNode.replication.min,前面可能会发生失败而不是满足dfs.replicaiton),然后成功返回。 300 | 301 | ES的写流程(简化版): 302 | 1. 客户端选择一个node发送请求过去,这个node就是coordinating node(协调节点)。 303 | 2. coordinating node对document进行路由,将请求转发给对应的node(有primary shard)。 304 | 3. 实际的node上的primary shard处理请求,然后将数据同步到replica node。 305 | 4. coordinating node如果发现primary node和replica node都成功后,就返回响应结果给客户端。 306 | 307 | Lucene存在的问题: 308 | 1. 没有并发设计;Lucene只是一个搜索引擎库,并没有涉及到分布式相关的设计,因此要想使用Lucene来处理海量数据,并利用分布式的能力,就必须在其之上进行分布式的相关设计。 309 | 2. 非实时;将文件写入Lucene后并不能立即被检索,需要等待Lucene生成一个完整的segment才能被检索。 310 | 3. 数据存储不可靠;写入Lucene的数据不会立即被持久化到磁盘,如果服务器宕机,那存储在内存中的数据就会丢失。 311 | 4. 不支持部分更新;Lucene中提供的updateDocument仅支持对文档的全量更新,对部分更新不支持。 312 | -------------------------------------------------------------------------------- /201611.txt: -------------------------------------------------------------------------------- 1 | 除了用插件来修改 MyBatis 核心行为之外,还可以通过完全覆盖配置类来达到目的。只需继承后覆盖其中的每个方法,再把它传递到 SqlSessionFactoryBuilder.build(myConfig) 方法即可。再次重申,这可能会严重影响 MyBatis 的行为,务请慎之又慎。 2 | 3 | 不过要记住:尽管可以配置多个环境,每个 SqlSessionFactory 实例只能选择其一。 4 | 所以,如果你想连接两个数据库,就需要创建两个 SqlSessionFactory 实例,每个数据库对应一个。而如果是三个数据库,就需要三个实例,依此类推,记起来很简单: 5 | 6 | CallableStatement: 调用存储过程. 7 | 元素中的回收策略(eviction属性)如下4种: 8 | LRU: 9 | FIFO: 10 | SOFT – 软引用:移除基于垃圾回收器状态和软引用规则的对象。 11 | WEAK – 弱引用:更积极地移除基于垃圾收集器状态和弱引用规则的对象。 12 | ---------------------------------------------------------------- 13 | GC Roots:1.虚拟机栈(栈帧中的本地变量表)中引用的对象;2. 方法区中的类静态属性引用的对象;3. 方法区中常量引用的对象;4. 本地方法栈中JNI中引用的对象。 14 | 15 | 没有GC Root可到达的对象,如果没有必要或者没有覆盖finalize(), 进入F-Queue,由一个低优先级的线程,执行finalize(),此时再次将this赋值给其他对象。 16 | ----------------------------------------------------------------- 17 | 不幸的是,它作为老年代的收集器,却无法与JDK 1.4.0中已经存在的新生代收集器Parallel Scavenge配合工作,所以在JDK 1.5中使用CMS来收集老年代的时候,新生代只能选择ParNew或Serial收集器中的一个。ParNew收集器也是使用 -XX: +UseConcMarkSweepGC选项后的默认新生代收集器,也可以使用 -XX:+UseParNewGC选项来强制指定它。 18 | 三种垃圾收集算法: 标记-清除、复制、标记-整理; 19 | 20 | CMS:初始标记->并发标记->重新标记->并发清除 21 | 22 | UseSerialGC: 虚拟机运行在Client模式下的默认值,使用Serial + Serial Old组合进行GC 23 | UseParNewGC: ParNew + Serial组合进行GC 24 | UseConcMarkSweepGC: 使用ParNew + CMS + Serial Old组合进行GC 25 | UseParallGC: 虚拟机在Server模式下的默认值,使用Parallel Scavenge + Serial Old组合进行GC 26 | UseParallOldGC:使用Paralle Scavenge + Paralle Old组合进行GC 27 | ---------------------------------------------------------------- 28 | RxJava Flowable在过载的情况下有三种策略: 缓存,丢弃,错误(抛出MissingBackpressureException) 29 | Observable不支持负载压力处理,这意味着Observable不会抛出MissingBackpressureExceptin 30 | 31 | -XX:+UseCompressedClassPointers 32 | -XX:OnOutOfMemoryError 用来指定一个可行性程序或者脚本的路径,当发生OOM的时候,去执行这个脚本。 33 | 34 | -XX:+PrintTenuringDistribution, 让JVM每次MinorGC后打印Surivivor空间中的对象的年龄分步. 35 | - -XX:InitialTenuringThreshold 表示对象被移到老年代的年龄阈值的初始值 36 | - -XX:MaxTenuringThreshold 表示对象被移到老年代的年龄阈值的最大值 37 | - -XX:TargetSurvivorRatio 表示MinorGC结束了Survivor区域中占用空间的期望比例。 38 | 39 | -XX:UseAdaptiveSizePolicy 表示是否开启自适应策略,打开这个开关后,JVM自动调节JVM的新生代大小,Eden和Survivor的比例等参数。用户只需要设置期望的吞吐量(-XX:GCTimeRatio)和期望的停顿时间(-XX:MaxGCPauseMillis)。然后,JVM会尽量去向用户期望的方向去优化 40 | 41 | - -XX:CMSInitiatingOccupancyFraction 指定在老生代用掉多少内存后开始进行垃圾回收。与吞吐量优先的回收器不同的是,吞吐量优先的回收器在老生代内存用尽了以后才开始进行收集,这对CMS来讲是不行的,因为吞吐量优先的垃圾回收器运行的时候会停止所有用户线程,所以不会产生新的对象,而CMS运行的时候,用户线程还有可能产生新的对象,所以不能等到内存用光后才开始运行。比如-XX:CMSInitiatingOccupancyFraction=75表示老生代用掉75%后开始回收垃圾。默认值是68。 42 | --------------------------------------------------------------- 43 | AynchronousServerSocketChannel: CompletionHandler 44 | 在AIO编程中,发出一个事件(accept read write等)之后要指定事件处理类(回调函数),AIO中的事件处理类是CompletionHandler,这个接口定义了如下两个方法,分别在异步操作成功和失败时被回调。 45 |    void completed(V result, A attachment); 46 |    void failed(Throwable exc, A attachment); 47 | --------------------- 48 | 每当一个新 session 被创建,MyBatis 就会创建一个与之相关联的本地缓存。任何在 session 执行过的查询语句本身都会被保存在本地缓存中,那么,相同的查询语句和相同的参数所产生的更改就不会二度影响数据库了。本地缓存会被增删改、提交事务、关闭事务以及关闭 session 所清空。 49 | 默认情况下,本地缓存数据可在整个 session 的周期内使用,这一缓存需要被用来解决循环引用错误和加快重复嵌套查询的速度,所以它可以不被禁用掉,但是你可以设置 localCacheScope=STATEMENT 表示缓存仅在语句执行时有效。 50 | 注意,如果 localCacheScope 被设置为 SESSION,那么 MyBatis 所返回的引用将传递给保存在本地缓存里的相同对象。对返回的对象(例如 list)做出任何更新将会影响本地缓存的内容,进而影响存活在 session 生命周期中的缓存所返回的值。因此,不要对 MyBatis 所返回的对象作出更改,以防后患。 51 | --------------------- 52 | 两个破坏类加载的双亲委派模型的例子: 53 | 1. JNDI; 2. Spring. 为了解决这个问题,使用线程上下文类加载器。Thread.setContextClassLoader(); Thread.currentThread().getContextClassLoader(); 54 | 55 | 56 | 逃逸分析: 如果分配在栈上,随着函数的调用,栈帧直接不可用。 57 | 58 | G1使用暂停预测模型(pause prediction model)来达到用户定义的目标暂停时间,并根据目标暂停时间来选择此次进行垃圾回收的heap区域数量. 59 | CMS垃圾收集器(Concurrent Mark Sweep,并发标记清理)不进行压缩. ParallelOld 垃圾收集只对整个堆执行压缩,从而导致相当长的暂停时间。 60 | 61 | -XX:+UseG1GC - 让 JVM 使用 G1 垃圾收集器. 62 | -XX:MaxGCPauseMillis=200 - 设置最大GC停顿时间(GC pause time)指标(target). 这是一个软性指标(soft goal), JVM 会尽力去达成这个目标. 所以有时候这个目标并不能达成. 默认值为 200 毫秒. 63 | -XX:InitiatingHeapOccupancyPercent=45 - 启动并发GC时的堆内存占用百分比. G1用它来触发并发GC周期,基于整个堆的使用率,而不只是某一代内存的使用比例。值为 0 则表示“一直执行GC循环)'. 默认值为 45 (例如, 全部的 45% 或者使用了45%). 64 | ----------------------------------------- 65 | RxJava2的Base Classes: 66 | RxJava 2 features several base classes you can discover operators on: 67 | - io.reactivex.Flowable: 0..N flows, supporting Reactive-Streams and backpressure 68 | - io.reactivex.Observable: 0..N flows, no backpressure, 69 | - io.reactivex.Single: a flow of exactly 1 item or an error, 70 | - io.reactivex.Completable: a flow without items but only a completion or error signal, 71 | - io.reactivex.Maybe: a flow with no items, exactly one item or an error. 72 | -------------------------------------------------------- 73 | Eureka满足AP,Zookeeper满足CP 74 | 75 | AnnotationConfigWebApplicationContext 76 | 77 | Collections.unmodifiableMap 78 | 79 | MultiValueMap extends Map> 80 | 81 | java.util.concurrent.CopyOnWriteArrayList; 82 | private final MultiValueMap targetRequestParams = new LinkedMultiValueMap<>(4); 83 | 84 | Spring中的@Nullable标识某个实体在某种情况下为NULL。 85 | 86 | 87 | Stream有三个match方法: 88 | - allMatch: Stream中全部元素符合传入的predicate, 返回true; 89 | - anyMatch: Stream中只要有1个元素符合传入的predicate, 返回true 90 | - noneMatch: Stream中没有一个元素符合传入的predicate, 返回true 91 | 92 | XsltView All implements Interfaces: Aware, BeanNameAware, InitializingBean, ApplicationContextAware, ServletContextAware, View. 93 | 94 | All model parameters are passed to the XSLT Transformer as parameters. 95 | In addition the user can configure output properties to be passed to the Transformer. 96 | 97 | ReflectionUtils.accessibleConstructor(Class clazz, Class... parameterTypes).newInstance(); 98 | 99 | org.sprongframework.beans.factory.InitializingBean; 100 | afterPropertiesSet(); 101 | 102 | 思考: 如果Spring Bean初始化后,要保证某个属性必须要设置,可以通过@Override InitializingBean的afterPropertiesSet方法来判断。并且只针对Singleton的Bean. 103 | 104 | Collections.unmodifiableMap(); 105 | 106 | ViewResolver -> InternalResourcreViewResolver -> BeanNameViewResolver -> ContentNegotiatingViewResolver 107 | 108 | javax.servlet.ServletContext; 109 | 110 | BeanDefinitionRegistry 111 | 112 | Builder:1. 由Client, Builder, Product, Director; 2. 由静态内部类进行无序构造 113 | 114 | java.util.ResourceBundle; 115 | 116 | 在Spring配置文件中,我们只需要给出字符串类型的path即可,Spring会通过ResourceEditor(java.beans.PropertyEditor的子类)和ResourceLoader把给定的path转换为相应的Resource。 117 | 118 | ApplicationContext接口也继承了ResourceLoader接口,所以它的所有实现类都实现了ResourceLoader接口,都可以用来获取Resource。 119 | 120 | destroy() in DisposableBean 121 | 122 | SpringBoot默认是无法使用矩阵变量绑定参数的,需要覆盖WebMvcConfigurer中的configurePathMatch方法。 123 | 124 | AnnotatedBeanDefinitionReader 125 | 126 | 组合模式的优缺点: 127 | 优点:1. 高层模块调用简单; 2. 节点自由增加 128 | 缺点:在使用组合模式时,其叶子和树枝的声明都是实现类,而不是接口,违反了依赖倒置原则。 129 | 使用场景:部分/整体场景,如树形菜单、文件/文件夹的管理。 130 | 131 | RuntimeBeanDefinition: 解析时,将ref属性解析为RuntimeBeanDefinition,并将这个对象放入BeanDefition的MutablePropertValues中。 132 | 133 | Returning eagerly cached instance of singleton bean, that is not fully initialized yet - a consequence of a circular reference. 134 | 135 | 设计模式的UML图 136 | 137 | private static final proxyClassNamePrefix = "$Proxy"; // Proxy Class的前缀是$Proxy 138 | // next number to use for generation of unique proxy class names 139 | private static final AtomicLong nextUniqueNumber = new AtomicLong(); 140 | 141 | 142 | ServletContext ServletConfig Servlet GenericServlet 143 | 144 | HttpServlet里的service方法使用了模板方法(doGet, doPost, doDelete) 145 | 146 | ParameterizableViewController: 参数化视图控制器 147 | Spring MVC里的ResourceChainRegistration使用了Fluent风格的构造方式。 148 | 149 | 150 | Tomcat的顶层结构: Server - Service - Connector - Container 151 | 152 | 在Spring MVC里有2个ApplicationContext,构成父子上下文。 153 | java.util.concurrent.CopyOnWriteArraySet; 154 | 155 | HandlerMethodArgumentResolver; 156 | HandlerMethodReturnValueHandler; 157 | 158 | CORS是如何绕过Same-origin Policy策略。 159 | Access-Control-Allow-Origin 160 | 161 | CORS将导致跨域访问的请求分为三种: Simple Request, Preflighted Request和Requests with Credential; 162 | 163 | BeanNameAware 164 | 165 | 在接口InitializingBean中有一个函数 void afterPropertiesSet() throws Exception 166 | 167 | RequestMappingHandlerAdapter 168 | HandlerMethodArgumentResolver 169 | 170 | @RequestParam 171 | ReentrantReadWriteLock; 172 | 173 | 依赖注入和依赖查找的区别: ApplicationContextAware和@Autowired的区别,主动和被动的区别。 174 | 175 | Collections.unmodifiableMap(); 176 | 177 | String转化为InputStream 178 | 1. InputStream inputStream = new ByteArrayInputStream(string.getBytest()); 179 | 2. InputStream inputStream = IOUtils.toInputStream(string, StandardCharsets.UTF_8); 180 | 3. InputStream inputStream = new ReaderInputStream(CharSource.wrap(string).openStream(), StandardCharsets.UTF_8); 181 | 182 | EIP : Enterprise Integration Patterns (企业集成模式) 183 | 184 | PropertiesPropertySourceLoader 185 | YamlPropertySourceLoader 186 | 187 | HandlerExceptionResolver 188 | class SimpleMappingExceptionResolver extends AbstractHandlerExceptionResolver 189 | 190 | Template Method 191 | 192 | 193 | XmlBeanDefinitionReader 194 | AnnotatedBeanDefinitionReader 195 | PropertiesBeanDefinitionReader 196 | 197 | XmlBeanDefinitionReader实现了BeanDefinitionReader接口,BeanDefinitionReader的设计用意是加载BeanDefintion对象 198 | 在Spring中直接使用XmlBeanDefinitionReader的容器有XmlWebApplicationContext、ClassPathXmlApplicationContext、FileSystemXmlApplicationContext 199 | BeanDefinitionRegistry对象是BeanDefinition对象的注册表,并且它是XmlBeanDefinitionReader对象创建时必须提供的 200 | 201 | Spring MVC MatrixVariable 202 | 203 | ControllerAdviceBean 204 | 205 | FSM: Finite-state machine 206 | 207 | Spring Statemachine: Event/Action/Status 208 | 209 | OGNL : Object-Graph-Navigation-Language 210 | 用于过滤和投影集合,如books.{?#this.price<100}; 211 | 212 | StateMachine里的Action的execute里最好再次判断一下Event类型,因为有可能是多个Event的Action,如果多个Event有可能会有不同的Action. 213 | 214 | 215 | public static List findAnnotatedBeans(ApplicationContext context) { 216 | return Arrays.stream(BeanFactoryUtils.beanNamesForTypeIncludingAncestors(context, Object.class)) 217 | .filter(name -> context.findAnnotationOnBean(name, ControllerAdvice.class) != null) 218 | .map(name -> new ControllerAdviceBean(name, context)) 219 | .collect(Collectors.toList()); 220 | } 221 | 222 | java.io.PushbackInputStream; 223 | java.util.EnumSet; 224 | 225 | Thread States: new, runnable, waiting, time-waiting, blocked, terminated; 226 | 227 | RequestParamMethodArgumentResolver 228 | RequestHeaderMethodArgumentResolver 229 | 若想获取所有的header信息:使用RequestHeaderMapMethodArgumentResolver, 用来获取所有的header信息. 230 | 231 | 返回只读List: Collections.unmodifiableList(list); 232 | 233 | ModelAndViewContainer 234 | 235 | java.io.PushbackInputStream; 236 | org.springframework.web.method.support.HandlerMethodArgumentResolver; 237 | 238 | RequestBodyAdvice; 239 | ResponseBodyAdvice; 240 | 241 | AbstractMessageConverterMethodArgumentResolver; 242 | 243 | 244 | ControllerAdvice; 245 | 246 | ControllerAdviceBean; 247 | 248 | HandlerTypePredicate; 249 | 250 | AnnotationAwareOrderComparator; 251 | 252 | @ModelAttribute 253 | @InitBinder: 用于在@Controller中标注于方法上,表示当前控制器注册一个属性编辑器,只对当前的Controller有效. @InitBinder标注的方法必须有一个参数WebDataBinder。 254 | 255 | Arrays.stream(BeanFactoryUtils.beanNamesForTypeIncludingAncestors(context, Object.class)) 256 | .filter(name -> context.findAnnotationOnBean(name, ControllerAdvice.class) != null) 257 | .map(name -> new ControllerAdviceBean(name, context)) 258 | .collect(Collectors.toList()); 259 | 260 | HandlerMethodArgumentResolver; 261 | 262 | WebDataBinder; 263 | 264 | PathExtensionContentNegotiationStrategy; 265 | 266 | HandlerMethodReturnValueHandler; 267 | 268 | SpringMVC方法中的参数是靠HandlerMethodArgumentResolver解析; 269 | 270 | java.lang.reflect.Type; 271 | 272 | ParameterizedType; 273 | TypeVariable; 274 | GenericArrayType; 275 | WildcardType; 276 | 277 | JavaBeans Activation Framework: JAF 278 | 279 | NativeWebRequest; 280 | 281 | Java8之前的Future功能较弱,仅支持两种用法:要么检查future是否已经完成,要么等待future完成; 282 | Java8增加了CompletableFuture类,它实现了新的CompletionStage接口,并对Future进行了扩展。 283 | 284 | 如果不用Future或CompletableFuture, 想要实现等待某个线程完成之后才做后续的事,可以用join或wait等。但是CompletableFuture更好。 285 | 286 | CompletableFuture; 287 | CompletionStage; 288 | 289 | static CompletableFuture allOf(CompletableFuture... cfs) 290 | //5. 返回一个新的CompletableFuture,当所有给定的CompletableFutures完成时,完成。 291 | static CompletableFuture anyOf(CompletableFuture... cfs) 292 | //6. 返回一个新的CompletableFuture,当任何一个给定的CompletableFutures完成时,完成相同的结果。 293 | 294 | ServletInvocableHandlerMethod; 295 | 296 | 297 | HandlerExceptionResolver->AbstractHandlerExceptionResolver->AbstractHandlerMethodExceptionResolver->ExceptionHandlerExceptionResolver; 298 | 299 | ControllerAdvice; 300 | HttpMessageConverter; 301 | WebDataBinderFactory; 302 | 303 | SpringMVC处理请求大致是这样的: 304 | 305 | 首先被DispatcherServlet截获,DispatcherServlet通过handlerMapping获得HandlerExecutionChain,然后获得HandlerAdapter。HandlerAdapter在内部对于每个请求,都会实例化一个ServletInvocableHandlerMethod进行处理,ServletInvocableHandlerMethod在进行处理的时候,会分两部分别对请求跟响应进行处理。之后HandlerAdapter得到ModelAndView,然后做相应的处理。 306 | 307 | ServletInvocableHandlerMethod; 308 | HandlerMethodArgumentResolverComposite; 309 | 310 | 从名字我们也看的出来, 以Resolver结尾的是实现了HandlerMethodArgumentResolver接口的类,以Processor结尾的是实现了HandlerMethodArgumentResolver和HandlerMethodReturnValueHandler的类。 311 | 312 | turbine聚合 313 | 314 | DefaultParameterNameDiscoverer; 315 | PrioritizedParameterNameDiscoverer; 316 | 317 | 通过spring的LocalVariableTableParameterNameDiscoverer 获取方法的参数,spring也是通过使用ASM通过字节码获取方法中参数的具体的名称 318 | 319 | java.util.PriorityQueue; 320 | org.springframework.web.context.request.async.DeferredResult; 321 | 322 | volatile的write为了保证对其他线程的可见性,会追加以下两个Fence(内存屏障) 323 | 1). StoreStore //在intel cup中,不存在[写写]重排序,可以直接省略 324 | 2). StoreLoad //这个是所有内存屏障里最耗性能的 325 | 326 | Atomic*里的lazySet()省去了StoreLoad,只留下了StoreStore,性能必然会提高不少(虽然不能禁止写读的重排序,保证不了可见性,但给其他应用场景提供了更好的选择) 327 | 328 | Spring Cloud Hystrix具有服务降级,依赖隔离,服务熔断,监控(Hystrix Dashboard)等功能。 329 | 330 | 331 | @Target(ElementType.TYPE) 332 | @Retention(RetentionPolicy.RUNTIME) 333 | @Documented 334 | @Inherited 335 | @SpringBootApplication 336 | @EnableDiscoveryClient 337 | @EnableCircuitBreaker 338 | public @interface SpringCloudApplication { 339 | } 340 | 341 | Servlet4.0 342 | 343 | 344 | @FunctionalInterface 345 | public interface StreamingResponseBody { 346 | void writeTo(OutputStream outputStream) throws IOException; 347 | } 348 | 349 | HTTP协议中Cache-Control常见的值有: private, public, no-store, no-cache, must-revalidate, max-age等; 350 | 351 | Cache-Control的no-cache和must-revalidate的区别: 352 | - no-cache:告诉浏览器/缓存服务器,不管本地副本是否过期,使用资源副本前,一定要去源服务器进行副本有效性校验; 353 | - must-revalidate: 告诉浏览器/缓存服务器,本地副本过期前,可以使用本地副本;本地副本一旦过期,必须去源服务器进行有效性校验。 354 | 355 | InternalResourceViewResolver 356 | 357 | 自动配置的静态资源 358 | extends WebMvcConfigurerAdapter; 359 | 360 | 响应式编程: Spring WebFlux, RxJava 361 | 362 | SpringBoot集成自定义HandlerMethodArgumentResolver; 363 | 364 | CachingResourceResolver; 365 | CachingResourceTransformer; 366 | ContentVersionStrategy; 367 | CssLinkResourceTransformer; 368 | public static List Collections.unmodifiableList(List list); 369 | 370 | java.util.ListIterator; 371 | 372 | For chained method invocation. 373 | 374 | WebApplicatioinContext; 375 | java.util.concurrent.CopyOnWriteArrayList; 376 | 377 | Spring WebFlux是基于响应式流的,因此可以用来建立异步的、非阻塞的、事件驱动的服务。它采用Reator作为首选的响应式流的实现库,不过也提供对RxJava的支持。 378 | 379 | 整个技术栈从命令式的、同步阻塞的【spring-webmvc + servlet + Tomcat】变成了响应式的、异步非阻塞的【spring-webflux + Reactor + Netty】。 380 | 381 | JDK8中新特性:CompletableFuture和CompletionStage。 382 | 383 | XmlBeanDefinitionReader; 384 | DefaultListableBeanFactory; 385 | 386 | Spring可用通过4种方式配置bean, 其一基于xml的配置,其二基于xml+注解的配置, 其三基于java+注解的配置,其四基于property文件的配置。 387 | 前两种的配置信息使用XmlBeanDefinitionReader对象来解析;第三种的配置信息使用AnnotatedBeanDefinitionReader对象来解析;最后一种的配置 388 | 信息使用PropertiesBeanDefinitionReader对象来解析。 389 | 390 | Thread.currentThread().getContextClassLoader(); 391 | java.util.ResourceBundle; 392 | 393 | 394 | Forward和Redirect的区别: 395 | 1.forward 396 | request.getRequestDispatcher("new.jsp").forward(request, response);   //转发到new.jsp 397 | 2.redirect 398 | response.sendRedirect("new.jsp");   //重定向到new.jsp 399 | 很明显一个是用request对象调用,一个是用response对象调用,那么,这两者有什么区别呢? 400 | 401 | 一、数据共享方面 402 | forward:转发页面和转发到的页面可以共享request里面的数据 403 | redirect:不能共享数据 404 | 405 | 二、地址栏显示方面 406 | forward是服务器请求资源,服务器直接访问目标地址的URL,把那个URL的响应内容读取过来,然后把这些内容再发给浏览器.浏览器根本不知道服务器发送的内容从哪里来的,所以它的地址栏还是原来的地址. 407 | redirect是服务端根据逻辑,发送一个状态码,告诉浏览器重新去请求那个地址.所以地址栏显示的是新的URL. 408 | 409 | 三、本质区别 410 | 转发是服务器行为,重定向是客户端行为。为什么这样说呢,这就要看两个动作的工作流程:  411 | 412 | 转发过程:客户浏览器发送http请求--->web服务器接受此请求--->调用内部的一个方法在容器内部完成请求处理和转发动作--->将目标资源 发送给客户;在这里,转发的路径必须是同一个web容器下的url,其不能转向到其他的web路径上去,中间传递的是自己的容器内的request。在客 户浏览器路径栏显示的仍然是其第一次访问的路径,也就是说客户是感觉不到服务器做了转发的。转发行为是浏览器只做了一次访问请求。  413 | 414 | 重定向过程:客户浏览器发送http请求--->web服务器接受后发送302状态码响应及对应新的location给客户浏览器--->客户浏览器发现 是302响应,则自动再发送一个新的http请求,请求url是新的location地址--->服务器根据此请求寻找资源并发送给客户。在这里 location可以重定向到任意URL,既然是浏览器重新发出了请求,则就没有什么request传递的概念了。在客户浏览器路径栏显示的是其重定向的 路径,客户可以观察到地址的变化的。重定向行为是浏览器做了至少两次的访问请求的。  415 | 416 | 417 | 重定向,其实是两次request:第一次,客户端request A,服务器响应,并response回来,告诉浏览器,你应该去B。这个时候IE可以看到地址变了,而且历史的回退按钮也亮了。重定向可以访问自己web应用以外的资源。在重定向的过程中,传输的信息会被丢失。  418 | 419 | 420 | ApplicationContextAware.setApplicationContext(); 421 | InitializingBean.afterPropertiesSet(); 422 | 423 | protected void renderMergedOutputModel(Map model, HttpServletRequest request, HttpServletResponse response) throws Exception; 424 | 425 | WebMvcAutoConfiguration就是对Spring MVC进行自动化配置,取代继承WebMvcConfigurerAdapter的方式。 426 | 427 | SpringBoot通过spring.factories实现了很好的扩展功能。自定义模块相关一般是通过实现对应的接口,并配置到文件中。 428 | 429 | 430 | BeanFactoryUtils.beanOfTypeIncludingAncestors(ListableBeanFactory lbf, Class type, boolean includeNonSingletons, boolean allowEagerInit) throws BeansException; 431 | 432 | 433 | 434 | 435 | 436 | 437 | 438 | 439 | 440 | -------------------------------------------------------------------------------- /202008.txt: -------------------------------------------------------------------------------- 1 | public class MapperScannerConfigurer implements BeanDefinitionRegistryPostProcessor, 2 | InitializingBean, ApplicationContextAware, BeanNameAware { 3 | private String basePackage; 4 | private boolean addToConifg = true; 5 | private String lazyInitialization; 6 | private SqlSessionFactory sqlSessionFactory; 7 | private SqlSesstionTemplate sqlSessionTemplate; 8 | private String sqlSessionFactoryBeanName; 9 | private String sqlSessionTemplateBeanName; 10 | private Class annotationClass; 11 | private Class markerInterface; 12 | private Class mapperFactoryBeanClass; 13 | private ApplicationContext applicationContext; 14 | private String beanName; 15 | private boolean processPropertyPlaceHolders; 16 | private BeanNameGenerator nameGenerator; 17 | ... 18 | } 19 | 20 | 一个m阶B+树具有如下几个特征(和B-树的区别): 21 | 1. 有k个子树的中间节点包含k个元素(B树是k-1个元素),每个元素不保存数据,只用来索引,所有数据都保存在叶子节点。 22 | 2. 所有的叶子节点中包含了全部元素的信息,及指向含这些元素记录的指针,且叶子节点本身依据关键字的大小自小而大顺序链接。 23 | 3. 所有的中间节点元素都同时存在于子节点,在子节点元素中是最大(或最小)元素。 24 | 25 | public interface RSocket extends Availability, Closable { 26 | Mono fireAndForget(Payload payload); 27 | Mono requestResponse(Payload payload); 28 | Flux requestStream(Payload payload); 29 | Flux requestChannel(Publisher payloads); 30 | Mono metadataPush(Payload payload); 31 | } 32 | 33 | /** 34 | * Internal utility used to load {@link AutoConfigurationMetadata}. 35 | */ 36 | final class AutoConfigurationMetadataLoader { 37 | 38 | protected static final String PATH = "META-INF/spring-autoconfigure-metadata.properties"; 39 | 40 | private AutoConfigurationMetadataLoader() { 41 | } 42 | 43 | public static AutoConfigurationMetadata loadMetadata(ClassLoader classLoader) { 44 | return loadMetadata(classLoader, PATH); 45 | } 46 | 47 | static AutoConfigurationMetadata loadMetadata(ClassLoader classLoader, String path) { 48 | try { 49 | Enumeration urls = (classLoader != null ? classLoader.getResources(path) 50 | : ClassLoader.getSystemResources(path)); 51 | Properties properties = new Properties(); 52 | while (urls.hasMoreElements()) { 53 | properties.putAll(PropertiesLoaderUtils 54 | .loadProperties(new UrlResource(urls.nextElement()))); 55 | } 56 | return loadMetadata(properties); 57 | } 58 | catch (IOException ex) { 59 | throw new IllegalArgumentException( 60 | "Unable to load @ConditionalOnClass location [" + path + "]", ex); 61 | } 62 | } 63 | 64 | static AutoConfigurationMetadata loadMetadata(Properties properties) { 65 | return new PropertiesAutoConfigurationMetadata(properties); 66 | } 67 | 68 | /** 69 | * {@link AutoConfigurationMetadata} implementation backed by a properties file. 70 | */ 71 | private static class PropertiesAutoConfigurationMetadata 72 | implements AutoConfigurationMetadata { 73 | 74 | private final Properties properties; 75 | 76 | PropertiesAutoConfigurationMetadata(Properties properties) { 77 | this.properties = properties; 78 | } 79 | 80 | @Override 81 | public boolean wasProcessed(String className) { 82 | return this.properties.containsKey(className); 83 | } 84 | 85 | @Override 86 | public Integer getInteger(String className, String key) { 87 | return getInteger(className, key, null); 88 | } 89 | 90 | @Override 91 | public Integer getInteger(String className, String key, Integer defaultValue) { 92 | String value = get(className, key); 93 | return (value != null ? Integer.valueOf(value) : defaultValue); 94 | } 95 | 96 | @Override 97 | public Set getSet(String className, String key) { 98 | return getSet(className, key, null); 99 | } 100 | 101 | @Override 102 | public Set getSet(String className, String key, 103 | Set defaultValue) { 104 | String value = get(className, key); 105 | return (value != null ? StringUtils.commaDelimitedListToSet(value) 106 | : defaultValue); 107 | } 108 | 109 | @Override 110 | public String get(String className, String key) { 111 | return get(className, key, null); 112 | } 113 | 114 | @Override 115 | public String get(String className, String key, String defaultValue) { 116 | String value = this.properties.getProperty(className + "." + key); 117 | return (value != null ? value : defaultValue); 118 | } 119 | 120 | } 121 | 122 | } 123 | 124 | ./bin/spark-submit --name "My app" --master local[4] --conf spark.eventLog.enabled=false --conf "spark.executor.extraJavaOptions=-XX:+PrintGCDetails -XX:+PrintGCTimeStamps" myApp.jar 125 | 126 | spark.driver.supervise: If true, restarts the driver automatically if it fails with a non-zero exit status. Only has effect in Spark standalone mode or Mesos cluster deploy mode. 127 | 128 | 129 | By default, Spark provides four codecs: lz4, lzf, snappy, and zstd. You can also use fully qualified class names to specify the codec, e.g: 130 | org.apache.spark.io.LZ4CompressionCodec, 131 | org.apache.spark.io.LZFCompressionCodec, 132 | org.apache.spark.io.SnappyCompressionCodec, 133 | org.apache.spark.io.ZstdCompressionCodec. 134 | 135 | 在启动java应用时,可以添加-XX:NativeMemoryTracking=detail JVM参数,使用命令jcmd VM.native_memory detail可以查看内存分布情况。 136 | 使用strace -f -e"brk,mmap,munmap" -p 追踪向OS申请内存请求,可以发现可疑的内存申请。 137 | 138 | PerpetualCache 139 | 140 | /* HttpRequestHandler that serves static resources in an optimized way according to the guidelines of Page Speed, YSlow, etc. 141 | The "locations" property takes a list of Spring Resource locations from which static resources are allowed to be served by this handler. Resouces could be served from a classpath location, e.g. "classpath:/META-INF/public-web-resources/", allowing convenient packaging and serving of resources such as .js, .css, and others in jar files. 142 | This request handler may also be configured with a resourcesResolver and resouceTransformer chains to support arbitrary resolution and transformation of resources being served. By default a PathResourceResolver simply finds resources based on the configured "locations". An application can configure additional resolvers and transformers such as the VersionResourceResolver which can resolve and prepare URLs for resources with a version in the URL. 143 | This handler also properly evaluates the Last-Modified header (if present) so that a 304 status code will be returned as appropriate, avoiding unnecessary overhead for resources that are already cached by the client. 144 | */ 145 | public class ResourceHttpRequestHandler extends WebContentGenerator implements HttpRequestHandler, 146 | EmbeddedValueResolverAware, InitializingBean, CorsConfigurationSource { 147 | ... 148 | } 149 | 150 | SQL语句的执行涉及到多个组件,包括MyBatis的四大核心: Executor, StatementHandler, ParameterHandler, ResultSetHandler。 151 | 152 | MyBatis层级结构各个组件,简单如下: 153 | 1. SqlSession: MyBatis核心API,主要用来执行命令、获取映射、管理事务。接收Statement Id和参数,并返回操作结果。 154 | 2. Executor: 执行器,是MyBatis调度的核心,负责SQL语句的生成以及查询缓存的维护。 155 | 3. StatementHandler: 封装了JDBC Statement操作,负责对JDBC Statement的操作,如设置参数、将Statement结果集转换成List集合。 156 | 4. ParameterHandler: 负责对用户传递的参数转换成JDBC Statement所需要的参数。 157 | 5. ResultSetHandler: 负责将JDBC返回的ResultSet结果集对象转换成List类型的集合。 158 | 6. TypeHandler: 用于Java类型和JDBC类型之间的转换; 159 | 7. MappedStatement: 动态SQL的封装; 160 | 8. SqlSource: 表示从XML文件或注释读取的映射语句的内容,它创建经从用户接收的输入参数传递给数据库的SQL。 161 | 9. Configuration: MyBatis所有的配置信息都维护在Configuration对象之中。 162 | 163 | 可以重写类型处理器或创建自己的类型处理器来处理不支持或非标准的类型。具体做法为: 实现org.apache.ibatis.type.TypeHandler接口,或者继承一个很方便的类org.apache.ibatis.type.BaseTypeHandler,然后可以选择性地将它映射到一个JDBC类型。 164 | 165 | MyBatis的事务管理分为两种形式: 166 | 1. 使用JDBC的事务管理机制:即利用java.sql.Connection对象完成事务的提交、回滚、关闭等; 167 | 2. 使用Managed的事务管理机制:这种机制MyBatis自身不会去实现事务管理,而是让程序的容器(JBoss, Weblogic)来实现对事务的管理; 168 | 169 | 170 | /* Enables the binding of targets annotated with @Input and @Output to a broker, according to the list of interfaces passed as value to the annotation. 171 | */ 172 | @Target({ElementType.TYPE, ElementType.ANNOTATION_TYPE}) 173 | @Retention(RetentionPolicy.RUNTIME) 174 | @Documented 175 | @Inherited 176 | @Configuration 177 | @Import({BindingServiceConfiguration.class, BindingBeansRegistrar.class, 178 | BinderFactoryConfiguration.class, SpelExpressionConverterConfiguration.class}) 179 | @EnableIntegration 180 | public @interface EnableBinding { 181 | Class[] value() default {}; 182 | } 183 | 184 | public class ExceptionHandlerExceptionResolver extends AbstractHandlerMethodExceptionResolver 185 | implements ApplicationContextAware, InitializingBean { 186 | private List customArgumentResolvers; 187 | private HandlerMethodArgumentResolverComposite argumentResolvers; 188 | private List customReturnValueHandlers; 189 | private HandlerMethodReturnValueHandlerComposite returnValueHandlers; 190 | private List> messageConverters; 191 | private ContentNegotiationManager contentNegotiationManager = new ContentNegotiationManager(); 192 | private final List responseBodyAdvice = new ArrayList<>(); 193 | private ApplicationContext applicationContext; 194 | private final Map, ExceptionHandlerMethodResolver> exceptionHandlerCache = 195 | new ConcurrentHashMap<>(64); 196 | private final Map exceptionHandlerAdviceCache = 197 | new LinkedHasnMap<>(); 198 | ... 199 | } 200 | 201 | RxJava2 features several base classes you can discover operators on: 202 | 1. io.reactivex.Flowable: 0..N flows, supporting Reactive Streams and backpressure; 203 | 2. io.reactivex.Obserable: 0..N flows, no backpressure; 204 | 3. io.reactivex.Single: a flow of exactly 1 item or an error; 205 | 4. io.reactivex.Completable: a flow without items but only a completion or error signal; 206 | 5. io.reactivex.Maybe: a flow with no items, exactly one item or an error; 207 | 208 | Linux dmesg命令用于显示开机信息。kernel会将开机信息存储在ring buffer中。若是开机来不及查看信息,可利用dmesg来查看。开机信息保存在/var/log目录中,名为dmesg的文件里。 209 | 210 | dumpe2fs: dump ext2/ext3 filesystem information 211 | ----------------------------- 212 | MyBatis: 213 | XMLConfigBuilder 214 | XMLMapperBuilder 215 | XMLStatementBuilder 216 | ErrorContext.instance(); 217 | MappedStatement 218 | StatementType 219 | ResultSetType 220 | SqlCommandType 221 | SqlSource 222 | DynamicSqlSource 223 | MappedStatementAssistant 224 | DefaultResultSetHandler 225 | ----------------------------- 226 | public interface BeanMetadataElement { 227 | @Nullable 228 | Object getSource(); 229 | } 230 | 231 | public interface ComponentDefinition extends BeanMetadataElement { 232 | String getName(); 233 | String getDescription(); 234 | BeanDefinition[] getBeanDefinitions(); 235 | BeanDefinition[] getInnerBeanDefintions(); 236 | BeanReference[] getBeanReferences(); 237 | } 238 | BeanReference <- RuntimeBeanNameReference 239 | 240 | public interface ServletContextListener extend EventListener { 241 | public default void contextInitialized(ServletContextEvent sce) {} 242 | public default void contextDestroyed(ServletContextEvent sce) {} 243 | } 244 | 245 | Tomcat容器的责任链模式: 246 | 1. 请求被Connector组件接收,创建Request和Response对象; 247 | 2. Connector将Request和Response交给Container,先通过Engine的pipeline组件流经内部的每个Valve; 248 | 3. 请求流转到Host的pipeline组件中,并且经过内部Valve的过滤; 249 | 4. 请求流转到Context的pipeline组件中,并且经过内部的Valve的过滤; 250 | 5. 请求流转到Wrapper的pipeline组件中,并且经过内部的Valve的过滤; 251 | 6. Wrapper内部的WrapperValve创建FilterChain实例,调用指定的Servlet实例处理请求; 252 | 7. 返回; 253 | 254 | MyBatis解析XML文件的核心流程: 255 | 1. 属性解析propertiesElement; 256 | 2. 加载settings节点settingsAsProperties 257 | 3. 加载自定义VFS loadCustomVfs; 258 | 4. 解析类型别名typeAliasesElement; 259 | 5. 加载插件pluginElement; 260 | 6. 加载对象工厂objecyFactoryElement; 261 | 7. 创建对象包装器工厂objectWrapperFactoryElement; 262 | 8. 加载反射工厂reflectorFactoryElement; 263 | 9. 元素设置settingsElement; 264 | 10. 加载环境配置environmentsElement; 265 | 11. 数据库厂商标识加载databaseProviderElement; 266 | 12. 加载类型处理器typeHandlerElement; 267 | 13. (核心)加载mapper文件mapperElement; 268 | 269 | public class MapperScannerConfigurer implements BeanDefinitionRegistryPostProcessor, InitializingBean, ApplicationContextAware, BeanNameAware { 270 | 271 | private String basePackage; 272 | 273 | private boolean addToConfig = true; 274 | 275 | private SqlSessionFactory sqlSessionFactory; 276 | 277 | private SqlSessionTemplate sqlSessionTemplate; 278 | 279 | private String sqlSessionFactoryBeanName; 280 | 281 | private String sqlSessionTemplateBeanName; 282 | 283 | private Class annotationClass; 284 | 285 | private Class markerInterface; 286 | 287 | private ApplicationContext applicationContext; 288 | 289 | private String beanName; 290 | 291 | private boolean processPropertyPlaceHolders; 292 | 293 | private BeanNameGenerator nameGenerator; 294 | ... 295 | } 296 | 297 | /* Handler execution chain, consisting of handler object and any handler interceptors. 298 | Returned by HandlerMapping's HandlerMapping#getHandler(). 299 | */ 300 | public class HandlerExecutionChain { 301 | ... 302 | private final Object handler; 303 | @Nullable 304 | private HandlerInterceptor[] interceptors; 305 | @Nullable 306 | private List interceptorList; 307 | private int interceptorIndex = -1; 308 | ... 309 | } 310 | 311 | /* BeanFactory that enables injection of MyBatis mapper interfaces. It can be set up with a SqlSessionFactory or apre-configured SqlSessionTemplate. 312 | 313 | Sample configuration: 314 | 315 | 316 | 317 | 318 | 319 | 320 | 321 | 322 | 323 | 324 | 325 | Note that this factory can only inject interfaces, not concrete classes. 326 | */ 327 | public class MapperFactoryBean extends SqlSessionDaoSupport implements FactoryBean { 328 | private Class mapperInterface; 329 | private boolean addToConfig = true; 330 | ... 331 | } 332 | 333 | Linux主流服务程序启动管理方式有以下几种: 334 | 1. daemon 335 | 2. sysvinit 336 | 3. systemd 337 | 4. nohup 338 | 339 | TCP Wrapper是一个开源软件,用来分析TCP/IP封包的软件,类似的还有iptables. Linux默认安装了tcp_wrapper. 作为一个安全的系统,Linux本身有两层安全防火墙,通过IP过滤机制的iptables实现第一层防护。iptables防火墙通过直观的监视系统的运行状况,阻挡网络中的恶意攻击,保护整个系统正常运行免遭攻击和破坏。如果通过第一层防护,那么下一层防护就是tcp_wrapper。通过tcp_wrapper可以实现对系统中提供的某些服务的开放和关闭、允许与禁止,从而更有效的保护系统安全运行。使用tcp_wrapper的功能仅需要两个配置文件:/etc/hosts.allow和/etc/hosts.deny. 340 | 341 | Jaeger Spark dependencies: This is a Spark job that collects spans from storage, analyze links between services, and stores them for later presentation in the UI. Note that it is needed for the production deployment. This job parses all traces on a given day, based on UTC. By default, it processes the current day, but other days can be explicitly specified. 342 | 343 | 除了线程池,还有其他几种比较典型的池化思想: 344 | 1. 内存池(Memory Pooling): 预先申请内存,提升申请内存速度,减少内存碎片; 345 | 2. 连接池(Connection Pooling): 预先申请数据库连接,提升申请连接的速度,降低系统的开销; 346 | 3. 实例池(Object Pooling): 循环使用对象,减少资源在初始化和释放时的昂贵损耗; 347 | 348 | ThreadPoolExecutor中的Worker是通过继承AQS,使用AQS来实现独占锁功能,没有使用可重入锁ReentrantLock,而是使用AQS,为的就是实现不可重入的特性去反应线程现在的执行状态,具体如下: 349 | 1. lock方法一旦获取了独占锁,表示当前线程正在执行任务中。 350 | 2. 如果正在执行任务,则不应该中断线程。 351 | 3. 如果线程现在不是独占锁的状态,也就是空闲状态,说明它没有处理任务,这时可以对该线程进行中断。 352 | 4. 线程池在执行shutdown方法或tryTerminate方法时会调用interruptIdleWorkers()方法来中断空闲的线程,interruptIdleWorkers方法会使用tryLock方法来判断线程池中的线程是否是空闲状态;如果线程是空闲状态则可以安全回收。 353 | 354 | Tomcat中的类加载器没有遵循双亲委派模型,原因如下: 355 | 1. 解决不同应用中使用某一第三方库的不同版本; 356 | 2. 部署在同一个web容器中相同的类库相同的版本可以共享; 357 | 3. Tomcat作为应用服务器,也有自己所依赖的类,需要与web应用隔离; 358 | 4. 支持jsp的热加载; 359 | 360 | 在TCP三次握手的时候,Linux内核会维护两个队列: 361 | 1. 半连接队列,也称为SYN队列; 362 | 2. 全连接队列,也称为Accept队列; 363 | 服务端收到客户端发起的 SYN 请求后,内核会把该连接存储到半连接队列,并向客户端响应 SYN+ACK,接着客户端会返回 ACK,服务端收到第三次握手的 ACK 后,内核会把连接从半连接队列移除,然后创建新的完全的连接,并将其添加到 accept 队列,等待进程调用 accept 函数时把连接取出来。 364 | 365 | TCP全连接队列最大值取决于somaxconn和backlog之间的最小值,也就是min(somaxconn, backlog)。 366 | 查看当前TCP半连接队列长度: netstat -natp | grep SYN_RECV | wc -l 367 | 368 | Linux内核中会因为三个条件而drop半连接: 369 | 1. 如果半连接队列满了,并且没有开启tcp_syncookies,则会丢弃; 370 | 2. 若全连接队列满了,且没有重传SYN+ACK包的连接请求多于1个,则会丢弃; 371 | 3. 如果没有开启tcp_syncookies,并且max_syn_backlog减去当前半连接队列长度小于(max_syn_backlog >> 2),则会丢弃; 372 | 开启tcp_syncookies是缓解SYN攻击的一种手段。 373 | 374 | syncookies参数主要有以下三个值: 375 | 1. 0:表示关闭该功能; 376 | 2. 1: 表示仅当SYN半连接队列放不下时,再启用它; 377 | 3. 2: 表示无条件开启功能; 378 | 379 | 几种防御SYN攻击的方法: 380 | 1. 增大半连接队列; 381 | 2. 开启tcp_syncookies功能; 382 | 3. 减少SYN+ACK重传次数;当服务端受到SYN攻击时,就会有大量处于SYN_RECV状态的TCP连接,处于这个状态的TCP会重传SYN+ACK,当重传次数超过上限后,就会断开连接。针对SYN攻击的场景,可以减少SYN+ACK重传次数,以加快处于SYN_RECV状态的TCP连接断开。 383 | 384 | top中的CPU指标: 385 | 5.9%us — 用户空间占用CPU的百分比。 386 | 3.4% sy — 内核空间占用CPU的百分比。 387 | 0.0% ni — 改变过优先级的进程占用CPU的百分比 388 | 90.4% id — 空闲CPU百分比 389 | 0.0% wa — IO等待占用CPU的百分比 390 | 0.0% hi — 硬中断(Hardware IRQ)占用CPU的百分比 391 | 0.2% si — 软中断(Software Interrupts)占用CPU的百分比 392 | 393 | 火焰图是基于perf结果产生的SVG图片,用来展示CPU的调用栈。Y轴表示调用栈,每一层都是一个函数。调用栈越深,火焰就越高,顶部就是正在执行的函数,下方都是它的父函数。X轴表示抽样数,如果一个函数在x轴占据的宽度越宽,就表示它被抽到的次数多,即执行的时间长。注意,X轴不代表时间,而是所有的调用栈合并后,按字母顺序排列的。火焰图就是看顶层的哪个函数占据的宽度最大。只要有"平顶"(plateaus),就表示该函数可能存在性能问题。颜色没有特殊含义,因为火焰图表示的是CPU的繁忙程度,所以一般选择暖色调。火焰图是SVG图片,可以与用户互动。 394 | 395 | /proc//smaps:反应了运行时进程的内存映像,系统的运行时库(so),堆,栈信息均可在其中看到。 396 | UNIX domain sockets又叫IPC(inter-process communication进程间通信)socket,用于实现同一主机上的进程间通信。socket原本是为网络通讯设计的,但后来在socket的框架上发展出一种IPC机制,就是Unix domain socket。虽然网络socket也可用于同一台主机的进程间通讯(通过loopback地址127.0.0.1),但是UNIX domain socket用于IPC更有效率:不需要经过网络协议栈,不需要打包拆包、计算校验和、维护序号和应答等,只是将应用数据从一个进程拷贝到另外一个进程。这是因为,IPC机制本质上是可靠的通讯,而网络协议是为不可靠的通讯设计的。 397 | 398 | 399 | 400 | -------------------------------------------------------------------------------- /201711.txt: -------------------------------------------------------------------------------- 1 | 在JDK8之前,分层编译默认是关闭的,可以添加-server -XX:+TieredCompilation参数进行开启。 2 | 逃逸分析并不是直接的优化手段,而是一个代码分析,通过动态分析对象的作用域,为其他优化手段如栈上分配,标量替换和同步消除等提供依据,发生逃逸行为的情况有两种: 3 | 1. 方法逃逸:当一个对象在方法中定义之后,作为参数传递到其他方法中。 4 | 2. 线程逃逸:如类变量或实例变量,可能被其他线程访问到; 5 | 如果不存在逃逸行为,则可以对该对象进行如下优化:同步消除,标量替换和栈上分配。 6 | 1. 同步消除 7 | 线程同步本身比较耗时,如果确定一个对象不会逃逸出线程,无法被其他线程访问到,那该对象的读写就不会存在竞争,则可以消除对该对象的同步锁,通过-XX:+EliminateLocks可以开启同步消除 8 | 2. 标量替换 9 | 1. 标量是指不可分割的量,如java中基本数据类型和reference类型,相对的一个数据可以继续分解,称聚合量 10 | 2. 如果把一个对象拆散,将其成员变量恢复到基本类型来访问就叫做标量替换; 11 | 3. 如果逃逸分析发现一个对象不会被外部访问,并且该对象可以被拆散,那么经过优化之后,并不直接生成该对象,而是在栈上创建若干成员变量; 12 | 通过-XX:+EliminateAllocations可以开启标量替换,-XX:+PrintEliminateAllocations查看标量替换情况。 13 | 3. 栈上分配 14 | 目前HotSpot并没有真正意义上的栈上分配,实际上是标量替换。 15 | 16 | 分层编译和逃逸分析在JDK8中是默认开启的。可以使用-XX:-DOEscapAnalysis关闭逃逸分析。 17 | -XX:CompileThreshold 18 | cglib: Code Generation Library,底层采用asm字节码生成框架生成代理类的字节码。 19 | 20 | jdk和cglib动态代理实现的区别: 21 | 1. jdk动态代理生成的代理类和委托类实现了相同的接口; 22 | 2. cglib动态代理中生成的字节码更加复杂,生成的代理类是委托类的子类,且不能处理被final修饰的方法; 23 | 3. jdk采用反射机制调用委托类的方法,cglib采用类似索引的方式直接调用委托类方法。 24 | 25 | 在JDK7、8中,可以通过-XX:StringTableSize参数StringTable大小 26 | 27 | Java源代码被编译成class字节码,最终需要加载到虚拟机中才能运行,整个生命周期包括:加载,验证,准备,解析,初始化,使用,卸载。 28 | 29 | 垃圾收集算法主要有三种:标记-清除,复制和标记-整理。 30 | 1. 标记-清除:对待回收的对象进行标记。 31 | 算法缺点:效率问题,标记和清除过程效率很低;空间问题,收集之后会产生大量的内存碎片,不利于大对象的分配。 32 | 2. 复制算法:将可用内存分成大小相等的两块,每次只使用其中一块,互相复制,并清除即将使用的那块。不仅提高了标记的效率,因为只需要标记存活的对象,同时也避免了内存碎片的问题,代价是可用内存缩小为原来的一半。 33 | 3. 标记-整理:在老年代中,对象存活率较高,复制算法的效率很低。在标记-整理算法中,标记出所有存活的对象,并移动到一端,然后直接清理边界以外的内存。 34 | 35 | 总过有7中垃圾收集器的组合: 36 | 1. 新生代: Serial + 老年代: Serial Old 37 | 2. 新生代: Serial + 老年代: CMS 38 | 3. 新生代: ParNew + 老年代: Serial Old 39 | 4. 新生代: ParNew + 老年代: CMS 40 | 5. 新生代: Parallel Scavenage + 老年代: Serial Old 41 | 6. 新生代: Parallel Scavenage + 老年代: Parallel Old 42 | 7. 新生代和老年代: G1 43 | 44 | 吞吐量 = 用户代码运行时间/(用户代码运行时间 + 垃圾收集时间) 45 | Paralle Scavenge提供了两个参数用于精确控制吞吐量: 46 | 1. -XX:MaxGCPauseMillis 设置垃圾收集的最大停顿时间 47 | 2. -XX:GCTimeRatio 设置吞吐量大小 48 | 49 | AbstractQueuedSynchronizer 50 | 51 | Unsafe.getAndAddInt 52 | public final int getAndAddInt(Object var1, long var2, int var4) { 53 | int var5; 54 | do { 55 | var5 = this.getIntVolatile(var1, var2); 56 | } while (!this.compareAndSwapInt(var1, var2, var5, var5 + var4)); 57 | return var5; 58 | } 59 | 60 | BlockingQueue workQueue; 61 | 1. ArrayBlockingQueue 62 | 2. PriorityBlockingQueue 63 | 3. SynchronousQueue 64 | 4. LinkedBlockingQueue 65 | 66 | public class ThreadPoolExecutor extends AbstractExecutorService { 67 | private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0)); 68 | private static final int COUNT_BITS = Integer.SIZE - 3; 69 | private static final int CAPACITY = (1 << COUNT_BITS) - 1; 70 | 71 | // runState is stored in the high-order bits 72 | private static final int RUNNING = -1 << COUNT_BITS; 73 | private static final int SHUTDOWN = 0 << COUNT_BITS; 74 | private static final int STOP = 1 << COUNT_BITS; 75 | private static final int TIDYING = 2 << COUNT_BITS; 76 | private static final int TERMINATED = 3 << COUNT_BITS; 77 | 78 | // Packing and unpacking ctl 79 | private static int runStateOf(int c) { return c & ~CAPACITY; } 80 | private static int workerCountOf(int c) { return c & CAPACITY; } 81 | private static int ctlOf(int rs, int wc) { return rs | wc; } 82 | } 83 | 84 | 在ThreadPoolExecutor中的TIDYING状态: All tasks have terminated, workerCount is zero, the thread transitioning to state TIDYING will run the terminated() hook method. 85 | 86 | objectFieldOffset: 能够获取到指定实例变量的在对象内存中的偏移量 87 | long offset = UNSAFE.objectFieldOffset(xx.class.getDeclaredField("state")); 88 | 89 | JDK8的实现抛弃了JDK7中的Segment分段锁,利用CAS+Synchronized来保证并发更新的安全,底层采用数组+链表+红黑树的存储结构。 90 | 91 | Aside from its content, the essential properties of a buffer are its capacity, limit, and position. 92 | 93 | ByteBuffer有几个实现:HeapByteBuffer和DirectByteBuffer 94 | 95 | NIO把支持的I/O对象抽象为Channel,Channel又称为通道,类似于I/O中的流Stream,但有所区别: 96 | 1. Stream是单向的,Channel是双向的,可读可写; 97 | 2. Stream读写是阻塞的,Channel可以异步读写; 98 | 3. Stream中的数据可以选择性的先读到缓存中,Channel的数据总是先读到一个缓存中,或总缓存写入。 99 | 常用的Channel实现类如下: 100 | 1. FileChannel 101 | 2. DatagramChannel 102 | 3. SocketChannel 103 | 4. ServerSocketChannel 104 | 105 | MappedByteBuffer 106 | 107 | private static boolean isPowerOfTwo(int val) { 108 | return (val & -val) == va; 109 | } 110 | 8 111 | 原码: 0000 1000 112 | 反码: 1111 0111 113 | 补码: 1111 1000 114 | 115 | 116 | Hashtable是怎么加锁的: 在每个有线程安全的函数里用synchronized修饰,包括size()和isEmpty(),定位数组的方法是:int index = (hash & 0x7FFFFFFF) % tab.length;,这种去余的计算比较耗时。 117 | 118 | Netty的boss线程主要负责监听并处理accept事件,将socketChannel注册到work线程的selector,由worker线程来监听并处理read事件。 119 | Spring中如何保证Controller并发安全: 由于默认scope是singleton,如果Controller有实例变量或者静态变量,多个线程访问时会有线程安全问题。解决思路如下, 1. 不可变对象,用final修饰变量;2. 线程封闭方式:使用ThreadLocal或者函数局部变量;3. 在类上增加@Scope("prototype") 120 | 121 | 三个坑:1. (Short)i - 1的类型为Integer,包装类型;2. 三元运算符的"双目数值提升";3. Object[]转Long[](涉及Set.toArray(new T[])) 122 | 123 | 三元运算符的双目数值提升的语言特性。所谓的双目数值提升,在三元运算符java开发的环境下可以简单的理解为双目运算符的类型转换问题,其具体规则总结如下: 124 | 1. 如果定义了数据类型的变量与未定义变量的数值共同参与三元运算符的后双目运算,那么返回结果就是范围大(精度高)类型; 125 | 2. 如果两个定义了数据类型的变量共同参与三元运算符的后双目运算,那么返回的结果就是范围大(精度高)类型。 126 | 3. 如果直接进行数值的比较,会自动转型成为范围大(精度高)的数据类型 。 127 | 128 | ArrayList的toArray(T[] a)的源码实现: 129 | 130 | @SuppressWarning("unchecked") 131 | public T[] toArray(T[] a) { 132 | if (a.length < size) { 133 | // Make a new array of a's runtime type, but my contents: 134 | return (T[]) Arrays.copyOf(elementData, size, a.getClass()); 135 | } 136 | System.arraycopy(elementData, 0, a, 0, size); 137 | if (a.length > size) { 138 | a[size] = null; 139 | } 140 | return a; 141 | } 142 | 143 | JVM发生OOM的8种原因及解决办法: 144 | 1. 堆内存不足: java.lang.OutOfMemoryError: Java heap space 145 | 1.1 原因:1:代码中可能存在大对象分配;2:可能存在内存泄露,导致多次GC后,还是无法找到一块足够大的内存容纳当前对象。 146 | 1.2 解决方法: 147 | 1.2.1 检查是否存在大对象的分配,最有可能是大数组分配 148 | 1.2.2 通过jmap,将堆dump下来,使用mat分析,检查是否有内存泄露 149 | 1.2.3 如果没有明显的内存泄露,使用-Xmx加大堆内存 150 | 1.2.4 检查是否有大量的自定义的Finalizable对象,也有可能是框架内部提供的,考虑其存在的必要性 151 | 2. 永久代/元空间溢出:java.lang.OutOfMemoryError: PermGen space 152 | java.lang.OutOfMemoryError: Metaspace 153 | 2.1 原因: 1:在JDK7之前,频繁的错误使用String.intern方法;2. 生成了大量的代理类,导致方法区被撑爆,无法卸载; 3. 应用长时间运行,没有重启 154 | 2.2 解决方法: 155 | 2.2.1 检查永久代或元空间设置的是否过小 156 | 2.2.2 检查代码中是否有大量的反射操作 157 | 2.2.3 dump之后通过mat检查是否存在大量反射生成的代理类 158 | 2.2.4 重启JVM 159 | 3. GC overhead limit exceeded: java.lang.OutOfMemoryError: GC overhead limit exceeded 160 | 3.1 原因: 这是JDK6新加入的错误类型,一般都是堆太小导致的。超过98%的时间用来做GC而且回收了不到2%的堆内存时会抛出此异常 161 | 3.2 解决方法: 162 | 3.2.1 检查项目中是否有大量的死循环或有使用大内存的代码,优化代码 163 | 3.2.2 添加参数-XX:-UseGCOverheadLimit禁用这个检查,其实这个参数解决不了内存问题,只是把错误的信息延后,最终出现java.lang.OutOfMemoryError: java heap space 164 | 3.2.3 dump内存,检查是否存在内存泄露,如果没有加大内存。 165 | 4. 方法栈溢出: java.lang.OutOfMemoryError: unable to create new native Thread 166 | 4.1 原因: 创建了大量的线程 167 | 4.2 解决方法: 168 | 4.2.1 通过-Xss降低每个线程栈的大小 169 | 4.2.2 线程总数也受到系统空闲内存和操作系统的限制,检查以下配置: 170 | /proc/sys/kernel/pid_max 171 | /proc/sys/kernel/thread-max 172 | max_user_process(ulimit -u) 173 | /proc/sys/vm/max_map_count 174 | 5. 分配超大数组(非常规溢出): java.lang.OutOfMemoryError: Requested array size exceeds VM limit 175 | 检查要分配的数组在该系统是否可以寻址(addressable)。解决方法:检查有无创建超大数组的代码 176 | 6. swap区溢出(非常规溢出):java.lang.OutOfMemoryError: Out of swap space 177 | 原因:1. swap区大小分配不足;2. 其他进程消耗了所有的内存 178 | 解决方法:1. 其他服务进程选择性的移除;2. 加大swap分区的大小,或者加大机器内存。 179 | 7. 本地方法溢出:java.lang.OutOfMemoryError: stack_trace_with_native_method 180 | 方法栈的溢出发生在JVM代码层面,而本地方法溢出发生在JNI代码或本地方法处。 181 | 182 | 在windows中,可以nslookup www.baidu.com 验证域名的ip地址是否可用 183 | 184 | MySQL分页查询优化方法: 185 | 1. 尽量给出查询的大致范围;比如 select * from table where id >= 20000 limit 10; 186 | 2. 子查询法; select * from table where id >= (select id from table limit 20000, 1) limit 10; 187 | 3. 只读索引方法; 188 | 优化前:select * from member order by last_active limit 50, 5; 189 | 优化后: select * from member inner join (select member_id from member order by last_active limit 50, 5) using (member_id); 190 | 优化前的SQL需要更多I/O浪费,因为先读索引,再读数据,然后抛弃无需的行。而优化后的SQL(子查询那条)只读索引(Cover index)就可以了,然后通过member_id读取需要的列。 191 | 4. 首先读取出ID,然后再用in读取所需的记录。如下: 192 | select id from table limit 20000, 10; 193 | select * from table where id in (上面查出来的id列表); 194 | 195 | join分页方式(逆序排列)的范例SQL: 196 | select * from content as t1 join 197 | (select id from content order by id desc limt (${page - 1}) * ${pageSize}, 1) as t2 198 | where t1.id <= t2.id order by t1.id desc limit ${pageSize}; 199 | 200 | 可以使用explain SQL大致查看SQL的扫描行数,使用索引情况等。 201 | 202 | StringBuilder建议设置初始长度(默认初始长度为16),防止append过程中,多次扩容,导致大量的Arrays.copyOf(); 203 | StringBuilder的toString方法如下: 204 | @Override 205 | public String toString() { 206 | // Create a copy, don't share the array 207 | return new String(value, 0, count); 208 | } 209 | 这个toString会导致浪费一半的字符串,可以重用StringBuilder来达到重用里面的char[]。 210 | 211 | -XX:+OptimizeStringConcat(JDK7u40后默认开启),会把相邻的(中间没有隔着控制语句)的StringBuilder合成一个,也会尽量设置长度。 212 | 213 | public final class StringBuilder extends AbstractStringBuilder 214 | implements java.io.Serializable, CharSequence {...} 215 | public final class StringBuffer extends AbstractStringBuilder 216 | implements java.io.Serializable, CharSequence {...} 217 | 218 | SO_LINGER: Socket延时关闭,优雅关闭。其底层的数据结构如下: 219 | typedef struct linger { 220 | u_short l_onoff; //开关,0或非0 221 | u_short l_linger; //优雅关闭最长时限 222 | } linger; 223 | SO_LINGER: 在默认情况下,当调用close关闭socket时,close会立即返回,但是如果send buffer中还有数据,系统会尝试把send buffer中的数据发送出去,然后close才返回。SO_LINGER选项是用来修改这种默认操作的。 224 | 有三种情况的设置: 225 | 1. l_onoff=0,l_linger值被忽略。将会关闭SO_LINGER选项,即TCP或SCTP保存默认操作:close立即返回。 226 | 2. l_onoff非0,l_linger=0。当调用close的时候,TCP连接会立即断开,send buffer中未被发送的数据将被丢弃,并向对方发送一个RST信息。由于这种方式是非正常挥手方式结束TCP连接。所以TCP连接不会进入TIME_WAIT状态,这样会导致新建立的连接造成混乱。 227 | 3. l_onoff和l_linger都非0。当调用close关闭socket时,内核会延迟,如果send buffer中还有数据没有发送,该进程会被休眠直到下列任何情况发生: 228 | 3.1 send buffer中的所有数据都被发送并且得到对方TCP的应答消息 229 | 3.2 延迟时间消耗完。在延迟时间被消耗完后,send buffer中的所有数据将会被丢弃。 230 | 上面1,2两种情况中,如果socket被设置未O_NONBLOCK状态,程序将不会等待close返回,send buffer中的所有数据都将会丢弃。所以需要判断close的返回值,在send buffer中的所有数据都被发送之前并且延迟时间没有消耗完,close返回的话,close将会返回一个EWOULDBLOCK的error。 231 | 232 | 常用的分布式配置中心: 233 | 1. Spring Cloud Config: 不支持热加载,按需获取配置(每次都是将所有配置刷新,通过refresh ApplicationContext上下文的方式),只支持Spring项目;不支持推送模式;需要依赖git,没有界面,需要手工批量刷新。 234 | 2. Consul: 一致性算法使用Raft。使用Gossip协议来传递信息,支持多个数据中心,有可视化界面。 235 | 3. Zookeeper: 除自研外,还可以使用Spring Cloud Zookeeper 236 | 4. Disconf: 支持传统的配置文件配置;也支持KV结构数据;使用ZK的Watch机制来做配置的实时修改; 237 | 5. Apollo: 能够集中化管理不同环境,不同集群的配置,配置修改后能够实时推送到应用端,并且具备规范的权限,流程治理等特性。还有版本发布管理,灰度发布功能。 238 | 6. Diamond: 每隔15秒拉一次全量数据,只支持KV结构数据,数据模型不支持文件; 阿里开源版本太简单,前端很丑,2年前已经不维护了.github Star 64 239 | 7. Nacos:并不是通过推的方式将服务端最新的配置信息发送给客户端,而是客户端维护了一个长轮询的任务,定时去拉取发生变更的配置信息,然后将最新的数据推送给Listener的持有者。 240 | 241 | Spring Cloud Zookeeper Features: 242 | 1. Service Discovery: instances can be registered with Zookeeper and clients can discover the instances using Spring-managed beans. 243 | 2. Supports Ribbon, the client side load-balancer via Spring Cloud Netflix. 244 | 3. Supports Zuul, a dynamic router and filter via Spring Cloud Netflix. 245 | 4. Distributed Configuration: using Zookeeper as a data store. 246 | 247 | Access Control Lists(ACLs): 248 | You can add authentication information for Zookeeper ACLs by calling the addAuthInfo method of a CuratorFramework bean. One way to accomplish this is to provide your own CuratorFramework bean. 249 | 250 | Apollo的Features如下: 251 | 1. 同一管理不同环境,不同集群,不同命名空间的配置; 252 | 2. 配置修改实时生效(热发布); 253 | 3. 版本发布管理; 254 | 4. 灰度发布; 255 | 5. 权限管理,发布审核,操作审计; 256 | 6. 客户端配置信息监控(可以方便的看到配置在哪些实例使用) 257 | 7. 提供Java和.Net原生客户端 258 | 8. 提供开放平台API 259 | 9. 部署简单 260 | 261 | Apollo客户端的实现原理: 262 | 1. 客户端和服务端保持了一个长连接,从而能第一时间获得配置更新的推送。 263 | 2. 客户端还会定时从Apollo配置中心服务端拉取应用的最新配置: 264 | 2.1 这是一个fallback机制,为了防止推送机制失效导致配置不更新; 265 | 2.2 客户端定时拉取上报本地版本,所以一般情况下,对于定时拉取的操作,服务端都会返回304(Not Modified) 266 | 2.3 定时频率为5分钟,客户端可以通过在运行时指定System Property: apollo.refreshInterval来覆盖,单位为分钟; 267 | 3. 客户端从Apollo配置中心服务端获取到应用的最新配置后,会保存在内存中; 268 | 4. 客户端会从服务端获取到的配置在本地文件系统缓存一份;在遇到服务不可用,或网络不通时,依然能从本地恢复配置; 269 | 5. 应用从Apollo客户端获取最新的配置,订阅配置更新通知。 270 | 271 | Apollo配置更新推送的实现原理:Apollo客户端和服务端保存了一个长连接,从而第一时间获得配置更新的推送。长连接实际上是通过Http Long Polling实现的,具体如下: 272 | 1. 客户端发起一个Http请求到服务端; 273 | 2. 服务端会保持这个连接60秒 274 | 2.1 如果在60秒内有客户端关心的配置变化,被保持住的客户端请求会立即返回,并告知客户端有配置变化的namespace信息,客户端会据此拉取对应的namespace的最新配置; 275 | 2.2 如果60秒内没有客户端关心的配置变化,那么返回304给客户端; 276 | 3. 客户端收到服务端请求后,立即重新发起连接,回到第一步; 277 | 考虑到数万客户端向服务端发起长连接,在服务端使用了async servlet(Spring DeferredResult)来服务Http Long Polling请求。 278 | 279 | public class WeakHashMap 280 | extends AbstractMap 281 | implements Map { 282 | ... 283 | //The entries int this has table extend WeakReference, using its main ref field as the key. 284 | private static class Entry extends WeakReference implements Map.Entry { 285 | V value; 286 | final int hash; 287 | Entry next; 288 | 289 | Entry(Object key, V value, ReferenceQueue queue, int hash, Entry next) { 290 | super(key, queue); 291 | this.value = value; 292 | this.hash = hash; 293 | this.next = next; 294 | } 295 | @SuppressWarning("unchecked") 296 | public K getKey() { 297 | return (K) WeakHashMap.unmaskNull(get()); 298 | } 299 | public V getValue() { 300 | return value; 301 | } 302 | public V setValue(V newValue) { 303 | V oldValue = value; 304 | value = newValue; 305 | return oldValue; 306 | } 307 | public boolean equals(Object o) { 308 | if (!(o instanceof Map.Entry)) return false; 309 | Map.Entry e = (Map.Entry)o; 310 | K k1 = getKey(); 311 | Object k2 = e.getKey(); 312 | if (k1 == k2 || (k1 != null && k1.equals(k2))) { 313 | V v1 = getValue(); 314 | Object v2 = e.getValue(); 315 | if (v1 == v2 || (v1 != null && v1.equals(v2))) { 316 | return true; 317 | } 318 | } 319 | return false; 320 | } 321 | public int hashCode() { 322 | K k = getKey(); 323 | V v = getValue(); 324 | return Objects.hashCode(k) ^ Objects.hashCode(v); 325 | } 326 | public String toString() { 327 | return getKey() + "=" + getValue(); 328 | } 329 | } 330 | } 331 | 332 | WeakHashMap中的expungeStaleEntries(): 333 | 334 | //Expunges stale entries from the table 335 | 336 | private void expungeStaleEntries() { 337 | for (Object x; (x = queue.poll()) != null;) { 338 | synchronized (queue) { 339 | @SuppressWarnings("unchecked") 340 | Entry e = (Entry)x; 341 | int i = indexFor(e.hash, table.length); 342 | 343 | Entry prev = table[i]; 344 | Entry p = prev; 345 | while (p != null) { 346 | Entry next = p.next; 347 | if (p == e) { 348 | if (prev == e) 349 | table[i] = next; 350 | else 351 | prev.next = next; 352 | // Must not null out e.next; 353 | // stale entries may be in use by a HashIterator 354 | e.value = null; //Help GC 355 | size --; 356 | break; 357 | } 358 | prev = p; 359 | p = next; 360 | } 361 | } 362 | } 363 | } 364 | 365 | public interface BeanFactory { 366 | String FACTORY_BEAN_PREFIX = "&"; 367 | Object getBean(String name) throws BeansException; 368 | T getBean(String name, Class requiredType) throws BeansException; 369 | Object getBean(String name, Object... args) throws BeansException; 370 | T getBean(Class requiredType) throws BeansException; 371 | T getBean(Class requiredType, Object... args) throws BeansException; 372 | boolean containsBean(String name); 373 | boolean isSingleton(String name) throws NoSuchBeanDefinitionException; 374 | boolean isPrototype(String name) throws NoSuchBeanDefinitionException; 375 | boolean isTypeMathc(String name, ResolvableType typeToMatch) throws NoSuchBeanDefinitionException; 376 | boolean isTypeMatch(String name, Class typeToMatch) throws NoSuchBeanDefinitionException; 377 | Class getType(String name) throws NoSuchBeanDefinitionException; 378 | String[] getAliases(String name); 379 | } 380 | 381 | public interface FactoryBean { 382 | T getObject() throws Exception; 383 | Class getObjectType(); 384 | boolean isSingleton(); 385 | } 386 | BufferedOutputStream/BufferedInputStream带有缓冲区的实现,可以避免频繁的磁盘操作,通过设计缓冲区将批量数据进行一次操作。 387 | 388 | 1. 同步和异步是针对应用程序和内核的交互的; 389 | 2. 阻塞和非阻塞是针对在访问数据的时候,根据IO操作的就绪状态来采取的不同方式。阻塞方式下读取或写入函数将一直等待,而非阻塞方式下,读取或写入函数会立即返回一个状态值。 390 | 391 | 392 | BIO:同步阻塞 393 | NIO:采用一种多路复用的机制,利用单线程轮询事件,高效定位就绪的Channel,只是Select阶段是阻塞式的,能有效避免大量连接数,频繁线程的切换带来的性能问题。NIO是同步非阻塞IO,一个线程同步进行轮询检查,Selector不断轮询注册其上的Channel。 394 | AIO: 异步非阻塞IO,在此模式下,用户进程只需要发起一个IO操作然后立即返回,等IO操作真正完成后,应用程序会得到IO操作完成的通知,此时用户进程只需要对数据进行处理就好了,不需要进行实际的IO读写操作,因为真正的IO读取或写入操作已经由内核完成了。 395 | 396 | BIO/NIO/AIO适用场景: 397 | BIO: 连接数目比较小且固定的架构,这种方式对服务器资源要求比较高,并发局限于应用中,是JDK1.4之前的唯一选择, 程序直观简单易理解; 398 | NIO: 连接数目多且连接比较短(轻操作)的架构,比如聊天服务器,并发局限于应用中,编程比较复杂,JDK1.4开始支持; 399 | AIO: 连接数目多且连接比较长(重操作)的架构,比如相册服务器,充分调用操作系统参与并发操作,编程比较复杂,JDK7开始支持。 400 | 401 | Reactor和Proactor模式的主要区别是真正的读取和写入操作是由谁来完成的,Reactor中需要应用程序自己读取或者写入数据。而Proactor模式中,应用程序不需要进行实际的读写过程,它只需要从缓冲区读取或者写入即可,操作系统会读取缓存区或者写入缓存区到真正的IO设备。 402 | 403 | public class HashSet extends AbstractSet 404 | implements Set, Cloneable, java.io.Serializable { 405 | static final long serialVersionUID = *L; 406 | private transient HashMap map; 407 | // Dummy value to associate with an Object int the backing Map 408 | private static final Object PRESENT = new Object(); 409 | ... 410 | } 411 | 412 | Spring中用到的设计模式: 413 | 1. 代理模式: Spring AOP中的JDK动态代理和Cglib动态代理 414 | 2. 责任链(职责链)模式: Spring Web MVC中的HandlerExecutionChain 415 | 3. 组合模式: Spring Web MVC中的WebMvcConfigurerComposite 416 | 4. 适配器模式: Spring Web MVC中的HandlerAdapter 417 | 5. 工厂方法模式: Spring DI中的FactoryBean/factory-method 418 | 6. 单例模式: Spring Bean的默认scope=single 419 | 7. 包装器模式: Spring reactive中的ServerHttpRequestDecorator, ServerHttpResponseDecorator 420 | 8. 观察者模式: Spring Context中的ApplicationListener 421 | 9. 策略模式: ContentNegotiationStrategy 422 | 10. 模板模式: JdbcTemplate, AbstractTemplateView 423 | 11. 原型模型: Bean的scope='prototype' 424 | 12. 构建者模式: BeanDefinitionBuilder, WebHttpHandlerBuilder 425 | 13. 抽象工厂模式: Spring AOP中的AopProxy的两个实现: CglibAopProxy和JdkDynamicAopProxy 426 | 14. 门面模式: 427 | 15. 中介者模式: 428 | 16. 享元模式: 429 | 17. 备忘录模式: 430 | 18. 迭代器模式: 431 | 19. 解释器模式: 432 | 20. 访问者模式: 433 | 21. 桥接模式: 434 | 22. 命令模式: 435 | 23. 状态模式: 436 | 437 | public interface Condition { 438 | void await() throws InterruptedException; 439 | void awaitUninterruptibly(); 440 | long awaitNanos(long nanosTimeout) throws InterruptedException; 441 | boolean await(long time, TimeUnit unit) throws InterruptedException; 442 | boolean awaitUnitl(Date deadline) throws InterruptedException; 443 | void signal(); 444 | void signalAll(); 445 | } 446 | 447 | Condition接口在JDK中只有两个实现,分别是AbstractQueuedSynchroinizer和AbstractQueuedLongSynchronizer中的ConditionObject。 448 | 449 | 450 | -------------------------------------------------------------------------------- /201704.txt: -------------------------------------------------------------------------------- 1 | Kafka如何保证消息的顺序性: 2 | 1. 一个topic,一个partition,一个consumer,内部单线程消费,单线程吞吐量太底,一般不会用这个 3 | 2. 写N个内存queue,具有相同key的数据都到同一个内存queue;然后对应N个线程,每个线程分别消费一个内存queue即可,这样就能保证顺序性。 4 | Spring Cloud如何实现异步调用 5 | 1. 使用Hystrix, 6 | @HystrixCommand(fallbackMethod="xxxfallBack") 7 | public Future helloComsumerAsync() { 8 | return new AsyncResult () { 9 | @Override 10 | public String invoke() { 11 | return restTemplate.getForEntity("xxURL", String.class).getBody; 12 | } 13 | } 14 | } 15 | 2. 响应式调用 16 | public Observable helloConsumerResponse() { 17 | return Observable.create(new Observable.OnSubscribe() { 18 | @Override 19 | public void call(Subscriber subscriber) { 20 | String body = restTemplate.getForEntity("xxURL", String.class).getBody; 21 | subscriber.onNext(body); 22 | subscriber.onCompleted(); 23 | } 24 | }); 25 | } 26 | 3. @Async, 配置自定义的Bean ThreadPoolTaskScheduler 27 | ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectedExecutionException异常。注:默认策略!!!!!! 28 | ThreadPoolExecutor.DiscardPolicy:也是丢弃任务,但是不抛出异常。 29 | ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程) 30 | ThreadPoolExecutor.CallerRunsPolicy:由调用线程处理该任务 31 | 32 | Proceed in 3 steps: 33 | 1. If fewer than corePoolSize threads are running, try to start a new thread whit the given 34 | command as its first task. The call to addWorker atomically checks runState and workerCount 35 | , and so prevents false alarms that would add threads when it should't, by returning false. 36 | 2. If a task can be successfully queued, then we still need to double-check whether we should have 37 | added a thread (because existing ones died since last checking) or that the pool shut down since 38 | entry into this method. So we recheck state and if necessary roll back the enqueuing if stopped, 39 | or start a new thread if there are none. 40 | 3. If we cannot queue task, then we try to add a new thread. If it fails, we know we are shut down 41 | or saturated and so reject the task. 42 | 43 | 问题:在使用Spring Cloud的Ribbon或Feign来实现服务调用的时候,如果我们的机器或网络环境等原因不是很好的话,有时候会发现这样一个问题:我们服务消费方调用服务提供方接口的时候,第一次请求经常会超时,而之后的调用就没有问题了。 44 | 原因:造成第一次服务调用出现失败的原因主要是Ribbon进行客户端负载均衡的Client并不是在服务启动的时候就初始化好的,而是在调用的时候才会去创建相应的Client,所以第一次调用的耗时不仅仅包含发送HTTP请求的时间,还包含了创建RibbonClient的时间,这样一来如果创建时间速度较慢,同时设置的超时时间又比较短的话,很容易就会出现上面所描述的显现。 45 | 解决方案:将ribbon设置为饥饿模型,如下: 46 | ribbon.eager-load.enabled=true 47 | ribbon.eager-load.clients=hello-service, user-service 48 | Spring Cloud Ribbon是基于Netflix Ribbon实现的一套客户端负载均衡的工具。它是一个基于HTTP和TCP的客户端负载均衡器。它可以通过在客户端中配置ribbonServerList来设置服务端列表去轮询访问以达到均衡负载的作用。 49 | 当Ribbon与Eureka联合使用时,ribbonServerList会被DiscoveryEnabledNIWSServerList重写,扩展成从Eureka注册中心获取服务实例列表。同时它也会用NIWSDiscoveryPing来取代IPing, 它将职责委托给Eureka来确认服务端释放已经启动。 50 | 当Ribbon与Consul联合使用时,ribbonServerList会被ConsulServerList来扩展成从Consul获取服务实例列表。同时由ConsulPing来作为IPing接口的实现。 51 | 在使用Spring Cloud Ribbon时,不管是Eureka还是Consul结合,都会在引入Spring Cloud Eureka或Spring Cloud Consul依赖的时候通过自动化配置来加载上述所说的配置内容,可以快速在Spring Cloud中实现服务间的负载均衡。 52 | Semaphore的实现原理: 53 | public class Semaphore implements java.io.Serializable { 54 | private static final long serialVersionUID = ***L; 55 | //All mechanics via AbstractQueuedSynchronizer subclass 56 | private final Sync sync; 57 | //Synchronization implementation for semaphore. Uses AQS state 58 | //to represent permits. Subclassed into fair and nonfair versions. 59 | abstract static clss Sync extends AbstractQueuedSynchronizer { 60 | private static final long serialVersionUID = 1192457210091910933L; 61 | 62 | Sync(int permits) { 63 | setState(permits); 64 | } 65 | 66 | final int getPermits() { 67 | return getState(); 68 | } 69 | 70 | final int nonfairTryAcquireShared(int acquires) { 71 | for (;;) { 72 | int available = getState(); 73 | int remaining = available - acquires; 74 | if (remaining < 0 || 75 | compareAndSetState(available, remaining)) 76 | return remaining; 77 | } 78 | } 79 | 80 | protected final boolean tryReleaseShared(int releases) { 81 | for (;;) { 82 | int current = getState(); 83 | int next = current + releases; 84 | if (next < current) // overflow 85 | throw new Error("Maximum permit count exceeded"); 86 | if (compareAndSetState(current, next)) 87 | return true; 88 | } 89 | } 90 | 91 | final void reducePermits(int reductions) { 92 | for (;;) { 93 | int current = getState(); 94 | int next = current - reductions; 95 | if (next > current) // underflow 96 | throw new Error("Permit count underflow"); 97 | if (compareAndSetState(current, next)) 98 | return; 99 | } 100 | } 101 | 102 | final int drainPermits() { 103 | for (;;) { 104 | int current = getState(); 105 | if (current == 0 || compareAndSetState(current, 0)) 106 | return current; 107 | } 108 | } 109 | } 110 | 111 | /** 112 | * NonFair version 113 | */ 114 | static final class NonfairSync extends Sync { 115 | private static final long serialVersionUID = -2694183684443567898L; 116 | 117 | NonfairSync(int permits) { 118 | super(permits); 119 | } 120 | 121 | protected int tryAcquireShared(int acquires) { 122 | return nonfairTryAcquireShared(acquires); 123 | } 124 | } 125 | 126 | /** 127 | * Fair version 128 | */ 129 | static final class FairSync extends Sync { 130 | private static final long serialVersionUID = 2014338818796000944L; 131 | 132 | FairSync(int permits) { 133 | super(permits); 134 | } 135 | 136 | protected int tryAcquireShared(int acquires) { 137 | for (;;) { 138 | if (hasQueuedPredecessors()) 139 | return -1; 140 | int available = getState(); 141 | int remaining = available - acquires; 142 | if (remaining < 0 || 143 | compareAndSetState(available, remaining)) 144 | return remaining; 145 | } 146 | } 147 | } 148 | } 149 | 150 | Spring Bean在ApplicationContext中的生命周期: 151 | 1. 启动容器 152 | 2. 调用BeanFactoryPostProcessor的postProcessBeanFactory()方法对工厂定义信息进行后处理 153 | 3. 通过getBean()调用某一个Bean 154 | 4. 调用InstantiationAwareBeanPostProcessor的postProcessBeforeInstantiation()方法 155 | 5. 实例化 156 | 6. 调用InstantiationAwareBeanPostProcessor的postProcessAfterInstantiation()方法 157 | 7. 调用InstantiationAwareBeanPostProcessor的postProcessPropertyValues()方法 158 | 8. 设置属性值 159 | 9. 调用BeanNameAware的setBeanName()方法 160 | 10. 调用BeanFactoryAware的SetBeanFactory()方法 161 | 11. 调用ApplicationContextAware的setApplicationContext()方法 162 | 12. 调用BeanPostProcessor的postProcessBeforeInitialization()方法 163 | 13. 调用InitializingBean的afterPropertiesSet()方法 164 | 14. 通过init-method属性配置的初始化方法 165 | 15. 调用BeanPostProcessor的postProcessAfterInitializaton()方法 166 | X. (对于prototype的Bean,后续交由调用者管理;对于singleton继续16) 167 | 16. 容器销毁 168 | 17. 调用DisposableBean的destory()方法 169 | 18. 通过destory-method属性设置的销毁方法 170 | 171 | //Run the Spring application, creating and refreshing a new ApplicationContext 172 | public ConfigurableApplicationContext run(String... args) { 173 | StopWatch stopWatch = new StopWatch(); 174 | stopWatch.start(); 175 | ConfigurableApplicationContext context = null; 176 | FailureAnalyzers analyzers = null; 177 | configureHeadlessProperty(); 178 | SpringApplicationRunListeners listeners = getRunListeners(args); 179 | listeners.starting(); 180 | try { 181 | ApplicationArguments applicationArguments = new DefaultApplicationArguments(args); 182 | ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments); 183 | Banner printedBanner = printBanner(environment); 184 | context = createApplicationContext(); 185 | analyzers = new FailureAnalyzers(context); 186 | prepareContext(context, environment, listeners, applicationArguments, printedBanner); 187 | refreshContext(context); 188 | afterRefresh(context, applicationArguments); 189 | listeners.finished(context, null); 190 | stopWatch.stop(); 191 | if (this.logStartupInfo) { 192 | new StartupInfoLogger(this.mainApplicationClass).logStarted( 193 | getApplicationLog(), stopWatch); 194 | } 195 | return context; 196 | } catch (Throwable ex) { 197 | handleRunFailure(context, listeners, analyzers, ex); 198 | throw new IllegalStateException(ex); 199 | } 200 | } 201 | Spring Boot中的Spring.factorites内容如下: 202 | # PropertySource Loaders 203 | org.springframework.boot.env.PropertySourceLoader=\ 204 | org.springframework.boot.env.PropertiesPropertySourceLoader,\ 205 | org.springframework.boot.env.YamlPropertySourceLoader 206 | 207 | # Run Listeners 208 | org.springframework.boot.SpringApplicationRunListener=\ 209 | org.springframework.boot.context.event.EventPublishingRunListener 210 | 211 | # Application Context Initializers 212 | org.springframework.context.ApplicationContextInitializer=\ 213 | org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer,\ 214 | org.springframework.boot.context.ContextIdApplicationContextInitializer,\ 215 | org.springframework.boot.context.config.DelegatingApplicationContextInitializer,\ 216 | org.springframework.boot.context.embedded.ServerPortInfoApplicationContextInitializer 217 | 218 | # Application Listeners 219 | org.springframework.context.ApplicationListener=\ 220 | org.springframework.boot.ClearCachesApplicationListener,\ 221 | org.springframework.boot.builder.ParentContextCloserApplicationListener,\ 222 | org.springframework.boot.context.FileEncodingApplicationListener,\ 223 | org.springframework.boot.context.config.AnsiOutputApplicationListener,\ 224 | org.springframework.boot.context.config.ConfigFileApplicationListener,\ 225 | org.springframework.boot.context.config.DelegatingApplicationListener,\ 226 | org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListener,\ 227 | org.springframework.boot.logging.ClasspathLoggingApplicationListener,\ 228 | org.springframework.boot.logging.LoggingApplicationListener 229 | 230 | # Environment Post Processors 231 | org.springframework.boot.env.EnvironmentPostProcessor=\ 232 | org.springframework.boot.cloud.CloudFoundryVcapEnvironmentPostProcessor,\ 233 | org.springframework.boot.env.SpringApplicationJsonEnvironmentPostProcessor 234 | 235 | # Failure Analyzers 236 | org.springframework.boot.diagnostics.FailureAnalyzer=\ 237 | org.springframework.boot.diagnostics.analyzer.BeanCurrentlyInCreationFailureAnalyzer,\ 238 | org.springframework.boot.diagnostics.analyzer.BeanNotOfRequiredTypeFailureAnalyzer,\ 239 | org.springframework.boot.diagnostics.analyzer.BindFailureAnalyzer,\ 240 | org.springframework.boot.diagnostics.analyzer.ConnectorStartFailureAnalyzer,\ 241 | org.springframework.boot.diagnostics.analyzer.NoUniqueBeanDefinitionFailureAnalyzer,\ 242 | org.springframework.boot.diagnostics.analyzer.PortInUseFailureAnalyzer,\ 243 | org.springframework.boot.diagnostics.analyzer.ValidationExceptionFailureAnalyzer 244 | 245 | # FailureAnalysisReporters 246 | org.springframework.boot.diagnostics.FailureAnalysisReporter=\ 247 | org.springframework.boot.diagnostics.LoggingFailureAnalysisReporter 248 | 249 | 在UML系统开发中有三个主要的模型: 250 | -功能模型:从用户的角度展示系统的功能,包括用例图 251 | -对象模型:采用对象,属性,操作,关联等概念展示系统的结构和基础,包括类图,对象图。 252 | -动态模型:展示系统的内部行为。包括序列图,活动图,状态图 253 | 区分UML模型和UML图是非常重要的,UML图包括用例图,协作图,活动图,序列图,部署图,构件图,类图,状态图,是模型中信息的图形表达方式,但是UML模型独立于UML图存在。UML的当前版本只提供了模型信息的交换,而没有提供图信息的交换。 254 | 255 | UML总共9种图,有不同的分类方式。 256 | UML图分为用例视图(用例图),设计视图(类图,对象图),进程视图(序列图,协作图,状态图,活动图),实现视图(构件图),拓扑视图(部署图)。 257 | UML图又可以分为静态视图和动态视图。静态图分为:用例图,类图,对象图,包图,构件图,部署图。动态图分为:状态图,活动图,协作图,序列图。 258 | 类图有下面6种关系:泛化(Generalization),实现(Realization),关联(Association),聚合(Aggregation),组合(Composition),依赖(Dependency)。 259 | 各种关系的强弱顺序:泛化 = 实现 > 组合 > 聚合 > 关联 > 依赖 260 | 用户根据用例图抽象成类,描述类的内部结构和类与类之间的关系,是一种静态结构图。 261 | 【泛化关系】是一种继承关系,表示一般与特殊的关系,它指定了子类如何继承父类的所有特征和行为 262 | 【实现关系】是一种类与接口的关系,表示类是接口所有特征和行为的实现。 263 | 【关联关系】是一种拥有的关系,它使一个类知道另一个类的属性和方法。关联可以单向或者双向。双向的关联可以有两个箭头或者没有箭头,单向的关联有一个箭头 264 | 【聚合关系】是整体和部分的关系,且部分可以离开整体而单独存在。聚合关系是关联关系的一种,是强的关联关系;关联和聚合在语法上无法区分,必须考察具体的逻辑关系。 265 | 【组合关系】是整体与部分的关系,但部分不能离开整体而单独存在。组合关系是关联关系的一种,是比聚合关系还要强的关系,它要求普通的聚合关系种代表整体的对象负责部分的对象的生命周期 266 | 【依赖关系】是一种使用的关系,即一个类的实现需要另一个类的协助,所以要尽量不使用双向的互相依赖。 267 | 状态图:是一种由状态,变迁,事件和活动组成的状态机,用来描述类的对象所有可能的状态以及事件发生时状态的转移条件 268 | 活动图:是状态图的一种特殊情况,这些状态大都处于活动状态。本质是一种流程图,它描述了活动到活动的控制流。 269 | 序列图: 270 | 构件图:用来表示系统中构件与构件之间,类或者接口与构件之间的关系图。其中,构件图之间的关系表现为依赖关系,定义的类或接口与类之间的关系表现为依赖关系或实现关系。 271 | 部署图:描述了系统运行时进行处理的结点以及在结点上活动的构件的配置。强调了物理设备以及之间的连接关系。 272 | 跨库的join的几种解决方案: 273 | 1. 全局表(适用条件:某些小表并且很少修改的,比如DRDS中的广播表) 274 | 2. 字段冗余(适用条件:字段较少,缺点:更新时需同时更新到不同的表,适用于不可变字段) 275 | 3. 数据同步 276 | 4. 系统层面封装(通过应用层先查询A库数据再查询B库数据,进行组合,只适用于很少场景) 277 | 5. 同步到数仓(将历史数据进行数据汇总后保存,当天数据不支持此操作,或者实时性不高) 278 | 6. 分割成多个join(按实际情况分割) 279 | 280 | Spring Cloud如何优雅的下线服务: 281 | 方式一:kill ,该方式借助于Spring Boot的Shutdown hook, 应用本身下线也是优雅的,但如果适用Eureka,默认会最长90秒的延迟,其他应用才会感知该服务下线。因此该方式不够优雅 282 | 方式二:/shutdown 端点(不建议),配置management.endpoint.shutdown.enabled=true, endpoints.web.exposure.include=shutdown,发送post到*/actuator/shutdown 283 | 方式三:/pause端点(生产可用,但有一点缺陷),暴露/pause端点,适用post调用*/actuator/pause后Eureka Server上状态会标记为DOWN,但应用本身可以正常对外服务,等90秒后,再停止服务。但是如果服务发现组件用的是Eureka并且开启了健康检查eureka.client.health.enabled = true, 那么/pause端点无效!!! 284 | 方式四:/service-registry端点(生产可用),暴露/service-registry端点 285 | 286 | Kafka通过Zookeeper管理集群配置,选举leader,以及在Consumer Group变化时进行rebalance。 287 | 288 | Leader Replica: 289 | 概念:每一个Partition有且只有一个Replica可以作为Leader 290 | 职责:负责处理所有Producer、Consumer的请求;与此同时,Leader还负责监管和维护ISR(In-Sync Replicas:副本同步队列)中所有follower的滞后状态。 291 | Follower Replica: 292 | 概念:每个Partition中除了Leader以外所有的Replica均为Follower 293 | 职责:不处理任何来自客户端的请求;只通过Fetch Request拉取Leader replica的数据进行同步 294 | Kafka中所说的offset本质上是一个逻辑值,代表的是目标数据对应在Partition上的偏移量;而数据在磁盘上的实际偏移量是存储在对应Segment的.index文件中。 295 | Offset相关概念: 296 | LEO(LogEndOffset):表示每个Partition中log最后一条message的位置 297 | HW(HighWatermark):表示Consumer能够看到该Partition的位置 298 | Replica相关概念: 299 | ISR(In-Sync Replicas):副本同步列表(包含Leader和Follower) 300 | OSR(Outof-Sync Replicas):由于同步落后而被剔除的副本列表,阈值参数:replica.lag.time.max.ms 301 | AR(Assigned Replica): 所有副本集;AR = ISR + OSR 302 | 303 | Kafka在Producer阶段通过request.required.acks参数提供了不同类型的应答机制以方便用户在系统吞吐量和一致性之间进行权衡: 304 | - 1(Default):表示Producter在ISR中的Leader成功接收到消息后并确认后,则表示消息成功写入。 305 | - 0:表示Producer将消息发送到Broker中后无需等待Broker的确认;即:只管发消息,不关心消息是否被成功接收 306 | - -1(all):表示Producer需要等待ISR中所有的Replica都确认收到消息才算写入成功;如果ISR中只剩下Leader,则等同于request.required.acks=1的效果 307 | 308 | 在老版本的Kafka(0.11.0.0以前)中,存在一个潜在的数据一致性问题,这个问题在0.11.0.0中通过leader epoch机制来消除该问题。可以把epoch理解为代(版本)的概念,即每一次的leader对应一个唯一的epoch,如果leader更换,则对应的epoch值也会随之更换,而过期的epoch请求则都会被忽略。 309 | 310 | Spring Cloud Consul: 311 | 1. Service Discovery with Consul; 312 | 2. 313 | 314 | Consul Agents集群之间通过Gossip协议进行通信,并且适用Raft一致性协议。 315 | 316 | ConcurrentLinkedQueue:This implementation employs an efficicent non-blocking algorithm based on one described in xx Simple, Fast, and Practical Non-Blocking and Blocking Concurrent Queue Algorithms. 317 | public class ConcurrentLinkedQueue extends AbstractQueue 318 | implements Queue, java.io.Serializable { 319 | private static final long serialVersionUID = *L; 320 | private static class Node { 321 | volatile E item; 322 | volatile Node next; 323 | Node(E item) { 324 | UNSAFE.putObject(this, itemOffset, item); 325 | } 326 | boolean casItem(E cmp, E val) { 327 | return UNSAFE.compareAndSwapObject(this, itemOffset, cmp, val); 328 | } 329 | void lazySetNext(Node val) { 330 | UNSAFE.putOrderedObject(this, nextOffset, val); 331 | } 332 | void casNext(Node cmp, Node val) { 333 | return UNSAFE.compareAndSwapObject(this, nextOffset, cmp, val); 334 | } 335 | private static final sun.misc.Unsafe UNSAFE; 336 | private static final long itemOffset; 337 | private static final long nextOffset; 338 | static { 339 | try { 340 | UNSAFE = sun.misc.Unsafe.getUnsafe(); 341 | Class k = Node.class; 342 | itemOffset = UNSAFE.objectFieldOffset(k.getDeclaredField("item")); 343 | nextOffset = UNSAFE.objectFieldOffset(k.getDeclaredField("next")); 344 | } catch (Exception e) { 345 | throw new Error(e); 346 | } 347 | } 348 | } 349 | private transient volatile Node head; 350 | private transient volatile Node tail; 351 | ... 352 | } 353 | 354 | 几种常用的消息队列: 355 | 1. RabbitMQ:支持AMQP高级消息队列协议,适用ErLang语言编写,有消息确认机制和持久化机制,可以自定义路由。 356 | 2. ActiveMQ: 是一个完全支持JMS1.1和J2EE规范的JMS Provider实现。适用JAVA编写 357 | 3. RocketMQ: 使用Java语言,参考kafka,消息可靠性比kafka更好。支持广播消费和集群消费,提供严格的消息顺序 358 | 4. Kafka 359 | 5. ZeroMQ: 360 | 6. Redis: 在Redis cluster集群中使用了本身提供的topic订阅功能,但不适合报文大的消息 361 | 7. MetaMQ 362 | 363 | 在事务的支持方面,RabbitMQ不支持事务,ActiveMQ和RocketMQ支持事务,Kafka不支持事务,但可以通过low level API保证仅消费一次。 364 | 365 | sample-client.ribbon.MaxAutoRetries=1 366 | sample-client.ribbon.MaxAutoRetriesNextServer=1 367 | sample-client.ribbon.OkToRetryOnAllOperations=true 368 | sample-client.ribbon.ServerListRefreshInterval=2000 369 | sample-client.ribbon.ConnectTimeout=3000 370 | sample-client.ribbon.readTimeout=3000 371 | Redis常用的集群部署方式: 372 | 1. 主从master-slave 373 | 2. 哨兵Redis-Sentinel 374 | 3. 集群Redis-Cluster 375 | 4. Codis(豌豆荚使用Go编写的Redis Proxy) 376 | 5. Twemproxy(Twitter开源的Redis代理) 377 | 378 | public class CyclicBarrier { 379 | private static class Generation { 380 | boolean broken = false; 381 | } 382 | private final ReentrantLock lock = new ReentrantLock(); 383 | private final Condition trip = lock.newCondition(); 384 | private final int parties; 385 | private final Runnable barrierCommand; 386 | private Generation generation = new Generation(); 387 | private int count; 388 | } 389 | JVM内存泄露的种类: 390 | 1. 虚拟机栈:StackOverflowError(线程请求栈深度大于虚拟机允许的深度), OutOfMemoryError(如果栈的扩展时无法申请到足够的内存) 391 | 2. 堆:OutOfMemoryError 392 | 3. 方法区(元空间):OutOfMemoryError 393 | 4. 程序计数器:不会出现泄露 394 | 5. 直接内存:OutOfMemoryError 395 | 396 | ReentrantLock: Also note that the untimed tryLock() method does not honor the fairness setting. It will succeed if the lock is available even if other threads are waiting. 397 | Serialization of this class behaves in the same way as build-in locks: a deserialized lock is in the unlocked state, regardless of its state when serialized. 398 | This lock supports a maximum of 2147483647 recursive locks by the same thread. Attempts to exceed this limit result in Error throws from locking methods. 399 | -------------------------------------------------------------------------------- /201707.txt: -------------------------------------------------------------------------------- 1 | public class ReentrantLock implements Lock, java.io.Serializable { 2 | private static final long serialVersionUID = *L; 3 | //Synchronizer providing all implementation mechanics 4 | private final Sync sync; 5 | abstract static class Sync extends AbstractQueuedSynchronizer { 6 | private static final long serialVersionUID = *L; 7 | abstract void lock(); 8 | final boolean nonfairTryAcquire(int acquires) { 9 | final Thread current = Thread.currentThread(); 10 | int c = getState(); 11 | if (c == 0) { 12 | if (compareAndSetState(0, acquires)) { 13 | setExclusiveOwnerThread(current); 14 | return true; 15 | } 16 | } else if (current == getExclusiveOwnerThread()) { 17 | int nextc = c + acquires; 18 | if (nextc < 0) { //overflow 19 | throw new Error("Maximum lock count exceeded"); 20 | } 21 | setState(nextc); 22 | return true; 23 | } 24 | return false; 25 | } 26 | protected final boolean tryRelease(int releases) { 27 | int c = getState() - releases; 28 | if (Thread.currentThread() != getExclusiveOwnerThread()) { 29 | throw new IllegalMonitorStateException(); 30 | } 31 | boolean free = false; 32 | if (c == 0) { 33 | free = true; 34 | setExclusiveOwnerThread(null); 35 | } 36 | setState(c); 37 | return free; 38 | } 39 | protected final boolean isHeldExclusively() { 40 | return getExclusiveOwnerThread() == Thread.currentThread(); 41 | } 42 | final ConditionObject newCondition() { 43 | return new ConditionObject(); 44 | } 45 | final Thread getOwner() { 46 | return isHeldExclusively() ? null : getExclusiveOwnerThread(); 47 | } 48 | final int getHoldCount() { 49 | return isHeldExclusively() ? getState() : 0; 50 | } 51 | final boolean isLocked() { 52 | return getState() != 0; 53 | } 54 | private void readObject(java.io.ObjectInputStream s) throws IOException, ClassNotFoundException{ 55 | s.defaultReadObject(); 56 | setState(0); 57 | } 58 | } 59 | 60 | //Sync object for non-fair locks 61 | static final class NonfairSync extends Sync { 62 | private static final long serialVersionUID = *L; 63 | final void lock() { 64 | if (compareAndSetState(0, 1)) { 65 | setExclusiveOwnerThread(Thread.currentThread()); 66 | } else { 67 | acquire(1); 68 | } 69 | } 70 | protected final boolean tryAcquire(int acquires) { 71 | return nonfairTryAcquire(acquires); 72 | } 73 | } 74 | //Sync object for fair locks 75 | static final class FairSync extends Sync { 76 | private static final long serialVersionUID = *L; 77 | final void lock() { 78 | acquire(1); 79 | } 80 | protected final boolean tryAcquire(int acquires) { 81 | final Thread current = Thread.currentThread(); 82 | int c = getState(); 83 | if (c == 0) { 84 | if (!hasQueuedPredecessors() && compareAndSetState(0, acquires)) { 85 | setExclusiveOwnerThread(current); 86 | return true; 87 | } 88 | } else if (current == getExclusiveOwnerThread()) { 89 | int nextc = c + acquires; 90 | if (nextc < 0) { 91 | throw new Error("Maximum lock count exceeded"); 92 | } 93 | setState(nextc); 94 | return true; 95 | } 96 | return false; 97 | } 98 | } 99 | } 100 | GC调优:(前提使用Parallel Scavenge GC)如果发现每次晋升到老年代的对象太多(通过GC日志可以计算出),导致YoungGC和FullGC都很频繁,可以考虑如下调优: 101 | -Xmn1350m -XX:-UseAdaptiveSizePolicy -XX:SurvivorRatio=6 102 | YoungGC每次晋升到old gen的内容较多,很可能是因为JVM动态的调整eden和survivor区,导致空间过小,部分本该在new gen呆着的对象跳到了old gen(此现象在survivor区较为明显,因为其本来就很小) 103 | 调优后发现Full GC次数减少,但每次时间还是很长,考虑启用CMS,并且启用碎片整理功能,降低FullGC的耗时。 104 | 但是发现oldgen GC开销还是较大,但比Pallel Scavenge 略好,通过GC日志发现,主要耗时都是在remark的rescan阶段。 105 | 所以需要降低remark的时间开销,加入参数:-XX:+CMSScavengBeforeRemark。 106 | 调优思路:通常情况下进行remark会先堆new gen进行一次扫描,而且这个开销占比很大,所以加上这个参数,在remark之前强制进行一次youngGC 107 | 最终的JVM参数如下: 108 | -Xms4096m -Xmx4096m -XX:PermSize=256M -XX:MaxPermSize=256M -XX:ReservedCodeCacheSize=1024M -XX:+UseCodeCacheFlushing -Xmn1350m -XX:-UseAdaptiveSizePolicy -XX:SurvivorRatio=6, -XX:UseConcMarkSweepGC -XX:UseCMSCompactAtFullCollection -XX:CMSFullGCsBeforeCompaction=0 -XX:UseCMSInitiatingOccupancyOnly -XX:CMSInitiatingOccupancyFraction=70 -XX:+CMSScavengeBeforeRemark 109 | AQS的唤醒原理: 110 | public abstract class AbstractQueuedSynchronizer extends AbstractOwnableSynchronizer 111 | implements java.io.Serializable { 112 | ... 113 | public final boolean release(int arg) { 114 | if (tryRelease(arg)) { 115 | Node h = head; 116 | if (h != null && h.waitStatus != 0) { 117 | unparkSuccessor(h); 118 | } 119 | return true; 120 | } 121 | return false; 122 | } 123 | //Wakes up node's successor, if one exists. 124 | private void unparkSuccessor(Node node) { 125 | int ws = node.waitStatus; 126 | if (ws < 0) { 127 | compareAndSetWaitStatus(node, ws, 0); 128 | } 129 | Node s = node.next; 130 | if (s == null || s.waitStatus > 0) { 131 | s = null; 132 | for (Node t = tail; t != null && t != node;t = t.prev) { 133 | if (t.waitStatus <= 0) { 134 | s = t; 135 | } 136 | } 137 | } 138 | if (s != null) { 139 | LockSupport.unpark(s.thread); 140 | } 141 | } 142 | } 143 | 架构设计产出文档: 144 | 1. 项目简介 145 | 2. 架构设计目标:关键功能,指标 146 | 3. 架构设计原则 147 | 4. 用例图 148 | 5. 逻辑视图:层次结构,和外部系统的关系 149 | 6. 进程视图 150 | 7. 数据视图 151 | 8. 非功能性指标 152 | 9. 部署图 153 | 154 | 如何在MySQL中开启慢查询日志: 155 | 方法一:修改my.ini,long_query_time=2, slow-query-log=On, slow_query_log_file="mysql_slow_query.log", //记录下没有使用索引的query 156 | log-query-not-using-indexes 157 | 方法二:通过数据库开启慢查询:set global slow_query_log=ON; 158 | set global long_query_time=3600; 159 | set log_queries_not_using_indexes=ON; 160 | 如何优化慢查询: 161 | 1. 利用explain关键字 162 | 2. 查看是否使用索引 163 | 2.1 使用like关键字进行查询时,如果匹配字符串的第一个字符"%",索引不会起作用。只有%不在第一个位置索引才会起作用 164 | 2.2 MySQL索引最多可以包括16个字段的索引,只有查询条件中使用了这些字段中的第一个字段时,索引才会被使用。 165 | 3. 如果某表包含的字段很多,里面有些字段不使用或很少使用,可以分解成多个表,来提高效率 166 | 4. 对于经常联合查询的表,可以建立中间表提高查询效率。 167 | 5. 分解关联查询,很多高性能的应用都会对关联查询进行分解,对每一个表进行单表查询,然后将查询结果在程序中进行关联。 168 | 6. 优化limit分页:limit m,n;当m,n中的偏移量m很大时,导致每次查询都要从整个结果集中找。可以通过考虑在筛选字段加索引或者先查询出主键然后根据主键然后limit; 169 | 7. 建立where字段和order by字段的复合索引,这样就不会有Using filesort了 170 | 8. 避免对索引字段进行计算或类型转化,不然不会使用索引 171 | public class ConcurrentLinkedDeque extends AbstractCollection 172 | implements Deque, java.io.Serializable { 173 | 174 | } 175 | Kafka的连接器中使用了死信队列,当配置的时候会使用。 176 | Kafka重复消费的原因:根本原因已经消费了数据,但是offset没有提交 177 | 1. 强行kill线程,导致消费后的数据,offset没有提交 178 | 2. 设置offset为自动提交,关闭kafka时,如果在close之前调用consumer.unsubscribe(0则有可能部分offset没提交,下次重启会重复消费 179 | 3. (重复消费最常见原因):消费后的数据,当offset还没有提交时,partition就断开连接。比如消费数据耗时太长,超过了kafka的session timeout时间(0.10默认30S),那么就会re-balance重平衡,此时有一定几率offset没提交,导致重平衡后重复消费 180 | 4. 当消费者重新分配partition的时候,可能出现从头开始消费的情况,导致重发问题。 181 | 5. 当消费者消费的速度很慢的时候,可能在一个session周期内还没完成,导致心跳机制检测问题。 182 | Kafka高性能/高吞吐量的原因: 183 | 1. 数据磁盘持久化:消息不在内存中,直接写入磁盘,充分利用磁盘的顺序读写性能(.index文件和log文件,称为segment),并且是顺序写磁盘。 184 | 2. zero-copy:减少IO操作步骤 185 | 3. 数据批量发送 186 | 4. 数据压缩 187 | 5. Topic划分为多个partition,提高parallelism 188 | 6. Page Cache:为了优化读写性能,kafka利用了操作系统本身的Page Cache,而不是JVM空间内存。通过操作系统的Page Cache,Kafka的读写操作基本上是基于内存的,读写速度得到了极大的提升。 189 | 190 | 191 | ThreadLocal的Thread.ThreadLocalMap这种存储的优点: 192 | 1. 线程消亡时,线程共享变量ThreadLocalMap一同销毁; 193 | 2. ThreadLocalMap键值对为ThreadLocal的数量,一般来说ThreaLocal数量很少,相比在ThreadLocal中用Map键值对存储线程共享变量(Thread数量一般来说比ThreadLocal数量多),性能提高很多。 194 | 关于ThreadLocalMap弱引用问题: 195 | 当线程没有结束,但是ThreadLocal已经被回收,则可能导致线程中存在ThreadLocalMap的键值对,造成内存泄露。(ThreadLocal被回收,ThreadLocal关联的线程共享变量还存在)。虽然ThreadLocal的get,set方法可以清除ThreadLocalMap中key为null的value,但是get,set方法在内存泄露后并不会必然调用,所以为了防止此类情况的出现,有两种手段: 196 | 1. 使用完线程共享变量后,显示调用ThreadLocalMap.remove方法清除线程共享变量。 197 | 2. JDK建议ThreadLocal定义为private static,这样ThreadLocal的弱引用问题则不存在了。 198 | 199 | 使用堆外内存的2种方式: 200 | 1. Unsafe.allocateMemory(size); 201 | 2. NIO包下的ByteBuffer.allocateDirect(int capacity); 202 | 设置堆外内存的大小:-XX:MaxDirectMemorySize=40M 203 | 204 | ByteBuffer.allocateDirect分配的堆外内存不需要手动释放,而且ByteBuffer中没有提高手动释放的API。也就是说使用ByteBuffer不用担心堆外内存的释放问题,除非堆内存中的ByteBuffer对象由于错误编码而出现内存泄露。 205 | 但使用Unsafe必须要手动释放内存 206 | 在Cleaner内部中通过一个列表,维护了针对每一个directBuffer的一个回收堆外内存的线程对象(Runnable),回收操作是发生在Cleaner的clean()方法中。 207 | 208 | 根据不同的实现技术AOP织入有三种: 209 | 1. 编译器织入,这要求有特殊的Java编译器; 210 | 2. 类装载期织入,这需要有特殊的类装载器; 211 | 3. 动态代理织入,在运行期为目标类添加增强Advice生成子类的方法。 212 | AspectJ是静态代理在编译期就生成了代理。 213 | Spring AOP是动态代理使用JDK或CGLIB动态代理。 214 | 最左匹配原则:mysql查询优化器会判断纠正SQL语句该以什么样的顺序执行效率最高,最后才生成真正的执行计划。B+树的数据项是复合的数据结构index(a, b, c),B+树是按照从左到右的顺序来建立搜索树的, 如果不包含a列,则B+树不知道怎么搜索于是用不到这个索引。 215 | 最左匹配原则:最左优先,以最左边的为起点任何连续的索引都能匹配上。同时遇到范围查询(>、<、between、like)就会停止匹配。 216 | 例如:b = 2 如果建立(a,b)顺序的索引,是匹配不到(a,b)索引的;但是如果查询条件是a = 1 and b = 2或者a=1(又或者是b = 2 and b = 1)就可以,因为优化器会自动调整a,b的顺序。再比如a = 1 and b = 2 and c > 3 and d = 4 如果建立(a,b,c,d)顺序的索引,d是用不到索引的,因为c字段是一个范围查询,它之后的字段会停止匹配。 217 | 218 | IdentityHashMap: 219 | 1. 使用==判断key的相等性; 220 | 2. IdentityHashMap不是Map的通用实现,违反了Map的常规协议,允许key和value都为Null 221 | 3. hash冲撞使用线性再探测的方式解决。private static int nextKeyIndex(int i, int len) { 222 | return (i + 2 < len ? i + 2 : 0); 223 | } 224 | 有关年轻代的JVM参数: 225 | 1. -XX:NewSize -XX:MaxNewSize 226 | 2. -XX:SurvivorRatio 227 | 3. -XX:+PrintTenuringDistribution 228 | 4. -XX:InitialTenuringThreshold -XX:MaxTenuringThreshold 229 | JVM为什么有1个Eden区和2个Survivor区: 230 | 如果没有Survivor区,此时每触发一次Minor GC,就会把Eden区的对象复制到老年代,这样当老年代满了之后就会触发MajorGC,比较耗时。如果只有一个Survivor区,那当Eden区满了之后,就会复制对象到Survivor区,容易产生内存碎片化,严重影响性能。所以使用2个Survivor,始终保持一个空的Survivor,可以避免内存碎片化。 231 | 分代收集算法:根据对象存活的周期的不同将内存划分为几块,然后再选主合适的收集算法。 232 | 一般把堆分成新生代和老年代,这样就可以根据各个年代的特点采用最合适的收集算法。在新生代中,每次垃圾收集都会有大量的对象销毁,只有少量存活,所以选用复制算法。老年代因为对象存活率高,没有额外空间进行分配担保,所以一般采用标记整理或标记清除算法进行回收。 233 | Kafka事务消息:从0.11.0开始支持事务消息, 234 | Kafka使用事务的两种方式: 235 | 1. 配置Kafka事务管理器(KafkaTransactionManager)并使用@Transactional注解 236 | 2. 使用KafkaTemplate的executeInTransaction方法 237 | 建索引的几大原则: 238 | 1.最左前缀匹配原则,非常重要的原则,mysql会一直向右匹配直到遇到范围查询(>、<、between、like)就停止匹配,比如a = 1 and b = 2 and c > 3 and d = 4 如果建立(a,b,c,d)顺序的索引,d是用不到索引的,如果建立(a,b,d,c)的索引则都可以用到,a,b,d的顺序可以任意调整。 239 | 2.=和in可以乱序,比如a=1 and b=2 and c=3 建立(a,b,c)索引可以任意顺序,mysql的查询优化器会帮你优化成索引可以识别的形式。 3.尽量选择区分度高的列作为索引,区分度的公式是count(distinct col)/count(*),表示字段不重复的比例,比例越大我们扫描的记录数越少,唯一键的区分度是1,而一些状态、性别字段可能在大数据面前区分度就是0,那可能有人会问,这个比例有什么经验值吗?使用场景不同,这个值也很难确定,一般需要join的字段我们都要求是0.1以上,即平均1条扫描10条记录。 240 | 4.索引列不能参与计算,保持列“干净”,比如from_unixtime(create_time) = ’2014-05-29’就不能使用到索引,原因很简单,b+树中存的都是数据表中的字段值,但进行检索时,需要把所有元素都应用函数才能比较,显然成本太大。所以语句应该写成create_time = unix_timestamp(’2014-05-29’)。 241 | 5.尽量的扩展索引,不要新建索引。比如表中已经有a的索引,现在要加(a,b)的索引,那么只需要修改原来的索引即可。 242 | 243 | Kafka Consumer的负载均衡算法: 244 | 1. A = partition数量/同组内消费者的总数 245 | 2. M = 对A取小数点第一位向上取整 246 | 3. 计算出该消费者拉取数据的partition集合:Ci = [P(M * i) ~ P((i + 1) * M - 1)] 247 | 248 | MySQL锁: 249 | 1. 按锁使用方式:乐观锁和悲观锁; 250 | 2. 按锁级别:共享锁,排他锁,意向锁,间隙锁 251 | 3. 按锁粒度:行级锁,表级锁,页级锁 252 | 4. 按操作方式:DDL锁,DML锁 253 | 5. 按加锁方式:自动锁,显示锁; 254 | 在InnoDB下,间隙锁的产生需要满足三个条件: 255 | 1. 隔离级别为RR 256 | 2. 当前读 257 | 3. 查询条件能够走到索引 258 | 间隙锁的作用:在RR模式的InnoDB中,间隙锁起到2个作用:1. 保障数据的恢复和复制;2. 防止幻读 259 | 260 | 分库分表的情况下,如何做排序: 261 | 1. 部分字段冗余存储/部分表冗余存储,或者广播表; 262 | 2. 通过数仓来做非实时数据,使用Kafka Stream做实时数据聚合 263 | 3. 在应用系统中进行处理。 264 | 265 | 如何排查死锁: 266 | 1. 使用JConsole,在“线程”里面有一个“检测死锁”的选项 267 | 2. 使用jstack, 将堆文件dump,如果有waiting to lock 268 | 3. 使用Arthas的jvm命令: DEADLOCK-COUNT:JVM当前死锁的线程数 269 | 270 | //If false(default), core threads stay alive even when idle. 271 | //If true, core threads use keepAliveTime to time out waiting for work. 272 | private volatile boolean allowCoreThreadTimeout; 273 | 274 | WebClient是Spring 5最新引入的,可以理解为Reactive版的RestTemplate。 275 | 276 | org.springframework.cloud 277 | spring-cloud-starter-openfeign 278 | 279 | 对于Spring Cloud老手来说,就算更换了Nacos作为服务注册中心,其实对于应用层面的代码是没有影响的。为什么Spring Cloud可以带给我们这样的完美编码体验呢?实际上,这完全归功于Spring Cloud Common的封装,由于在服务注册与发现、客户端负载均衡等都做了抽象,而上层应用方面依赖的都是这些抽象接口,而非针对某个具体中间件的实现。所以,在Spring Cloud中,可以很方便的去切换服务治理方面的中间件。 280 | 281 | 为了实现开始时间不确定的定时任务触发,引入延迟消息。RabbitMQ中提供了延迟消息的插件,可以利用Spring Cloud Stream以及RabbitMQ实现延迟消息。 282 | 283 | Tomcat是如何完成多个Web应用之间相互隔离,又如何保证多个Web应用都能加载到基础类库的 284 | Tomcat的ClassLoader:Tomcat本身也是java项目,因此也需要被JDK的类加载机制加载,也就必然存在引导类加载器,扩展类加载器和应用(系统)类加载器。Tomcat自身定义的类加载器主要有下面组成,CommonClassLoader作为CatalinaClassLoader和SharedClassLoader的parent,而SharedClassLoader又可能存在多个clildren类加载器WebAppClassLoader,一个WebAppClassLoader实际上就对应一个Web应用,一个Web应用就有可能存在Jsp页面,这些Jsp页面最终会转化成clall类被加载,因此也需要一个jsp类加载器:JasperLoader 285 | 需要注意的是,在代码层面CatalinaClass, SharedClassLoader, CommonClassLoader对应的实体类实际上都是URLClassLoader或者SecureClasssLoader,一般我们只是根据加载内容的不同和加载父子顺序的关系,在逻辑上划分为这三个类加载器;而WebAppClassLoader和JasperLoader都是存在对应的类加载器类的。 286 | 综上所述,Tomcat的类加载机制不能算完全"正统"的双亲委派,WebappClassLoader内部重写了loadClass和findClass方法,实现了绕过"双亲委派"直接加载web应用内部的资源,当然可以通过在Context.xml文件中加上开启正统的"双亲委派"加载机制。 287 | 288 | CAS使用场景: 289 | 1. 在线程冲突严重时,会大幅降低程序性能;CAS只适合线程冲突较少的情况使用; 290 | 2. Synchronized在JDK6之后已经改进优化,底层使用Lock-Free的队列,基本思路是自旋后阻塞,竞争切换后继续竞争锁,稍微牺牲了公平性,但获得了高吞吐量。在线程冲突较少的情况下,可以获得和CAS类似的性能。而线程冲突严重的情况下,性能远高于CAS。 291 | 292 | public class AtomicInteger extends Number implements java.io.Serializable { 293 | private static final long serialVersionUID = *L; 294 | private static final Unsafe unsafe = Unsafe.getUnsafe(); 295 | private static final long valueOffset; 296 | static { 297 | try { 298 | valueOffset = unsafe.objectFieldOffset(AtomictInteger.class.getDeclaredField("value")); 299 | } catch (Exception ex) {throw new Error(ex);} 300 | } 301 | private volatile int value; 302 | ... 303 | } 304 | volatile变量两个特性:1. 可见性;2. 禁止指令重排 305 | 在硬件指令集的发展驱动下,使得 "操作和冲突检测" 这种看起来需要多次操作的行为只需要一条处理器指令便可以完成,这些指令中就包括非常著名的CAS指令(Compare-And-Swap比较并交换)。 306 | 由于Unsafe类不是提供给用户程序调用的类(Unsafe.getUnsafe()的代码中限制了只有启动类加载器(BootstrapClassLoader)加载的Class才能访问它),因此,如果不采用反射手段,只能通过其他JavaAPI来间接使用它,如JUC包里面的AtomicInteger,其中compareAndSet()和getAndIncrement()等方法使用了Unsafe类的CAS操作。 307 | 308 | Kafka的replica: 309 | 存储系统高效,充分利用磁盘顺序读写,kafka的replica 310 | 复制协议 Broker,如何自动调优kafka副本的工作方式 311 | 如何避免follower进入和退出同步副本列表ISR 312 | topics处于under replicated 状态,这些副本处于同步失败或者失效状态,更意味着 313 | 数据没有被复制到足够数量Broker从而增加数据丢失的概率。 314 | kafka集群中处于under replicated中partition数要密切监控。 315 | offset,用来确定消息在分区日志中的唯一位置。 316 | kafka通过多副本机制实现故障自动转移。 317 | partition的N个replica中一个replica是leader,其他是follower。leader处理partition的所有读写请求。 318 | 于此同时,follower会被动定期去复制leader上的数据。 319 | leader负责维护和跟踪ISR中所有follower滞后状态。消息复制延迟受最慢的follower限制,重要的是快速检测慢replica,如果follower落后太多或者失效,leader将会把它从ISR中移除。 320 | AR = ISR + OSR 321 | AR,ISR,OSR,LEO,HW这些信息都被保存在Zookeeper中。 322 | 假设replica.lag.max.messages=4,表明只要follower落后leader不超过3,就不会标记为死亡,也不会从同步副本列表中移除。 323 | log end offset 324 | 一个副本和leader不同步的几个原因: 325 | 1. 慢副本:在一定周期时间内follower不能追赶上leader。最常见的原因之一是I/O瓶颈导致follower追加复制消息速度慢于从leader拉取速度。 326 | 2. 卡住副本:在一定周期时间内follower停止从leader拉取请求。follower replica卡住了是由于GC暂停或follower失效或死亡。 327 | 3. 新启动副本:当用户给主题增加副本因子时,新的follower不在同步副本列表中,直到完全赶上了leader日志。 328 | 在Kafka-0.8.2.x中,副本滞后判断依据是副本落后于leader最大消息数量(replica.lag.max.messages)或replicas响应partition leader的最长等待时间(replica.lag.time.max.ms)。前者是用来检测缓慢的副本,而后者是用来检测失效或死亡的副本。 329 | 避免写消息延迟增加。 330 | Consul是一个支持多数据中心分布式高可用的服务发现和配置共享的服务软件,采用Go语言开发。 331 | Consul支持健康检查,并允许http和dns协议调用api存储键值对。Consul采用Raft一致性协议算法,来保证服务的高可用;使用goosip协议管理成员和广播消息,并且支持ACL访问控制。 332 | 漏桶算法:水-请求先进入到漏桶里,漏桶以一定的速度出水,当水流速度过大会直接溢出,漏桶算法能强行限制数据的传输速率。 333 | 令牌桶算法:对应很多应用除了要求能够限制数据的平均传输速率外,还要求允许某种程度的突发传输。这个适合漏桶算法就不适合了,令牌桶算法更为合适。令牌桶算法的原理是系统以一个恒定的速度往桶里放入令牌,而如果请求需要被处理,则需要先从桶里获取一个令牌,当桶里没有令牌可取时,则拒绝服务。 334 | Guava提供了限流工具类RateLimiter,基于令牌桶算法实现流量限制。 335 | RateLimiter通过限制后面请求的等待时间,来支持一定程度的突发请求(预消费)。 336 | Guava有两种限流模式,一种为稳定模式(SmoothBursty:令牌生成速度恒定),一种为渐进模式(SmoothWarmingUp:令牌生成速度缓慢提升直到维持在一个稳定值),主要区别在等待时间的计算上。 337 | 338 | //Google实现的uninterruptibly的sleep 339 | public static void sleepUninterruptibly(long sleepFor, TimeUnit unit) { 340 | boolean interrupted = false; 341 | try { 342 | long remainingNanos = unit.toNanos(sleepFor); 343 | long end = System.nanoTime() + remainingNanos; 344 | while (true) { 345 | try { 346 | NANOSECONDS.sleep(remainingNanos); 347 | return; 348 | } catch (InterruptedException e) { 349 | interrupted = true; 350 | remainingNanos = end - System.nanoTime(); 351 | } 352 | } 353 | } finally { 354 | if (interrupted) { 355 | Thread.currentThread().interrupt(); 356 | } 357 | } 358 | } 359 | 0.10kafka的rebalance条件: 360 | 条件1. 有新的consumer加入 361 | 条件2. 旧的consumer挂了 362 | 条件3. coordinator挂了,集群选举出新的coordinator(0.10特有的) 363 | 条件4. topic的partition新增 364 | 条件5. consumer调用unsubscrible(),取消topic订阅 365 | 当consumer启动时,触发下面操作: 366 | 1. 首先进行"Consumer Id注册" 367 | 2. 然后在"Consumer id"节点下注册一个watch用来监听当前group中其他consumer的"退出"和"加入";只要此znode path下节点列表变更,都会触发此group下consumer的负载均衡.(比如一个consumer失效,那么其他consumer接管partitions). 368 | 3. 在"Broker id"节点下,注册一个watch用来监听broker的存活情况;如果broker列表变更,将会触发所有的groups下的consumer重新balance. 369 | Consumer分配Partition算法: 370 | 1) 假如topic1,具有如下partitions: P0,P1,P2,P3 371 | 2) 加入group中,有如下consumer: C0,C1 372 | 3) 首先根据partition索引号对partitions排序: P0,P1,P2,P3 373 | 4) 根据(consumer.id + '-'+ thread序号)排序: C0,C1 374 | 5) 计算倍数: M = [P0,P1,P2,P3].size / [C0,C1].size,本例值M=2(向上取整) 375 | 6) 然后依次分配partitions: C0 = [P0,P1],C1=[P2,P3],即Ci = [P(i * M),P((i + 1) * M -1)] 376 | 377 | 根据Kafka社区wiki,Kafka作者正在考虑在还未发布的0.9.x版本中使用中心协调器(Coordinator)。大体思想是为所有Consumer Group的子集选举出一个Broker作为Coordinator,由它来管理Consumer的增减,然后生成Rebalance命令,并检查是否这些Rebalance。 378 | consumer rebalance失败是0.8版本的bug,在0.9以后,这个模块由组件Coordinator负责,能够保证rebalance成功。 379 | 从0.10.0-src来看,ZookeeperConsumerConnector已经重构了,新增了ConsumerCoordinator。 380 | 381 | public ReentrantLock(boolean fair) { 382 | sync = fair ? new FairSync() : new NonfairSync(); 383 | } 384 | AdaptiveSizePolicy(自适应大小策略)是JVM GC Ergonomics(人类工程学)的一部分。如果开启AdaptiveSizePolicy,则每次GC后会重新计算Eden, From和To的大小,计算依据是GC过程中统计的GC时间/吞吐量/内存占用量。 385 | -XX:+UseAdaptiveSizePolicy 386 | JDK 1.8 默认使用 UseParallelGC 垃圾回收器,该垃圾回收器默认启动了 AdaptiveSizePolicy。 387 | AdaptiveSizePolicy有三个目标: 388 | Pause goal:应用达到预期的 GC 暂停时间。 389 | Throughput goal:应用达到预期的吞吐量,即应用正常运行时间 / (正常运行时间 + GC 耗时)。 390 | Minimum footprint:尽可能小的内存占用量。 391 | 392 | AdaptiveSizePolicy 为了达到三个预期目标,涉及以下操作: 393 | 如果 GC 停顿时间超过了预期值,会减小内存大小。理论上,减小内存,可以减少垃圾标记等操作的耗时,以此达到预期停顿时间。 394 | 如果应用吞吐量小于预期,会增加内存大小。理论上,增大内存,可以降低 GC 的频率,以此达到预期吞吐量。 395 | 如果应用达到了前两个目标,则尝试减小内存,以减少内存消耗。 396 | 397 | jinfo -flags 查看当前jvm的参数 398 | jmap -histo 查看当前jvm中每个类的实例数和占用子节数 399 | jstat -gcutil 查看自启动到现在GC情况 400 | jstat -gcutil 2s 每隔2秒查看GC情况(每个区域大小,YGC,FGC次数等) 401 | CMS默认关闭AdaptiveSizePolicy 402 | jmap -heap 查看所有区域的大小和使用大小,和配置的参数 403 | 404 | kafka重复消费优化措施: 405 | 1. 提供partition的数量,从而提高consumer的并行能力,从而提高数据的消费能力 406 | 2. 对于单partition的消费线程,增加一个固定长度的阻塞队列和工作线程池进一步提高并行消费的能力 407 | 3. 由于使用了spring-kafka,则把kafka-client的enable.auto.commit设置成了false,表示禁止kafka-client自动提交offset,因为就是之前的自动提交失败,导致offset永远没有更新,从而转向使用spring-kafka的offset提交机制。并且spring-kafka提高了多种提交策略:这些策略保证了一批消息没有完成消费的情况下,也能提交offset,从而避免完全提交不上导致永远重复消费的问题。 408 | 4. 可以根据消费者的消费速度堆session.timeout.ms的时间进行设置,适当延长 409 | 5. 减少每次从partition里面捞取的数据分片的大小,提高消费者的消费速度。 410 | 411 | 412 | -------------------------------------------------------------------------------- /201802.txt: -------------------------------------------------------------------------------- 1 | /* 2 | @return the value to return from the method invocation on the proxy instance. If the declared return type of the interface method must be an instance of the corresponding primitive wrapper class; otherwise, it must be a type assignable to the declared return type. If the value returned by this method is null and the interface method's return type is primitive, then a NullPointerException will be thrown by the method invocation on the proxy instance. If the value returned by this method is otherwise not compatible with the interface method's declared return type as described above, a ClassCastException will be thrown by the method invocation on the proxy instance. 3 | */ 4 | public interface InvocationHandler { 5 | public Object invoke(Object proxy, Method method, Object[] args) throws Throwable; 6 | } 7 | 8 | public class Proxy implements java.io.Serializable { 9 | private static final long serialVersionUID = -2222568056686623797L; 10 | private static final Class[] constructorParams = {InvocationHandler.class}; 11 | //a cache of proxy classes 12 | private static final WeakCache[], Class> proxyClassCache = 13 | new WeakCache<>(new KeyFactory(), new ProxyClassFactory()); 14 | protected InvocationHandler h; 15 | private static final Object key0 = new Object(); 16 | ... 17 | private static final class ProxyClassFactory 18 | implements BiFunction[], Class> { 19 | private static final String proxyClassNamePrefix = "$Proxy"; 20 | private static final AtomicLong nextUniqueNumber = new AtomicLong(); 21 | 22 | @Override 23 | public Class apply(ClassLoader loader, Class[] interfaces) { 24 | Map, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length); 25 | for (Class intf : interfaces) { 26 | Class interfaceClass = null; 27 | try { 28 | interfaceClass = Class.forName(intf.getName(), false, loader); 29 | } catch (ClassNotFoundException e) { 30 | } 31 | if (interfaceClass != intf) { 32 | throw new IllegalArgumentException(intf + " is not visible from class loader"); 33 | } 34 | if (!interfaceClass.isInterface()) { 35 | throw new IllegalArgumentException(interfaceClass.getName() + " is not an interface"); 36 | } 37 | if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) { 38 | throw new IllegalArgumentException("repeated interface: " + interfaceClass.getName()); 39 | } 40 | } 41 | 42 | String proxyPkg = null; 43 | int accessFlags = Modifier.PUBLIC | Modifier.FINAL; 44 | for (Class intf : interfaces) { 45 | int flags = intf.getModifiers(); 46 | if (!Modifier.isPublic(flags)) { 47 | accessFlags = Modifier.FINAL; 48 | String name = intf.getName(); 49 | int n = name.lastIndexOf('.'); 50 | String pkg = ((n == -1) ? "" : name.subString(0, n + 1)); 51 | if (proxyPkg == null) { 52 | proxyPkg = pkg; 53 | } else if (!pkg.equals(proxyPkg)) { 54 | throw new IllegalArgumentException("non-public interfaces from different packages"); 55 | } 56 | } 57 | } 58 | if (proxyPkg == null) { 59 | proxyPkg = ReflectUtil.PROXY_PACKAGE + "."; 60 | } 61 | 62 | long num = nextUniqueNumber.getAndIncrement(); 63 | String proxyName = proxyPkg + proxyClassNamePrefix + num; 64 | byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, 65 | interfaces, accessFlags); 66 | try { 67 | return defineClass0(loader, proxyName, proxyClassFile, 0, 68 | proxyClassFile.length); 69 | } catch (ClassFormatError e) { 70 | throw new IllegalArgumentException(e.toString()); 71 | } 72 | } 73 | } 74 | ... 75 | } 76 | 77 | 由于Java面向对象的思想,在JVM中需要存储对象,存储时为了实现额外的功能,需要在对象中添加一些标记字段用于增强对象功能,这些标记字段组成了对象头。 78 | JVM中对象头有两种: 79 | 1. 普通对象:Mark Word, Klass Word 80 | 2. 数组对象: Mark Word, Klass Word, Array Length 81 | 82 | 对于Mark Word, mark word的位长度位JVM的一个word大小,32位JVM为32位,64位JVM为64位。为了让一个word存储更多信息,JVM将最低的两个位设置为标记位,不同的标记位的Mark Word有不同的含义。 83 | 84 | |------------------------------------------------------------------------| 85 | |Mark Word(32 bits) | State | 86 | |------------------------------------------------------------------------| 87 | |identity_hascode:25 | age:4 | biased_lock:1 | lock:2 | Normal | 88 | |------------------------------------------------------------------------| 89 | |thread:23 | epoch:2 | age:4 | biased_lock:1 | lock:2 | Biased | 90 | |------------------------------------------------------------------------| 91 | | ptr_to_lock_record:30 | lock:2 |Lightweight Locked| 92 | |------------------------------------------------------------------------| 93 | | ptr_to_heavyweight_monitor:30 | lock:2 |Heavyweight Locked| 94 | |------------------------------------------------------------------------| 95 | | | lock:2 | Marked for GC | 96 | |------------------------------------------------------------------------| 97 | 对于lock的2位,表示如下: 98 | biased_lock lock 状态 99 | 0 01 无锁 100 | 1 01 偏向锁 101 | 0 00 轻量级锁 102 | 0 10 重量级锁 103 | 0 11 GC标记 104 | biased_lock:对象是否启用偏向锁标记,1表示启用偏向锁 105 | age:4位的Java对象年龄。由于age只有4位,所以最大值为15.这就是-XX:MaxTenuringThreshold最大值为15 106 | identity_hashcode:25位对象标识Hash码,次啊用延迟加载技术。调用System.identityHashCode()计算,并将结果写到对象头中,当对象被锁定时,该值会移动到管程Monitor中。 107 | thread:持有偏向锁的线程ID 108 | epoch:偏向时间戳 109 | ptr_tolock_record:指向栈中锁记录的指针。 110 | ptr_to_heavyweight_monitor:指向管程Monitor的指针。 111 | 对于Klass Word,这部分用于存储对象的类型指针,该指针指向它的类元数据,JVM通过这个指针确定对象是哪个类的实例。该指针的位长度为JVM的一个字大小,即32JVM为32位,64位JVM为64位。 112 | 如果应用的对象过多,使用64位的指针将浪费大量内存,统计而言,64位的JVM将会比32位的JVM多耗费50%的内存。为了节约内存可以使用选项+UseCompressedOops开启指针压缩,其中,oop即ordinary object pointer普通对象指针。开启该选项后,下列指针将压缩至32位: 113 | 1. 每个Class的属性指针(即静态变量) 114 | 2. 每个对象的属性指针(即对象变量) 115 | 3. 普通对象数组的每个元素指针。 116 | 当然,也不是所有的指针都会压缩,一些特殊类型的指针JVM不会优化,比如指向PermGen的Class对象指针(JDK8中指向元空间的Class对象指针)、本地变量、堆栈元素、入参、返回值和NULL指针等。 117 | 118 | 如果对象是一个数组,那么对象头还有空间用于存储数组的长度,也是32位的JVM,长度为32,64位的JVM长度为64位的空间。64位JVM如果开启+UseCompressedOops,该区域也将由64位压缩至32位。 119 | 120 | JDK11的ZGC是Oracle为OpenJDK开源的新垃圾收集器。专注于减少暂停时间的同时仍然压缩堆。 121 | DDD中的基本概念: 122 | 1. 实体(Entity) 123 | 2. 值对象(Value Object) 124 | 3. 领域服务(Domain Service) 125 | 4. 聚合及聚合根(Aggregate, Aggregate Root) 126 | 5. 工厂(Factory) 127 | 6. 仓储(Repository) 128 | 129 | 数据库模型的粒度如果很小,那么大量的表连接很快就会让数据库跑不动了。如果数据库模型的粒度很大(这是大部分项目的选择),代码的质量(重用性、稳定性、扩展性)就很差。由于没有从业务的角度去仔细定义每个对象,每个人会根据自己的需要建立各种QueryModel或ViewModel,慢慢地类会很多。或如果不建立各种Model,强行重用DataModel的话,那么接口提供的内容往往都不是想要的。 130 | 131 | DDD领域驱动设计的优点: 132 | 1. 从技术维度实现分层: 能够在每层关注自己的事情,比如领域层关注业务逻辑的事情,仓储关注持久化数据的事情,应用服务层关注用例的事情,接口层关注暴露给前端的事情。 133 | 2. 业务维度: 将大系统划分成多个上下文,可以让不同团队和不同人只关注当前上下文的开发。 134 | 3. 时间维度:通过敏捷式迭代快速验证,快速修正。 135 | 136 | 命令查询职责分离(CQRS: Command Query Responsibility Segregation) 137 | 界限上下文(Bounded Context): 通常来说,一个领域有且只有一个核心问题,我们称之为该领域的“核心领域”。在核心领域、通用领域、支撑领域梳理的同时,会定义子域中的"限界上下文"及其关系,用来阐述子域之间的关系。界限上下文可以简单理解成一个子系统或组件模块。 138 | 139 | Dubbo的集群容错模式: 140 | 1. 自行扩展集群容错策略; 141 | 2. Failover Cluster 142 | 3. Failfast Cluster 143 | 4. Failsafe Cluster 144 | 5. Failback Cluster 145 | 6. Forking Cluster 146 | 7. Broadcast Cluster 147 | Dubbo的负载均衡策略: 148 | 1. 自行扩展负载均衡策略 149 | 2. Random LoadBalance 150 | 3. RoundRobin LoadBalance 151 | 4. LeastActive LoadBalance 152 | 5. ConsistentHash LoadBalance 153 | Dubbo支持以下注册中心-Registry: 154 | 1. Zookeeper 155 | 2. Redis 156 | 3. Simple 157 | 4. Multicast 158 | 5. Etcd3 159 | 160 | try-with-resource: 161 | public interface AutoCloseable { 162 | void close() throws Exception; 163 | } 164 | public interface Closeable extends AutoCloseable { 165 | public void close() throws IOException; 166 | } 167 | 1. 当外部资源的句柄对象实现了AutoCloseable接口,JDK7可以利用try-with-resource语法更优雅的关闭资源,消除样板式代码。 168 | 2. try-with-resource时,如果对外部资源的处理和对外部资源的关闭都遇到了异常,"关闭异常"将被抑制,"处理异常"将被抛出,但"关闭异常"并没有丢弃,而是存放在"处理异常"的被抑制的异常列表中(可以通过Throwable的Throwable[] getSuppressed()获取)。 169 | 170 | /* A package-local class holding common representation and mechanics for classes supporting dynamic 171 | striping on 64bit values. The class extends Number so that concrete subclasses must publicly do so. 172 | */ 173 | @SuppressWarnings("serial") 174 | abstract class Striped64 extends Number { 175 | ... 176 | } 177 | 178 | ForkJoinPool核心是work-stealing算法,工作窃取算法。 179 | ForkJoinPool里有三个重要的角色: 180 | 1. ForkJoinWorkerThread: 即worker, 包装Thread 181 | 2. WorkQueue: 任务队列,双向 182 | 3. ForkJoinTask: worker执行的对象,实现了Future。。两种类型: submission和task。 183 | ForkJoinPool使用数组保存所有的WorkQueue,每个worker有属于自己的WorkQueue,但不是每个WorkQueue都有对应的worker。 184 | - 没有worker的WorkQueue:保存的是submission,来自外部提交,在WorkQueue[]的下标是偶数。 185 | - 属于worker的WorkQueue: 保存的是Task,在WorkQueu[]的下标是奇数。 186 | WorkQueue是一个双端队列,同时支持LIFO的push和pop操作,和FIFO的poll操作,分别操作top端和base端。worker操作自己的WorkQueue是LIFO操作(可选FIFO),除此之外,worker会尝试steal其他WorkQueue里的任务,这个时候执行的是FIFO操作。 187 | 分开两端取任务的好处: 188 | - LIFO操作只有对应的worker才能执行,push和pop不需要考虑并发; 189 | - 拆分时,越大的任务越在WorkQueue的base端,尽早分解,能够尽快进入计算。 190 | ForkJoinPool执行任务的对象是ForkJoinTask,是一个抽象类,有两个具体实现类:RecursiveAction和RecursiveTask。 191 | ForkJoinTask的抽象方法exec由RecursiveAction和RecursiveTask实现,它被定义为final,具体的执行步骤compute延迟到子类实现。很容易看出RecursiveAction和RecursiveTask的区别,前者没有result,getRawResult返回空,它们对应不需要返回结果和需要返回结果两种场景。 192 | 193 | /* 194 | Central dispatcher for HTTP request handlers/controllers, e.g. for Web UI controllers 195 | or HTTP-based remote service exporters. Dispatchers to registered handlers for processing 196 | a web request, providing convenient mapping and exception handling facilities. 197 | 198 | This servlet is very flexible: It can be used with just about any workflow, with the 199 | installation of the appropriate adapter classed. It offers the following functionality 200 | that distinguishes it from other request-driven web MVC frameworks: 201 | 202 | It is based around a JavaBeans configuration mechanism. 203 | 204 | 205 | */ 206 | @SuppressWarnings("serial") 207 | public class DispatcherServlet extends FrameworkServlet { 208 | ... 209 | } 210 | 211 | 支付掉单的原因: 212 | 1. 银行根本没有收到支付请求,严格来说不算掉单,只能算未支付成功。 213 | 2. 网络阻塞/系统BUG,导致支付成功/失败的通知没有及时返回回来。 214 | 3. 系统BUG或者未告知所有结果,导致没有覆盖全所有的支付结果。 215 | 4. 数据库宕机/不可用,系统不可用等,导致没有将支付结果逻辑处理完毕。 216 | 5. 前端跳转和后台交互没有考虑周全,比如付款按钮后,立即取消,但已经发起了付款等等情况。 217 | 怎么减少掉单: 218 | 1. 超时的请求,及时查询结果,然后进行处理。 219 | 2. 确认支付结果后,进行正确的补单操作(自动补单/手工补单) 220 | 3. 日切后的对账 221 | 4. 前后端交互需仔细考虑各种情况,以及做好各种限制。 222 | 5. 平台方需要保证支付通知在失败时,进行重试通知,直到一定次数;并提供接口查询。 223 | 224 | @SuppressWarnings("serial") 225 | public abstract class AbstractAutoProxyCreator extends ProxyProcessorSupport 226 | implements SmartInstantiationAwareBeanPostProcessor, BeanFactoryAware { 227 | protected static final Object[] DO_NOT_PROXY = null; 228 | protected static final Object[] PROXY_WITHOUT_ADDITIONAL_INTERCEPTORS = new Object[0]; 229 | protected final Log logger = LogFactory.getLog(getClass()); 230 | private boolean freezeProxy = false; 231 | private String[] interceptorNames = new String[0]; 232 | private boolean applyCommonInterceptorsFirst = true; 233 | private TargetSourceCreator[] customTargetSourceCreators; 234 | private BeanFactory beanFactory; 235 | private final Set targetSourcedBeans = 236 | Collections.newSetFromMap(new ConcurrentHashMap(16)); 237 | private final Set earlyProxyReferences = 238 | Collections.newSetFromMap(new ConcurrentHashMap(16)); 239 | private final Map> proxyTypes = new ConcurrentHashMap>(16); 240 | private final Map advisedBeans = new ConcurrentHashMap(256); 241 | } 242 | 243 | /* Default is global AdvisorAdapterRegistry */ 244 | private AdvisorAdapterRegistry advisorAdapterRegistry = GlobalAdvisorAdapterRegistry.getInstance(); 245 | 246 | @SuppressWarnings("serial") 247 | public class BeanNameAutoProxyCreator extends AbstractAutoProxyCreator { 248 | private List beanNames; 249 | public void setBeanNames(String... beanNames) { 250 | Assert.notEmpty(beanNames, "'beanNames' must not be empty"); 251 | this.beanNames = new ArrayList(beanNames.length); 252 | for (String mappedName : beanNames) { 253 | this.beanNames.add(StringUtils.trimWhitespace(mappedName)); 254 | } 255 | } 256 | 257 | @Override 258 | protected Object[] getAdvicesAndAdvisorsForBean(Class beanClass, String beanName, TargetSource targetSource) { 259 | if (this.beanNames != null) { 260 | for (String mappedName : this.beanNames) { 261 | if (FactoryBean.class.isAssignableFrom(beanClass)) { 262 | if (!mappedName.startsWith(BeanFactory.FACTORY_BEAN_PREFIX)) { 263 | continue; 264 | } 265 | mappedName = mappedName.substring(BeanFactory.FACTORY_BEAN_PREFIX.length()); 266 | } 267 | if (isMatch(beanName, mappedName)) { 268 | return PROXY_WITHOUT_ADDITIONAL_INTERCEPTORS; 269 | } 270 | BeanFactory beanFactory = getBeanFactory(); 271 | if (beanFactory != null) { 272 | String[] aliases = beanFactory.getAliases(beanName); 273 | for (String alias : aliases) { 274 | if (isMatch(alias, mappedName)) { 275 | return PROXY_WITHOUT_ADDITIONAL_INTERCEPTORS; 276 | } 277 | } 278 | } 279 | } 280 | } 281 | return DO_NOT_PROXY; 282 | } 283 | 284 | protected boolean isMatch(String beanName, String mappedName) { 285 | return PatternMatchUtils.simpleMatch(mappedName, beanName); 286 | } 287 | } 288 | 289 | public interface NavigableMap extends SortedMap { 290 | Map.Entry lowerEntry(K key); 291 | K lowerKey(K key); 292 | Map.Entry floorEntry(K key); 293 | K floorKey(K key); 294 | Map.Entry ceilingEntry(K key); 295 | K ceilingKey(K key); 296 | Map.Entry higherEntry(K key); 297 | K higherKey(K key); 298 | Map.Entry firstEntry(); 299 | Map.Entry lastEntry(); 300 | Map.Entry pollFirstEntry(); 301 | Map.Entry pollLastEntry(); 302 | NavigableMap descendingMap(); 303 | NavigableSet navigableKeySet(); 304 | NavigableSet descendingKeySet(); 305 | NavigableMap subMap(K fromKey, boolean fromInclusive, K toKey, boolean toInclusive); 306 | NavigableMap headMap(K toKey, boolean inclusive); 307 | NavigableMap tailMap(K fromKey, boolean inclusive); 308 | SortedMap subMap(K fromKey, K toKey); 309 | SortedMap headMap(K toKey); 310 | sortedMap tailMap(K fromKey); 311 | } 312 | 313 | skip list是一种随机化的数据结构,基于并联的链表,实现简单,插入、删除、查找的复杂度均为O(logN)(大多数情况下),因为其性能匹敌红黑树且实现简单,很多著名项目使用跳表来代替红黑树,例如LevelDB, Redis的底层存储结构使用SkipList。 314 | 目前常用的key-value数据结构有三种: Hash, 红黑树, SkipList。他们有各自的优缺点: 315 | 1. Hash:插入、查找最快,为O(1);如使用链表实现则可以实现无锁;数据有序性需要显式的排序操作; 316 | 2. 红黑树: 插入、查找为O(logN), 但常数项较少;无锁化实现复杂性很高,一般需要加锁;数据天然有序; 317 | 3. SkipList: 插入、查找为O(logN),但常数项比红黑树要大;底层结构为链表,可无锁实现;数据天然有序; 318 | 一个跳表,具有以下几个特征: 319 | 1. 一个跳表应该有几个层(level)组成;通常是10-20层,levelDB中默认为12层; 320 | 2. 跳表的第0层包含所有的元素;且节点值是有序的; 321 | 3. 每一层都是一个有序的链表;层数越高越稀疏,这样在高层中能跳过很多不符合条件的数据 322 | 4. 如果元素x出现在第i层,则所有比i小的层都包含x; 323 | 5. 每个节点包含key及其对应的value和一个指向第n层的下个节点的指针数组x->next[level]表示第level层的x的下一个节点。 324 | 325 | public class ConcurrentSkipListSet extends AbstractSet 326 | implements NavigableSet, Cloneable, java.io.Serializable { 327 | private static final long serialVersionUID = -2479143111061671589L; 328 | private final ConcurrentNavigableMap m; 329 | 330 | public ConcurrentSkipListSet() { 331 | m = new ConcurrentSkipListMap(); 332 | } 333 | 334 | public ConcurrentSkipListSet(Comparator comparator) { 335 | m = new ConcurrentSkipListMap(comparator); 336 | } 337 | 338 | public ConcurrentSkipListSet(Collection c) { 339 | m = new ConcurrentSkipListMap(); 340 | addAll(c); 341 | } 342 | 343 | public ConcurrentSkipListSet(SortedMap s) { 344 | m = new ConcurrentSkipListMap(s.comparator()); 345 | addAll(s); 346 | } 347 | 348 | ConcurrentSkipListSet(ConcurrentNavigableMap m) { 349 | this.m = m; 350 | } 351 | 352 | private static final sum.misc.Unsafe UNSAFE; 353 | private static final long mapOffset; 354 | static { 355 | try { 356 | UNSAFE = sun.misc.Unsafe.getUnsafe(); 357 | Class k = ConcurrentSkipListSet.class; 358 | mapOffset = UNSAFE.objectFieldOffset(k.getDeclaredField("m)); 359 | } catch (Exception e) { 360 | throw new Error(e); 361 | } 362 | } 363 | } 364 | 365 | JDK5中引入的Executor框架把任务的提交和执行进行解耦,只需要定义好任务,然后提交给线程池,而不用关系该任务是如何执行、被哪个线程执行,以及什么时候执行。 366 | Executors是java线程池的工厂类,通过它可以快速初始化一个符合业务需求的线程池,如Executors.newFixedThreadPool方法。 367 | public static ExecutorService newFixedThreadPool(int nThreads) { 368 | return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue()); 369 | } 370 | 371 | public class ThreadPoolExecutor extends AbstractExecutorSerive { 372 | ... 373 | /* Starts a core thread, causing it to idly wait for work. This override the default policy of 374 | starting core threads only when new tasks are executed. This method return false if all 375 | core threads have already been started. 376 | */ 377 | public boolean prestartCoreThread() { 378 | return workerCountOf(ctl.get()) < corePoolSize && addWorker(null, true); 379 | } 380 | public int prestartAllCoreThreads() { 381 | int n = 0; 382 | while (addWorker(null, true)) 383 | ++n; 384 | return n; 385 | } 386 | ... 387 | } 388 | 389 | JDK中提供了几种BlockingQueue: ArrayBlockingQueue, DelayedWorkQueue, DelayQueue, LinkedBlockingQueue, PriorityBlockingQueu, SynchronousQueue。 390 | 391 | public static ExecutorService newCachedThreadPool() { 392 | return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 393 | 60L, TimeUnit.SECONDS, 394 | new SynchronousQueu()); 395 | } 396 | 397 | 398 | ThreadPoolExecutor, Creating new Threads: 399 | New Threads are created using a ThreadFactory. If not otherwise specified, a Executors.defaultThreadFactory is used, that crates threads to all be in the same ThreadGroup and with same NORM_PRIORITY priority and non-daemon status. By supplying a different ThreadFactory, you can alter the thread's name, thread group, priority, daemon status, etc. If a ThreadFactory fails to create a thread when asked by returning null from newThread(), the executor will continue, but might not be able to execute any tasks. Threads should possess the "modifyThread" RuntimePermission. If worker threads or other threads using the pool do not possess this permission, service may be degraded: configuration changes may not take effect in a timely manner, and a shutdown pool may remain in a state in which termination is possible but not completed. 400 | 401 | By default, the keep-alive policy applies only when there are more than corePoolSize threads. But method allowCoreThreadTimeOut(boolean) can be used to apply this time-out policy to core threads as well, so lang as the KeepAliveTime value is non-zero. 402 | 403 | RejectedExecutionHandler 404 | 405 | Eclipse的插件: AmastersUML,可以生成Activity Diagram, Class Diagram, Sequence Diagram, Use case Diagram. 406 | -------------------------------------------------------------------------------- /201610.txt: -------------------------------------------------------------------------------- 1 | 桥接模式将继承关系转换为关联关系,从而降低了类与类之间的耦合,减少了代码编写量。 2 | 将两个角色之间的继承关系改为聚合关系,就是将它们之间的强关联改换成为弱关联。因此,桥梁模式中的所谓脱耦,就是指在一个软件系统的抽象化和实现化之间使用组合/聚合关系而不是继承关系,从而使两者可以相对独立地变化。这就是桥梁模式的用意。 3 | 4 | 桥梁模式所涉及的角色有: 5 | 抽象化(Abstraction)角色:抽象化给出的定义,并保存一个对实现化对象的引用。修正抽象化(Refined Abstraction)角色:扩展抽象化角色,改变和修正父类对抽象化的定义。实现化(Implementor)角色:这个角色给出实现化角色的接口,但不给出具体的实现。必须指出的是,这个接口不一定和抽象化角色的接口定义相同,实际上,这两个接口可以非常不一样。实现化角色应当只给出底层操作,而抽象化角色应当只给出基于底层操作的更高一层的操作。具体实现化(Concrete Implementor)角色:这个角色给出实现化角色接口的具体实现。 6 | --------------------- 7 | 在以下情况下可以使用桥接模式: 8 | - 如果一个系统需要在构件的抽象化角色和具体化角色之间增加更多的灵活性,避免在两个层次之间建立静态的继承联系,通过桥接模式可以使它们在抽象层建立一个关联关系。 9 | - 抽象化角色和实现化角色可以以继承的方式独立扩展而互不影响,在程序运行时可以动态将一个抽象化子类的对象和一个实现化子类的对象进行组合,即系统需要对抽象化角色和实现化角色进行动态耦合。 10 | - 一个类存在两个独立变化的维度,且这两个维度都需要进行扩展。虽然在系统中使用继承是没有问题的,但是由于抽象化角色和具体化角色需要独立变化,设计要求需要独立管理这两者。 11 | - 对于那些不希望使用继承或因为多层次继承导致系统类的个数急剧增加的系统,桥接模式尤为适用。 12 | --------------------- 13 | 组合模式(Composite Pattern),又叫部分整体模式,是用于把一组相似的对象当作一个单一的对象。组合模式依据树形结构来组合对象,用来表示部分以及整体层次。这种类型的设计模式属于结构型模式,它创建了对象组的树形结构。这种模式创建了一个包含自己对象组的类。该类提供了修改相同对象组的方式。 14 | 组合模式(Composite Pattern):组合多个对象形成树形结构以表示具有“整体—部分”关系的层次结构。组合模式对单个对象(即叶子对象)和组合对象(即容器对象)的使用具有一致性,组合模式又可以称为“整体—部分”(Part-Whole)模式,它是一种对象结构型模式。 15 | --------------------- 16 | 装饰器模式: 17 | (1)在不影响其他对象的情况下,以动态、透明的方式给单个对象添加职责。 18 | (2)处理那些可以撤消的职责。 19 | (3)当不能采用生成子类的方法进行扩充时。一种情况是,可能有大量独立的扩展,为支持每一种组合将产生大量的 子类,使得子类数目呈爆炸性增长。另一种情况可能是因为类定义被隐藏,或类定义不能用于生成子类。 20 | 以下这种模式叫做双重检查: Double Check: 21 | if(null == xx) { 22 | sychronized(xx.class) { 23 | if(null == xx){ 24 | xx = new XX(); 25 | return xx; 26 | } 27 | return xx; 28 | } 29 | } 30 | 31 | IdentityHashMap: 通过 == 比较是否相等 32 | HashMap: 通过equals比较是否相等 33 | 34 | transient: 如果用transient声明一个实例变量,当对象存储时,它的值不需要维持。换句话来说就是,用transient关键字标记的成员变量不参与序列化过程。 35 | 36 | HashMap和IdentityHashMap为什么modCount使用transient来修饰, 因为这个类本身实现了Serializable,表示可以序列化,如果反序列化后需要将其初始化为0。在clone和readObject里都通过reinitialize,将modCount初始化为0 37 | 38 | new xxMap(length); 如果可以预知,必须写大小,如果不可以就设置为16。不然就会几次扩展。 39 | 40 | AtomicLong 41 | 42 | 43 | 动态代理类使用字节码动态生成加载技术,在运行时生成加载类。生成动态代理类的方法很多,如,JDK 自带的动态处理、CGLIB、Javassist 或者 ASM 库。JDK 的动态代理使用简单,它内置在JDK中,因此不需要引入第三方Jar包,但相对功能比较弱。CGLIB和Javassist都是高级的字节码生成库,总体性能比JDK 自带的动态代理好,而且功能十分强大。ASM是低级的字节码生成工具,使用ASM已经近乎于在使用Java bytecode 编程,对开发人员要求最高,当然,也是性能最好的一种动态代理生成工具。但ASM的使用很繁琐,而且性能也没有数量级的提升,与CGLIB 等高级字节码生成工具相比,ASM 程序的维护性较差,如果不是在对性能有苛刻要求的场合,还是推荐 CGLIB 或者 Javassist。 44 | 45 | 以 CGLIB 为例,使用 CGLIB 生成动态代理,首先需要生成 Enhancer 类实例,并指定用于处理代理业务的回调类。在 Enhancer.create() 方法中,会使用 DefaultGeneratorStrategy.Generate() 方法生成动态代理类的字节码,并保存在 byte 数组中。接着使用 ReflectUtils.defineClass() 方法,通过反射,调用 ClassLoader.defineClass() 方法,将字节码装载到 ClassLoader 中,完成类的加载。最后使用 ReflectUtils.newInstance() 方法,通过反射,生成动态类的实例,并返回该实例。基本流程是根据指定的回调类生成 Class 字节码—通过 defineClass() 将字节码定义为类—使用反射机制生成该类的实例。 46 | 47 | 48 | ------------------------------------------------------------------------------------------------------ 49 | ParameterizedType 50 | HashMap: 51 | Thus, it's very important not to set the initial capacity too high (or the load factor too low) if iteration performance is important. 52 | When the number of entries in the hash table exceeds the product of the load factor and the current capacity, the hash table is rehashed (that is, internal data structures are rebuilt) so that the hash table has approximately twice the number of buckets. 53 | If the initial capacity is greater than the maximum number of entries divided by the load factor, no rehash operations will ever occur. 54 | Note that using many keys with the same hashCode() is a sure way to slow down performance of any hash table. To ameliorate impact, when keys are Comparable, this class may use comparison order among keys to help break ties. 55 | Map m = Collections.synchronizedMap(new HashMap(...)); fast-fail机制 56 | ------------------------------------------------------------------------------------------------------ 57 | ^异或运算,相等取0,不等取1 58 | 59 | >>:带符号右移。正数右移高位补0,负数右移高位补1。比如: 60 | 4 >> 1,结果是2;-4 >> 1,结果是-2。-2 >> 1,结果是-1。 61 | >>>:无符号右移。无论是正数还是负数,高位通通补0。 62 | 对于正数而言,>>和>>>没区别。 63 | 64 | transient 65 | 66 | threshold: The next size value at which to resize(capacity * load factor); 67 | 68 | 红黑树: 69 | (1)每个节点或者是黑色,或者是红色。 70 | (2)根节点是黑色。 71 | (3)每个叶子节点(NIL)是黑色。 [注意:这里叶子节点,是指为空(NIL或NULL)的叶子节点!] 72 | (4)如果一个节点是红色的,则它的子节点必须是黑色的。 73 | (5)从一个节点到该节点的子孙节点的所有路径上包含相同数目的黑节点。 74 | 75 | 76 | SortedMap sm = Collections.sychronizedSortedMap(new TreeMap(...)); 77 | 78 | 被final修饰的方法不能被重写,但是:重写的前提是子类可以从父类中继承此方法,所以当父类中被final修饰的方法的访问权限为private时,子类中就可以重写该方法了。 79 | 此处的例外,是由类的访问权限控制的。 80 | 81 | 82 | 本类 本包 子类 外部包 83 | public OK OK OK OK 84 | protected OK OK OK NULL 85 | default OK OK NULL NULL 86 | private OK NULL NULL NULL 87 | 88 | 89 | final String: 安全性、String里面有很多native方法。 90 | 91 | ParameterzedType.getRawType()方法返回声明了这个类型的类或接口,也就是去掉了泛型参数部分的类型对象。 92 | 93 | XX:+UseGCLogFileRotation 94 | 95 | stop-the-world pause 96 | 97 | GCViewer 98 | jps 99 | jinfo 100 | 101 | 查看GC的次数和时间间隔 102 | 103 | opentracing 104 | 105 | 熟悉http协议的同学应该知道,如果浏览器发送的数据包头含有Accept-Encoding: gzip,即告诉服务器:“我可以接受gzip压缩过的数据包”。这时后端就会将返回包压缩后发送,并包含返回头Content-Encoding: gzip,浏览器根据是否含有这个头对返回数据包进行解压显示。 106 | 107 | sampling.priority:如果大于0,Tracer实现应该尽可能捕捉这个调用链。如果等于0,则表示不需要捕捉此调用链。如果不存在,Tracer使用默认的采样机制。 108 | 109 | OpenTracing中的Trace(调用链)通过归属于此调用链的Span来隐性的定义。 110 | 特别说明,一条Trace(调用链)可以被认为是一个由多个Span组成的有向无环图(DAG图), 111 | Span与Span的关系被命名为References。 112 | 113 | 114 | jaeger 115 | 116 | application 117 | opentracing 118 | jaeger-client 119 | 120 | jaeger-agent: udp监听,守护进程,批量将数据发送到jaeger-collector,屏蔽了jaeger-client和jaeger-collector的,解耦,路由发现。 121 | 122 | udp通信协议 Thrift报文协议 123 | 124 | jaeger-collector -> DB 125 | 126 | Adaptive Sampling 127 | 128 | jaeger-query 展示层 129 | 130 | 解释器模式-Interpreter,给定一个语言,定义它的文法的一种表示,并定义一个解释器,这个解释器使用该表示来解释语言中的句子。 131 | AbstractExpression 132 | TerminalExpression 133 | NonTerminalExpression 134 | Context 135 | Client 136 | 137 | 创建HashMap设置充足的初始容量:预计大小/负载因子 + 1 138 | 139 | kubectl get pods -n kube-system |grep -v Running 140 | 141 | 142 | 启动Java时,加入下面打开JMX remote连接: 143 | -Dcom.sun.management.jmxremote.port=7199 144 | -Dcom.sun.management.jmxremote.authenticate=false 145 | -Dcom.sun.management.jmxremote.ssl=false 146 | 147 | JConsole 148 | 149 | -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:+UseFastUnorderedTimeStamps -XX:-UseLargePagesIndividualAllocation -XX:+UseParallelGC 150 | 151 | -XX:+UseParallelGC:设置并行收集器;选择垃圾收集器为并行收集器,此配置仅对年轻代有效。即年轻代使用并发收集,而年老代仍旧使用串行收集。 152 | 153 | MetaSpace: 154 | Compressed Class Space: 155 | 156 | java -javaagent:***.jar 157 | 158 | OGNL: Object-Graph Navigation Language,对象图导航语言。 159 | ognl.Ognl类:这个类主要用来解析和解释执行Ognl表达式 160 | ognl.OgnlContext类:这个类为Ognl表达式提供了一个执行环境,这个类实现了Map接口,所以允许通过put(key,obj)方法向OgnlContext环境中方式各种类型的对象,需要注意的是在OgnlContext中对象分为两种,第一种是叫做root对象(根对象),在整个OgnlContext中有且最多只能有一个根对象,可以通过调用OgnlContext.setRoot(obj)设置为根对象,另外一种就是OgnlContext中的普通对象,这种个数类型不受限制,那么既然分为两种方式,肯定在获取对象属性的方式上是有所不同的 161 | --------------------- 162 | 163 | 几种代理:1. jdk;2. CGLIB;3. ASM;4. Javassist. 164 | 165 | Spliterator(splitable iterator可分割迭代器)接口是Java为了并行遍历数据源中的元素而设计的迭代器,这个可以类比最早Java提供的顺序遍历迭代器Iterator,但一个是顺序遍历,一个是并行遍历。 166 | Spliterator既可以实现多线程操作提供效率,又可以避免普通迭代器的fast-fail机制所带来的异常。Spliterator可以配合jdk8新加的Stream进行并行流的实现。 167 | 168 | boolean tryAdvance(Consumer action); 169 | 170 | Happen-Before 171 | volatile满足Happen-Before规则 172 | 173 | 174 | ForkJoinPool 175 | 176 | 177 | -XX:+HeapDumpOnOutOfMemoryError 178 | JVM 就会在发生内存泄露时抓拍下当时的内存状态,也就是我们想要的堆转储文件。 179 | -XX:+HeapDumpOnCtrlBreak 180 | 181 | -XX:+UseConcMarkSweepGC 182 | 183 | ====================================================================================================== 184 | jmap,获取堆转储文件: 185 | Usage: 186 | jmap [option] 187 | (to connect to running process) 188 | jmap [option] 189 | (to connect to a core file) 190 | jmap [option] [server_id@] 191 | (to connect to remote debug server) 192 | where