控制Bean初始化顺序的方法
-
构造方法依赖
通过构造函数依赖,被依赖的bean先初始化
-
@DependOn 注解
@DepondOn("Bean2")在初始化Bean1之前,会先去初始化Bean2
-
BeanPostProcessor 扩展
实现BeanPostProcessor接口,会在所有Bean之前初始化
@Order只能控制注解的优先级
工作亮点
使用redis完成大量数据缓存,提高数据查询效率
使用mybati优化数据查询效率
使用xx解决安装速度慢问题
一次优化n个微服务的启动时间
背景
5个微服务,需要在服务启动脚本中,完成配置文件(5G典型)上传Hadoop的动作,耗时3分钟,串行执行,耗时15分钟,随着配置文件的增大,时间逐渐劣化
问题
上传过程在安装流程里,所以是串行,无优化空间,将上传动作迁移到服务启动过程中,5个服务并发完成
解决
上传时机:Hadoop服务启动成功后,微服务运行前
底层服务(sdk)提供@EnableAutoUploadConfig注解,需要上传的服务打上注解,需要原始配置文件
注解中完成@Import(UpLoadImportSelector.class)
解决问题一:什么时候做:时机确认
新增2个bean,HadoopEnvWatcher,ServiceWatcher 实现BeanPostProcessor接口,保证第一个初始化
实现InitializingBean接口,在afterPropertiesSet方法实现上传逻辑
ServiceWatcher 中autoWired了HadoopenvWatcher
解决问题二:谁来做:多实例服务
从环境中解析配置文件,单实例服务直接做 SingleUploader -> upload
多实例任务只有一个节点做 ClusterUploader -> chooseMaster -> upload
实现ImortSelector接口,不同场景放入不同的bean实例
选主:curator通过zk选主
创建zkClient(ip,retryPolicy,errorPolicy)
创建interProcessMutex(client,path)
interProcessMutex.acquire(); 获取锁 通过多个进程同时在路径下写顺序节点,谁的id最小,谁获取锁
interProcessMutex.release(); 释放锁
完成上传后设置flag
解决问题三:如何做
解析配置文件到对象
上传内容:文件、路径
上传场景:安装、升级 策略模式:
上传策略:覆盖、跳过 模板方法:支持替换文件、创建目录、保留
优化导入性能40w - 2分钟
流程
导入-校验-写审计日志-入库-分发
40w - 10分钟 2G
问题
1.数据校验 每次从数据库获取校验逻辑,耗时长
2.每次数据是逐行写入,写入磁盘效率低
3.写审计日志耗时长
优化点
1.校验逻辑变更少,缓存校验逻辑到Hashmap,空间换时间
2.使用values批量插入,对应mybatis的foreach
一次批量插入的数据 2000行一次,效率最高
2.结合stream的并行流, streamList.parallelStream().forEach(consumer);多线程导入
<insert id="insertHistory">
INSERT INTO ^{tableName}
<foreach collection="list" item="item" index="index" open="(" separator=", " close=")">^{item} </foreach>
VALUES
<foreach collection="array" item="item" index="index" open="(" separator=", " close=")">#{item} </foreach>
</insert>
3.后台线程写入审计日志
4.POI 三方包性能低,使用EasyExcel提高性能
5.GC使用并发GC 代替CMS,减少线程占用,提升吞吐量
使用gstat查看gc状况,发现多次fullgc,fullgc占用时间长
jmap查看对象:大量的数据对象和poi对象
6.其他不要在循环中打印日志
CMS
对比
如果系统追求低延迟,那么可以选择CMS垃圾收集器,只是STW的时间缩短了,但是整个GC的时间相对更长了; 如果系统追求高吞吐,那么可以选择并行Parallel GC,虽然STW的时间长,但是可以保证非GC时间,整个系统的资源全部被应用线程占用。
设置并发线程数:默认为 核心数的 5/8 + 3 最终设置为4个
作用范围: 老年代 算法: 并发标记清除算法。 启用参数: -XX:+UseConMarkSweepGC 默认回收线程数: (处理器核心数量 + 3)/4
CMS GC的几个大阶段
- 1、初始标记(CMS initial mark) stw - 标记gcroot关联对象
- 2、并发标记(CMS concurrent mark) 遍历老年代,标记存活对象
- 3、并发标记预清理 并发标记垃圾对象,卡片标记来记录“脏区” (新加入老年代的对象)
- 3、重新标记(CMS remark) stw 扫描范围: 新生代对象+GC Roots+被标记为“脏”区的对象
- 4、并发清除(CMS concurrent sweep) 回收空间
java -XX:+PrintFlagsFinal -version
kill -3 打印进程堆栈
kill - 9 强制中断
kill -15 告诉进程自动中断
G1
G1最主要的设计目标:STW停顿时间和比例可预期,可配置
物理上不分区,逻辑分区
G1划分逻辑区 - 2048个Region 每个1MB
可以分为Eden区(5%-60%),存活区,老年代,大对象四种区域
每个区,垃圾占用到一定比例80%,标记为回收集,CollectionSet
垃圾最多的块优先收集
每次回收一部分垃圾(10%)
参数:MaxGcPauseMill:执行gc操作的暂停时间,默认为200毫秒,不一定做得到
巨型对象:52W长度的数组 大于region一半的对象
解决办法:调整RegionSize
吞吐量:并行
低延迟:CMS
内存较大:G1 - 16G以上G1
32G以上ZGC
ZGC
ZGC JDK 11 (实验性开关) JDK 15天然支持
原理:着色指针和读屏障,几乎全部并发执行
gc时间短 不超过10ms 进一步缩短STW时间
堆大小支持广:100M-16T
吞吐量低于G1 15%
只支持linus
Epsilon实验性GC,用于分析GC情况
ShennandoahGC
并行GC,不设置并行线程数,虚拟机有8个核
线程过度,频繁抢占核心, 切换线程,实际执行的GC线程48个,jvm拿到的是72个和 72 * 5/8 + 3 = 48
解决:显示的配置线程个数