Merge remote-tracking branch 'origin/master'

pull/69/head
AmyliaY 5 years ago
commit 7bf687d681

@ -0,0 +1,17 @@
name: Merge Branch
on:
push:
branches: [ imgbot ]
jobs:
merge-branch:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: everlytic/branch-merge@1.1.0
with:
github_token: ${{ github.token }}
source_ref: ${{ github.ref }}
target_branch: 'master'
commit_message_template: '[Automated] Merged {source_ref} into {target_branch}'

@ -166,6 +166,7 @@
### Netty 技术细节源码分析
* [FastThreadLocal源码分析](docs/Netty/Netty技术细节源码分析/FastThreadLocal源码分析.md)
* [Recycler对象池原理分析](docs/Netty/Netty技术细节源码分析/Recycler对象池原理分析.md)
* [MpscLinkedQueue队列原理分析](docs/Netty/Netty技术细节源码分析/MpscLinkedQueue队列原理分析.md)
## Dubbo

@ -0,0 +1,69 @@
该文所涉及的 netty 源码版本为 4.1.6。
## MpscLinkedQueue 是什么
在 Netty 的核心中的核心成员 NioEventLoop 中,其中任务队列的实现 taskQueue 便是 MpscLinkedQueue。MpscLinkedQueue 是 Netty 所实现的一个基于多生产者单消费者的无锁队列,针对 NioEventLoop 中任务队列的特点,其单消费者的场景在一开始就避免了从队列中取数据时加锁的必要,而其最精妙的地方便是在多生产者并发从队列中添加数据的时候也没有加锁,达到 Netty 所期望的高性能实现。这是如何实现的?
## MpscLinkedQueue 无锁并发线程安全写入原理
### MpscLinkedQueue 对于尾结点的维护
首先MpscLinkedQueue 继承自 AtomicReference也就是说 MpscLinkedQueue 通过继承自 AtomicReference 的方式,显式地维护了一个提供原子读写能力的变量 value。而在 MpscLinkedQueue 中,这个 value 是其内部维护的队列的尾结点。
### MpscLinkedQueue 对于头结点的维护
而后,来看 MpscLinkedQueue 的构造方法。
```java
MpscLinkedQueue() {
MpscLinkedQueueNode<E> tombstone = new DefaultNode<E>(null);
headRef = new FullyPaddedReference<MpscLinkedQueueNode<E>>();
headRef.set(tombstone);
setTail(tombstone);
}
```
在 MpscLinkedQueue 中,维护着 headRef 头结点字段,其队列内部节点的实现是一个 MpscLinkedQueueNode。MpscLinkedQueueNode 是一个除了存放具体队列元素外只有 next 字段的节点也就是说MpscLinkedQueue 的队列是单向的。在构造方法的最后,通过 setTail()方法的,将 MpscLinkedQueue 的尾结点字段 value 也设置为头结点。MpscLinkedQueue 的头结点字段 headRef 的存在可以方便后续直接从头结点开始的队列操作,消费者可以简单判断头尾节点是否相等来确认队列中是否有元素可以消费。
### MpscLinkedQueue 如何做到线程安全的无锁加入
```java
@Override
@SuppressWarnings("unchecked")
public boolean offer(E value) {
if (value == null) {
throw new NullPointerException("value");
}
final MpscLinkedQueueNode<E> newTail;
if (value instanceof MpscLinkedQueueNode) {
newTail = (MpscLinkedQueueNode<E>) value;
newTail.setNext(null);
} else {
newTail = new DefaultNode<E>(value);
}
MpscLinkedQueueNode<E> oldTail = replaceTail(newTail);
oldTail.setNext(newTail);
return true;
}
private MpscLinkedQueueNode<E> replaceTail(MpscLinkedQueueNode<E> node) {
return getAndSet(node);
}
```
MpscLinkedQueue 的 offer()方法很简短,但是恰恰就是整个添加队列元素加入的流程,当元素被加入的时候,首先判断加入的元素是否是 MpscLinkedQueueNode如果不是则进行封装。之后便是整个操作的重点
- 通过 replaceTail()方法,将当前被加入的节点通过 AtomicReference 所提供的 getAndSet()方法将其设为队列的尾结点,并返回先前的尾结点。这次操作由 UNSAFE 的 CAS 来保证操作的原子性。
- 之后将之前的尾结点的 next 指向新加入的节点,本次加入宣告结束。
整个操作就到此结束这里可以看出MpscLinkedQueue 利用了 AtomicReference 底层 UNSAFE 的能力,通过 CAS 确保新设置进入 value 的节点必定能够和原先的节点达成一个且唯一的联系,那么只需要自顶向下不断通过将这个联系变成引用,那么一条队列便形成了。由于其实现是链表而不是数组,也就没有涉及到资源的竞争,在不加锁的前提下其队列顺序可能不会严格按照加入顺序,但这在当前场景下并不是问题。在这个前提,高并发的插入场景下,每个新进入的新节点都将获取原尾位置 value 上的节点而自身将会被设置为其后驱节点重新放到尾结点位置上CAS 在不加锁的前提下保证了前后节点对应关系的唯一性,完成了并发条件下不加锁的线程安全写入。
### MpscLinkedQueue 不支持 remove()
在 MpscLinkedQueue 中,是不支持 remove()的方法去从队列中移除任意一个元素的。原因很简单,消费者和生产者是无锁的,消费者可以通过比较队首和队尾元素是否一致来保证线程安全地从队首取数据,但是 remove()从队列中任意位置修改数据是线程不安全的,主要体现在移除队尾元素可能会导致正在加入的新元素被丢弃。
## MpscLinkedQueue 另外的实现细节
- MpscLinkedQueue 中的头节点被通过 FullyPaddedReference 封装。其内部前后分别填充 56 字节和 64 字节来进行填充以避免伪共享导致的性能损耗,使得其头结点可以高效被访问。关于伪共享的相关知识可以通过搜索引擎进行查询。
- MpscLinkedQueue 在消费者消费数据后,当将下一个节点设置为头结点的时候,并不是直接进行赋值,而是通过 UNSAFE 来根据偏移量赋值,这样做将略微提高性能,主要是内存屏障 storestrore 和 loadstrore 之间的性能差异。

