├── .gitignore
├── 1.Java Based.md
├── 10.Spring.md
├── 11.Spring Boot.md
├── 12.Dubbo.md
├── 13.Spring Cloud.md
├── 14.Message Queue.md
├── 15.Mybatis.md
├── 16.Zookeeper.md
├── 17.Maven.md
├── 18.Open Question.md
├── 19.Distribute_MicroService.md
├── 2.Java Concurrent.md
├── 20.Java Performance.md
├── 21. Nginx.md
├── 22. ShardingJDBC.md
├── 23.ES.md
├── 3.Java Lock.md
├── 4.JVM.md
├── 5.Java Reflect_IO.md
├── 6.Design Pattern.md
├── 7.Data Structure.md
├── 8.DataBase.md
├── 9.Redis.md
├── Bug分享
└── 1.Lock Transactional.md
├── Other Interview.md
├── README.md
├── media
├── 006y8mN6ly1g8xdtvdrcyj31jy06adjs.jpg
├── 006y8mN6ly1g8xdwn9fq8j31ki0r8nfb.jpg
├── 006y8mN6ly1g8xe5miksnj30w201w74f.jpg
├── 006y8mN6ly1g8xetz5v5jj31so04sgmx.jpg
├── 006y8mN6ly1g8xf7p2kg1j30h004omxn.jpg
├── 006y8mN6ly1g8xfievgsdj31kk03awf9.jpg
├── 006y8mN6ly1g8xfrevz58j30v602yaah.jpg
├── 01.png
├── 252461fbb6d64d3dbc1914b7eadbfb86.jpeg
├── 36465fd7d91b3a4aeb3b28c3777649e6.jpeg
├── 4935fcc0a209fd1d4b70cade94986f59.jpeg
├── 6650aa32de0def76db0e4c5228619aef.jpeg
├── IMG_082BD9A3B24F-1.jpeg
├── distributed-system-request-sequence.png
├── dubbo-keep-connection.png
├── dubbo-not-keep-connection.png
├── dubbo-service-invoke-road.png
├── image-20191114193034340-3731511.png
├── image-20191114193034340.png
├── image-20191114194022209.png
├── image-20191114194055084.png
├── image-20191114194125306.png
├── image-20191114194139915.png
├── image-20191114194157527.png
├── image-20191114194224919.png
├── image-20191114194236837.png
├── image-20191114194247158.png
├── image-20191114194335314.png
├── image-20191114194356825.png
├── image-20191114194415039.png
├── image-20191114194430674.png
├── image-20191114194439833.png
├── image-20191114194449725.png
├── image-20191114194503521.png
├── image-20191114194740033.png
├── image-20191114194815842.png
├── image-20191114194918523.png
├── image-20191114194952076.png
├── image-20191114195007116.png
├── image-20191114195010955.png
├── image-20191114195023197.png
├── image-20191114195045400.png
├── image-20191114195113356.png
├── image-20191114195209444.png
├── image-20191114195228161.png
├── image-20191114195305300.png
├── image-20191114195334883.png
├── image-20191114195346681.png
├── image-20191114195421280.png
├── image-20191114195509483.png
├── image-20191114200036013.png
├── image-20191114203012441.png
├── image-20191114203913199.png
├── image-20191114204200585.png
└── serialize-deserialize.png
├── 大厂面经
├── Interview Exp1.md
├── Interview Exp2.md
├── Interview Exp3.md
├── Interview Exp4.md
├── Interview Exp5.md
├── Interview Exp6.md
├── Interview Exp7.md
├── Interview Exp8.md
├── Interview Exp9.md
└── media
│ ├── 006tNbRwgy1g9ux8rjqfwj30g70fhdm4.jpg
│ ├── 006tNbRwgy1g9ux8w8661j30hc08u403.jpg
│ ├── 006tNbRwgy1g9ux90cmw4j30h803iab4.jpg
│ ├── 006tNbRwgy1g9ux96vbg7j30i407sgmz.jpg
│ ├── 006tNbRwgy1g9ux9a6cfxj308b049gm0.jpg
│ ├── 006tNbRwgy1g9ux9dyrn5j30ax0a43zm.jpg
│ ├── 640-20191213213310547
│ ├── 640-20191213213310630
│ ├── 640-20191213213310681
│ ├── 640-20191213213310684
│ ├── 640-20191213213310692
│ ├── 640-20191213213310736
│ ├── 640-20191213213310747
│ ├── 640-20191213213310749
│ ├── 640-20191213213310764
│ ├── 640-20191213213310784
│ ├── 640-20191213213310802
│ ├── 640-20191213213310803
│ ├── 640-20191213213310828
│ ├── 640-20191213213310899
│ ├── 640-20191213213322270
│ ├── 640-20191213213322273
│ ├── 640-20191213213322276
│ ├── 640-20191213213322278
│ ├── 640-20191213213322285
│ ├── 640-20191213213322329
│ ├── 640-20191213213322353
│ ├── 640-20191213213327997
│ ├── 640-20191213213328002
│ ├── 640-20191213213328002-6244008
│ ├── 640-20191213213328002-6244008.
│ ├── 640-20191213213328004
│ ├── 640-20191213213328010
│ ├── 640-20191213213328034
│ ├── 640-20191213213328040
│ ├── 640-20191213213349268
│ ├── 640-20191213213349274
│ ├── 640-20191213213349278
│ ├── 640-20191213213349286
│ ├── 640-20191213213349309
│ ├── 640-20191213213349324
│ ├── 640-20191213213349350
│ ├── 640-20191213213358869
│ ├── 640-20191213213358870
│ ├── 640-20191213213358873
│ ├── 640-20191213213358875
│ ├── 640-20191213213358889
│ ├── 640-20191213213358898
│ ├── 640-20191213213358910
│ ├── 640-20191213213554931
│ ├── 640-20191213213602270
│ ├── image-20191213092233778.png
│ ├── image-20191213092446557.png
│ ├── image-20191213093127572.png
│ ├── image-20191213093225461.png
│ ├── image-20191213093243414.png
│ ├── image-20191213093257647.png
│ ├── image-20191213213621574.png
│ ├── image-20191213213740733.png
│ ├── image-20191213213805148.png
│ ├── image-20191213213928996.png
│ ├── image-20191213214128396.png
│ └── image-20191213214535975.png
├── 学习资料
├── Learning.md
└── media
│ ├── 006tNbRwly1ga6z6ig0vej30ck0ju75u.jpg
│ ├── image-20191223212524703.png
│ ├── image-20191223212734977.png
│ ├── image-20191223212741417.png
│ ├── image-20191223212859627.png
│ ├── image-20191223212918623.png
│ ├── image-20191223212936257.png
│ ├── image-20191223212952306.png
│ ├── image-20191223213011426.png
│ ├── image-20191223213029704.png
│ └── image-20191223213731596.png
└── 源码分析
├── CompletableFuture.md
├── ConcurrentLinkedQueue.md
├── Flow_SubmissionPublisher.md
├── Helpers.md
├── LinkedBlockingQueue.md
├── LinkedHashMap.md
├── LongAdder.md
├── Spring Boot
└── Spring Boot Reference 2.6.11 .pdf
├── SynchronousQueue.md
├── ThreadLocal.md
├── Unsafe.md
└── media
├── 5cd1ba2adf7c0-20191214003715601.jpg
├── 5cd1ba2adf7c0.jpg
├── 5cd1ba2d418b6-20191214003733877.jpg
└── 5cd1ba2d418b6.jpg
/.gitignore:
--------------------------------------------------------------------------------
1 | /notes/
2 | boostnote.json
3 | .DS_Store
4 | yuque.yml
5 | ._*
6 | ._1
7 |
--------------------------------------------------------------------------------
/10.Spring.md:
--------------------------------------------------------------------------------
1 | # 10.Spring
2 |
3 | [TOC]
4 |
5 | > 《精通Spring 4.x 企业应用开发实战》、《Spring技术内幕:深入Spring架构与设计原理》、《Spring源码深度解析》
6 |
7 | ### BeanFactory 与ApplicationContext 是干什么的,两者的区别
8 |
9 | BeanFactory、ApplicationContext都代表容器,BeanFactory是一个基础接口,实现了容器基础的功能,ApplicationContext是容器的高级形态,增加了许多了特性,顶级父类是BeanFactory。
10 |
11 | 跟FactoryBean的区别是:
12 |
13 | FactoryBean 是一个Bean,用于创建或修饰其他的Bean实例,典型应用是AOP代理类,使用'&'获取FactoryBean本身,通过getObject来获取原来的Bean实例
14 |
15 | BeanFactory 是一个工厂,是容器的顶层接口
16 |
17 | ### BeanPostProcessor 的实现
18 |
19 | Bean的后置处理器,是一个监听器,可以监听容器触发的事件。将它向IOC容器注册后,容器中管理的Bean具备了接收IOC容器事件回调的能力。BeanPostProcessor是一个接口类,有两个接口方法,postProcessBeforeInitialization提供Bean初始化前的回调入口;postProcessAfterInitialization 提供Bean初始化后的回调入口`AbstractAutowireCapableBeanFactory#initializeBean`,这个类可以对项目中的Bean进行修饰,所有Bean都会调用该实现。
20 |
21 | ### BeanDefinition 的实现
22 |
23 | BeanDefinition抽象了对Bean的定义,其是容器实现依赖反转功能的核心数据结构
24 |
25 | ### Spring IOC容器的实现
26 |
27 | Spring提供了各种各样的容器,有DefaultListableBeanFactory、FileSystemXmlApplicationContext等,这些容器都是基于BeanFactory,BeanFactory实现了容器的基础功能,包括containsBean能够判断容器是否含有指定名称的Bean,getBean获取指定名称参数的Bean等。
28 |
29 | Spring通过`refresh()`方法对容器进行初始化和资源的载入
30 |
31 | 首先通过ResourceLoader的Resource接口定位到存储Bean信息的路径
32 |
33 | 第二个过程是BeanDefinition载入,把定义好的Bean表示成IOC容器的内部数据结构BeanDefinition,通过定义BeanDefinition来管理应用的各种对象及依赖关系,其是容器实现依赖反转功能的核心数据结构
34 |
35 | 第三个过程是BeanDefinition注册,容器解析得到BeanDefinition后,需要在容器中注册,这由IOC实现BeanDefinitionRegistry接口来实现,注册过程是IOC容器内部维护了一个ConcurrentHasmap来保存得到的BeanDefinition。如果某些Bean设置了lazyinit属性,Bean的依赖注入会在这个过程预先完成,而不需要等到第一次使用Bean的时候才触发。
36 |
37 | ### Spring DI(依赖注入)的实现
38 |
39 | Spring 的依赖注入发生在以下两种情况:
40 |
41 | 1. 用户第一次调用`getBean()`方法
42 | 2. bean配置了lazy-init=false,容器会在解析注册Bean定义的时候进行预实例化,触发依赖注入
43 |
44 | getBean()方法定义在BeanFactory接口中,具体实现在子类AbstractBeanFactory中,过程如下:
45 |
46 | 1. getBean()方法最终是委托给doGetBean方法来实例化Bean,doGetBean方法会先从缓存中找是否有创建过,没有再从父工厂中去查找
47 | 2. 如果父工厂中没有找到,会根据Bean定义的模式来创建Bean,单例模式的Bean会先从缓存中查找,确保只创建一次,原型模式的Bean每次都会创建,其他模式根据配置的不同生命周期来选择合适的方法创建。创建的具体方法通过匿名类中getObject,并委托给createBean来完成bean的实例化。
48 |
49 | 3. 在createBean中,先对Bean进行一些准备工作,然后会应用配置的前后处理器,如果创建成功就直接返回该代理Bean
50 | 4. 没有创建代理Bean的话,会创建指定的Bean实例,委托给doCreateBean完成,该过程会通过提前实例化依赖Bean,并写入缓存来解决Bean的循环依赖
51 | 5. 通过populateBean注入Bean属性,并调用init-method初始化方法
52 | 6. 注册实例化的Bean
53 |
54 | ### Spring如何解决循环依赖问题(三级缓存)
55 |
56 | 比如A依赖B, B依赖A.
57 |
58 | 创建A的时候,会把A对应的ObjectFactory放入缓存中,当注入的时候发现需要B, 就会去调用B对象,B对象会先从singletonObjects 查找,没有再从earlySingletonObjects找,还没有就会调用singletonFactory创建对象B,B对象也是先从singletonObjects,earlySingletonObjects,singletonFactories三个缓存中搜索,只要找到就返回,相关方法`AbstractBeanFactory.doGetBean()`
59 |
60 | ### Spring Bean 生命周期
61 |
62 | 1. Bean实例的创建
63 | 2. 为Bean实例设置属性
64 | 3. 调用Bean的初始化方法
65 | 4. 应用可以通过IOC容器使用Bean
66 | 5. 当容器关闭时,调用Bean的销毁方法
67 |
68 | ### Spring Bean的作用域,默认是哪个?
69 |
70 | Singleton: 单例模式,IOC容器中只会存在一个共享的Bean实例,是Spring的默认模式;
71 |
72 | prototype: 原型模式,每次从IOC容器获取Bean的时候,都会创建一个新的Bean实例;
73 |
74 | request: 每次请求都生成一个实例;
75 |
76 | session: 在一次请求会话中,容器返回该Bean的同一实例,不同的Session请求不同的实例,实例仅在该Session内有效,请求结束,则实例销毁;
77 |
78 | globalsession: 全局的session中,容器返回该Bean的同一个实例,仅在portlet context 有效
79 |
80 | ### AOP两种代理方式
81 |
82 | AOP面向切面编程,可以通过预编译和运行时动态代理,实现在不修改源代码的情况下给程序动态添加功能。
83 |
84 | 程序入口是:`AbstractAutowireCapableBeanFactory -> doCreateBean -> initializeBean -> applyBeanPostProcessorsAfterInitialization`
85 |
86 | 默认策略是如果目标类是接口,则使用JDK动态代理技术,否则使用Cglib来生成代理
87 |
88 | 1. JDK 动态代理
89 |
90 | 主要涉及java.lang.reflect中的两个类,Proxy 和 InvocationHandler
91 |
92 | InvocationHandler是一个接口,通过实现该接口定义横切逻辑,并通过反射机制调用目标类的代码,动态将横切逻辑和业务逻辑编辑在一起。只能为实现接口的类创建代理。
93 |
94 | 2. Cglib 代理
95 |
96 | 是一个强大的高性能,高质量的代码生成类库,可以在运行期扩展Java类与实现Java接口,Cglib封装了asm,可以在运行期动态生成新的class。可以是普通类,也可以是实现接口的类
97 |
98 | ### Spring AOP实现原理
99 |
100 | 通过JDK代理,和CGLIB代理两种方式生成动态代理,构造不同的回调方法来对拦截器链的调用,比如JdkDynamicAopProxy的invoke方法,Cglib2AopProxy中的DynamicAdvisedInterceptor的intercept方法,首先获取配置的拦截器链,通过ReflectiveMethodInvocation的proceed方法实现对拦截器链的调用, 首先需要根据配置来对拦截器进行匹配,匹配成功后,拦截器发挥作用,在对拦截器调用完成后,再对目标对象的方法调用,这样一个普通的Java对象的功能就得到了增强
101 |
102 | ### 哪些方法不能被AOP增强
103 |
104 | 1. 基于JDK代理,除public外的其他所有方法,包括public static也不能被增强
105 | 2. 基于CGLIB代理,由于其通过生成目标类子类的方式来增强,因此不能被子类继承的方法都不能被增强,private、static、final 方法
106 |
107 | ### AOP 切点函数
108 |
109 | | 类别 | 函数 | 入参 | 说明 |
110 | | ------------------ | ------------- | -------------- | ------------------------------------------------------------ |
111 | | 方法切入点函数 | execution() | 方法匹配模式串 | 满足某一匹配模式的所有目标类方法连接点。
如execution(* greetTo(..)) 表示所有目标类中的greetTo()方法 |
112 | | | @annotation() | 方法注解类名 | 标注了特定注解的目标类方法连接点。
如@annotation(com.smart.anno.NeedTest)表示任何标注了@NeedTest注解的目标类方法 |
113 | | 方法入参切入点函数 | args() | 类名 | 通过判断目标类方法运行时入参对象的类型定义指定连接点。
如args(com.smart.Waiter)表示所有有且仅有一个按类型匹配于Waiter入参的方法 |
114 | | | @args() | 类型注解类名 | 通过判断目标类方法运行时入参对象的类是否标注特定注解来指定连接点。
如@args(com.smart.Monitorable)表示任何这样的一个目标方法:它有一个入参且`入参对象的类`标注@Monitorable注解 |
115 | | 目标类切点函数 | within() | 类名匹配串 | 表示特定域下的所有连接点。
如within(com.smart.service.\*) 表示com.smart.service 包中的所有连接点,即包中所有类的所有方法;
而within(com.smart.service.\*Service)表示在com.smart.service包中所有以Service结尾的类的所有连接点 |
116 | | | target() | 类名 | 假如目标类按类型匹配于指定类,则目标类的所有连接点匹配这个切点
如通过target(com.smart.Waiter),Waiter及Waiter实现类NaiveWaiter中的所有连接点都匹配该切点 |
117 | | | @within() | 类型注解类名 | 假如目标类型按类型匹配于某个类A, 且类A标注了特定注解,则目标类的所有连接点匹配该切点
如@within(com.smart.Monitorable) 假如Waiter类标注了@Monitorable注解,则Waiter的所有连接点都匹配该切点,`说是这个注解也会匹配Waiter的子类,但试了后并没有用,Spring 5.1` |
118 | | | @target | 类型注解类名 | 假如目标类标注了特定注解,则目标类的所有连接点都匹配该切点。
如@target(com.smart.Monitorable),假如NaiveWaiter标注了@Monitorable,则NaiveWaiter的所有连接点都匹配这个切点 |
119 | | 代理类切点函数 | this() | 类名 | 代理类按类型匹配于指定类,则被代理的目标类的所有连接点都匹配该切点。
如this(com.smart.Seller) 匹配任何运行期对象为Seller类型的类 |
120 |
121 |
122 |
123 | ### 六种增强类型
124 |
125 | 1. @Before 前置增强,相当于BeforeAdvice
126 | 2. @AfterReturning 后置增强,相当于AfterReturningAdvice
127 | 3. @Around 环绕增强,相当于MethodInterceptor
128 | 4. @AfterThrowing 抛出增强,相当于ThrowsAdvice
129 | 5. @AfterFinal增强,不管抛出异常还是正常退出,都会执行,没有对应的增强接口,一般用于释放资源
130 | 6. @DeclareParents 引介增强,相当于IntroductionInterceptor
131 |
132 | ### Spring MVC运行流程
133 |
134 |
135 |
136 | 1. 客户端请求到DispatcherServlet
137 | 2. DispatcherServlet根据请求地址查询映射处理器HandleMapping,获取Handler
138 | 3. 请求HandlerAdatper执行Handler
139 | 4. 执行相应的Controller方法,执行完毕返回ModelAndView
140 | 5. 通过ViewResolver解析视图,返回View
141 | 6. 渲染视图,将Model数据转换为Response响应
142 | 7. 将结果返回给客户端
143 |
144 | `2,3 两步都在DispatcherServlet -> doDispatch中进行处理`
145 |
146 | ### Spring MVC 启动流程
147 |
148 | 1. 在Tomcat启动的时候,ServletContext 会根据web.xml加载ContextLoaderListener,继而通过ContextLoaderListener 载入IOC容器,具体过程有ContextLoader完成,这个IOC容器是在Web环境下使用的WebApplicationContext, 这个容器在后面的DispatcherServlet中作为双亲根上下文来使用
149 | 2. IOC容器加载完成后,开始加载DIspatcherServlet,这是Spring MVC的核心,由`HttpServletBean -> initServeltBean`启动(HttpServletBean是DispatcherServlet的父类,HttpServletBean继承了HttpServlet),最终调用`DispatcherServlet -> initStrategies` 方法对HandlerMapping、ViewResolver等进行初始化,至此,DispatcherServelt就初始化完成了,它持有一个第一步完成的上下文作为根上下文,以自己的Servlet名称命名的IOC容器,这个容器是一个WebApplicationContext对象。
150 |
151 | ### Spring 事务实现方式、事务的传播机制、默认的事务类别·
152 |
153 | 1. 事务实现方式
154 |
155 | - 声明式,在xml文件中通过tx:advice来配置事务
156 | - 注解式,在xml文件中定一个事务管理对象(DataSourceTransactionManager),然后加入\, 这样就可以使用@Transactional注解配置事务
157 |
158 | 2. 事务的传播机制
159 |
160 | 一共7种事务传播行为,相关code: `AbstractPlatformTransactionManager -> getTransaction`
161 |
162 | - PROPAGATION_REQUIRED
163 |
164 | 如果当前没有事务,则新建一个事务;如果已经存在一个事务,则加入到这个事务中,这也是默认事务类别
165 |
166 | - PROPAGATION_SUPPORTS
167 |
168 | 支持当前事务。如果当前没有事务,则以非事务方式执行
169 |
170 | - PROPAGATION_MANDATORY
171 |
172 | 使用当前事务。如果当前没有事务,则抛出异常
173 |
174 | - PROPAGATION_REQUIRES_NEW
175 |
176 | 新建事务。如果当前存在事务,则把当前事务挂起
177 |
178 | - PROPAGATION_NOT_SUPPORTED
179 |
180 | 以非事务方式执行操作。如果当前存在事务,则把当前事务挂起
181 |
182 | - PROPAGATION_NEVER
183 |
184 | 以非事务方式执行。如果当前存在事务,则抛出异常
185 |
186 | - PROPAGATION_NESTED
187 |
188 | 如果当前存在事务,则在嵌套事务内执行;如果当前没有事务,则执行与PROPAGATION_REQUIRED类似的操作
189 |
190 | ### Spring 事务模版
191 |
192 | TransactionTemplate 事务模版是对原始事务管理方式的封装,原始事务管理是基于`TransactionDefinition`、`PlatformTransactionManager`、`TransactionStatus` 的编程式事务
193 |
194 | 事务模版主要通过execute(TransactionCallback action)来执行事务,TransactionCallback 有两种方式一种是有返回值TransactionCallback,一种是没有返回值TransactionCallbackWithoutResult。
195 |
196 | ### Spring 事务底层原理
197 |
198 | 1. 事务的准备
199 |
200 | 在声明式事务处理中,需要Ioc容器配置TransactionProxyFactoryBean,其父类AbstractSingletonProxyFactoryBean实现了InitializingBeean接口,因此在初始化过程中会调用afterPropertiesSet方法,这个方法实例化了ProxyFactory, 并为其设置了通知,目标对象后,最终返回Proxy代理对象,对象建立起来后,在调用其代理方法的时候,会调用相应的TransactionInterceptor拦截器,在这个调用中,会根据TransactionAttribute配置的事务属性进行配置,为事务处理做好准备
201 |
202 | 2. 事务拦截器实现
203 |
204 | 经过TransactionProxyFactoryBean的AOP包装后,此时如果对目标对象进行方法调用,实际上起作用的是一个Proxy代理对象,拦截器会拦截其中的事务处理,在调用Proxy对象的代理方法时会触发invoke回调,其中会根据事务属性配置决定具体用哪一个PlatformTransactionManager来完成事务操作
205 |
206 | ### Spring事务失效(事务嵌套), JDK动态代理给Spring事务埋下的坑
207 |
208 | https://blog.csdn.net/bntx2jsqfehy7/article/details/79040349
209 |
210 | ### Spring 单例实现原理
211 |
212 | 在创建Bean的时候`AbstractAutowireCapableBeanFactory -> doCreateBea0` 通过BeanDefinition 设置的是否单例属性,来判断该bean是否是单例,如果是单例就 根据Bean的名称删除bean缓存中同名称的bean,再在后面重新创建bean.
213 |
214 | ### Spring 中有哪些不同类型的事件
215 |
216 | 对于ApplicationEvent 类和在ApplicationContext接口中处理的事件,如果一个Bean实现了ApplicationListener接口,但一个ApplicationEvent发布后,Bean会自动被通知
217 |
218 | ```java
219 | public class AllApplicationEventListener implements ApplicationListener {
220 | @Override
221 | public void onApplicationEvent(ApplicationEvent applicationEvent) {
222 |
223 | }
224 | }
225 | ```
226 |
227 | Spring 提供了5种标准的事件:
228 |
229 | 1. 上下文更新事件(ContextRefreshedEvent):该事件会在ApplicationContext被初始化或者更新时发布。也可以在ConfigurableApplicationContext 接口中的refresh()方法时触发
230 | 2. 上下文开始事件(ContextStartedEvent):当容器调用ConfigurableApplicationContext的Start()方法开始或重新开始容器时触发该事件
231 | 3. 上下文停止事件(ContextStoppedEvent):当容器调用ConfigurableApplicationContext的Stop()方法停止容器时触发该事件
232 | 4. 上下文关闭事件(ContextCloseEvent):当ApplicationContext被关闭时触发该事件。容器被关闭时,其管理的所有单例Bean都被销毁
233 | 5. 请求处理事件(RequestHandledEvent):在Web应用中,当一个Http请求(Request)结束时触发该事件
234 | 6. 自定义事件继承ApplicationEvent, 在通过ApplicationContext 接口的publishEvent()方法发布事件
235 |
236 | ### Spring 扩展点总结
237 |
238 | 可以看看这篇文章:https://www.diguage.com/post/spring-extensions-overview/
239 |
240 | ### 缓存的一些策略有哪几种类型
241 |
242 | 常见的有FIFO、LRU、LFU、TTL、TTI
243 |
244 | FIFO:先进先出策略,先放入缓存的数据先被移除
245 |
246 | LRU:最久未使用策略,即使用时间距离现在最久的那个数据被移除
247 |
248 | LFU:最近最少使用策略,即一定时间段内使用次数(频率)最少的那个数据被移除
249 |
250 | TTL:存活期,即从缓存中创建时间点开始直至到期的一个时间段(不管这个时间段内有没有访问都将过期)
251 |
252 | TTI:空闲期,即一个数据多久没被访问就从缓存中移除的时间
253 |
254 |
255 |
256 | ### Spring Cache 注解
257 |
258 | | 注解 | 用法 |
259 | | ------------ | -------------------------------------------------- |
260 | | @Cacheable | 先查询缓存,如果没有执行方法并缓存结果,用于取数据 |
261 | | @CachePut | 先执行方法,然后将返回值放入缓存,用于更新数据 |
262 | | @CacheEvict | 删除缓存,用于删除数据 |
263 | | @Caching | 基于前3者的注解数组,多用于一个类有多种实现的情况 |
264 | | @CacheConfig | 全局缓存注解,用于类上 |
265 |
266 | 缓存管理器
267 |
268 | 1. SimpleCacheManager 可以配置缓存列表名称,基本的缓存管理器
269 | 2. NoOpCacheManager 不缓存任何数据,用于测试
270 | 3. ConcurrentMapCacheManager 不用配置缓存列表,自动生成缓存ConcurrentMapCache
271 | 4. CompositeCacheManager 可以将不同的缓存管理器组合在一起,不同的缓存使用不同的缓存管理器,并且可以通过fallbackToNoOpCache属性回到NoOpCacheManager
272 |
273 |
274 |
275 | ### Spring BeanUtils bean拷贝工具用过吗?它是浅拷贝还是深拷贝?怎么实现的?有没有什么坑?其他还有什么bean 拷贝的方法,是浅拷贝还是深拷贝?如何实现深拷贝?
276 |
277 | BeanUtils是通过java反射机制来实现从源Bean将属性的值赋值到目标Bean中,两个Bean是不同的对象,但如果Bean中的属性是引用对象,那么copy的只是引用地址。其他的CGLIB的BeanCopier,Apache的BeanUtils都不是真正的深拷贝。
278 |
279 | 要实现真正的深度copy,需要将bean转化成json,再将json转化成目标bean。或者使用流的方法,将源bean转化成流,再从流读取bean。
280 |
281 | ```java
282 | public class User implements Serializable {
283 | private int id;
284 | private String userName;
285 |
286 | public User myCopy() {
287 | User copy = null;
288 | try {
289 | ByteArrayOutputStream baos = new ByteArrayOutputStream();
290 | ObjectOutputStream oos = new ObjectOutputStream(baos);
291 | oos.writeObject(this);
292 | //将流序列化成对象
293 | ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
294 | ObjectInputStream ois = new ObjectInputStream(bais);
295 | copy = (User)ois.readObject();
296 | } catch(Exception ex) {
297 | ex.printStackTrace();
298 | }
299 | }
300 | }
301 | ```
302 |
303 |
--------------------------------------------------------------------------------
/11.Spring Boot.md:
--------------------------------------------------------------------------------
1 | # 11.Spring Boot
2 |
3 | > 这里我找了一些个人感觉容易问到的面试题
4 |
5 | ### 什么是 Spring Boot?
6 |
7 | Spring Boot是 Spring 的子项目,正如其名字,提供 Spring 的引导( **Boot** )的功能。
8 |
9 | 通过 Spring Boot ,我们开发者可以快速配置 Spring 项目,引入各种 Spring MVC、Spring Transaction、Spring AOP、MyBatis 等等框架,而无需不断重复编写繁重的 Spring 配置,降低了 Spring 的使用成本。
10 |
11 |
12 |
13 | ### Spring Boot启动流程
14 |
15 | 1. 启动类里面调用SpringApplication.run方法
16 | 2. 在run方法中,首先构造SpringApplication对象,然后再调用run方法
17 | 3. 在构造SpringApplication对象中,做了如下工作
18 | - 将sources放入primarySources变量中
19 | - 判断webApplication是什么类型的
20 | - 设置ApplicationContextInitializer,ApplicationListener,通过加载META-INF/spring.factories中配置的类
21 | - 找到main方法找到启动主类
22 |
23 | 4. run方法中,做的工作
24 |
25 | - StopWatch主要是监控启动过程,统计启动时间,检测应用是否已经启动或者停止。
26 |
27 | - 加载SpringApplicationRunListener(也是通过META-INF/spring.factories),默认加载的是EventPublishingRunListener
28 |
29 | - 调用RunListener.starting()方法。
30 |
31 | - 根据args创建应用参数解析器ApplicationArguments;
32 |
33 | - 准备环境变量:获取环境变量environment,将应用参数放入到环境变量持有对象中,监听器监听环境变量对象的变化(listener.environmentPrepared)
34 |
35 | - 打印Banner信息(SpringBootBanner)
36 |
37 | - 创建SpringBoot的应用上下文(AnnotationConfigEmbeddedWebApplicationContext)
38 |
39 | - prepareContext上下文之前的准备
40 |
41 | - refreshContext刷新上下文
42 |
43 | - afterRefresh(ApplicationRunner,CommandLineRunner接口实现类的启动)
44 |
45 | - 返回上下文对象
46 |
47 |
48 |
49 | ### Spring Boot启动的时候会加载哪些包?
50 |
51 | 在web项目中,会在Maven中配置 spring-boot-starter-web 包,该包中包含了spring-core、spring-content、servlet、tomcat、jackson、HikariCP、junit、jdbc、slf4j 等
52 |
53 |
54 |
55 | ### 如何重新加载 Spring Boot 上的更改,而无需重新启动服务器?
56 |
57 | - 【推荐】`spring-boot-devtools` 插件。注意,这个工具需要配置 IDEA 的自动编译。
58 |
59 | - Spring Loaded 插件。
60 |
61 | `Spring Boot 2.X 后,官方宣布不再支持 Spring Loaded 插件 的更新,所以基本可以无视它了。`
62 |
63 | - [JRebel](https://www.jianshu.com/p/bab43eaa4e14) 插件,需要付费。
64 |
65 | - 使用插件化开发,插件化代码使用手动注册Bean的方式
66 |
67 | 关于如何使用 `spring-boot-devtools` 和 Spring Loaded 插件,可以看看 [《Spring Boot 学习笔记:Spring Boot Developer Tools 与热部署》](https://segmentfault.com/a/1190000014488100) 。
68 |
69 |
70 |
71 | ### 什么是 Spring Boot 自动配置?
72 |
73 | 1. Spring Boot 在启动时扫描项目所依赖的 jar 包,寻找包含`spring.factories` 文件的 jar 包。
74 | 2. 根据 `spring.factories` 配置加载 AutoConfigure 类。
75 | 3. 根据 `@Conditional` 等条件注解的条件,进行自动配置并将 Bean 注入 Spring IoC 中。
76 |
77 | https://my.oschina.net/itsaysay/blog/3011826
78 |
79 |
--------------------------------------------------------------------------------
/12.Dubbo.md:
--------------------------------------------------------------------------------
1 | # 12.Dubbo
2 |
3 | > 最好的文档:http://dubbo.apache.org/zh-cn/docs/user/quick-start.html
4 |
5 | ### Dubbo SPI的理解
6 |
7 | Dubbo SPI 跟Java SPI很相似,Java SPI是Java内置的一种服务提供发现功能,一种动态替换发现机制。
8 |
9 | Java SPI使用方法:
10 |
11 | 1. 在META-INF/services 目录下放置配置文件,文件名是接口全路径名,文件内部是要实现接口的实现类全路径名,编码用UTF-8
12 | 2. 使用ServiceLoad.load(xx.class)调用
13 |
14 | Dubbo比Java 增加了:
15 |
16 | 1. 可以方便地获取某一个想要的扩展实现
17 | 2. 对于扩展实现IOC依赖注入功能
18 | 3. @SPI声明一个扩展接口,@Adaptive用在方法上,表示自动生成和编译一个动态的Adaptive类,如果用在类上表示一个装饰模式的类
19 |
20 | Dubbo 通过ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension()方法进行加载,每个SPI接口(@SPI注解的接口)都会产生一个ExtensionLoader扩展加载器实例,保存在名为EXTENSION_LOADERS的ConcureentMap中,通过扩展加载器调用getAdaptiveExtension()方法来获得自适应对象,并注入对象依赖属性
21 |
22 | Dubbo扩展接口文件放在META-INF/dubbo 目录下,文件名是接口全路径名,文件内部是接口实现类的全路径名
23 |
24 | ### Dubbo 基本原理、执行流程
25 |
26 | - 基本原理
27 |
28 | 1. service 层,接口层,给服务提供者和消费者来实现的
29 |
30 | 2. config 层,配置层,主要是对 dubbo 进行各种配置的
31 |
32 | 3. proxy 层,服务代理层,无论是 consumer 还是 provider,dubbo 都会给你生成代理,代理之间进行网络通信
33 |
34 | 4. registry 层,服务注册层,负责服务的注册与发现
35 | 5. cluster 层,集群层,封装多个服务提供者的路由以及负载均衡,将多个实例组合成一个服务
36 | 6. monitor 层,监控层,对 rpc 接口的调用次数和调用时间进行监控
37 | 7. protocal 层,远程调用层,封装 rpc 调用
38 | 8. exchange 层,信息交换层,封装请求响应模式,同步转异步
39 | 9. transport 层,网络传输层,抽象 mina 和 netty 为统一接口
40 | 10. serialize 层,数据序列化层
41 |
42 | - 执行流程
43 | 1. 服务容器Container负责启动加载运行服务提供者Provider,根据Provider配置文件根据协议发布服务,完成服务初始化
44 | 2. 在Provider(服务提供者)启动时,根据配置中的Registry地址连接注册中心,将Provider的服务信息发布到注册中心
45 | 3. Consumer(消费者)启动时,根据消费者配置文件中的服务引用信息,连接到注册中心,向注册中心订阅自己所需的服务
46 | 4. 注册中心根据服务订阅关系,返回Provider地址列表给Consumer,如果有变更,注册中心会推送最新的服务地址信息给Consumer
47 | 5. Consumer调用远程服务时,根据路由策略,先从缓存的Provider地址列表中选择一台进行,跨进程调用服务,假如调用失败,再重新选择其他调用
48 | 6. 服务Provider和Consumer,会在内存中记录调用次数和调用时间,每分钟发送一次统计数据到Monitor
49 |
50 | ### Dubbo 负载均衡策略、集群策略
51 |
52 | 负载均衡策略:
53 |
54 | 1. RoundRobinLoadBalance 权重轮询算法,按照公约后的权重设置轮询比例,把来自用户的请求轮流分配给内部的服务器
55 | 2. LeastActiveLoadBalance 最少活跃调用数均衡算法,活跃数是指调用前后计数差,使慢的机器收到的更少
56 | 3. ConsistenHashLoadBalance 一致性Hash算法,相同参数的请求总是发到同一个提供者
57 | 4. RandomLoadBlance 随机均衡算法,按权重设置随机概率,如果每个提供者的权重都相同,那么随机选一个,如果权重不同,则先累加权重值,从0~累加权重值选一个随机数,判断该随机数落在哪个提供者上
58 |
59 | 集群容错策略:
60 |
61 | 1. FailoverCluster: 失败转移,当出现失败时,重试其他服务器,通常用于读操作,但重试会带来更长延迟,默认集群策略
62 | 2. FailfastCluster: 快速失败,只发起一次调用,失败立即报错,通常用于非幂等性操作
63 | 3. FailbackCluster: 失败自动恢复, 对于Invoker调用失败,后台记录失败请求,任务定时重发,通常用于通知
64 | 4. BroadcastCluster: 广播调用,遍历所有Invokers,如果调用其中某个invoker报错,则catch住异常,这样就不影响其他invoker调用
65 | 5. AvailableCluster: 获取可用调用,遍历所有Invokers并判断Invoker.isAvalible,只要有一个为true就直接调用返回,不管成不成功
66 | 6. FailsafeCluster: 失败安全,出现异常时,直接忽略,通常用于写入审计日志等操作
67 | 7. ForkingCluster: 并且调用,只要一个成功即返回,通常用于实时性要求较高的操作,但需要更多的服务资源
68 | 8. MergeableCluster: 分组聚合,按组合并返回结果,比如某个服务接口有多种实现,可以用group区分,调用者调用多种实现并将得到的结果合并
69 |
70 | ### 注册中心挂了还可以通信吗?
71 |
72 | 可以。对于正在运行的 Consumer 调用 Provider 是不需要经过注册中心,所以不受影响。并且,Consumer 进程中,内存已经缓存了 Provider 列表。
73 |
74 | 那么,此时 Provider 如果下线呢?如果 Provider 是**正常关闭**,它会主动且直接对和其处于连接中的 Consumer 们,发送一条“我要关闭”了的消息。那么,Consumer 们就不会调用该 Provider ,而调用其它的 Provider 。
75 |
76 | 另外,因为 Consumer 也会持久化 Provider 列表到本地文件。所以,此处如果 Consumer 重启,依然能够通过本地缓存的文件,获得到 Provider 列表。
77 |
78 | 再另外,一般情况下,注册中心是一个集群,如果一个节点挂了,Dubbo Consumer 和 Provider 将自动切换到集群的另外一个节点上。
79 |
80 |
81 |
82 | ### dubbo 支持哪些通信协议?支持哪些序列化协议?说一下 Hessian 的数据结构?
83 |
84 | **序列化**,就是把数据结构或者是一些对象,转换为二进制串的过程,而**反序列化**是将在序列化过程中所生成的二进制串转换成数据结构或者对象的过程。
85 |
86 | 
87 |
88 | dubbo 支持不同的通信协议
89 |
90 | - dubbo 协议
91 |
92 | **默认**就是走 dubbo 协议,单一长连接,进行的是 NIO 异步通信,基于 hessian 作为序列化协议。使用的场景是:传输数据量小(每次请求在 100kb 以内),但是并发量很高。
93 |
94 | 为了要支持高并发场景,一般是服务提供者就几台机器,但是服务消费者有上百台,可能每天调用量达到上亿次!此时用长连接是最合适的,就是跟每个服务消费者维持一个长连接就可以,可能总共就 100 个连接。然后后面直接基于长连接 NIO 异步通信,可以支撑高并发请求。
95 |
96 | 长连接,通俗点说,就是建立连接过后可以持续发送请求,无须再建立连接。
97 |
98 | 
99 |
100 | 而短连接,每次要发送请求之前,需要先重新建立一次连接。
101 |
102 | 
103 |
104 | - rmi 协议
105 |
106 | 走 Java 二进制序列化,多个短连接,适合消费者和提供者数量差不多的情况,适用于文件的传输,一般较少用。
107 |
108 | - hessian 协议
109 |
110 | 走 hessian 序列化协议,多个短连接,适用于提供者数量比消费者数量还多的情况,适用于文件的传输,一般较少用。
111 |
112 | - http 协议
113 |
114 | 走 json 序列化。
115 |
116 | - webservice
117 |
118 | 走 SOAP 文本序列化。
119 |
120 |
121 |
122 | dubbo 支持的序列化协议
123 |
124 | dubbo 支持 hession、Java 二进制序列化、json、SOAP 文本序列化多种序列化协议。但是 hessian 是其默认的序列化协议。
125 |
126 |
127 |
128 | 说一下 Hessian 的数据结构
129 |
130 | Hessian 的对象序列化机制有 8 种原始类型:
131 |
132 | - 原始二进制数据
133 | - boolean
134 | - 64-bit date(64 位毫秒值的日期)
135 | - 64-bit double
136 | - 32-bit int
137 | - 64-bit long
138 | - null
139 | - UTF-8 编码的 string
140 |
141 | 另外还包括 3 种递归类型:
142 |
143 | - list for lists and arrays
144 | - map for maps and dictionaries
145 | - object for objects
146 |
147 | 还有一种特殊的类型:
148 |
149 | - ref:用来表示对共享对象的引用。
150 |
151 |
152 |
153 | ### 如何基于 dubbo 进行服务治理、服务降级、失败重试以及超时重试?
154 |
155 | ### 服务治理
156 |
157 | 1. 调用链路自动生成
158 |
159 | 一个大型的分布式系统,或者说是用现在流行的微服务架构来说吧,**分布式系统由大量的服务组成**。那么这些服务之间互相是如何调用的?调用链路是啥?说实话,几乎到后面没人搞的清楚了,因为服务实在太多了,可能几百个甚至几千个服务。
160 |
161 | 那就需要基于 dubbo 做的分布式系统中,对各个服务之间的调用自动记录下来,然后自动将**各个服务之间的依赖关系和调用链路生成出来**,做成一张图,显示出来,大家才可以看到对吧。
162 |
163 | 
164 |
165 | 2. 服务访问压力以及时长统计
166 |
167 | 需要自动统计**各个接口和服务之间的调用次数以及访问延时**,而且要分成两个级别。
168 |
169 | - 一个级别是接口粒度,就是每个服务的每个接口每天被调用多少次,TP50/TP90/TP99,三个档次的请求延时分别是多少;
170 | - 第二个级别是从源头入口开始,一个完整的请求链路经过几十个服务之后,完成一次请求,每天全链路走多少次,全链路请求延时的 TP50/TP90/TP99,分别是多少。
171 |
172 | 这些东西都搞定了之后,后面才可以来看当前系统的压力主要在哪里,如何来扩容和优化啊。
173 |
174 | 3. 其它
175 |
176 | - 服务分层(避免循环依赖)
177 | - 调用链路失败监控和报警
178 | - 服务鉴权
179 | - 每个服务的可用性的监控(接口调用成功率?几个 9?99.99%,99.9%,99%)
180 |
181 | ### 服务降级
182 |
183 | 比如说服务 A 调用服务 B,结果服务 B 挂掉了,服务 A 重试几次调用服务 B,还是不行,那么直接降级,走一个备用的逻辑,给用户返回响应。
184 |
185 | 举个栗子,我们有接口 `HelloService`。`HelloServiceImpl` 有该接口的具体实现。
186 |
187 | ```xml
188 | public interface HelloService {
189 | void sayHello();
190 | }
191 |
192 | public class HelloServiceImpl implements HelloService {
193 | public void sayHello() {
194 | System.out.println("hello world......");
195 | }
196 | }
197 |
198 |
201 |
202 |
203 |
204 |
205 |
206 |
207 |
208 |
209 |
210 |
211 |
215 |
216 |
217 |
218 |
219 |
220 |
221 |
222 |
223 |
224 | ```
225 |
226 | 我们调用接口失败的时候,可以通过 `mock` 统一返回 null。
227 |
228 | mock 的值也可以修改为 true,然后再跟接口同一个路径下实现一个 Mock 类,命名规则是 “接口名称+`Mock`” 后缀。然后在 Mock 类里实现自己的降级逻辑。
229 |
230 | ```
231 | public class HelloServiceMock implements HelloService {
232 | public void sayHello() {
233 | // 降级逻辑
234 | }
235 | }
236 | ```
237 |
238 | ### 失败重试和超时重试
239 |
240 | 所谓失败重试,就是 consumer 调用 provider 要是失败了,比如抛异常了,此时应该是可以重试的,或者调用超时了也可以重试。配置如下:
241 |
242 | ```
243 |
244 | ```
245 |
246 | 举个栗子。
247 |
248 | 某个服务的接口,要耗费 5s,你这边不能干等着,你这边配置了 timeout 之后,我等待 2s,还没返回,我直接就撤了,不能干等你。
249 |
250 | 可以结合你们公司具体的场景来说说你是怎么设置这些参数的:
251 |
252 | - `timeout`:一般设置为 `200ms`,我们认为不能超过 `200ms` 还没返回。
253 | - `retries`:设置 retries,一般是在读请求的时候,比如你要查询个数据,你可以设置个 retries,如果第一次没读到,报错,重试指定的次数,尝试再次读取。
--------------------------------------------------------------------------------
/13.Spring Cloud.md:
--------------------------------------------------------------------------------
1 | # 13.Spring Cloud
2 |
3 | ### Spring Cloud 核心功能是什么?
4 |
5 | Spring Cloud 主要提供了如下核心的功能:
6 |
7 | - Distributed/versioned configuration 分布式/版本化的配置管理
8 | - Service registration and discovery 服务注册与服务发现
9 | - Routing 路由
10 | - Service-to-service calls 端到端的调用
11 | - Load balancing 负载均衡
12 | - Circuit Breakers 断路器
13 | - Global locks 全局锁
14 | - Leadership election and cluster state 选举与集群状态管理
15 | - Distributed messaging 分布式消息
16 |
17 | ### Spring Cloud 有哪些组件?
18 |
19 | 脑图如下:
20 |
21 | 
22 |
23 | 由于 [Spring Cloud Netflix](https://github.com/spring-cloud/spring-cloud-netflix) 要进入维护模式,下面是一些可以替代组件
24 |
25 | | | Netflix | 阿里 | 其它 |
26 | | -------- | ------- | -------- | ------------------------------------------------------------ |
27 | | 注册中心 | Eureka | Nacos | Zookeeper、Consul、Etcd |
28 | | 熔断器 | Hystrix | Sentinel | Resilience4j |
29 | | 网关 | Zuul | 暂无 | Spring Cloud Gateway |
30 | | 负载均衡 | Ribbon | Dubbo | [`spring-cloud-loadbalancer`](https://github.com/spring-cloud/spring-cloud-commons/tree/master/spring-cloud-loadbalancer) |
31 |
32 | ### Spring Cloud 和 Spring Boot 的区别和关系?
33 |
34 | 1. Spring Boot 专注于快速方便的开发单个个体微服务。
35 | 2. Spring Cloud 是关注全局的微服务协调整理治理框架以及一整套的落地解决方案,它将 Spring Boot 开发的一个个单体微服务整合并管理起来,为各个微服务之间提供:配置管理,服务发现,断路器,路由,微代理,事件总线等的集成服务。
36 | 3. Spring Boot 可以离开 Spring Cloud 独立使用,但是 Spring Cloud 离不开 Spring Boot ,属于依赖的关系。
37 |
38 | 总结:
39 |
40 | - Spring Boot ,专注于快速,方便的开发单个微服务个体。
41 | - Spring Cloud ,关注全局的服务治理框架。
42 |
43 | ### 有哪些可以作为Spring Cloud的注册中心
44 |
45 | 在 Spring Cloud 中,能够使用的注册中心,还是比较多的,如下:
46 |
47 | - [`spring-cloud-netflix-eureka-server`](https://github.com/spring-cloud/spring-cloud-netflix/tree/master/spring-cloud-netflix-eureka-server) 和 [`spring-cloud-netflix-eureka-client`](https://github.com/spring-cloud/spring-cloud-netflix/tree/master/spring-cloud-netflix-eureka-server) ,基于 Eureka 实现。
48 | - [`spring-cloud-alibaba-nacos-discovery`](https://github.com/spring-cloud-incubator/spring-cloud-alibaba/tree/master/spring-cloud-alibaba-nacos-discovery) ,基于 Nacos 实现。
49 | - [`spring-cloud-zookeeper-discovery`](https://github.com/spring-cloud/spring-cloud-zookeeper/tree/master/spring-cloud-zookeeper-discovery) ,基于 Zookeeper 实现。
50 | - … 等等
51 |
52 | 以上的实现,都是基于 [`spring-cloud-commons`](https://github.com/spring-cloud/spring-cloud-commons) 的 [`discovery`](https://github.com/spring-cloud/spring-cloud-commons/blob/master/spring-cloud-commons/src/main/java/org/springframework/cloud/client/discovery/) 的 [DiscoveryClient](https://github.com/spring-cloud/spring-cloud-commons/blob/master/spring-cloud-commons/src/main/java/org/springframework/cloud/client/discovery/DiscoveryClient.java) 接口,实现统一的客户端的注册发现。
53 |
54 | ### SpringCloud的注册和发现流程,以Eureka为注册中心
55 |
56 |
57 |
58 | 1. 服务启动时会生成服务的基本信息对象InstanceInfo,然后再启动时注册到服务治理中心
59 | 2. 服务注册完成后,会从服务治理中心拉取所有的服务信息,缓存在本地
60 | 3. 之后服务会根据配置的指定间隔时间发送一个心跳信息,续约服务
61 | 4. 如果服务治理中心在90s内没有收到一个服务的续约,就会认为服务已经挂了,会把服务注册信息删除
62 | 5. 服务停止前,会主动发送一个停止请求,服务治理中心会删除这个服务的信息
63 | 6. 如果Eureka收到的心跳包不足正常值的85%(可配置)就会进入自我保护模式,这种模式,Eureka不会删除任何服务信息
64 |
65 | ### Eureka 如何实现集群?
66 |
67 | 详细文章:[《配置 Eureka Server 集群》](https://www.jianshu.com/p/5d5b2cf7d476)
68 |
69 | 此处,也很容易引申出一个问题,为什么 Eureka 被设计成 AP 的系统,答案可以看看 [《为什么不应该使用 ZooKeeper 做服务发现》](http://dockone.io/article/78) 。
70 |
71 | ### 聊聊 Eureka 缓存机制?
72 |
73 | [《Eureka 缓存细节以及生产环境的最佳配置》](http://bhsc881114.github.io/2018/04/01/eureka缓存细节以及生产环境的最佳配置/)
74 |
75 | ### 什么是 Eureka 自我保护机制?
76 |
77 | [《[Spring Cloud\] Eureka 的自我保护模式及相关问题》](https://blog.csdn.net/t894690230/article/details/78207495)
78 |
79 | ### 说说Spring Cloud 的负载均衡
80 |
81 | 在 Spring Cloud 中,能够使用的负载均衡,如下:
82 |
83 | - [`spring-cloud-netflix-ribbon`](https://github.com/spring-cloud/spring-cloud-netflix/tree/master/spring-cloud-netflix-ribbon) ,基于 Ribbon 实现。
84 | - [`spring-cloud-loadbalancer`](https://github.com/spring-cloud/spring-cloud-commons/tree/master/spring-cloud-loadbalancer) ,提供简单的负载均衡功能。
85 |
86 | 以上的实现,都是基于 [`spring-cloud-commons`](https://github.com/spring-cloud/spring-cloud-commons) 的 [`loadbalancer`](https://github.com/spring-cloud/spring-cloud-commons/tree/master/spring-cloud-commons/src/main/java/org/springframework/cloud/client/loadbalancer) 的 [ServiceInstanceChooser](https://github.com/spring-cloud/spring-cloud-commons/blob/ecabe2bb8d9cb14aa6edcff41fdb79dc304ed004/spring-cloud-commons/src/main/java/org/springframework/cloud/client/loadbalancer/ServiceInstanceChooser.java) 接口,实现统一的服务的选择。并且,负载均衡组件在选择需要调用的服务之后,还提供调用该服务的功能,具体方法见 [LoadBalancerClient](https://github.com/spring-cloud/spring-cloud-commons/blob/master/spring-cloud-commons/src/main/java/org/springframework/cloud/client/loadbalancer/LoadBalancerClient.java) 接口的 `#execute(...)` 方法。
87 |
88 | ### 为什么要负载均衡?
89 |
90 | 简单来说,随着业务的发展,单台服务无法支撑访问的需要,于是搭建多个服务形成集群。那么随之要解决的是,每次请求,调用哪个服务,也就是需要进行负载均衡。
91 |
92 | 目前负载均衡有两种模式:
93 |
94 | 1. 客户端模式
95 | 2. 服务端模式
96 |
97 | 在 Spring Cloud 中,我们使用前者,即客户端模式。
98 |
99 | 详细的内容,可以看看 [《客户端负载均衡与服务端负载均衡》](https://blog.csdn.net/u014401141/article/details/78676296) 。
100 |
101 | 在计算中,负载平衡可以改善跨计算机,计算机集群,网络链接,中央处理单元或磁盘驱动器等多种计算资源的工作负载分布。负载平衡旨在优化资源使用,最大化吞吐量,最小化响应时间并避免任何单一资源的过载。使用多个组件进行负载平衡而不是单个组件可能会通过冗余来提高可靠性和可用性。负载平衡通常涉及专用软件或硬件,例如多层交换机或域名系统服务器进程。
102 |
103 | ### Feign 实现原理
104 |
105 | **Feign的一个关键机制就是使用了动态代理**。咱们一起来看看下面的图,结合图来分析:
106 |
107 | - 首先,如果你对某个接口定义了 `@FeignClient` 注解,Feign 就会针对这个接口创建一个动态代理。
108 | - 接着你要是调用那个接口,本质就是会调用 Feign 创建的动态代理,这是核心中的核心。
109 | - Feign的动态代理会根据你在接口上的 `@RequestMapping` 等注解,来动态构造出你要请求的服务的地址。
110 | - 最后针对这个地址,发起请求、解析响应。
111 |
112 | 
113 |
114 | ### Feign 和 Ribbon 的区别?
115 |
116 | Ribbon 和 Feign 都是使用于调用用其余服务的,不过方式不同。
117 |
118 | - 启动类用的注解不同。
119 | - Ribbon 使用的是 `@RibbonClient` 。
120 | - Feign 使用的是 `@EnableFeignClients` 。
121 | - 服务的指定位置不同。
122 | - Ribbon 是在 `@RibbonClient` 注解上设置。
123 | - Feign 则是在定义声明方法的接口中用 `@FeignClient` 注解上设置。
124 | - 调使用方式不同。
125 | - Ribbon 需要自己构建 Http 请求,模拟 Http 请求而后用 RestTemplate 发送给其余服务,步骤相当繁琐。
126 | - Feign 采使用接口的方式,将需要调使用的其余服务的方法定义成声明方法就可,不需要自己构建 Http 请求。不过要注意的是声明方法的注解、方法签名要和提供服务的方法完全一致。
127 |
128 | ### 为什么要网关服务?
129 |
130 | 使用网关服务,我们实现统一的功能:
131 |
132 | - 动态路由
133 | - 灰度发布
134 | - 健康检查
135 | - 限流
136 | - 熔断
137 | - 认证: 如数支持 HMAC, JWT, Basic, OAuth 2.0 等常用协议
138 | - 鉴权: 权限控制,IP 黑白名单,同样是 OpenResty 的特性
139 | - 可用性
140 | - 高性能
141 |
142 | ### 熔断和降级区别
143 |
144 | 熔断是下层服务一旦产生故障就断掉;降级需要对服务进行分级,把产生故障的服务丢掉,换一个轻量级的方案。
145 |
146 |
147 |
148 | ### Spring Cloud Gateway
149 |
150 | #### 过滤器
151 |
152 | 分类:
153 |
154 | 1、 GatewayFilter,网关过滤器,只应用在单个路由或者一个分组的路由上
155 |
156 | - AddRequestHeader:用于在请求头中添加自定义键值对
157 | - AddRequestParameter:用于在请求中添加请求参数的键值对
158 | - AddResponseHeader:用于在响应头中添加键值对
159 | - Hystrix网关过滤工厂:用于将断路器引入网关路由中
160 | - PrefixPath:用于使用简单的Prefix参数
161 | - PreserveHostHeader:用于设置路由过滤器的请求属性,检查是否发送原始主机头或由HTTP客户端确定主机头
162 | - RequestRateLimiter:用于确定当前请求是否允许继续,如果不允许,返回提示“HTTP 429 - Too Many Requests”
163 | - RedirectTo:用于接收请求的状态和URL参数,该状态是一个重定向的300系列的HTTP代码,如301,URL是Location的头部值
164 | - RemoveNonProxyHeaders:用于从转发的请求中删除请求头
165 | - RemoveRequestHeader:用于删除请求头,需要请求头名
166 | - RemoveResponseHeader:用于响应头,需要响应头名
167 | - RewritePath:用于使用Java正则表达式重写请求路径
168 | - SaveSession:用于在转发下游调用之前强制执行保存Session操作
169 | - SecureHeaders:用于为响应添加安全头
170 | - SetPath:允许通过路径的模版段来操作请求的路径,使用了Spring框架的URI模版,支持多种匹配
171 | - SetResponseHeader:用于设置响应头,需要有一个Key-Value对
172 | - SetStatus:用于设置请求响应状态,需要一个Status参数,该参数的值必须是有效的SpringHttpStatus,
173 | - StripPrefix:用于剥离前缀,需要parts参数,表明在请求被发送到下游之前从请求路径中剥离的元素数量
174 | - Retry:用于重试
175 | - RequestSize:用于限制请求的大小,当请求超过限制时启用,限制请求到达下游服务,该过滤器将RequestSize作为参数
176 |
177 | 2、 GlobalFilter,全局过滤器,应用在所有的路由上
178 |
179 | - Forward Routing Filter
180 | - LoadBalancerClientFilter
181 | - Netty Routing Filter
182 | - Netty Write Response Filter
183 | - RouteToRequestUrl Filter
184 | - Websocket Routing Filter
185 | - GateWay Metrics Filter 网关指标过滤器
186 | - Combined Global Filter and GateWayFilter 组合式全局过滤器和网关过滤器排序
187 | - Marking An Exchange As Routed 路由交换
188 |
189 | > 详细见:[Spring Cloud Gateway 参考指南_cloud: gateway: metrics: enabled: true-CSDN博客](https://blog.csdn.net/weixin_40972073/article/details/125840118?ops_request_misc=%7B%22request%5Fid%22%3A%22171941508116800184151243%22%2C%22scm%22%3A%2220140713.130102334.pc%5Fblog.%22%7D&request_id=171941508116800184151243&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~blog~first_rank_ecpm_v1~rank_v31_ecpm-1-125840118-null-null.nonecase&utm_term=gateway&spm=1018.2226.3001.4450)
190 |
191 |
--------------------------------------------------------------------------------
/14.Message Queue.md:
--------------------------------------------------------------------------------
1 | # 14.消息队列
2 |
3 | > 《深入理解Kafka 核心设计与实践原理》
4 |
5 | ### Kafka
6 |
7 | #### 说一下Kafka
8 |
9 | Kafka是一款高性能的消息中间件,包括Producer,Consumer,Broker,以及Zookeeper,Zookeeper用来负责集群元数据管理,控制器的选举等操作,Producer将消息发送到Broker,由Broker负责将收到的消息存储到磁盘中,Consumer负责从Broker订阅并消费消息。Kafka中的消息是以主题为单位,主题可以分布在不同的分区,分区可以分布于不同的Broker,分区有Leader 与副本follower,follower负责从leader同步数据,leader负责读写请求
10 |
11 |
12 |
13 | #### 消息的幂等性处理思路
14 |
15 | 主要是防止消息重复消费,通过业务方去保证消息的幂等性,每条消息设置一个唯一Id,数据库的话通过唯一索引。
16 |
17 |
18 |
19 | #### 消息队列如何保证高可用
20 | kafka可以有多个Borker,一个topic会将数据存储在不同的partiton上, 并且有多个副本来同步数据,但只会有一个leader,数据以Log的形式存储在硬盘中,并且记录了消费的offset。如果leader挂掉,会从ISR集合中的副本选出一个做为leader。
21 |
22 |
23 |
24 | #### Kafka 新建的分区会在哪个目录下创建
25 |
26 | 在启动Kafka集群之前,我们需要配置好log.dirs参数,其值是kafka数据的存放目录,这个参数可以配置多个目录,目录之间使用逗号分隔,通常这些目录是分布在不同的磁盘上用于提高读写性能。
27 |
28 | 也可以配置log.dir参数,含义一样,只需要设置其中一个即可。如果log.dirs参数只配置了一个目录,那么分配到各个Broker上的分区肯定只能在这个目录下创建文件夹用于存储数据。
29 |
30 | 但是如果log.dirs参数配置了多个目录,Kafka会在含有分区目录最少的文件夹中创建新的分区目录,分区目录名为Topic名 + 分区ID。`注意,是分区文件夹总数最少的目录,而不是磁盘使用量最少的目录`
31 |
32 |
33 |
34 | #### Kafka的 ack 机制
35 |
36 | ack 有三个值 0 1 -1
37 |
38 | 0 : 生产者不会等待borker返回,延迟最低但是存储的保证最弱当server挂掉的时候就会丢数据
39 |
40 | 1 : 等待leader确认消息返回,但如果Leader挂掉后不保证是否复制完成
41 |
42 | -1: 等待所有的副本确认消息返回
43 |
44 |
45 |
46 | #### 如何保证消息可靠性
47 |
48 | kafka保证消息可靠性,可以通过如下几个配置:
49 | 1. 生产者配置 acks = all (-1) ISR中的所有副本都写入成功,才代表该消息发送成功
50 | 2. min.insync.replicas默认为1,指定了ISR集合中最小的副本数,不满足条件就会抛出NotEnoughReplicasException 或 NotEnoughReplicasAfterAppendException,也就是必须保证有一个副本同步数据跟得上leader
51 | 3. unclean.leader.election.enable 默认为false, 当leader下线的时候可以从非ISR集合中选举新的leader,这样能提高可用性,但会丢数据
52 | 4. 配置log.flush.interval.messages 和 log.flush.interval.ms 同步刷盘策略
53 | 5. 消费端手动提交位移,enable.auto.commit 设置为false,自动提交会带来重复消费和消息丢失的问题,客户端如果消息消费失败应该放入死信队列,以便后期排除故障
54 | 6. 回溯消费功能,消息貌似已经成功消费,但实际消息失败了,不知道是什么错误造成的,就可以通过回溯消费补偿,或者复现 “丢失”,seek()方法
55 |
56 |
57 |
58 | #### 消息积压问题
59 |
60 | 如果消息积压了太多,一直消费不了,需要检查是不是consumer有问题,或者服务端磁盘是否快满了。
61 | 1. consumer有问题就修复问题。但由于积压的数据太多,用原程序消费还是太慢。就需要扩容,新建临时topic,将分区改为原来的10倍,写程序将原来积压的消息发送到新建的topic中,启动10倍的机器来消费这些数据
62 | 2. 服务端磁盘满,就只能扩容服务端磁盘,再采用第一种办法来修复问题
63 |
64 |
65 |
66 | #### 怎么保证消息顺序消费
67 |
68 | 消息要顺序消费的场景,比如发送了一个用户新增的消息,随后用户修改了发送了一个修改的消息,最后又删除了发送了一个删除的消息,由于Kafka的多分区,多消费者,消费端势必会变成无序消费,但消费端业务需要顺序处理,如果先消费了删除消息,根本没数据,随后又消费了新增消息,最后消息没有删除,变成了脏数据。
69 |
70 | 解决方法是:生产者发送消息的时候,根据用户id指定分区key,指定后kafka会将消息发送到指定的分区中,这样保证了分区中消息的顺序。消费端,可以使用单线程从指定分区中消费,如果要保证性能,消费端定义多个内存队列,将相同用户id的消息发送到同一个内存队列中,然后开启多线程从来消费多个内存队列,一个线程处理一个内存队列
71 |
72 |
73 |
74 | #### kafka的分区策略
75 |
76 | 消费者客户端参数partition.assignment.strategy 来配置消费分区策略
77 | 1. RangeAssignor 默认分配策略 通过 分区数/消费者总数 来获得一个跨度进行分配
78 | 2. RoundRobinAssignor 轮询分配策略
79 | 3. StickyAssignor 能够使分区的分配尽可能与上一次保持一致,避免过度重分配
80 | 4. 自定义分配,实现PartitionAssignor接口
81 |
82 |
83 |
84 | #### kafka 集群如何搭建
85 |
86 | - 安装zk集群,修改各个节点的kafka配置文件server.properties(broker.id、listeners、zookeeper.connect)
87 | - 启动zk、启动kafka
88 |
89 | k8s 上创建:[K8s - 安装部署Kafka、Zookeeper集群教程(支持从K8s外部访问) - 蜂蜜log - 博客园 (cnblogs.com)](https://www.cnblogs.com/fengyuanfei/p/17789107.html)
90 |
91 |
92 |
93 | #### 什么是ISR
94 |
95 | - AR(Assigned Repllicas)一个partition的所有副本(即使replica,不区分leader或follower)
96 | - ISR(In-Sync Replicas)能够和leader保持同步的follower+leader本身组成的集合
97 | - OSR(Out-Sync Relipcas)不能和leader 保持同步的follower集合
98 |
99 |
--------------------------------------------------------------------------------
/15.Mybatis.md:
--------------------------------------------------------------------------------
1 | # 15.Mybatis
2 |
3 | ### MyBatis 编程步骤
4 |
5 | 1. 创建 SqlSessionFactory 对象。
6 |
7 | 2. 通过 SqlSessionFactory 获取 SqlSession 对象。
8 |
9 | 3. 通过 SqlSession 获得 Mapper 代理对象。
10 |
11 | 4. 通过 Mapper 代理对象,执行数据库操作。
12 |
13 | 5. 执行成功,则使用 SqlSession 提交事务。
14 |
15 | 6. 执行失败,则使用 SqlSession 回滚事务。
16 |
17 | 7. 最终,关闭会话。
18 |
19 |
20 |
21 | ### MyBatis 如何执行批量插入?
22 |
23 | 1. 使用Sql 拼接,有语句大小限制
24 |
25 | ```sql
26 | INSERT INTO [表名]([列名],[列名])
27 | VALUES
28 | ([列值],[列值])),
29 | ([列值],[列值])),
30 | ([列值],[列值]));
31 | ```
32 |
33 | 2. 使用Mybatis 的批量插入方式
34 |
35 | ```java
36 | private static SqlSessionFactory sqlSessionFactory;
37 |
38 | @Test
39 | public void testBatch() {
40 | // 创建要插入的用户的名字的数组
41 | List names = new ArrayList<>();
42 | names.add("张三");
43 | names.add("李四");
44 | names.add("李二");
45 | names.add("王五");
46 |
47 | // 获得执行器类型为 Batch 的 SqlSession 对象,并且 autoCommit = false ,禁止事务自动提交
48 | try (SqlSession session = sqlSessionFactory.openSession(ExecutorType.BATCH, false)) {
49 | // 获得 Mapper 对象
50 | UserMapper mapper = session.getMapper(UserMapper.class);
51 | // 循环插入
52 | for (String name : names) {
53 | mapper.insertUser(name);
54 | }
55 | // 提交批量操作
56 | session.commit();
57 | }
58 | }
59 | ```
60 |
61 | 3. 使用循环一条条插入
62 |
63 |
64 |
65 | ### Mybatis 的 XML Mapper文件中,不同的 XML 映射文件,id 是否可以重复?
66 |
67 | 不同的 XML Mapper 文件,如果配置了 `"namespace"` ,那么 id 可以重复;如果没有配置 `"namespace"` ,那么 id 不能重复。毕竟`"namespace"` 不是必须的,只是最佳实践而已。
68 |
69 | 原因就是,`namespace + id` 是作为 `Map` 的 key 使用的。如果没有 `"namespace"`,就剩下 id ,那么 id 重复会导致数据互相覆盖。如果有了 `"namespace"`,自然 id 就可以重复,`"namespace"`不同,`namespace + id` 自然也就不同。
70 |
71 |
72 |
73 | ### 简述 Mybatis 的 XML 映射文件和 Mybatis 内部数据结构之间的映射关系?
74 |
75 | Mybatis 将所有 XML 配置信息都封装到 All-In-One 重量级对象Configuration内部。
76 |
77 | 在 XML Mapper 文件中:
78 |
79 | - `` 标签,会被解析为 ParameterMap 对象,其每个子元素会被解析为 ParameterMapping 对象。
80 |
81 | - `` 标签,会被解析为 ResultMap 对象,其每个子元素会被解析为 ResultMapping 对象。
82 |
83 | - 每一个 `