2.Framework
Java · 98 篇笔记
- 2.1 BloomFilterhistorical
1. 使用 2. 源码分析 - 关键属性 - 创建BloomFilter - put方法 MURMUR128 MITZ 32默认 抽象来看,put是写,mightContain是读,两个方法的代码有一点相似,都是先利用murmur3 hash对输入的funnel计算得到128位的字节数组,然后高低分
- 2.3 介绍historical
为什么有MyBatis ----------- - JDBC SQL写在Java代码中,硬编码严重 - Hibernate 完全的ORM,用面向对象的方式操作数据库。 但是不够灵活 - Mybatis 可以手写代码,SQL写在配置文件中 流程 -- 1. mybatis配置 SqlMapConfig
- 2.4 SpringCloudhistorical
1. 是什么 基于SpringBoot提供了一套微服务解决方案,包括服务网关、服务注册与发现、配置中心、全链路监控、负载均衡、熔断器等组件 2. 参考 - SpringCloud 面试题\ 江城宴的博客\-CSDN博客
- 2.7 介绍historical
组件 核心:DispatcherServlet 三大组件:HandlerMapping、HandlerAdapter、ViewResolver 用户编写:Handler、View 流程 1. 用户发送请求至前端控制器DispatcherServlet 2. DispatcherServlet收到请求
- 2.8 RateLimiterhistorical
1. 是什么 Google基于令牌桶算法实现的限流工具 2. 使用 3. 源码分析 3.1. 创建RateLimiter - RateLimiter - SmoothBursty - SmoothRateLimiter 3.2. 获取锁 - RateLimiter - SmoothRateLimit
- 2.9 使用方式historical
新建maven项目 pom.xml 配置mybatis - resources/jdbc.properties - resources/mybatis-config.xml 新建mapper - com.zsk.model.User - com.zsk.dao.UserMapper - mapper
- 2.10 Spring模块historical
1. Spring 核心容器 该层基本上是 Spring Framework 的核心。 - Spring Core - Spring Bean - SpEL (Spring Expression Language) - Spring Context 2. 数据访问/集成 该层提供与数据库交互的支持。
- 2.12 使用方式historical
新建项目 第一种方式 新建web项目 导入jar包依赖 在WEB-INF下新建lib目录,导入如下依赖。并且add as library 第二种方式 新建maven工程 添加web框架支持 maven pom 依赖 修改project配置lib包 配置spring mvc - WEB-INF/web
- 2.13 源码分析historical
1. 框架分层 2. 四大对象 MyBatis创建四大对象的时候,使用动态代理机制添加了拦截器. - Executor - ParameterHandler - ResultSetHandler - StatementHandler 3. 要分析的代码 4. 获取SqlSessionFactory对
- 2.14 源码分析historical
1. 打断点 我们在com.zsk.controller.TestController#test的方法上打上断点,查看调用栈如下 从中间一段调用栈看出主要经过 HttpServlet 、 FrameworkServlet 、 DispatcherServlet 这几个类的方法,我们从搞清楚这几个类的
- 2.16 线程安全historical
1. 线程安全问题 SpringMVC默认是单例的,所以如果有个实例属性并发修改的话会出问题。 1.1. 解决 - 使用ThreadLocal - 改用prototype多例 2. 参考链接 - Spring 是如何解决并发访问的线程安全性问题的\ 葛明的博客\-CSDN博客
- 2.19 Netty3.x源码分析historical
1. 总览 创建两个线程池,一个用于boss数组初始化,一个用于worker数组初始化 2. boss数组初始化的过程 所有的boss共享同一个线程池 1. 使用Selector.open打开选择器 2. 通过线程池执行一个run方法(这个方法是个死循环) 2.1 设置wakeup为false 2.
- 2.20 Apollohistorical
1. Apollo是什么 配置中心 2. 如何使用Apollo 3. Apollo原理 3.1. 架构 3.2. 推拉结合 - 修改配置后会存储到DB中 - AdminService会定时扫描DB中的配置,有变化则推送给客户端 - - 4. 参考 - ctripcorp/apollo: Apollo
- 2.21 Eurekahistorical
1. 是什么 如何设计注册中心.md - 采用了C-S架构的服务注册中心,提供服务注册与发现的功能 2. 使用 Getting Started \ Service Registration and Discovery 3. 原理 3.1. 服务注册与发现 3.1.1. 多级缓存机制 - 新的服务注册
- 2.22 Feignhistorical
1. Feign是什么 - 远程接口调用组件 - Feign=注解+Ribbon+RestTemplate 2. 为什么有Feign - Ribbon 使用HttpClient 或 RestTemplate 模拟http请求,步骤相当繁琐。 - 而Feign采用接口+注解的方式 。将需要调用的其他服
- 2.23 Gatewayhistorical
1. 是什么 Zuul 1.x版本的替代 2. 使用 2.1. 三大概念 - Route: 由ID、目标URI、一系列的断言和过滤器组成,如果断言为true则匹配 - Predicate: 路由的匹配条件 - Filter: 请求被路由前或者路由后进行处理 3. 原理 3.1. 异步非阻塞线程模型
- 2.24 Hystrixhistorical
1. Hystrix是什么 微服务的服务容错组件 2. 为什么需要Hystrix 本质上就是为什么需要熔断组件 如何设计容错组件.md 3. Hystrix使用 4. Hystrix功能 4.1. 服务熔断 1. 调用出现错误,开启一个时间窗(10s) 2. 在这个时间窗内,统计调用次数是否达到最小
- 2.25 Ribbonhistorical
1. Ribbon是什么 客户端+独立的负载均衡组件 2. 为什么需要Ribbon 换句话说就是为什么需要负载均衡。 所谓负载均衡就是把压力平均得分散到每个节点上,这样子一方面可以提高吞吐量,另一方面可以避免单节点压力过大宕机。 3. 使用 3.1. 关闭Ribbon懒加载 每个服务第一次请求的时候
- 2.26 Sleuthhistorical
1. 是什么 SpringCloud的链路监控,兼容Zipkin 2. 原理 2.1. 基于Zipkin - 库存服务调用商品服务,会把trace data丢给zipkin记录,dashboard上就能看到这条链路了 -
- 2.27 Zuulhistorical
1. 是什么 SpringCloud的网关组件 如何设计API网关.md 2. 使用 2.1. 灰度发布 开发了新功能,部署在少量机器上,在网关配置少量请求走这个新功能 2.2. 动态路由 所谓动态路由就是后端服务URI不是写死的,而是动态获取的 将后端服务URI存在配置中心或者数据库,定时加载,这
- 2.28 ApplicationListenerhistorical
1. ApplicationListener是什么 就是观察者模式。生产者发布事件,多个消费者监听这个事件处理各自的逻辑,如此解耦了生产者消费者。后续如果还有消费者需要处理这个事件再增加一个Listener即可 2. 如何使用事件监听机制 比如我们有个需要上报工单后需要写邮件、写日志,取消工单后也需
- 2.29 Bean作用域historical
1. 作用域分类 - singleton:每次从容器中获取的bean是同一个 - prototype:每次从容器中获取的bean,都会创建一个新的 - request:在Http请求中使用的同一个bean - session:在http session中使用的是同一个bean 2. 为什么有prot
- 2.32 Autowired原理historical
1. 类继承图 2. 原理分析 1. spring容器refresh的时候有两个关键步骤,registerBeanPostProcessors和finishBeanFactoryInitialization 2. registerBeanPostProcessors会向spring容器注入Autow
- 2.33 Spring事务源码分析historical
1. 开启事务管理注解 - @EnableTransactionManagement 这个注解向spring容器中导入了TransactionManagementConfigurationSelector,我们继续研究 2. TransactionManagementConfigurationSel
- 2.34 自定义Startershistorical
1. 两个问题 1. 这个场景需要使用的依赖是什么 2. 如何编写自动配置 - AutoConfiguration - Properites - 其他Service Bean 2. 实战 2.1. 创建empty project 2.2. 在project settings中添加两个maven pr
- 2.35 使用historical
1. 创建项目 2. pom.xml 3. 代码 - Application.java - TestApplication.java - TestController - TestService
- 2.36 Spring Boot Jar启动原理historical
1. 实验 - pom.xml - com.zsk.test.Application - 打jar包 - 解压后,使用tree命令查看结构 2. 可执行Jar的结构 - 包含 - Jar描述:META-INF/MANIFEST.MF - Spring Boot Loader:org/springfr
- 2.37 MyBatis自动配置historical
1. 开启MyBatis自动配置 spring boot自动配置mybatis是通过@MybatisAutoConfiguration注解开启的,如下 实现了InitializingBean,在spring实例化MybatisAutoConfiguration这个bean之后会调用其afterPro
- 2.38 原理分析historical
1. spring-boot-starter-web预先导入的自动配置类 - @WebMvcAutoconfiguration WebMvcAutoConfigurationAdapter是Spring Boot开启web mvc自动配置的类,如下 - 里面有一个静态内部配置类, WebMvcAut
- 2.40 原理分析historical
1. 开启ServletContainer自动配置 - @EmbeddedServletContainerAutoConfiguration EmbeddedServletContainerAutoConfiguration是spring boot自动配置嵌入式的servlet容器的自动配置类 他会
- 2.41 学习资源historical
参考 - 这可能是目前最透彻的Netty原理架构解析 \- 掘金 - 浅谈Netty的线程模型 \- 掘金 - 认真的 Netty 源码解析(二) \- 掘金 - 认真的 Netty 源码解析(一) \- 掘金 - Netty 源码阅读的思考\-\-\-\-\-\-耗时业务到底该如何处理 \- 莫那
- 2.43 线程隔离historical
1. 为什么需要线程隔离 防止有问题的调用耗尽整个服务的线程资源A 2. 隔离模式 2.1. 普通模式 默认是10个线程池,如下图 2.2. 舱壁模式 3. 线程池VS信号量 - 线程池隔离 - 使用的是独立的线程池 - 支持排队和超时 - 支持异步调用 - 线程调用会产生额外的开销 - 适用于 -
- 2.44 Bean生命周期historical
1. 流程 - 对Bean进行实例化 - 将值和引用注入到Bean对应的属性中 - 通过Aware接口把Spring底层组件注入Bean - BeanPostProcessor。执行额外的逻辑。在InitializingBean的前后执行,会把当前正在创建的Bean传入 - Initializing
- 2.45 是什么historical
1. AOP是什么 面向切面编程。不改变原有代码的基础上增加功能。 没有AOP之前,我们要增加一个打印日志的功能,可以直接修改原有代码,但这样业务代码和其他代码耦合在一起,不好维护; 有了AOP之后,我只需要在AOP中写这个打印日志的代码,原有的代码不用改动。 2. 实现原理 代理模式.md Spr
- 2.46 是什么historical
1. IOC解释 控制反转。 没有IOC之前我们使用一个对象首先要自己new出来; 有了Spring后是他帮我们new,然后放在容器里,我们直接从容器中获取对象使用。 2. 实现原理 工厂模式+反射+xml解析 扫描所有xml文件,使用dom4j解析,然后创建bean放入工厂中的map中,我们get
- 2.47 Spring常用注解historical
注解作用 @Configuration 相当于之前的applicationContext.xml,就是一个配置文件 @ComponentScan 配置扫描包,如果有@Controller,@Service,@Repositiry,@Component修饰的,则把它加入ioc容器中 @Bean 配合@
- 2.48 传播行为historical
1. 是什么 事务传播行为是Spring对数据库事务的增强特性,用来描述由某一个事务传播行为修饰的方法被嵌套进另一个方法时事务如何传播。 一般我们的事务都是写在service方法上,调用dao的方法时生效。 但是如果service A的方法调用service B的方法这种情况该怎么定义呢? 1.1.
- 2.49 原理分析historical
缓存管理和缓存类是怎么生成的 1. spring-boot-starter预先导入的自动配置类 - @CacheAutoConfiguration cache自动配置的类在于CacheAutoConfiguration,他导入了一个CacheConfigurationImportSelector 我
- 2.50 组件介绍historical
1. 是什么 一个异步事件驱动的网络框架,提供的api比Java原生的BIO、NIO的api好用的多,并且Netty能随意的切换NIO或者BIO的实现方式 2. 组件 2.1. Bootstrap 用于启动Netty。包含创建线程,创建socket等工作 2.2. EventLoop 是一个死循环,
- 2.51 堆外内存historical
1. 什么是堆外内存 就是内存不是分配在Java堆上的,而是在操作系统内存上的 2. NIO堆外内存 JDK NIO ByteBuffer.allocate方法分配的内存是在Java堆上面的,当数据需要传输的时候需要把堆上的这块内存数据原封不动的copy到操作系统内存中 而Java NIO Byte
- 2.52 BeanFactoryhistorical
1. BeanFactory和ApplicationContext BeanFacotry是实例化、配置、管理Bean的容器 ApplicationContext接口继承了BeanFactory接口,所以提供了BeanFactory的所有功能。他同时还继承了其他接口,如下图: 提供了额外的功能,如更
- 2.54 循环依赖historical
1. 循环依赖是什么 假设有两个Bean,Bean1和Bean2,如果Bean1依赖Bean2,Bean2依赖Bean1。 那么Spring创建Bean1的时候需要先创建Bean2,创建Bean2又需要先创建Bean1,这就叫做循环依赖 2. 循环依赖会导致什么问题 2.1. 构造器注入会抛出异常
- 2.55 Spring中如何让A和B两个bean按顺序加载historical
1. 使用@DependsOn注解 - TestController - TestService - 启动的时候输出 2. 参考 - Spring 中如何控制2个bean中的初始化顺序? \- 知乎 - @DependsOn或depends\-on配置的使用 \- 掘金
- 2.56 1.创建NioEventLoopGrouphistorical
1. NioEventLoopGroup类体系 如图,NioEventLoopGroup就是一个线程池,因此我们可以把任务封装成Runnable提交给NioEventLoopGroup执行 由继承的MultithreadXXX的名字可以看出,这玩意是一个多线程的池子,每个线程是什么呢--其实是Nio
- 2.57 2.Bootstrap的创建historical
1. 要分析的代码 2. 创建ServerBootstrap 创建ServerBootstrap没什么好说的,就是调用构造方法 3. 设置ServerBootstrap的属性 接下来就是设置他的属性,包括channel、handler、childHandler、option等 其中最重要channe
- 2.58 3.绑定端口historical
1. 要分析的代码 2. 服务端绑定端口 我们继续看绑定监听端口的逻辑 - bind 沿着bind往下追,最后到达doBind--关键方法 分为两步,一个是实例化、初始化并注册channel,另一个是真正绑定监听端口 2.1. 实例化、初始化并注册channel 1.实例化channel.md 2.
- 2.59 1.检测新连接historical
1. 打断点 有新连接过来的时候,会调用NioEventLoop中run方法的processSelectedKeys中OP ACCEPT逻辑 1.1. 启动客户端连接 我们在unsafe.read打个断点,启动服务器,并用telnet或者nc链接 2. 新连接进入 - 进入AbstractNioMe
- 2.60 pipeline的初始化historical
1. pipeline在什么时候被创建 不管是客户端还是服务端channel,都会在AbstractChannel的构造函数创建,每个channel都有一个自己的pipeline - newPipeline 2. pipeline是怎样的 - DefaultChannelPipeline pipel
- 2.61 1.服务端的创建historical
bind 从这行代码开始,追踪bind 最终会来到io.netty.bootstrap.AbstractBootstrap#doBind - initAndRegister channel 要知道channelFactory是哪个,我们需要回到这行代码 - io.netty.bootstrap.Ab
- 2.62 1.创建spring容器historical
1. 要分析的代码 分成创建ApplicationContext和从ApplicationContext中获取Calc的bean这两个过程,我们一步步跟踪 2. 创建ApplicationContext 2.1. AnnotationConfigApplicationContext构造方法 refr
- 2.63 启动原理historical
1. 启动SpringBoot 我们直接从SpringBoot启动的这行代码开始 忽略简单的嵌套调用,最终调用的代码如下: 首先会使用主配置类Application.class创建SpringApplication,然后传入启动参数args启动 2. 创建SpringApplication的过程 我
- 2.64 创建Executorhistorical
1. 要分析的代码 2. 创建构造线程的工厂 - newDefaultThreadFactory() 2.1. 设置线程池以及线程名 - DefaultThreadFactory - 设置了线程池的名字:nioEventLoopGroup - 每个线程的名字则是:nioEventLoopGroup-
- 2.65 2.创建NioSocketChannelhistorical
1. 要分析的代码 this是netty服务端的channel,ch是jdk nio客户端的channel,NioSocketChannel是netty客户端的channel 2. NioSocketChannel构造函数 2.1. 设置对OP READ感兴趣 - 父类AbstractNioByte
- 2.66 添加删除ChannelHandlerhistorical
1. 添加handler - DefaultChannelPipeline#addLast 1.1. 判断是否重复添加 - checkMultiplicity 1.2. 创建节点并添加至链表 - newContext - DefaultChannelHandlerContext - addLast0
- 2.67 2.服务端初始化historical
调用 bind - doBind - initAndRegister - channelFactory.newChannel() - init(channel) 我们重点看这个 ServerBootstrap - init
- 2.68 2.调用BeanFactoryPostProcessor的postProcess方法historical
1. 要研究的代码 - AbstractApplicationContext invokeBeanFactoryPostProcessors 关键的是这一句 PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanF
- 2.69 自动配置原理historical
1. 开启spring boot自动配置 - @SpringBootApplicaiton 2. 向容器注入AutoConfigurationImportSelector - @EnableAutoConfiguration - EnableAutoConfigurationImportSelect
- 2.70 创建NioEventLoop数组historical
1. 要分析的代码 2. 传入NioEventLoop构造的参数 我们接着看 newChild(executor, args) ,跳转到 - NioEventLoopGroup#newChild 调用构造方法传入的参数 - this是NioEventLoopGroup自己 - executor是刚刚
- 2.71 3.分配线程及注册selectorhistorical
1. 要分析的代码 - io.netty.channel.nio.AbstractNioMessageChannel.NioMessageUnsafe#read中有一段逻辑 2. 通过pipeline传播 pipeline.fireChannelRead(readBuf.get(i)) 会从pipe
- 2.72 3.注册selectorhistorical
调用 bind - doBind - initAndRegister - channelFactory.newChannel() - init(channel) - ChannelFuture regFuture = config().group().register(channel);我们重点看这
- 2.73 3.注册BeanPostProcessorhistorical
1. 要研究的代码 - AbstractApplicationContext registerBeanPostProcessors PostProcessorRegistrationDelegate.registerBeanPostProcessors(beanFactory, this); 这个作
- 2.74 4.向selector注册读事件historical
1. 要分析的代码 - io.netty.channel.AbstractChannel.AbstractUnsafe#register中的register操作 2. 打断点使用nc连接 其实跟服务器启动的时候向selector注册accept一样,我们看io.netty.channel.Abstr
- 2.75 4.服务端口绑定historical
调用 bind - doBind - initAndRegister - channelFactory.newChannel() - init(channel) - ChannelFuture regFuture = config().group().register(channel); - doB
- 2.76 4.实例化所有非懒加载的单实例beanhistorical
1. 要研究的代码 - finishBeanFactoryInitialization 这个步骤中尤其重要,他的preInstantiateSingletons会实例化所有非懒加载的单实例bean 2. 实例化所有非懒加载的单实例bean 2.1. 获取所有BeanName,一个个创建 - Defa
- 2.79 1.实例化channelhistorical
1. 要分析的代码 2. 反射调用Channel.class的构造方法创建实例 通过反射调用NioServerSocketChannel的构造方法 3. NioServerSocketChannel 3.1. 类体系 3.2. 构造方法 3.2.1. 创建JDK NIO底层的ServerSocket
- 2.80 2.真正绑定监听端口historical
1. 要分析的代码 这段逻辑将 channel.bind(localAddress, promise).addListener(ChannelFutureListener.CLOSE ON FAILURE); 做成一个Runnable,丢进channel关联的NioEventLoop去执行 1.1.
- 2.81 Inbound事件historical
1. 添加handler以备实验 我们添加三个InboundHandler进行试验 - InboundHandlerA - InboundHandlerB、C都一样 - NettyServer 2. 从pipeline开始调用 但我们使用ctx.pipeline().fireChannelRead(
- 2.82 ByteBufhistorical
数据结构 有两个指针,一个标记读的位置,一个标记写的位置。 0 < 读 < 写 < capacity 其中0到读之间的数据是无效的,读到写之间的数据是可读的,写到capacity之间是可写的 Api readXXX表示读数据,读指针往后移动 writeXXX表示写数据,写指针往后移动 set不移动指
- 2.83 1.给容器中注入AspectJAnnotationAutoProxyCreator组件historical
一般我们开启aop的功能是通过@EnableAspectJAutoProxy,所以首先查看其源码 1. 开启AOP - @EnableAspectJAutoProxy 关键在于导入的AspectJAutoProxyRegistrar这个类,他想容器中注入了一些组件 2. 注册AspectJAnnot
- 2.84 2.1AnnotationAwareAspectJAutoProxyCreator_Bean实例是如何创建的historical
1. 类继承图 如下图可看出AnnotationAwareAspectJAutoProxyCreator既是一个Bean后置处理器,又是一个实现了BeanFactoryAware接口的类 2. 分析调用栈 我们首先再注入的Calc和LogAspects以及上图关键方法打上断点,运行发现调用流程如下
- 2.85 Spring_AOP源码分析总结historical
创建日期 星期一 22 七月 2019 源码分析 ---- 1. @EnableAspectJAutoProxy @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Import(AspectJAuto
- 2.86 2.初始化channelhistorical
1. 要分析的代码 2. 获取channel pipeline并加入一个ChannelInitializer 如上的代码最关键的在于获取当前channel对应的pipeline,并加入了一个ChannelInitializer。 而在这个ChannelInitializer中,把我们自己在 Serv
- 2.87 NioEventLoop的run方法historical
1. 要分析的代码 如上代码主要分为三块逻辑 - 死循环 - 检测是否有IO事件 select - 处理IO事件 processSelectedKeys - 处理异步任务队列 runAllTasks 处理IO事件和处理外部线程的异步任务两者的时间由ioRatio平衡,默认情况下50,代表的意思是一半
- 2.88 Outbound事件historical
1. 添加handler以便实验 - OutboundHandlerA、OutboundHandlerC - OutboundHandlerB - NettyServer 1.1. 使用nc连接 1.2. 输出结果 1.3. 解释 可以看出是从尾巴往头部调用我们的handler 2. 从pipeli
- 2.89 ByteBufAllocatorhistorical
类体系 分配内存的工具类 ByteBufAllocator 这里的api只能区分heap还是direct,另外两个维度由AbstractByteBufAllocator的buffer方法实现 AbstractByteBufAllocator buffer方法 我们看看他的buffer方法 分配直接内
- 2.90 2.2AnnotationAwareAspectJAutoProxyCreator是在什么时候起作用的historical
1. 继续研究BeanPostProcessor的postProcessBeforeInstantiation和postProcessAfterInitialization 我们把断点放行到 - AbstractAutoProxyCreator#postProcessBeforeInitializa
- 2.91 3.注册channelhistorical
1. 要分析的代码 2. 选择一个NioEventLoop - config()返回ServerBootstrap - Serverbootstrap的group()返回EventLoopGroup,EventLoopGroup继承了MultithreadEventLoopGroup,所以调用的是M
- 2.92 异常事件historical
1. 准备实验数据 - NettyServer - InboundHandlerA - InboundHandlerB - OutboundHandlerA - OutboundHandlerB 2. 开始debug 在 com.zsk.server.handler.InboundHandlerA#
- 2.94 2.3AnnotationAwareAspectJAutoProxyCreator具体是怎么创建代理对象的historical
1. 跟踪调用栈至postProcessBeforeInstantiation 我们继续放行断点,观察Calc实例的创建,断点主要停留在3个地方 2. 判断是否需要创建代理对象 - org.springframework.aop.framework.autoproxy.AbstractAutoPro
- 2.95 Channel类体系historical
1. Channel类体系 1.1. Channel 用于网络IO 1.2. AbstractChannel Channel的骨架实现 1.3. AbstractNioChannel 使用Selector实现的Channel 1.4. AbstractNioByteChannel 操作字节流、对RE
- 2.96 2.4目标方法是怎么执行的historical
1. 继续放行断点执行Calc.div 2. 进入到代理对象的intercept方法 - CglibAopProxy.DynamicAdvisedInterceptor#intercept 2.1. 获取拦截器链 - AdvisedSupport#getInterceptorsAndDynamicI
- 2.97 PooledByteBufAllocatorhistorical
newDirectBuffer 主要分为两步,第一步时获取PoolThreadCache,进而获取PoolArena,第二步是通过PoolArena分配内存 通过PoolThreadLocalCache获取PoolThreadCache PoolThreadLocalCache其实是一个FastTh