@ -97,7 +97,7 @@ public enum SearchStrategy {
- 类图
![image-20200824085726621](//images/SpringBoot//SpringBoot/image-20200824085726621.png)
![image-20200824085726621](../../images/SpringBoot//SpringBoot/image-20200824085726621.png)
在看这部分源码之前需要先了解 `Conditional`和`Condition`的源码
@ -421,7 +421,7 @@ for (String type : spec.getTypes()) {
- 在忽略 bean 找到之后做一个类型移除的操作.
![image-20200825140750035](/images/SpringBoot//image-20200825140750035.png)
![image-20200825140750035](../../images/SpringBoot//image-20200825140750035.png)
### 返回值
@ -469,7 +469,7 @@ public static ConditionOutcome noMatch(ConditionMessage message) {
return ConditionOutcome.match(matchMessage);
```
![image-20200825141506531](/images/SpringBoot//image-20200825141506531.png)
![image-20200825141506531](../../images/SpringBoot//image-20200825141506531.png)
- 到此结果封装完毕.回到方法`org.springframework.boot.autoconfigure.condition.SpringBootCondition#matches(org.springframework.context.annotation.ConditionContext, org.springframework.core.type.AnnotatedTypeMetadata)` 继续进行
- 再往后就继续执行 spring 的 bean 初始化咯
@ -492,7 +492,7 @@ public static ConditionOutcome noMatch(ConditionMessage message) {
- 根据类的注解信息我们可以找到有`ResourceBundleCondition`
![image-20200825092343271](/images/SpringBoot//image-20200825092343271.png)
![image-20200825092343271](../../images/SpringBoot//image-20200825092343271.png)
- 获取类名或者方法名的结果是`MessageSourceAutoConfiguration`全路径
@ -592,8 +592,8 @@ org.springframework.boot.autoconfigure.condition.OnWebApplicationCondition
- 此时我们可以和前文的源码分析连接起来有一个完整的认识了
![image-20200825142332485](/images/SpringBoot//image-20200825142332485.png)
![image-20200825142332485](../../images/SpringBoot//image-20200825142332485.png)
- 最后来看整体类图
![image-20200825142418115](/images/SpringBoot//image-20200825142418115.png)
![image-20200825142418115](../../images/SpringBoot//image-20200825142418115.png)

@ -33,7 +33,7 @@ public @interface ConfigurationPropertiesScan {}
## ConfigurationPropertiesScanRegistrar
![image-20200323094446756](../../../images/SpringBoot/image-20200323094446756.png)
![image-20200323094446756](../../images/SpringBoot/image-20200323094446756.png)
- debug 没有抓到后续补充
@ -137,11 +137,11 @@ public @interface EnableConfigurationProperties {
- 先看输入参数 **metadata**
![image-20200323134135926](../../../images/SpringBoot/image-20200323134135926.png)
![image-20200323134135926](../../images/SpringBoot/image-20200323134135926.png)
- getTypes 结果
![image-20200323134325955](../../../images/SpringBoot/image-20200323134325955.png)
![image-20200323134325955](../../images/SpringBoot/image-20200323134325955.png)
- 源码开始,先找出刚才的对象`org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration`
@ -192,7 +192,7 @@ public @interface EnableConfigurationProperties {
## ConfigurationPropertiesBindingPostProcessor
![image-20200323095626953](../../../images/SpringBoot/image-20200323095626953.png)
![image-20200323095626953](../../images/SpringBoot/image-20200323095626953.png)
### postProcessBeforeInitialization
@ -301,15 +301,15 @@ public @interface EnableConfigurationProperties {
- `annotation`
![image-20200323104711545](../../../images/SpringBoot/image-20200323104711545.png)
![image-20200323104711545](../../images/SpringBoot/image-20200323104711545.png)
- `bindType`
![image-20200323104815305](../../../images/SpringBoot/image-20200323104815305.png)
![image-20200323104815305](../../images/SpringBoot/image-20200323104815305.png)
- 返回对象
![image-20200323105053757](../../../images/SpringBoot/image-20200323105053757.png)
![image-20200323105053757](../../images/SpringBoot/image-20200323105053757.png)
- 此时数据还没有进去
@ -319,7 +319,7 @@ public @interface EnableConfigurationProperties {
直接看结果
![image-20200323105155998](../../../images/SpringBoot/image-20200323105155998.png)
![image-20200323105155998](../../images/SpringBoot/image-20200323105155998.png)
- 上述配置和我在配置文件中写的配置一致
@ -362,7 +362,7 @@ BindResult<?> bind(ConfigurationPropertiesBean propertiesBean) {
}
```
![image-20200323105830138](../../../images/SpringBoot/image-20200323105830138.png)
![image-20200323105830138](../../images/SpringBoot/image-20200323105830138.png)
##### findProperty
@ -428,11 +428,11 @@ BindResult<?> bind(ConfigurationPropertiesBean propertiesBean) {
```
![image-20200323115408877](../../../images/SpringBoot/image-20200323115408877.png)
![image-20200323115408877](../../images/SpringBoot/image-20200323115408877.png)
![image-20200323115701118](../../../images/SpringBoot/image-20200323115701118.png)
![image-20200323115701118](../../images/SpringBoot/image-20200323115701118.png)
![image-20200323115711826](../../../images/SpringBoot/image-20200323115711826.png)
![image-20200323115711826](../../images/SpringBoot/image-20200323115711826.png)
##### getBindHandler
@ -465,7 +465,7 @@ private <T> BindHandler getBindHandler(Bindable<T> target, ConfigurationProperti
- 最终获取得到的处理器
![image-20200323110603959](../../../images/SpringBoot/image-20200323110603959.png)
![image-20200323110603959](../../images/SpringBoot/image-20200323110603959.png)
- 最后的 bind
@ -499,7 +499,7 @@ private <T> BindHandler getBindHandler(Bindable<T> target, ConfigurationProperti
```
![image-20200323112945449](../../../images/SpringBoot/image-20200323112945449.png)
![image-20200323112945449](../../images/SpringBoot/image-20200323112945449.png)
配置信息到此绑定成功,关于如何处理集合相关的配置请各位读者自行学习

@ -19,7 +19,7 @@ public enum LogLevel {
- `org.springframework.boot.logging.java.JavaLoggingSystem`
![image-20200323144523848](../../../images/SpringBoot/image-20200323144523848.png)
![image-20200323144523848](../../images/SpringBoot/image-20200323144523848.png)
```JAVA
static {
@ -128,7 +128,7 @@ public static LoggingSystem get(ClassLoader classLoader) {
```
![image-20200323151409473](../../../images/SpringBoot/image-20200323151409473.png)
![image-20200323151409473](../../images/SpringBoot/image-20200323151409473.png)
- 默认日志: `org.springframework.boot.logging.logback.LogbackLoggingSystem`
@ -136,7 +136,7 @@ public static LoggingSystem get(ClassLoader classLoader) {
- 初始化之前
![image-20200323154205484](../../../images/SpringBoot/image-20200323154205484.png)
![image-20200323154205484](../../images/SpringBoot/image-20200323154205484.png)
- 链路
1. `org.springframework.boot.context.logging.LoggingApplicationListener#onApplicationEvent`
@ -352,9 +352,9 @@ public static LoggingSystem get(ClassLoader classLoader) {
- 添加配置文件
![image-20200323161442058](../../../images/SpringBoot/image-20200323161442058.png)
![image-20200323161442058](../../images/SpringBoot/image-20200323161442058.png)
![image-20200323161522570](../../../images/SpringBoot/image-20200323161522570.png)
![image-20200323161522570](../../images/SpringBoot/image-20200323161522570.png)
- 此时配置文件地址出现了

@ -9,17 +9,17 @@
2. 全局搜索 yml
![image-20200319083048849](../../../images/SpringBoot/image-20200319083048849.png)
![image-20200319083048849](../../images/SpringBoot/image-20200319083048849.png)
3. 换成`properties`搜索
![image-20200319083140225](../../../images/SpringBoot/image-20200319083140225.png)
![image-20200319083140225](../../images/SpringBoot/image-20200319083140225.png)
4. 我们以`yml`为例打上断点开始源码追踪
看到调用堆栈
![image-20200319083345067](../../../images/SpringBoot/image-20200319083345067.png)
![image-20200319083345067](../../images/SpringBoot/image-20200319083345067.png)
- 一步一步回上去看如何调用具体方法的
@ -29,9 +29,9 @@
### 调用过程
![image-20200319082131146](../../../images/SpringBoot/image-20200319082131146.png)
![image-20200319082131146](../../images/SpringBoot/image-20200319082131146.png)
![image-20200319082544653](../../../images/SpringBoot/image-20200319082544653.png)
![image-20200319082544653](../../images/SpringBoot/image-20200319082544653.png)
`org.springframework.boot.context.config.ConfigFileApplicationListener#addPropertySources`
@ -68,13 +68,13 @@ protected void addPropertySources(ConfigurableEnvironment environment, ResourceL
- 搜索目标: `org.springframework.boot.env.PropertySourceLoader`
![image-20200319084141748](../../../images/SpringBoot/image-20200319084141748.png)
![image-20200319084141748](../../images/SpringBoot/image-20200319084141748.png)
![image-20200319084151997](../../../images/SpringBoot/image-20200319084151997.png)
![image-20200319084151997](../../images/SpringBoot/image-20200319084151997.png)
观察发现里面有一个`YamlPropertySourceLoader`和我们之前找 yml 字符串的时候找到的类是一样的。说明搜索方式没有什么问题。
![image-20200319084357652](../../../images/SpringBoot/image-20200319084357652.png)
![image-20200319084357652](../../images/SpringBoot/image-20200319084357652.png)
初始化完成,后续进行解析了
@ -110,7 +110,7 @@ protected void addPropertySources(ConfigurableEnvironment environment, ResourceL
### initializeProfiles
- 初始化`private Deque<Profile> profiles;` 属性
- ![image-20200319084902957](../../../images/SpringBoot/image-20200319084902957.png)
- ![image-20200319084902957](../../images/SpringBoot/image-20200319084902957.png)
### load
@ -135,7 +135,7 @@ private void load(Profile profile, DocumentFilterFactory filterFactory, Document
- 资源路径可能性
![image-20200319085446640](../../../images/SpringBoot/image-20200319085446640.png)
![image-20200319085446640](../../images/SpringBoot/image-20200319085446640.png)
该方法采用循环每个路径下面都去尝试一遍
@ -190,7 +190,7 @@ private void load(Profile profile, DocumentFilterFactory filterFactory, Document
```
![image-20200319090446231](../../../images/SpringBoot/image-20200319090446231.png)
![image-20200319090446231](../../images/SpringBoot/image-20200319090446231.png)
- `PropertiesPropertySourceLoader`解析同理不在次展开描述了

@ -53,7 +53,7 @@ public @interface EnableAutoConfiguration {
- 类图
![image-20200320150642022](../../../images/SpringBoot/image-20200320150642022.png)
![image-20200320150642022](../../images/SpringBoot/image-20200320150642022.png)
## getAutoConfigurationMetadata()
@ -107,7 +107,7 @@ public @interface EnableAutoConfiguration {
```
![image-20200320160423991](../../../images/SpringBoot/image-20200320160423991.png)
![image-20200320160423991](../../images/SpringBoot/image-20200320160423991.png)
- `protected static final String PATH = "META-INF/spring-autoconfigure-metadata.properties";`
@ -131,11 +131,11 @@ public @interface EnableAutoConfiguration {
org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration,\
```
![image-20200320162835665](../../../images/SpringBoot/image-20200320162835665.png)
![image-20200320162835665](../../images/SpringBoot/image-20200320162835665.png)
同样找一下 redis
![image-20200320163001728](../../../images/SpringBoot/image-20200320163001728.png)
![image-20200320163001728](../../images/SpringBoot/image-20200320163001728.png)
- 仔细看`org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration`类
@ -213,13 +213,13 @@ public class RedisProperties {
- `org.springframework.boot.autoconfigure.AutoConfigurationImportSelector.AutoConfigurationGroup#process`
![image-20200320163806852](../../../images/SpringBoot/image-20200320163806852.png)
![image-20200320163806852](../../images/SpringBoot/image-20200320163806852.png)
再此之前我们看过了`getAutoConfigurationMetadata()`的相关操作
关注 `AnnotationMetadata annotationMetadata` 存储了一些什么
![image-20200320164145286](../../../images/SpringBoot/image-20200320164145286.png)
![image-20200320164145286](../../images/SpringBoot/image-20200320164145286.png)
这里简单理解
@ -271,7 +271,7 @@ public class RedisProperties {
```
![image-20200320171138431](../../../images/SpringBoot/image-20200320171138431.png)
![image-20200320171138431](../../images/SpringBoot/image-20200320171138431.png)
### getCandidateConfigurations
@ -289,7 +289,7 @@ public class RedisProperties {
```
![image-20200320171734270](../../../images/SpringBoot/image-20200320171734270.png)
![image-20200320171734270](../../images/SpringBoot/image-20200320171734270.png)
- 第一个是我自己写的一个测试用
@ -341,7 +341,7 @@ public class RedisProperties {
```
![image-20200323080611527](../../../images/SpringBoot/image-20200323080611527.png)
![image-20200323080611527](../../images/SpringBoot/image-20200323080611527.png)
- 修改启动类
@ -350,7 +350,7 @@ public class RedisProperties {
```
![image-20200323081009823](../../../images/SpringBoot/image-20200323081009823.png)
![image-20200323081009823](../../images/SpringBoot/image-20200323081009823.png)
### checkExcludedClasses
@ -418,7 +418,7 @@ public class RedisProperties {
- `getAutoConfigurationImportFilters()` 从`spring.factories` 获取 `AutoConfigurationImportFilter`的接口
![image-20200323081903145](../../../images/SpringBoot/image-20200323081903145.png)
![image-20200323081903145](../../images/SpringBoot/image-20200323081903145.png)
- 循环内执行`Aware`系列接口
@ -426,7 +426,7 @@ public class RedisProperties {
- `filter.match(candidates, autoConfigurationMetadata)` 比较判断哪些是需要自动注入的类
![image-20200323082553595](../../../images/SpringBoot/image-20200323082553595.png)
![image-20200323082553595](../../images/SpringBoot/image-20200323082553595.png)
### fireAutoConfigurationImportEvents
@ -448,11 +448,11 @@ public class RedisProperties {
```
![image-20200323083149737](../../../images/SpringBoot/image-20200323083149737.png)
![image-20200323083149737](../../images/SpringBoot/image-20200323083149737.png)
- `AutoConfigurationImportEvent event = new AutoConfigurationImportEvent(this, configurations, exclusions);`
![image-20200323083247061](../../../images/SpringBoot/image-20200323083247061.png)
![image-20200323083247061](../../images/SpringBoot/image-20200323083247061.png)
- `org.springframework.boot.autoconfigure.AutoConfigurationImportListener#onAutoConfigurationImportEvent` 在执行自动配置时触发 , 实现类只有 **`ConditionEvaluationReportAutoConfigurationImportListener`**
@ -470,7 +470,7 @@ public class RedisProperties {
```
![image-20200323083656670](../../../images/SpringBoot/image-20200323083656670.png)
![image-20200323083656670](../../images/SpringBoot/image-20200323083656670.png)
- 初始化完
@ -478,7 +478,7 @@ public class RedisProperties {
- `org.springframework.boot.autoconfigure.AutoConfigurationImportSelector.AutoConfigurationGroup#process`
![image-20200323084922159](../../../images/SpringBoot/image-20200323084922159.png)
![image-20200323084922159](../../images/SpringBoot/image-20200323084922159.png)
- 后续的一些行为相对简单,直接放个源码了.

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 85 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 71 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 65 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 92 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 371 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 123 KiB

Loading…
Cancel
Save