mirror of
https://github.com/youthlql/JavaYouth.git
synced 2026-03-13 21:33:42 +08:00
Dubbo源码系列更新两篇文章
This commit is contained in:
21
README.md
21
README.md
@@ -12,7 +12,7 @@
|
||||
>
|
||||
> 6、所有更新日志,写作计划,公告等均在此发布 ==> [时间轴](https://imlql.cn/timeline/)。
|
||||
>
|
||||
> 7、由于现在上班挺忙的,更新频率会下降。但是基本可以保证一个月一篇文章(我文章基本都是万字长文这种)。具体的看下[时间轴](https://imlql.cn/timeline/)
|
||||
> 7、由于现在上班挺忙的,更新频率会下降。具体安排看下[时间轴](https://imlql.cn/timeline/)
|
||||
|
||||
|
||||
|
||||
@@ -23,14 +23,15 @@
|
||||
# 目录
|
||||
|
||||
- [Java](#java)
|
||||
|
||||
- [基础](#基础)
|
||||
- [容器](#容器)
|
||||
- [并发](#并发)
|
||||
- [JVM](#JVM)
|
||||
- [各版本新特性](#各版本新特性)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
- [计算机网络](#计算机网络)
|
||||
|
||||
|
||||
@@ -181,6 +182,20 @@ AQS剩余部分,以及阻塞队列源码暂时先搁置一下。
|
||||
|
||||
[Netty入门-第三话](docs/netty/introduction/Netty入门-第三话.md):对前面两话一些迷惑的点进行细说,讲解handler调用机制,TCP粘包,以及用netty写一个十分简单的RPC
|
||||
|
||||
|
||||
|
||||
# RPC
|
||||
|
||||
## Dubbo源码
|
||||
|
||||
[Dubbo基本应用与高级应用介绍](docs/rpc/dubbo/Dubbo源码系列V1-01和02.Dubbo第一二节-基本应用与高级应用.md)
|
||||
|
||||
[Dubbo可扩展机制SPI源码解析](docs/rpc/dubbo/Dubbo源码系列V1-03.Dubbo第三节-可扩展机制SPI源码解析.md)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# Apollo
|
||||
|
||||
[Apollo简单入门](docs/Apollo/Apollo简单入门.md)
|
||||
|
||||
@@ -8,7 +8,7 @@ categories:
|
||||
- 05.行为型
|
||||
keywords: 观察者模式,模板模式
|
||||
description: 观察者模式,模板模式。很常用的两个模式
|
||||
cover: 'https://cdn.jsdelivr.net/gh/youthlql/lqlp@master/design_patterns/logo.jpg'
|
||||
cover: 'https://unpkg.zhimg.com/youthlql@1.0.0/design_patterns/logo.jpg'
|
||||
abbrlink: dd09051e
|
||||
date: 2021-07-14 16:51:58
|
||||
---
|
||||
@@ -34,7 +34,7 @@ date: 2021-07-14 16:51:58
|
||||
|
||||
### 方案一
|
||||
|
||||
<img src="https://cdn.jsdelivr.net/gh/youthlql/lqlp@master/design_patterns/behavior_type/05.01/0001.png"/>
|
||||
<img src="https://unpkg.zhimg.com/youthlql@1.0.0/design_patterns/behavior_type/05.01/0001.png"/>
|
||||
|
||||
|
||||
|
||||
@@ -721,11 +721,11 @@ public DObserver{
|
||||
1. Guava EventBus 的功能我们已经讲清楚了,总体上来说,还是比较简单的。接下来,我们就重复造轮子,“山寨”一个 EventBus 出来。
|
||||
2. 我们重点来看,EventBus 中两个核心函数 register() 和 post() 的实现原理。弄懂了它们,基本上就弄懂了整个 EventBus 框架。下面两张图是这两个函数的实现原理图。
|
||||
|
||||
<img src="https://cdn.jsdelivr.net/gh/youthlql/lqlp@master/design_patterns/behavior_type/05.01/0002.png"/>
|
||||
<img src="https://unpkg.zhimg.com/youthlql@1.0.0/design_patterns/behavior_type/05.01/0002.png"/>
|
||||
|
||||
|
||||
|
||||
<img src="https://cdn.jsdelivr.net/gh/youthlql/lqlp@master/design_patterns/behavior_type/05.01/0003.png"/>
|
||||
<img src="https://unpkg.zhimg.com/youthlql@1.0.0/design_patterns/behavior_type/05.01/0003.png"/>
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ categories:
|
||||
- 05.行为型
|
||||
keywords: 策略模式,职责链模式
|
||||
description: 不多说,看文章
|
||||
cover: 'https://cdn.jsdelivr.net/gh/youthlql/lqlp@master/design_patterns/logo.jpg'
|
||||
cover: 'https://unpkg.zhimg.com/youthlql@1.0.0/design_patterns/logo.jpg'
|
||||
abbrlink: 2c3cc5fd
|
||||
date: 2021-08-01 15:51:58
|
||||
---
|
||||
@@ -1171,7 +1171,7 @@ public class SensitiveWordFilter {
|
||||
|
||||
Servlet Filter 是 Java Servlet 规范中定义的组件,翻译成中文就是过滤器,它可以实现对 HTTP 请求的过滤功能,比如鉴权、限流、记录日志、验证参数等等。因为它是 Servlet 规范的一部分,所以,只要是支持 Servlet 的 Web 容器(比如,Tomcat、Jetty 等),都支持过滤器功能。为了帮助你理解,我画了一张示意图阐述它的工作原理,如下所示。
|
||||
|
||||
<img src="https://cdn.jsdelivr.net/gh/youthlql/lqlp@master/design_patterns/behavior_type/05.02/0001.png"/>
|
||||
<img src="https://unpkg.zhimg.com/youthlql@1.0.0/design_patterns/behavior_type/05.02/0001.png"/>
|
||||
|
||||
在实际项目中,我们该如何使用 Servlet Filter 呢?我写了一个简单的示例代码,如下所示。添加一个过滤器,我们只需要定义一个实现 javax.servlet.Filter 接口的过滤器类,并且将它配置在 web.xml 配置文件中。Web 容器启动的时候,会读取 web.xml 中的配置,创建过滤器对象。当有请求到来的时候,会先经过过滤器,然后才由 Servlet 来处理。
|
||||
|
||||
@@ -1279,7 +1279,7 @@ ApplicationFilterChain 中的 doFilter() 函数的代码实现比较有技巧,
|
||||
1. 刚刚讲了 Servlet Filter,现在我们来讲一个功能上跟它非常类似的东西,Spring Interceptor,翻译成中文就是拦截器。尽管英文单词和中文翻译都不同,但这两者基本上可以看作一个概念,都用来实现对 HTTP 请求进行拦截处理。
|
||||
2. 它们不同之处在于,Servlet Filter 是 Servlet 规范的一部分,实现依赖于 Web 容器。Spring Interceptor 是 Spring MVC 框架的一部分,由 Spring MVC 框架来提供实现。客户端发送的请求,会先经过 Servlet Filter,然后再经过 Spring Interceptor,最后到达具体的业务代码中。我画了一张图来阐述一个请求的处理流程,具体如下所示。
|
||||
|
||||
<img src="https://cdn.jsdelivr.net/gh/youthlql/lqlp@master/design_patterns/behavior_type/05.02/0002.png"/>
|
||||
<img src="https://unpkg.zhimg.com/youthlql@1.0.0/design_patterns/behavior_type/05.02/0002.png"/>
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ categories:
|
||||
- 05.行为型
|
||||
keywords: 状态模式,迭代器模式
|
||||
description: 看文章
|
||||
cover: 'https://cdn.jsdelivr.net/gh/youthlql/lqlp@master/design_patterns/logo.jpg'
|
||||
cover: 'https://unpkg.zhimg.com/youthlql@1.0.0/design_patterns/logo.jpg'
|
||||
abbrlink: 877f4ef2
|
||||
date: 2021-08-02 15:51:58
|
||||
---
|
||||
@@ -30,7 +30,7 @@ date: 2021-08-02 15:51:58
|
||||
4. 实际上,马里奥形态的转变就是一个状态机。其中,马里奥的不同形态就是状态机中的“状态”,游戏情节(比如吃了蘑菇)就是状态机中的“事件”,加减积分就是状态机中的“动作”。比如,吃蘑菇这个事件,会触发状态的转移:从小马里奥转移到超级马里奥,以及触发动作的执行(增加 100 积分)。
|
||||
5. 为了方便接下来的讲解,我对游戏背景做了简化,只保留了部分状态和事件。简化之后的状态转移如下图所示:
|
||||
|
||||
<img src="https://cdn.jsdelivr.net/gh/youthlql/lqlp@master/design_patterns/behavior_type/05.03/0001.png"/>
|
||||
<img src="https://unpkg.zhimg.com/youthlql@1.0.0/design_patterns/behavior_type/05.03/0001.png"/>
|
||||
|
||||
|
||||
|
||||
@@ -180,7 +180,7 @@ public class MarioStateMachine {
|
||||
1. 实际上,上面这种实现方法有点类似 hard code,对于复杂的状态机来说不适用,而状态机的第二种实现方式查表法,就更加合适了。接下来,我们就一块儿来看下,如何利用查表法来补全骨架代码。
|
||||
2. 实际上,除了用状态转移图来表示之外,状态机还可以用二维表来表示,如下所示。在这个二维表中,第一维表示当前状态,第二维表示事件,值表示当前状态经过事件之后,转移到的新状态及其执行的动作。
|
||||
|
||||
<img src="https://cdn.jsdelivr.net/gh/youthlql/lqlp@master/design_patterns/behavior_type/05.03/0002.png"/>
|
||||
<img src="https://unpkg.zhimg.com/youthlql@1.0.0/design_patterns/behavior_type/05.03/0002.png"/>
|
||||
|
||||
3. 相对于分支逻辑的实现方式,查表法的代码实现更加清晰,可读性和可维护性更好。当修改状态机时,我们只需要修改 transitionTable 和 actionTable 两个二维数组即可。实际上,如果我们把这两个二维数组存储在配置文件中,当需要修改状态机时,我们甚至可以不修改任何代码,只需要修改配置文件就可以了。具体的代码如下所示:
|
||||
|
||||
@@ -511,7 +511,7 @@ public class MarioStateMachine {
|
||||
2. 在开篇中我们讲到,它用来遍历集合对象。这里说的“集合对象”也可以叫“容器”“聚合对象”,实际上就是包含一组对象的对象,比如数组、链表、树、图、跳表。迭代器模式将集合对象的遍历操作从集合类中拆分出来,放到迭代器类中,让两者的职责更加单一。
|
||||
3. 迭代器是用来遍历容器的,所以,一个完整的迭代器模式一般会涉及**容器和容器迭代器**部分两部分内容。为了达到基于接口而非实现编程的目的,容器又包含容器接口、容器实现类,迭代器又包含迭代器接口、迭代器实现类。对于迭代器模式,我画了一张简单的类图,你可以看一看,先有个大致的印象。
|
||||
|
||||
<img src="https://cdn.jsdelivr.net/gh/youthlql/lqlp@master/design_patterns/behavior_type/05.03/0003.png">
|
||||
<img src="https://unpkg.zhimg.com/youthlql@1.0.0/design_patterns/behavior_type/05.03/0003.png">
|
||||
|
||||
|
||||
|
||||
@@ -629,7 +629,7 @@ public class Demo {
|
||||
2. 结合刚刚的例子,我们来总结一下迭代器的设计思路。总结下来就三句话:迭代器中需要定义 hasNext()、currentItem()、next() 三个最基本的方法。待遍历的容器对象通过依赖注入传递到迭代器类中。容器通过 iterator() 方法来创建迭代器。
|
||||
3. 这里我画了一张类图,如下所示。实际上就是对上面那张类图的细化,你可以结合着一块看。
|
||||
|
||||
<img src="https://cdn.jsdelivr.net/gh/youthlql/lqlp@master/design_patterns/behavior_type/05.03/0004.png"/>
|
||||
<img src="https://unpkg.zhimg.com/youthlql@1.0.0/design_patterns/behavior_type/05.03/0004.png"/>
|
||||
|
||||
|
||||
|
||||
@@ -752,7 +752,7 @@ public class Demo {
|
||||
4. 为了保持数组存储数据的连续性,数组的删除操作会涉及元素的搬移。当执行到第 57 行代码的时候,我们从数组中将元素 a 删除掉,b、c、d 三个元素会依次往前搬移一位,这就会导致游标本来指向元素 b,现在变成了指向元素 c。原本在执行完第 56 行代码之后,我们还可以遍历到 b、c、d 三个元素,但在执行完第 57 行代码之后,我们只能遍历到 c、d 两个元素,b 遍历不到了。
|
||||
5. 对于上面的描述,我画了一张图,你可以对照着理解。
|
||||
|
||||
<img src="https://cdn.jsdelivr.net/gh/youthlql/lqlp@master/design_patterns/behavior_type/05.03/0005.png"/>
|
||||
<img src="https://unpkg.zhimg.com/youthlql@1.0.0/design_patterns/behavior_type/05.03/0005.png"/>
|
||||
|
||||
6. 不过,如果第 57 行代码删除的不是游标前面的元素(元素 a)以及游标所在位置的元素(元素 b),而是游标后面的元素(元素 c 和 d),这样就不会存在任何问题了,不会存在某个元素遍历不到的情况了。
|
||||
7. 所以,我们前面说,在遍历的过程中删除集合元素,结果是不可预期的,有时候没问题(删除元素 c 或 d),有时候就有问题(删除元素 a 或 b),这个要视情况而定(到底删除的是哪个位置的元素),就是这个意思。
|
||||
@@ -778,7 +778,7 @@ public class Demo {
|
||||
10. 跟删除情况类似,如果我们在游标的后面添加元素,就不会存在任何问题。所以,在遍历的同时添加集合元素也是一种不可预期行为。
|
||||
11. 同样,对于上面的添加元素的情况,我们也画了一张图,如下所示,你可以对照着理解。
|
||||
|
||||
<img src="https://cdn.jsdelivr.net/gh/youthlql/lqlp@master/design_patterns/behavior_type/05.03/0006.png"/>
|
||||
<img src="https://unpkg.zhimg.com/youthlql@1.0.0/design_patterns/behavior_type/05.03/0006.png"/>
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ categories:
|
||||
- 03.创建型
|
||||
keywords: 设计模式,单例
|
||||
description: 详解了单例设计模式。
|
||||
cover: 'https://cdn.jsdelivr.net/gh/youthlql/lqlp@master/design_patterns/logo.jpg'
|
||||
cover: 'https://unpkg.zhimg.com/youthlql@1.0.0/design_patterns/logo.jpg'
|
||||
abbrlink: b5a1ed4a
|
||||
date: 2021-06-26 21:51:58
|
||||
---
|
||||
|
||||
@@ -10,7 +10,7 @@ categories:
|
||||
- 03.创建型
|
||||
keywords: 设计模式,工厂,建造者,原型
|
||||
description: 详解常用的工厂模式和建造者模式,以及不常用的原型模式
|
||||
cover: 'https://cdn.jsdelivr.net/gh/youthlql/lqlp@master/design_patterns/logo.jpg'
|
||||
cover: 'https://unpkg.zhimg.com/youthlql@1.0.0/design_patterns/logo.jpg'
|
||||
abbrlink: ba432704
|
||||
date: 2021-06-27 00:51:58
|
||||
---
|
||||
@@ -727,7 +727,7 @@ public class BeansFactory {
|
||||
1. 在平时的开发中,创建一个对象最常用的方式是,使用 new 关键字调用类的构造函数来完成。我的问题是,什么情况下这种方式就不适用了,就需要采用建造者模式来创建对象呢?你可以先思考一下,下面我通过一个例子来带你看一下。
|
||||
2. 假设有这样一道设计面试题:我们需要定义一个资源池配置类 ResourcePoolConfig。这里的资源池,你可以简单理解为线程池、连接池、对象池等。在这个资源池配置类中,有以下几个成员变量,也就是可配置项。现在,请你编写代码实现这个 ResourcePoolConfig 类。
|
||||
|
||||
<img src="https://cdn.jsdelivr.net/gh/youthlql/lqlp@master/design_patterns/creational/03.02/0001.png" />
|
||||
<img src="https://unpkg.zhimg.com/youthlql@1.0.0/design_patterns/creational/03.02/0001.png" />
|
||||
|
||||
|
||||
|
||||
@@ -1003,7 +1003,7 @@ r.setHeight(3); // r is valid
|
||||
2. 如果你熟悉的是 Java 语言,可以直接使用语言中提供的 HashMap 容器来实现。其中,HashMap 的 key 为搜索关键词,value 为关键词详细信息(比如搜索次数)。我们只需要将数据从数据库中读取出来,放入 HashMap 就可以了。
|
||||
3. 不过,我们还有另外一个系统 B,专门用来分析搜索日志,定期(比如间隔 10 分钟)批量地更新数据库中的数据,并且标记为新的数据版本。比如,在下面的示例图中,我们对 v2 版本的数据进行更新,得到 v3 版本的数据。这里我们假设只有更新和新添关键词,没有删除关键词的行为。
|
||||
|
||||
<img src="https://cdn.jsdelivr.net/gh/youthlql/lqlp@master/design_patterns/creational/03.02/0002.png"/>
|
||||
<img src="https://unpkg.zhimg.com/youthlql@1.0.0/design_patterns/creational/03.02/0002.png"/>
|
||||
|
||||
|
||||
|
||||
@@ -1150,15 +1150,15 @@ public class Demo {
|
||||
|
||||
我们来看,在内存中,用散列表组织的搜索关键词信息是如何存储的。我画了一张示意图,大致结构如下所示。从图中我们可以发现,散列表索引中,每个结点存储的 key 是搜索关键词,value 是 SearchWord 对象的内存地址。SearchWord 对象本身存储在散列表之外的内存空间中。
|
||||
|
||||
<img src="https://cdn.jsdelivr.net/gh/youthlql/lqlp@master/design_patterns/creational/03.02/0003.png" />
|
||||
<img src="https://unpkg.zhimg.com/youthlql@1.0.0/design_patterns/creational/03.02/0003.png" />
|
||||
|
||||
浅拷贝和深拷贝的区别在于,浅拷贝只会复制图中的索引(散列表),不会复制数据(SearchWord 对象)本身。相反,深拷贝不仅仅会复制索引,还会复制数据本身。浅拷贝得到的对象(newKeywords)跟原始对象(currentKeywords)共享数据(SearchWord 对象),而深拷贝得到的是一份完完全全独立的对象。具体的对比如下图所示:
|
||||
|
||||
<img src="https://cdn.jsdelivr.net/gh/youthlql/lqlp@master/design_patterns/creational/03.02/0004.png"/>
|
||||
<img src="https://unpkg.zhimg.com/youthlql@1.0.0/design_patterns/creational/03.02/0004.png"/>
|
||||
|
||||
|
||||
|
||||
<img src="https://cdn.jsdelivr.net/gh/youthlql/lqlp@master/design_patterns/creational/03.02/0005.png" />
|
||||
<img src="https://unpkg.zhimg.com/youthlql@1.0.0/design_patterns/creational/03.02/0005.png" />
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ categories:
|
||||
- 01.设计思想
|
||||
keywords: 设计模式,设计思想
|
||||
description: 设计模式第一部分-常用设计思想。
|
||||
cover: 'https://cdn.jsdelivr.net/gh/youthlql/lqlp@master/design_patterns/logo.jpg'
|
||||
cover: 'https://unpkg.zhimg.com/youthlql@1.0.0/design_patterns/logo.jpg'
|
||||
abbrlink: c3dcce5d
|
||||
date: 2021-06-07 17:21:58
|
||||
---
|
||||
@@ -198,7 +198,7 @@ public class Ostrich extends AbstractBird { //鸵鸟
|
||||
1. 这种设计思路虽然可以解决问题,但不够优美。因为除了鸵鸟之外,不会飞的鸟还有很多,比如企鹅。对于这些不会飞的鸟来说,我们都需要重写 fly() 方法,抛出异常。这样的设计,一方面,徒增了编码的工作量;另一方面,也违背了我们之后要讲的最小知识原则(Least Knowledge Principle,也叫最少知识原则或者迪米特法则),暴露不该暴露的接口给外部,增加了类使用过程中被误用的概率。
|
||||
2. 你可能又会说,那我们再通过 AbstractBird 类派生出两个更加细分的抽象类:会飞的鸟类 AbstractFlyableBird 和不会飞的鸟类 AbstractUnFlyableBird,让麻雀、乌鸦这些会飞的鸟都继承 AbstractFlyableBird,让鸵鸟、企鹅这些不会飞的鸟,都继承 AbstractUnFlyableBird 类,不就可以了吗?具体的继承关系如下图所示:
|
||||
|
||||
<img src="https://cdn.jsdelivr.net/gh/youthlql/lqlp@master/design_patterns/design_ideas/0001.png"/>
|
||||
<img src="https://unpkg.zhimg.com/youthlql@1.0.0/design_patterns/design_ideas/0001.png"/>
|
||||
|
||||
|
||||
|
||||
@@ -206,7 +206,7 @@ public class Ostrich extends AbstractBird { //鸵鸟
|
||||
|
||||
2. 是否会飞?是否会叫?两个行为搭配起来会产生四种情况:会飞会叫、不会飞会叫、会飞不会叫、不会飞不会叫。如果我们继续沿用刚才的设计思路,那就需要再定义四个抽象类(AbstractFlyableTweetableBird、AbstractFlyableUnTweetableBird、AbstractUnFlyableTweetableBird、AbstractUnFlyableUnTweetableBird)。
|
||||
|
||||
<img src="https://cdn.jsdelivr.net/gh/youthlql/lqlp@master/design_patterns/design_ideas/0002.png"/>
|
||||
<img src="https://unpkg.zhimg.com/youthlql@1.0.0/design_patterns/design_ideas/0002.png"/>
|
||||
|
||||
|
||||
|
||||
@@ -376,7 +376,7 @@ demofunction(client);
|
||||
|
||||
|
||||
|
||||
<img src="https://cdn.jsdelivr.net/gh/youthlql/lqlp@master/design_patterns/design_ideas/0003.png"/>
|
||||
<img src="https://unpkg.zhimg.com/youthlql@1.0.0/design_patterns/design_ideas/0003.png"/>
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ categories:
|
||||
- 02.经典设计原则
|
||||
keywords: 设计模式,经典设计原则
|
||||
description: 设计模式-经典设计原则,例如:单一职责原则,开闭原则,接口隔离原则等等。
|
||||
cover: 'https://cdn.jsdelivr.net/gh/youthlql/lqlp@master/design_patterns/logo.jpg'
|
||||
cover: 'https://unpkg.zhimg.com/youthlql@1.0.0/design_patterns/logo.jpg'
|
||||
abbrlink: fc1c7619
|
||||
date: 2021-06-13 19:21:58
|
||||
---
|
||||
|
||||
@@ -8,7 +8,7 @@ categories:
|
||||
- 02.经典设计原则
|
||||
keywords: 设计模式,经典设计原则
|
||||
description: 设计模式-经典设计原则,例如:迪米特法则,依赖反转原则,KISS等等。
|
||||
cover: 'https://cdn.jsdelivr.net/gh/youthlql/lqlp@master/design_patterns/logo.jpg'
|
||||
cover: 'https://unpkg.zhimg.com/youthlql@1.0.0/design_patterns/logo.jpg'
|
||||
abbrlink: 994a8ed3
|
||||
date: 2021-06-20 19:21:58
|
||||
---
|
||||
@@ -724,7 +724,7 @@ public class UserRepo {
|
||||
|
||||
前面也提到,“高内聚”有助于“松耦合”,同理,“低内聚”也会导致“紧耦合”。关于这一点,我画了一张对比图来解释。图中左边部分的代码结构是“高内聚、松耦合”;右边部分正好相反,是“低内聚、紧耦合”。
|
||||
|
||||
<img src="https://cdn.jsdelivr.net/gh/youthlql/lqlp@master/design_patterns/design_principles/0001.png" />
|
||||
<img src="https://unpkg.zhimg.com/youthlql@1.0.0/design_patterns/design_principles/0001.png" />
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ categories:
|
||||
- 04.结构型
|
||||
keywords: 设计模式,代理模式,桥接模式,装饰器模式,适配器模式
|
||||
description: 对代理模式,桥接模式,装饰器模式,适配器模式这4个模式进行了比较详细的讲述。其实学习设计模式主要是为了后序看源码
|
||||
cover: 'https://cdn.jsdelivr.net/gh/youthlql/lqlp@master/design_patterns/logo.jpg'
|
||||
cover: 'https://unpkg.zhimg.com/youthlql@1.0.0/design_patterns/logo.jpg'
|
||||
abbrlink: 926a065c
|
||||
date: 2021-07-04 00:51:58
|
||||
---
|
||||
@@ -608,7 +608,7 @@ IUserController userController = (IUserController) proxy.createProxy(new UserCon
|
||||
|
||||
现在对不同手机类型的不同品牌实现操作编程(比如:开机、关机、上网,打电话等),如图:
|
||||
|
||||
<img src="https://cdn.jsdelivr.net/gh/youthlql/lqlp@master/design_patterns/structural_type/04.01/0001.png"/>
|
||||
<img src="https://unpkg.zhimg.com/youthlql@1.0.0/design_patterns/structural_type/04.01/0001.png"/>
|
||||
|
||||
|
||||
|
||||
@@ -616,7 +616,7 @@ IUserController userController = (IUserController) proxy.createProxy(new UserCon
|
||||
|
||||
传统方法对应的类图
|
||||
|
||||
<img src="https://cdn.jsdelivr.net/gh/youthlql/lqlp@master/design_patterns/structural_type/04.01/0007.png"/>
|
||||
<img src="https://unpkg.zhimg.com/youthlql@1.0.0/design_patterns/structural_type/04.01/0007.png"/>
|
||||
|
||||
1. 扩展性问题(**类爆炸**),如果我们再增加手机的样式(旋转式),就需要增加各个品牌手机的类,同样如果我们增加一个手机品牌,也要在各个手机样式类下增加。
|
||||
2. 违反了单一职责原则,当我们增加手机样式时,要同时增加所有品牌的手机,这样增加了代码维护成本.
|
||||
@@ -933,7 +933,7 @@ public class DriverManager {
|
||||
|
||||
|
||||
|
||||
<img src="https://cdn.jsdelivr.net/gh/youthlql/lqlp@master/design_patterns/structural_type/04.01/0002.png">
|
||||
<img src="https://unpkg.zhimg.com/youthlql@1.0.0/design_patterns/structural_type/04.01/0002.png">
|
||||
|
||||
|
||||
|
||||
@@ -1112,7 +1112,7 @@ public class TrivialNotification extends Notification {
|
||||
|
||||
### 方案一
|
||||
|
||||
<img src="https://cdn.jsdelivr.net/gh/youthlql/lqlp@master/design_patterns/structural_type/04.01/0003.png"/>
|
||||
<img src="https://unpkg.zhimg.com/youthlql@1.0.0/design_patterns/structural_type/04.01/0003.png"/>
|
||||
|
||||
|
||||
|
||||
@@ -1127,7 +1127,7 @@ public class TrivialNotification extends Notification {
|
||||
|
||||
前面分析到方案 1 因为咖啡单品+调料组合会造成类的倍增,因此可以做改进,将调料内置到 Drink 类,这样就不会造成类数量过多。从而提高项目的维护性(如图)
|
||||
|
||||
<img src="https://cdn.jsdelivr.net/gh/youthlql/lqlp@master/design_patterns/structural_type/04.01/0004.png">
|
||||
<img src="https://unpkg.zhimg.com/youthlql@1.0.0/design_patterns/structural_type/04.01/0004.png">
|
||||
|
||||
|
||||
|
||||
@@ -1142,7 +1142,7 @@ public class TrivialNotification extends Notification {
|
||||
|
||||
### 装饰器模式代码
|
||||
|
||||
<img src="https://cdn.jsdelivr.net/gh/youthlql/lqlp@master/design_patterns/structural_type/04.01/0005.png"/>
|
||||
<img src="https://unpkg.zhimg.com/youthlql@1.0.0/design_patterns/structural_type/04.01/0005.png"/>
|
||||
|
||||
#### Drink【抽象类-主体Component】
|
||||
|
||||
@@ -1390,7 +1390,7 @@ Java IO 类库非常庞大和复杂,有几十个类,负责 IO 数据的读
|
||||
|
||||
针对不同的读取和写入场景,Java IO 又在这四个父类基础之上,扩展出了很多子类。具体如下所示:
|
||||
|
||||
<img src="https://cdn.jsdelivr.net/gh/youthlql/lqlp@master/design_patterns/structural_type/04.01/0006.png"/>
|
||||
<img src="https://unpkg.zhimg.com/youthlql@1.0.0/design_patterns/structural_type/04.01/0006.png"/>
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ categories:
|
||||
- 04.结构型
|
||||
keywords: 设计模式,门面模式,组合模式,享元模式
|
||||
description: 对代理模式,门面模式,组合模式,享元模式这3个设计模式进行了比较详细的讲述。
|
||||
cover: 'https://cdn.jsdelivr.net/gh/youthlql/lqlp@master/design_patterns/logo.jpg'
|
||||
cover: 'https://unpkg.zhimg.com/youthlql@1.0.0/design_patterns/logo.jpg'
|
||||
abbrlink: 5ea604e0
|
||||
date: 2021-07-05 16:51:58
|
||||
---
|
||||
@@ -75,7 +75,7 @@ date: 2021-07-05 16:51:58
|
||||
|
||||
### 传统方案
|
||||
|
||||
<img src="https://cdn.jsdelivr.net/gh/youthlql/lqlp@master/design_patterns/structural_type/04.02/0001.png" />
|
||||
<img src="https://unpkg.zhimg.com/youthlql@1.0.0/design_patterns/structural_type/04.02/0001.png" />
|
||||
|
||||
1. 在 ClientTest 的 main 方法中,创建各个子系统的对象,并直接去调用子系统(对象)相关方法,会造成调用过程 混乱,没有清晰的过程 不利于在 ClientTest 中,去维护对子系统的操作
|
||||
|
||||
@@ -623,7 +623,7 @@ public class Demo {
|
||||
|
||||
|
||||
|
||||
<img src="https://cdn.jsdelivr.net/gh/youthlql/lqlp@master/design_patterns/structural_type/04.02/0002.png"/>
|
||||
<img src="https://unpkg.zhimg.com/youthlql@1.0.0/design_patterns/structural_type/04.02/0002.png"/>
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ categories:
|
||||
- 入门
|
||||
keywords: Netty
|
||||
description: 第一话对BIO和NIO进行了讲解,为后续做准备。
|
||||
cover: 'https://cdn.jsdelivr.net/gh/youthlql/lqlp@master/netty/netty_logo.jpg'
|
||||
cover: 'https://unpkg.zhimg.com/youthlql@1.0.0/netty/netty_logo.jpg'
|
||||
abbrlink: 3f9283e7
|
||||
date: 2021-04-08 14:21:58
|
||||
---
|
||||
@@ -33,7 +33,7 @@ date: 2021-04-08 14:21:58
|
||||
|
||||
相对简单的一个体系图
|
||||
|
||||
<img src="https://cdn.jsdelivr.net/gh/youthlql/lqlp@master/netty/introduction/chapter_001/0001.png"/>
|
||||
<img src="https://unpkg.zhimg.com/youthlql@1.0.0/netty/introduction/chapter_001/0001.png"/>
|
||||
|
||||
## Netty 的应用场景
|
||||
|
||||
@@ -67,7 +67,7 @@ date: 2021-04-08 14:21:58
|
||||
|
||||
## Netty 的学习资料参考
|
||||
|
||||
<img src="https://cdn.jsdelivr.net/gh/youthlql/lqlp@master/netty/introduction/chapter_001/0002.png"/>
|
||||
<img src="https://unpkg.zhimg.com/youthlql@1.0.0/netty/introduction/chapter_001/0002.png"/>
|
||||
|
||||
|
||||
|
||||
@@ -97,11 +97,11 @@ date: 2021-04-08 14:21:58
|
||||
2. `Java` 共支持 `3` 种网络编程模型 `I/O` 模式:`BIO`、`NIO`、`AIO`。
|
||||
3. `Java BIO`:同步并阻塞(传统阻塞型),服务器实现模式为一个连接一个线程,即客户端有连接请求时服务器端就需要启动一个线程进行处理,如果这个连接不做任何事情会造成不必要的线程开销。【简单示意图】
|
||||
|
||||
<img src="https://cdn.jsdelivr.net/gh/youthlql/lqlp@master/netty/introduction/chapter_001/0003.png"/>
|
||||
<img src="https://unpkg.zhimg.com/youthlql@1.0.0/netty/introduction/chapter_001/0003.png"/>
|
||||
|
||||
4. `Java NIO`:同步非阻塞,服务器实现模式为一个线程处理多个请求(连接),即客户端发送的连接请求都会注册到多路复用器上,多路复用器轮询到连接有 `I/O` 请求就进行处理。【简单示意图】
|
||||
|
||||
<img src="https://cdn.jsdelivr.net/gh/youthlql/lqlp@master/netty/introduction/chapter_001/0004.png"/>
|
||||
<img src="https://unpkg.zhimg.com/youthlql@1.0.0/netty/introduction/chapter_001/0004.png"/>
|
||||
|
||||
5. `Java AIO(NIO.2)`:异步非阻塞,`AIO` 引入异步通道的概念,采用了 `Proactor` 模式,简化了程序编写,有效的请求才启动线程,它的特点是先由操作系统完成后才通知服务端程序启动线程去处理,一般适用于连接数较多且连接时间较长的应用。
|
||||
6. 我们依次展开讲解。
|
||||
@@ -120,7 +120,7 @@ date: 2021-04-08 14:21:58
|
||||
|
||||
## Java BIO 工作机制
|
||||
|
||||
<img src="https://cdn.jsdelivr.net/gh/youthlql/lqlp@master/netty/introduction/chapter_001/0005.png"/>
|
||||
<img src="https://unpkg.zhimg.com/youthlql@1.0.0/netty/introduction/chapter_001/0005.png"/>
|
||||
|
||||
对 `BIO` 编程流程的梳理
|
||||
|
||||
@@ -207,7 +207,7 @@ public class BIOServer {
|
||||
}
|
||||
```
|
||||
|
||||
<img src="https://cdn.jsdelivr.net/gh/youthlql/lqlp@master/netty/introduction/chapter_001/0006.png"/>
|
||||
<img src="https://unpkg.zhimg.com/youthlql@1.0.0/netty/introduction/chapter_001/0006.png"/>
|
||||
|
||||
|
||||
|
||||
@@ -277,7 +277,7 @@ public class BasicBuffer {
|
||||
3. `BIO` 基于字节流和字符流进行操作,而 `NIO` 基于 `Channel`(通道)和 `Buffer`(缓冲区)进行操作,数据总是从通道读取到缓冲区中,或者从缓冲区写入到通道中。`Selector`(选择器)用于监听多个通道的事件(比如:连接请求,数据到达等),因此使用单个线程就可以监听多个客户端通道。
|
||||
4. Buffer和Channel之间的数据流向是双向的
|
||||
|
||||
<img src="https://cdn.jsdelivr.net/gh/youthlql/lqlp@master/netty/introduction/chapter_001/0007.png"/>
|
||||
<img src="https://unpkg.zhimg.com/youthlql@1.0.0/netty/introduction/chapter_001/0007.png"/>
|
||||
|
||||
## NIO 三大核心原理示意图
|
||||
|
||||
@@ -287,7 +287,7 @@ public class BasicBuffer {
|
||||
|
||||
关系图的说明:
|
||||
|
||||
<img src="https://cdn.jsdelivr.net/gh/youthlql/lqlp@master/netty/introduction/chapter_001/0008.png"/>
|
||||
<img src="https://unpkg.zhimg.com/youthlql@1.0.0/netty/introduction/chapter_001/0008.png"/>
|
||||
|
||||
1. 每个 `Channel` 都会对应一个 `Buffer`。
|
||||
2. `Selector` 对应一个线程,一个线程对应多个 `Channel`(连接)。
|
||||
@@ -303,17 +303,17 @@ public class BasicBuffer {
|
||||
|
||||
缓冲区(`Buffer`):缓冲区本质上是一个**可以读写数据的内存块**,可以理解成是一个**容器对象(含数组)**,该对象提供了一组方法,可以更轻松地使用内存块,,缓冲区对象内置了一些机制,能够跟踪和记录缓冲区的状态变化情况。`Channel` 提供从文件、网络读取数据的渠道,但是读取或写入的数据都必须经由 `Buffer`,如图:【后面举例说明】
|
||||
|
||||
<img src="https://cdn.jsdelivr.net/gh/youthlql/lqlp@master/netty/introduction/chapter_001/0009.png"/>
|
||||
<img src="https://unpkg.zhimg.com/youthlql@1.0.0/netty/introduction/chapter_001/0009.png"/>
|
||||
|
||||
### Buffer 类及其子类
|
||||
|
||||
1. 在 `NIO` 中,`Buffer` 是一个顶层父类,它是一个抽象类,类的层级关系图:
|
||||
|
||||
<img src="https://cdn.jsdelivr.net/gh/youthlql/lqlp@master/netty/introduction/chapter_001/0010.png" />
|
||||
<img src="https://unpkg.zhimg.com/youthlql@1.0.0/netty/introduction/chapter_001/0010.png" />
|
||||
|
||||
2. `Buffer` 类定义了所有的缓冲区都具有的四个属性来提供关于其所包含的数据元素的信息:
|
||||
|
||||
<img src="https://cdn.jsdelivr.net/gh/youthlql/lqlp@master/netty/introduction/chapter_001/0011.png"/>
|
||||
<img src="https://unpkg.zhimg.com/youthlql@1.0.0/netty/introduction/chapter_001/0011.png"/>
|
||||
|
||||
|
||||
|
||||
@@ -321,13 +321,13 @@ public class BasicBuffer {
|
||||
|
||||
3. `Buffer` 类相关方法一览
|
||||
|
||||
<img src="https://cdn.jsdelivr.net/gh/youthlql/lqlp@master/netty/introduction/chapter_001/0013.png" />
|
||||
<img src="https://unpkg.zhimg.com/youthlql@1.0.0/netty/introduction/chapter_001/0013.png" />
|
||||
|
||||
### ByteBuffer
|
||||
|
||||
从前面可以看出对于 `Java` 中的基本数据类型(`boolean` 除外),都有一个 `Buffer` 类型与之相对应,最常用的自然是 `ByteBuffer` 类(二进制数据),该类的主要方法如下:
|
||||
|
||||
<img src="https://cdn.jsdelivr.net/gh/youthlql/lqlp@master/netty/introduction/chapter_001/0014.png"/>
|
||||
<img src="https://unpkg.zhimg.com/youthlql@1.0.0/netty/introduction/chapter_001/0014.png"/>
|
||||
|
||||
## 通道(Channel)
|
||||
|
||||
@@ -343,7 +343,7 @@ public class BasicBuffer {
|
||||
5. `FileChannel` 用于文件的数据读写,`DatagramChannel` 用于 `UDP` 的数据读写,`ServerSocketChannel` 和 `SocketChannel` 用于 `TCP` 的数据读写。
|
||||
6. 图示
|
||||
|
||||
<img src="https://cdn.jsdelivr.net/gh/youthlql/lqlp@master/netty/introduction/chapter_001/0015.png"/>
|
||||
<img src="https://unpkg.zhimg.com/youthlql@1.0.0/netty/introduction/chapter_001/0015.png"/>
|
||||
|
||||
### FileChannel 类
|
||||
|
||||
@@ -444,7 +444,7 @@ public class NIOFileChannel02 {
|
||||
2. 拷贝一个文本文件 `1.txt`,放在项目下即可
|
||||
3. 代码演示
|
||||
|
||||
<img src="https://cdn.jsdelivr.net/gh/youthlql/lqlp@master/netty/introduction/chapter_001/0016.png"/>
|
||||
<img src="https://unpkg.zhimg.com/youthlql@1.0.0/netty/introduction/chapter_001/0016.png"/>
|
||||
|
||||
```java
|
||||
package com.atguigu.nio;
|
||||
@@ -721,7 +721,7 @@ public class ScatteringAndGatheringTest {
|
||||
|
||||
### Selector 示意图和特点说明
|
||||
|
||||
<img src="https://cdn.jsdelivr.net/gh/youthlql/lqlp@master/netty/introduction/chapter_001/0017.png"/>
|
||||
<img src="https://unpkg.zhimg.com/youthlql@1.0.0/netty/introduction/chapter_001/0017.png"/>
|
||||
|
||||
说明如下:
|
||||
|
||||
@@ -733,7 +733,7 @@ public class ScatteringAndGatheringTest {
|
||||
|
||||
### Selector 类相关方法
|
||||
|
||||
<img src="https://cdn.jsdelivr.net/gh/youthlql/lqlp@master/netty/introduction/chapter_001/0018.png"/>
|
||||
<img src="https://unpkg.zhimg.com/youthlql@1.0.0/netty/introduction/chapter_001/0018.png"/>
|
||||
|
||||
### 注意事项
|
||||
|
||||
@@ -748,7 +748,7 @@ public class ScatteringAndGatheringTest {
|
||||
|
||||
`NIO` 非阻塞网络编程相关的(`Selector`、`SelectionKey`、`ServerScoketChannel` 和 `SocketChannel`)关系梳理图
|
||||
|
||||
<img src="https://cdn.jsdelivr.net/gh/youthlql/lqlp@master/netty/introduction/chapter_001/0019.png"/>
|
||||
<img src="https://unpkg.zhimg.com/youthlql@1.0.0/netty/introduction/chapter_001/0019.png"/>
|
||||
|
||||
对上图的说明:
|
||||
|
||||
@@ -938,21 +938,21 @@ public static final int OP_ACCEPT = 1 << 4;
|
||||
|
||||
2. `SelectionKey` 相关方法
|
||||
|
||||
<img src="https://cdn.jsdelivr.net/gh/youthlql/lqlp@master/netty/introduction/chapter_001/0020.png"/>
|
||||
<img src="https://unpkg.zhimg.com/youthlql@1.0.0/netty/introduction/chapter_001/0020.png"/>
|
||||
|
||||
### ServerSocketChannel
|
||||
|
||||
1. `ServerSocketChannel` 在服务器端监听新的客户端 `Socket` 连接,负责监听,不负责实际的读写操作
|
||||
2. 相关方法如下
|
||||
|
||||
<img src="https://cdn.jsdelivr.net/gh/youthlql/lqlp@master/netty/introduction/chapter_001/0021.png"/>
|
||||
<img src="https://unpkg.zhimg.com/youthlql@1.0.0/netty/introduction/chapter_001/0021.png"/>
|
||||
|
||||
### SocketChannel
|
||||
|
||||
1. `SocketChannel`,网络 `IO` 通道,**具体负责进行读写操作**。`NIO` 把缓冲区的数据写入通道,或者把通道里的数据读到缓冲区。
|
||||
2. 相关方法如下
|
||||
|
||||
<img src="https://cdn.jsdelivr.net/gh/youthlql/lqlp@master/netty/introduction/chapter_001/0022.png"/>
|
||||
<img src="https://unpkg.zhimg.com/youthlql@1.0.0/netty/introduction/chapter_001/0022.png"/>
|
||||
|
||||
## NIO网络编程应用实例 - 群聊系统
|
||||
|
||||
@@ -965,7 +965,7 @@ public static final int OP_ACCEPT = 1 << 4;
|
||||
5. 目的:进一步理解 `NIO` 非阻塞网络编程机制
|
||||
6. 示意图分析和代码
|
||||
|
||||
<img src="https://cdn.jsdelivr.net/gh/youthlql/lqlp@master/netty/introduction/chapter_001/0023.png"/>
|
||||
<img src="https://unpkg.zhimg.com/youthlql@1.0.0/netty/introduction/chapter_001/0023.png"/>
|
||||
|
||||
代码:
|
||||
|
||||
@@ -1243,7 +1243,7 @@ socket.getOutputStream().write(arr);
|
||||
|
||||
### 传统 IO 模型
|
||||
|
||||
<img src="https://cdn.jsdelivr.net/gh/youthlql/lqlp@master/netty/introduction/chapter_001/0024.png"/>
|
||||
<img src="https://unpkg.zhimg.com/youthlql@1.0.0/netty/introduction/chapter_001/0024.png"/>
|
||||
|
||||
**DMA**:`direct memory access` 直接内存拷贝(不使用 `CPU`)
|
||||
|
||||
@@ -1252,19 +1252,19 @@ socket.getOutputStream().write(arr);
|
||||
1. `mmap` 通过内存映射,将文件映射到内核缓冲区,同时,用户空间可以共享内核空间的数据。这样,在进行网络传输时,就可以减少内核空间到用户空间的拷贝次数。如下图
|
||||
2. `mmap` 示意图
|
||||
|
||||
<img src="https://cdn.jsdelivr.net/gh/youthlql/lqlp@master/netty/introduction/chapter_001/0025.png"/>
|
||||
<img src="https://unpkg.zhimg.com/youthlql@1.0.0/netty/introduction/chapter_001/0025.png"/>
|
||||
|
||||
### sendFile 优化
|
||||
|
||||
1. `Linux2.1` 版本提供了 `sendFile` 函数,其基本原理如下:数据根本不经过用户态,直接从内核缓冲区进入到 `SocketBuffer`,同时,由于和用户态完全无关,就减少了一次上下文切换
|
||||
2. 示意图和小结
|
||||
|
||||
<img src="https://cdn.jsdelivr.net/gh/youthlql/lqlp@master/netty/introduction/chapter_001/0026.png"/>
|
||||
<img src="https://unpkg.zhimg.com/youthlql@1.0.0/netty/introduction/chapter_001/0026.png"/>
|
||||
|
||||
3. 提示:零拷贝从操作系统角度,是没有 `cpu` 拷贝
|
||||
4. `Linux在2.4` 版本中,做了一些修改,避免了从内核缓冲区拷贝到 `Socketbuffer` 的操作,直接拷贝到协议栈,从而再一次减少了数据拷贝。具体如下图和小结:
|
||||
|
||||
<img src="https://cdn.jsdelivr.net/gh/youthlql/lqlp@master/netty/introduction/chapter_001/0027.png"/>
|
||||
<img src="https://unpkg.zhimg.com/youthlql@1.0.0/netty/introduction/chapter_001/0027.png"/>
|
||||
|
||||
5. 这里其实有一次 `cpu` 拷贝 `kernel buffer` -> `socket buffer` 但是,拷贝的信息很少,比如 `lenght`、`offset` 消耗低,可以忽略
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ categories:
|
||||
- 入门
|
||||
keywords: Netty
|
||||
description: 对前面两话一些迷惑的点进行细说,讲解handler调用机制,TCP粘包,以及用netty写一个十分简单的RPC。
|
||||
cover: 'https://cdn.jsdelivr.net/gh/youthlql/lqlp@master/netty/netty_logo.jpg'
|
||||
cover: 'https://unpkg.zhimg.com/youthlql@1.0.0/netty/netty_logo.jpg'
|
||||
abbrlink: 429acc6d
|
||||
date: 2021-04-21 17:38:58
|
||||
---
|
||||
@@ -25,7 +25,7 @@ date: 2021-04-21 17:38:58
|
||||
1. 编写网络应用程序时,因为数据在网络中传输的都是二进制字节码数据,在发送数据时就需要编码,接收数据时就需要解码[示意图]
|
||||
2. `codec`(编解码器)的组成部分有两个:`decoder`(解码器)和 `encoder`(编码器)。`encoder` 负责把业务数据转换成字节码数据,`decoder` 负责把字节码数据转换成业务数据
|
||||
|
||||
<img src="https://cdn.jsdelivr.net/gh/youthlql/lqlp@master/netty/introduction/chapter_003/0001.png"/>
|
||||
<img src="https://unpkg.zhimg.com/youthlql@1.0.0/netty/introduction/chapter_003/0001.png"/>
|
||||
|
||||
## Netty 本身的编码解码的机制和问题分析
|
||||
|
||||
@@ -54,7 +54,7 @@ date: 2021-04-21 17:38:58
|
||||
8. 然后通过 `protoc.exe` 编译器根据 `.proto` 自动生成 `.java` 文件
|
||||
9. `protobuf` 使用示意图
|
||||
|
||||
<img src="https://cdn.jsdelivr.net/gh/youthlql/lqlp@master/netty/introduction/chapter_003/0002.png"/>
|
||||
<img src="https://unpkg.zhimg.com/youthlql@1.0.0/netty/introduction/chapter_003/0002.png"/>
|
||||
|
||||
## Protobuf 快速入门实例
|
||||
|
||||
@@ -91,7 +91,7 @@ message Student { //会在 StudentPOJO 外部类生成一个内部类 Student,
|
||||
protoc.exe --java_out=.Student.proto
|
||||
将生成的 StudentPOJO 放入到项目使用
|
||||
|
||||
<img src="https://cdn.jsdelivr.net/gh/youthlql/lqlp@master/netty/introduction/chapter_003/0003.png"/>
|
||||
<img src="https://unpkg.zhimg.com/youthlql@1.0.0/netty/introduction/chapter_003/0003.png"/>
|
||||
|
||||
生成的StudentPOJO代码太长就不贴在这里了
|
||||
|
||||
@@ -676,7 +676,7 @@ public class NettyClientHandler extends ChannelInboundHandlerAdapter {
|
||||
2. `ChannelHandler` 充当了处理入站和出站数据的应用程序逻辑的容器。例如,实现 `ChannelInboundHandler` 接口(或 `ChannelInboundHandlerAdapter`),你就可以接收入站事件和数据,这些数据会被业务逻辑处理。当要给客户端发送响应时,也可以从 `ChannelInboundHandler` 冲刷数据。业务逻辑通常写在一个或者多个 `ChannelInboundHandler` 中。`ChannelOutboundHandler` 原理一样,只不过它是用来处理出站数据的
|
||||
3. `ChannelPipeline` 提供了 `ChannelHandler` 链的容器。以客户端应用程序为例,如果事件的运动方向是从客户端到服务端的,那么我们称这些事件为出站的,即客户端发送给服务端的数据会通过 `pipeline` 中的一系列 `ChannelOutboundHandler`,并被这些 `Handler` 处理,反之则称为入站的
|
||||
|
||||
<img src="https://cdn.jsdelivr.net/gh/youthlql/lqlp@master/netty/introduction/chapter_003/0004.png"/>
|
||||
<img src="https://unpkg.zhimg.com/youthlql@1.0.0/netty/introduction/chapter_003/0004.png"/>
|
||||
|
||||
> 出站,入站如果搞不清楚,看下面的**Netty的handler链的调用机制**,通过一个例子和图讲清楚
|
||||
|
||||
@@ -689,12 +689,12 @@ public class NettyClientHandler extends ChannelInboundHandlerAdapter {
|
||||
|
||||
1. 关系继承图
|
||||
|
||||
<img src="https://cdn.jsdelivr.net/gh/youthlql/lqlp@master/netty/introduction/chapter_003/0005.png"/>
|
||||
<img src="https://unpkg.zhimg.com/youthlql@1.0.0/netty/introduction/chapter_003/0005.png"/>
|
||||
|
||||
2. 由于不可能知道远程节点是否会一次性发送一个完整的信息,`tcp` 有可能出现粘包拆包的问题,这个类会对入站数据进行缓冲,直到它准备好被处理.【后面有说TCP的粘包和拆包问题】
|
||||
3. 一个关于 `ByteToMessageDecoder` 实例分析
|
||||
|
||||
<img src="https://cdn.jsdelivr.net/gh/youthlql/lqlp@master/netty/introduction/chapter_003/0006.png"/>
|
||||
<img src="https://unpkg.zhimg.com/youthlql@1.0.0/netty/introduction/chapter_003/0006.png"/>
|
||||
|
||||
|
||||
|
||||
@@ -710,7 +710,7 @@ public class NettyClientHandler extends ChannelInboundHandlerAdapter {
|
||||
|
||||
> 读者可以看下这个图,带着这个图去看下面的例子。
|
||||
|
||||
<img src="https://cdn.jsdelivr.net/gh/youthlql/lqlp@master/netty/introduction/chapter_003/0007.png"/>
|
||||
<img src="https://unpkg.zhimg.com/youthlql@1.0.0/netty/introduction/chapter_003/0007.png"/>
|
||||
|
||||
|
||||
|
||||
@@ -978,11 +978,11 @@ public class MyLongToByteEncoder extends MessageToByteEncoder<Long> {
|
||||
|
||||
### 效果
|
||||
|
||||
<img src="https://cdn.jsdelivr.net/gh/youthlql/lqlp@master/netty/introduction/chapter_003/0008.png"/>
|
||||
<img src="https://unpkg.zhimg.com/youthlql@1.0.0/netty/introduction/chapter_003/0008.png"/>
|
||||
|
||||
|
||||
|
||||
<img src="https://cdn.jsdelivr.net/gh/youthlql/lqlp@master/netty/introduction/chapter_003/0009.png"/>
|
||||
<img src="https://unpkg.zhimg.com/youthlql@1.0.0/netty/introduction/chapter_003/0009.png"/>
|
||||
|
||||
|
||||
|
||||
@@ -1002,7 +1002,7 @@ public class MyLongToByteEncoder extends MessageToByteEncoder<Long> {
|
||||
|
||||
|
||||
|
||||
<img src="https://cdn.jsdelivr.net/gh/youthlql/lqlp@master/netty/introduction/chapter_003/0007.png"/>
|
||||
<img src="https://unpkg.zhimg.com/youthlql@1.0.0/netty/introduction/chapter_003/0007.png"/>
|
||||
|
||||
|
||||
|
||||
@@ -1090,13 +1090,13 @@ public class MyByteToLongDecoder extends ByteToMessageDecoder {
|
||||
|
||||
如下图验证结果:
|
||||
|
||||
<img src="https://cdn.jsdelivr.net/gh/youthlql/lqlp@master/netty/introduction/chapter_003/0010.png"/>
|
||||
<img src="https://unpkg.zhimg.com/youthlql@1.0.0/netty/introduction/chapter_003/0010.png"/>
|
||||
|
||||
|
||||
|
||||
2. 同时又引出了一个小问题
|
||||
|
||||
<img src="https://cdn.jsdelivr.net/gh/youthlql/lqlp@master/netty/introduction/chapter_003/0011.png"/>
|
||||
<img src="https://unpkg.zhimg.com/youthlql@1.0.0/netty/introduction/chapter_003/0011.png"/>
|
||||
|
||||
当我们`MyClientHandler`传一个Long时,会调用我们的`MyLongToByteEncoder`的编码器。那么控制台就会打印这样一句话:**MyLongToByteEncoder encode 被调用**。但是这里并没有调用编码器,这是为什么呢?
|
||||
|
||||
@@ -1149,7 +1149,7 @@ public class MyByteToLongDecoder extends ByteToMessageDecoder {
|
||||
ctx.writeAndFlush(Unpooled.copiedBuffer("abcdabcdabcdabcd",CharsetUtil.UTF_8));
|
||||
```
|
||||
|
||||
<img src="https://cdn.jsdelivr.net/gh/youthlql/lqlp@master/netty/introduction/chapter_003/0012.png" />
|
||||
<img src="https://unpkg.zhimg.com/youthlql@1.0.0/netty/introduction/chapter_003/0012.png" />
|
||||
|
||||
|
||||
|
||||
@@ -1198,7 +1198,7 @@ public class MyByteToLongDecoder2 extends ReplayingDecoder<Void> {
|
||||
|
||||
## 其它编解码器
|
||||
|
||||
<img src="https://cdn.jsdelivr.net/gh/youthlql/lqlp@master/netty/introduction/chapter_003/0013.png"/>
|
||||
<img src="https://unpkg.zhimg.com/youthlql@1.0.0/netty/introduction/chapter_003/0013.png"/>
|
||||
|
||||
|
||||
|
||||
@@ -1249,7 +1249,7 @@ log4j.appender.stdout.layout.ConversionPattern=[%p]%C{1}-%m%n
|
||||
|
||||
3. 演示整合
|
||||
|
||||
<img src="https://cdn.jsdelivr.net/gh/youthlql/lqlp@master/netty/introduction/chapter_003/0014.jpg"/>
|
||||
<img src="https://unpkg.zhimg.com/youthlql@1.0.0/netty/introduction/chapter_003/0014.jpg"/>
|
||||
|
||||
|
||||
|
||||
@@ -1261,7 +1261,7 @@ log4j.appender.stdout.layout.ConversionPattern=[%p]%C{1}-%m%n
|
||||
2. 由于 `TCP` 无消息保护边界,需要在接收端处理消息边界问题,也就是我们所说的粘包、拆包问题,看一张图
|
||||
3. `TCP` 粘包、拆包图解
|
||||
|
||||
<img src="https://cdn.jsdelivr.net/gh/youthlql/lqlp@master/netty/introduction/chapter_003/0015.png"/>
|
||||
<img src="https://unpkg.zhimg.com/youthlql@1.0.0/netty/introduction/chapter_003/0015.png"/>
|
||||
|
||||
|
||||
假设客户端分别发送了两个数据包 `D1` 和 `D2` 给服务端,由于服务端一次读取到字节数是不确定的,故可能存在以下四种情况:
|
||||
@@ -1502,11 +1502,11 @@ public class MyClientHandler extends SimpleChannelInboundHandler<ByteBuf> {
|
||||
|
||||
**Client**
|
||||
|
||||
<img src="https://cdn.jsdelivr.net/gh/youthlql/lqlp@master/netty/introduction/chapter_003/0016.png"/>
|
||||
<img src="https://unpkg.zhimg.com/youthlql@1.0.0/netty/introduction/chapter_003/0016.png"/>
|
||||
|
||||
**Server**
|
||||
|
||||
<img src="https://cdn.jsdelivr.net/gh/youthlql/lqlp@master/netty/introduction/chapter_003/0017.png"/>
|
||||
<img src="https://unpkg.zhimg.com/youthlql@1.0.0/netty/introduction/chapter_003/0017.png"/>
|
||||
|
||||
|
||||
|
||||
@@ -1514,13 +1514,13 @@ public class MyClientHandler extends SimpleChannelInboundHandler<ByteBuf> {
|
||||
|
||||
**Client**
|
||||
|
||||
<img src="https://cdn.jsdelivr.net/gh/youthlql/lqlp@master/netty/introduction/chapter_003/0018.png"/>
|
||||
<img src="https://unpkg.zhimg.com/youthlql@1.0.0/netty/introduction/chapter_003/0018.png"/>
|
||||
|
||||
|
||||
|
||||
**Server**
|
||||
|
||||
<img src="https://cdn.jsdelivr.net/gh/youthlql/lqlp@master/netty/introduction/chapter_003/0019.png"/>
|
||||
<img src="https://unpkg.zhimg.com/youthlql@1.0.0/netty/introduction/chapter_003/0019.png"/>
|
||||
|
||||
|
||||
|
||||
@@ -1538,7 +1538,7 @@ public class MyClientHandler extends SimpleChannelInboundHandler<ByteBuf> {
|
||||
1. 要求客户端发送 `5` 个 `Message` 对象,客户端每次发送一个 `Message` 对象
|
||||
2. 服务器端每次接收一个 `Message`,分 `5` 次进行解码,每读取到一个 `Message`,会回复一个 `Message` 对象给客户端。
|
||||
|
||||
<img src="https://cdn.jsdelivr.net/gh/youthlql/lqlp@master/netty/introduction/chapter_003/0020.png"/>
|
||||
<img src="https://unpkg.zhimg.com/youthlql@1.0.0/netty/introduction/chapter_003/0020.png"/>
|
||||
|
||||
|
||||
|
||||
@@ -1996,7 +1996,7 @@ MyMessageEncoder encode 方法被调用
|
||||
1. `RPC(Remote Procedure Call)`—远程过程调用,是一个计算机通信协议。该协议允许运行于一台计算机的程序调用另一台计算机的子程序,而程序员无需额外地为这个交互作用编程
|
||||
2. 两个或多个应用程序都分布在不同的服务器上,它们之间的调用都像是本地方法调用一样(如图)
|
||||
|
||||
<img src="https://cdn.jsdelivr.net/gh/youthlql/lqlp@master/netty/introduction/chapter_003/0021.png"/>
|
||||
<img src="https://unpkg.zhimg.com/youthlql@1.0.0/netty/introduction/chapter_003/0021.png"/>
|
||||
|
||||
过程:
|
||||
|
||||
@@ -2018,11 +2018,11 @@ MyMessageEncoder encode 方法被调用
|
||||
|
||||
3. 常见的 `RPC` 框架有:比较知名的如阿里的 `Dubbo`、`Google` 的 `gRPC`、`Go` 语言的 `rpcx`、`Apache` 的 `thrift`,`Spring` 旗下的 `SpringCloud`。
|
||||
|
||||
<img src="https://cdn.jsdelivr.net/gh/youthlql/lqlp@master/netty/introduction/chapter_003/0022.png"/>
|
||||
<img src="https://unpkg.zhimg.com/youthlql@1.0.0/netty/introduction/chapter_003/0022.png"/>
|
||||
|
||||
## 我们的RPC 调用流程图
|
||||
|
||||
<img src="https://cdn.jsdelivr.net/gh/youthlql/lqlp@master/netty/introduction/chapter_003/0023.png"/>
|
||||
<img src="https://unpkg.zhimg.com/youthlql@1.0.0/netty/introduction/chapter_003/0023.png"/>
|
||||
|
||||
**RPC 调用流程说明**
|
||||
|
||||
@@ -2052,7 +2052,7 @@ MyMessageEncoder encode 方法被调用
|
||||
3. 创建一个消费者,该类需要透明的调用自己不存在的方法,内部需要使用 `Netty` 请求提供者返回数据
|
||||
4. 开发的分析图
|
||||
|
||||
<img src="https://cdn.jsdelivr.net/gh/youthlql/lqlp@master/netty/introduction/chapter_003/0024.png" />
|
||||
<img src="https://unpkg.zhimg.com/youthlql@1.0.0/netty/introduction/chapter_003/0024.png" />
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ categories:
|
||||
- 入门
|
||||
keywords: Netty
|
||||
description: 对Netty的架构进行了解析,主要是Reactor设计模式的多种解决方案。同时讲解了Netty的核心模块组件。
|
||||
cover: 'https://cdn.jsdelivr.net/gh/youthlql/lqlp@master/netty/netty_logo.jpg'
|
||||
cover: 'https://unpkg.zhimg.com/youthlql@1.0.0/netty/netty_logo.jpg'
|
||||
abbrlink: f846f3f
|
||||
date: 2021-04-15 14:21:58
|
||||
---
|
||||
@@ -29,7 +29,7 @@ date: 2021-04-15 14:21:58
|
||||
|
||||
Netty is an asynchronous event-driven network application framework for rapid development of maintainable high performance protocol servers & clients.
|
||||
|
||||
<img src="https://cdn.jsdelivr.net/gh/youthlql/lqlp@master/netty/introduction/chapter_002/0001.png"/>
|
||||
<img src="https://unpkg.zhimg.com/youthlql@1.0.0/netty/introduction/chapter_002/0001.png"/>
|
||||
|
||||
## Netty 的优点
|
||||
|
||||
@@ -80,7 +80,7 @@ Netty is an asynchronous event-driven network application framework for rapid de
|
||||
1. 当并发数很大,就会创建大量的线程,占用很大系统资源
|
||||
2. 连接创建后,如果当前线程暂时没有数据可读,该线程会阻塞在 Handler对象中的`read` 操作,导致上面的处理线程资源浪费
|
||||
|
||||
<img src="https://cdn.jsdelivr.net/gh/youthlql/lqlp@master/netty/introduction/chapter_002/0002.png" />
|
||||
<img src="https://unpkg.zhimg.com/youthlql@1.0.0/netty/introduction/chapter_002/0002.png" />
|
||||
|
||||
## Reactor 模式
|
||||
|
||||
@@ -97,7 +97,7 @@ Netty is an asynchronous event-driven network application framework for rapid de
|
||||
1. 基于线程池复用线程资源:不必再为每个连接创建线程,将连接完成后的业务处理任务分配给线程进行处理,一个线程可以处理多个连接的业务。(解决了当并发数很大时,会创建大量线程,占用很大系统资源)
|
||||
2. 基于 `I/O` 复用模型:多个客户端进行连接,先把连接请求给`ServiceHandler`。多个连接共用一个阻塞对象`ServiceHandler`。假设,当C1连接没有数据要处理时,C1客户端只需要阻塞于`ServiceHandler`,C1之前的处理线程便可以处理其他有数据的连接,不会造成线程资源的浪费。当C1连接再次有数据时,`ServiceHandler`根据线程池的空闲状态,将请求分发给空闲的线程来处理C1连接的任务。(解决了线程资源浪费的那个问题)
|
||||
|
||||
<img src="https://cdn.jsdelivr.net/gh/youthlql/lqlp@master/netty/introduction/chapter_002/0003.png"/>
|
||||
<img src="https://unpkg.zhimg.com/youthlql@1.0.0/netty/introduction/chapter_002/0003.png"/>
|
||||
|
||||
|
||||
|
||||
@@ -105,7 +105,7 @@ Netty is an asynchronous event-driven network application framework for rapid de
|
||||
|
||||
### I/O 复用结合线程池,就是 Reactor 模式基本设计思想,如图
|
||||
|
||||
<img src="https://cdn.jsdelivr.net/gh/youthlql/lqlp@master/netty/introduction/chapter_002/0004.png"/>
|
||||
<img src="https://unpkg.zhimg.com/youthlql@1.0.0/netty/introduction/chapter_002/0004.png"/>
|
||||
|
||||
对上图说明:
|
||||
|
||||
@@ -132,7 +132,7 @@ Netty is an asynchronous event-driven network application framework for rapid de
|
||||
|
||||
原理图,并使用 `NIO` 群聊系统验证
|
||||
|
||||
<img src="https://cdn.jsdelivr.net/gh/youthlql/lqlp@master/netty/introduction/chapter_002/0005.png" />
|
||||
<img src="https://unpkg.zhimg.com/youthlql@1.0.0/netty/introduction/chapter_002/0005.png" />
|
||||
|
||||
### 方案说明
|
||||
|
||||
@@ -155,7 +155,7 @@ Netty is an asynchronous event-driven network application framework for rapid de
|
||||
|
||||
### 方案说明
|
||||
|
||||
<img src="https://cdn.jsdelivr.net/gh/youthlql/lqlp@master/netty/introduction/chapter_002/0006.png"/>
|
||||
<img src="https://unpkg.zhimg.com/youthlql@1.0.0/netty/introduction/chapter_002/0006.png"/>
|
||||
|
||||
|
||||
|
||||
@@ -178,11 +178,11 @@ Netty is an asynchronous event-driven network application framework for rapid de
|
||||
|
||||
针对单 `Reactor` 多线程模型中,`Reactor` 在单线程中运行,高并发场景下容易成为性能瓶颈,可以让 `Reactor` 在多线程中运行
|
||||
|
||||
<img src="https://cdn.jsdelivr.net/gh/youthlql/lqlp@master/netty/introduction/chapter_002/0007.png" />
|
||||
<img src="https://unpkg.zhimg.com/youthlql@1.0.0/netty/introduction/chapter_002/0007.png" />
|
||||
|
||||
|
||||
|
||||
<img src="https://cdn.jsdelivr.net/gh/youthlql/lqlp@master/netty/introduction/chapter_002/0008.jpg" />
|
||||
<img src="https://unpkg.zhimg.com/youthlql@1.0.0/netty/introduction/chapter_002/0008.jpg" />
|
||||
|
||||
> SubReactor是可以有多个的,如果只有一个SubReactor的话那和`单 Reactor 多线程`就没什么区别了。
|
||||
|
||||
@@ -197,7 +197,7 @@ Netty is an asynchronous event-driven network application framework for rapid de
|
||||
|
||||
### Scalable IO in Java 对 Multiple Reactors 的原理图解
|
||||
|
||||
<img src="https://cdn.jsdelivr.net/gh/youthlql/lqlp@master/netty/introduction/chapter_002/0009.jpg"/>
|
||||
<img src="https://unpkg.zhimg.com/youthlql@1.0.0/netty/introduction/chapter_002/0009.jpg"/>
|
||||
|
||||
### 方案优缺点说明
|
||||
|
||||
@@ -230,7 +230,7 @@ Netty is an asynchronous event-driven network application framework for rapid de
|
||||
|
||||
`Netty` 主要基于主从 `Reactors` 多线程模型(如图)做了一定的改进,其中主从 `Reactor` 多线程模型有多个 `Reactor`
|
||||
|
||||
<img src="https://cdn.jsdelivr.net/gh/youthlql/lqlp@master/netty/introduction/chapter_002/0010.png"/>
|
||||
<img src="https://unpkg.zhimg.com/youthlql@1.0.0/netty/introduction/chapter_002/0010.png"/>
|
||||
|
||||
**对上图说明**
|
||||
|
||||
@@ -240,7 +240,7 @@ Netty is an asynchronous event-driven network application framework for rapid de
|
||||
|
||||
### 工作原理示意图2 - 进阶版
|
||||
|
||||
<img src="https://cdn.jsdelivr.net/gh/youthlql/lqlp@master/netty/introduction/chapter_002/0011.png"/>
|
||||
<img src="https://unpkg.zhimg.com/youthlql@1.0.0/netty/introduction/chapter_002/0011.png"/>
|
||||
|
||||
|
||||
|
||||
@@ -248,7 +248,7 @@ Netty is an asynchronous event-driven network application framework for rapid de
|
||||
|
||||
### 工作原理示意图3 - 详细版
|
||||
|
||||
<img src="https://cdn.jsdelivr.net/gh/youthlql/lqlp@master/netty/introduction/chapter_002/0012.png"/>
|
||||
<img src="https://unpkg.zhimg.com/youthlql@1.0.0/netty/introduction/chapter_002/0012.png"/>
|
||||
|
||||
|
||||
|
||||
@@ -669,9 +669,9 @@ public class NettyServerHandler extends ChannelInboundHandlerAdapter {
|
||||
|
||||
下面第一张图就是管道,中间会经过多个handler
|
||||
|
||||
<img src="https://cdn.jsdelivr.net/gh/youthlql/lqlp@master/netty/introduction/chapter_002/0013.png"/>
|
||||
<img src="https://unpkg.zhimg.com/youthlql@1.0.0/netty/introduction/chapter_002/0013.png"/>
|
||||
|
||||
<img src="https://cdn.jsdelivr.net/gh/youthlql/lqlp@master/netty/introduction/chapter_002/0014.png"/>
|
||||
<img src="https://unpkg.zhimg.com/youthlql@1.0.0/netty/introduction/chapter_002/0014.png"/>
|
||||
|
||||
说明:
|
||||
|
||||
@@ -917,11 +917,11 @@ public class TestHttpServerHandler extends SimpleChannelInboundHandler<HttpObjec
|
||||
2. `ChannelHandler` 本身并没有提供很多方法,因为这个接口有许多的方法需要实现,方便使用期间,可以继承它的子类
|
||||
3. `ChannelHandler` 及其实现类一览图(后)
|
||||
|
||||
<img src="https://cdn.jsdelivr.net/gh/youthlql/lqlp@master/netty/introduction/chapter_002/0015.png"/>
|
||||
<img src="https://unpkg.zhimg.com/youthlql@1.0.0/netty/introduction/chapter_002/0015.png"/>
|
||||
|
||||
4. 我们经常需要自定义一个 `Handler` 类去继承 `ChannelInboundHandlerAdapter`,然后通过重写相应方法实现业务逻辑,我们接下来看看一般都需要重写哪些方法
|
||||
|
||||
<img src="https://cdn.jsdelivr.net/gh/youthlql/lqlp@master/netty/introduction/chapter_002/0016.png"/>
|
||||
<img src="https://unpkg.zhimg.com/youthlql@1.0.0/netty/introduction/chapter_002/0016.png"/>
|
||||
|
||||
## Pipeline 和 ChannelPipeline
|
||||
|
||||
@@ -931,7 +931,7 @@ public class TestHttpServerHandler extends SimpleChannelInboundHandler<HttpObjec
|
||||
2. `ChannelPipeline` 实现了一种高级形式的拦截过滤器模式,使用户可以完全控制事件的处理方式,以及 `Channel` 中各个的 `ChannelHandler` 如何相互交互
|
||||
3. 在 `Netty` 中每个 `Channel` 都有且仅有一个 `ChannelPipeline` 与之对应,它们的组成关系如下
|
||||
|
||||
<img src="https://cdn.jsdelivr.net/gh/youthlql/lqlp@master/netty/introduction/chapter_002/0017.png"/>
|
||||
<img src="https://unpkg.zhimg.com/youthlql@1.0.0/netty/introduction/chapter_002/0017.png"/>
|
||||
|
||||
4. 常用方法
|
||||
`ChannelPipeline addFirst(ChannelHandler... handlers)`,把一个业务处理类(`handler`)添加到链中的第一个位置`ChannelPipeline addLast(ChannelHandler... handlers)`,把一个业务处理类(`handler`)添加到链中的最后一个位置
|
||||
@@ -940,7 +940,7 @@ public class TestHttpServerHandler extends SimpleChannelInboundHandler<HttpObjec
|
||||
|
||||
从head看一下debug
|
||||
|
||||
<img src="https://cdn.jsdelivr.net/gh/youthlql/lqlp@master/netty/introduction/chapter_002/0018.jpg"/>
|
||||
<img src="https://unpkg.zhimg.com/youthlql@1.0.0/netty/introduction/chapter_002/0018.jpg"/>
|
||||
|
||||
- `TestServerInitializer`和`HttpServerCodec`这些东西本身也是`handler`
|
||||
- 一般来说事件从客户端往服务器走我们称为出站,反之则是入站。
|
||||
@@ -950,7 +950,7 @@ public class TestHttpServerHandler extends SimpleChannelInboundHandler<HttpObjec
|
||||
1. 保存 `Channel` 相关的所有上下文信息,同时关联一个 `ChannelHandler` 对象
|
||||
2. 即 `ChannelHandlerContext` 中包含一个具体的事件处理器 `ChannelHandler`,同时 `ChannelHandlerContext` 中也绑定了对应的 `pipeline` 和 `Channel` 的信息,方便对 `ChannelHandler` 进行调用。
|
||||
|
||||
<img src="https://cdn.jsdelivr.net/gh/youthlql/lqlp@master/netty/introduction/chapter_002/0019.jpg"/>
|
||||
<img src="https://unpkg.zhimg.com/youthlql@1.0.0/netty/introduction/chapter_002/0019.jpg"/>
|
||||
|
||||
3. 常用方法
|
||||
|
||||
@@ -959,14 +959,14 @@ public class TestHttpServerHandler extends SimpleChannelInboundHandler<HttpObjec
|
||||
- `ChannelFuture writeAndFlush(Object msg)`,将数据写到
|
||||
- `ChannelPipeline` 中当前 `ChannelHandler` 的下一个 `ChannelHandler` 开始处理(出站)
|
||||
|
||||
<img src="https://cdn.jsdelivr.net/gh/youthlql/lqlp@master/netty/introduction/chapter_002/0020.png"/>
|
||||
<img src="https://unpkg.zhimg.com/youthlql@1.0.0/netty/introduction/chapter_002/0020.png"/>
|
||||
|
||||
## ChannelOption
|
||||
|
||||
1. `Netty` 在创建 `Channel` 实例后,一般都需要设置 `ChannelOption` 参数。
|
||||
2. `ChannelOption` 参数如下:
|
||||
|
||||
<img src="https://cdn.jsdelivr.net/gh/youthlql/lqlp@master/netty/introduction/chapter_002/0021.png"/>
|
||||
<img src="https://unpkg.zhimg.com/youthlql@1.0.0/netty/introduction/chapter_002/0021.png"/>
|
||||
|
||||
## EventLoopGroup 和其实现类 NioEventLoopGroup
|
||||
|
||||
@@ -974,7 +974,7 @@ public class TestHttpServerHandler extends SimpleChannelInboundHandler<HttpObjec
|
||||
2. `EventLoopGroup` 提供 `next` 接口,可以从组里面按照一定规则获取其中一个 `EventLoop` 来处理任务。在 `Netty` 服务器端编程中,我们一般都需要提供两个 `EventLoopGroup`,例如:`BossEventLoopGroup` 和 `WorkerEventLoopGroup`。
|
||||
3. 通常一个服务端口即一个 `ServerSocketChannel` 对应一个 `Selector` 和一个 `EventLoop` 线程。`BossEventLoop` 负责接收客户端的连接并将 `SocketChannel` 交给 `WorkerEventLoopGroup` 来进行 `IO` 处理,如下图所示
|
||||
|
||||
<img src="https://cdn.jsdelivr.net/gh/youthlql/lqlp@master/netty/introduction/chapter_002/0022.png"/>
|
||||
<img src="https://unpkg.zhimg.com/youthlql@1.0.0/netty/introduction/chapter_002/0022.png"/>
|
||||
|
||||
4. 常用方法
|
||||
`public NioEventLoopGroup()`,构造方法
|
||||
@@ -985,11 +985,11 @@ public class TestHttpServerHandler extends SimpleChannelInboundHandler<HttpObjec
|
||||
1. `Netty` 提供一个专门用来操作缓冲区(即 `Netty` 的数据容器)的工具类
|
||||
2. 常用方法如下所示
|
||||
|
||||
<img src="https://cdn.jsdelivr.net/gh/youthlql/lqlp@master/netty/introduction/chapter_002/0023.png"/>
|
||||
<img src="https://unpkg.zhimg.com/youthlql@1.0.0/netty/introduction/chapter_002/0023.png"/>
|
||||
|
||||
3. 举例说明 `Unpooled` 获取 `Netty` 的数据容器 `ByteBuf` 的基本使用
|
||||
|
||||
<img src="https://cdn.jsdelivr.net/gh/youthlql/lqlp@master/netty/introduction/chapter_002/0024.png"/>
|
||||
<img src="https://unpkg.zhimg.com/youthlql@1.0.0/netty/introduction/chapter_002/0024.png"/>
|
||||
|
||||
案例 1
|
||||
|
||||
@@ -1096,7 +1096,7 @@ public class NettyByteBuf02 {
|
||||
|
||||
|
||||
|
||||
<img src="https://cdn.jsdelivr.net/gh/youthlql/lqlp@master/netty/introduction/chapter_002/0025.png"/>
|
||||
<img src="https://unpkg.zhimg.com/youthlql@1.0.0/netty/introduction/chapter_002/0025.png"/>
|
||||
|
||||
代码如下:
|
||||
|
||||
@@ -1523,7 +1523,7 @@ public class MyServerHandler extends ChannelInboundHandlerAdapter {
|
||||
4. 客户端浏览器和服务器端会相互感知,比如服务器关闭了,浏览器会感知,同样浏览器关闭了,服务器会感知
|
||||
5. 运行界面
|
||||
|
||||
<img src="https://cdn.jsdelivr.net/gh/youthlql/lqlp@master/netty/introduction/chapter_002/0026.png"/>
|
||||
<img src="https://unpkg.zhimg.com/youthlql@1.0.0/netty/introduction/chapter_002/0026.png"/>
|
||||
|
||||
|
||||
|
||||
@@ -1724,7 +1724,7 @@ public class MyTextWebSocketFrameHandler extends SimpleChannelInboundHandler<Tex
|
||||
|
||||
可以看到并不是发一次数据,连接就关闭了,而是可以继续发送。
|
||||
|
||||
<img src="https://cdn.jsdelivr.net/gh/youthlql/lqlp@master/netty/introduction/chapter_002/0027.png"/>
|
||||
<img src="https://unpkg.zhimg.com/youthlql@1.0.0/netty/introduction/chapter_002/0027.png"/>
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ categories:
|
||||
- 操作系统
|
||||
keywords: 操作系统,IO,零拷贝
|
||||
description: 基本面试会问到的IO进行了详解,同时本篇文章也对面试以及平时工作中会看到的零拷贝进行了充分的解析。万字长文系列,读到就是赚到。
|
||||
cover: 'https://cdn.jsdelivr.net/gh/youthlql/lqlp@master/os/os_logo.jpg'
|
||||
cover: 'https://unpkg.zhimg.com/youthlql@1.0.0/os/os_logo.jpg'
|
||||
abbrlink: e959db2e
|
||||
date: 2021-04-08 15:21:58
|
||||
---
|
||||
@@ -90,7 +90,7 @@ date: 2021-04-08 15:21:58
|
||||
4. 通过Reactor的方式,可以将用户线程轮询IO操作状态的工作统一交给handle_events事件循环进行处理。用户线程注册事件处理器之后可以继续执行做其他的工作(异步),而Reactor线程负责调用内核的select函数检查socket状态。当有socket被激活时(就是数据准备好的时候),则通知相应的用户线程(或执行用户线程的回调函数),执行handle_event进行数据读取、处理的工作。
|
||||
5. 由于select函数是阻塞的,因此多路IO复用模型也被称为异步阻塞IO模型。注意,这里的所说的阻塞是指select函数执行时线程被阻塞,而不是指socket。(一般在使用IO多路复用模型时,socket都是设置为NONBLOCK的,不过这并不会产生影响,因为用户发起IO请求时,数据已经到达了,用户线程一定不会被阻塞。)
|
||||
|
||||
<img src="https://cdn.jsdelivr.net/gh/youthlql/lqlp@master/os/IO_and_Zero-copy/0001.png">
|
||||
<img src="https://unpkg.zhimg.com/youthlql@1.0.0/os/IO_and_Zero-copy/0001.png">
|
||||
|
||||
|
||||
|
||||
@@ -196,7 +196,7 @@ date: 2021-04-08 15:21:58
|
||||
|
||||
1. 来看一个标准设备(不是真实存在的,相当于一个逻辑上抽象的东西),通过它来帮助我们更好地理解设备交互的机制。可以看到一个包含两部分重要组件的设备。第一部分是向系统其他部分展现的硬件接口(interface)。同软件一样,硬件也需要一些接口,让系统软件来控制它的操作。因此,所有设备都有自己的特定接口以及典型交互的协议。
|
||||
|
||||
<img src="https://cdn.jsdelivr.net/gh/youthlql/lqlp@master/os/IO_and_Zero-copy/0002.png"/>
|
||||
<img src="https://unpkg.zhimg.com/youthlql@1.0.0/os/IO_and_Zero-copy/0002.png"/>
|
||||
|
||||
2. 第2部分是它的内部结构(internal structure)。这部分包含设备相关的特定实现,负责具体实现设备展示给系统的抽象接口。
|
||||
|
||||
@@ -231,11 +231,11 @@ While (STATUS == BUSY);//wait until device is done with your request
|
||||
|
||||
1. 没有中断时:进程1在CPU上运行一段时间(对应CPU那一行上重复的1),然后发出一个读取数据的I/O请求给磁盘。如果没有中断,那么操作系统就会简单自旋,不断轮询设备状态,直到设备完成I/O操作(对应其中的p)。当设备完成请求的操作后,进程1又可以继续运行。
|
||||
|
||||
<img src="https://cdn.jsdelivr.net/gh/youthlql/lqlp@master/os/IO_and_Zero-copy/0003.png"/>
|
||||
<img src="https://unpkg.zhimg.com/youthlql@1.0.0/os/IO_and_Zero-copy/0003.png"/>
|
||||
|
||||
2. 有了中断后:中断允许计算与I/O重叠(overlap),这是提高CPU利用率的关键。我们利用中断并允许重叠,操作系统就可以在等待磁盘操作时做其他事情。
|
||||
|
||||
<img src="https://cdn.jsdelivr.net/gh/youthlql/lqlp@master/os/IO_and_Zero-copy/0004.png"/>
|
||||
<img src="https://unpkg.zhimg.com/youthlql@1.0.0/os/IO_and_Zero-copy/0004.png"/>
|
||||
|
||||
- 在这个例子中,在磁盘处理进程1的请求时,操作系统在CPU上运行进程2。磁盘处理完成后,触发一个中断,然后操作系统唤醒进程1继续运行。这样,在这段时间,无论CPU还是磁盘都可以有效地利用。
|
||||
|
||||
@@ -243,7 +243,7 @@ While (STATUS == BUSY);//wait until device is done with your request
|
||||
|
||||
中断仍旧存在的缺点:
|
||||
|
||||
<img src="https://cdn.jsdelivr.net/gh/youthlql/lqlp@master/os/IO_and_Zero-copy/0005.png"/>
|
||||
<img src="https://unpkg.zhimg.com/youthlql@1.0.0/os/IO_and_Zero-copy/0005.png"/>
|
||||
|
||||
IO过程简述:
|
||||
|
||||
@@ -268,7 +268,7 @@ IO过程简述:
|
||||
>
|
||||
> 标准协议还有一点需要我们注意。具体来说,如果使用编程的I/O将一大块数据传给设备,CPU又会因为琐碎的任务而变得负载很重,浪费了时间和算力,本来更好是用于运行其他进程。下面的时间线展示了这个问题:
|
||||
>
|
||||
> <img src="https://cdn.jsdelivr.net/gh/youthlql/lqlp@master/os/IO_and_Zero-copy/0006.png"/>
|
||||
> <img src="https://unpkg.zhimg.com/youthlql@1.0.0/os/IO_and_Zero-copy/0006.png"/>
|
||||
>
|
||||
> 进程1在运行过程中需要向磁盘写一些数据,所以它开始进行I/O操作,将数据从内存拷贝到磁盘(其中标示c的过程)。**拷贝结束后,磁盘上的I/O操作开始执行,此时CPU才可以处理其他请求。**
|
||||
|
||||
@@ -278,13 +278,13 @@ IO过程简述:
|
||||
>
|
||||
> DMA工作过程如下。为了能够将数据传送给设备,操作系统会通过编程告诉DMA引擎数据在内存的位置,要拷贝的大小以及要拷贝到哪个设备。在此之后,操作系统就可以处理其他请求了。当DMA的任务完成后,DMA控制器会抛出一个中断来告诉操作系统自己已经完成数据传输。修改后的时间线如下:
|
||||
>
|
||||
> <img src="https://cdn.jsdelivr.net/gh/youthlql/lqlp@master/os/IO_and_Zero-copy/0007.png"/>
|
||||
> <img src="https://unpkg.zhimg.com/youthlql@1.0.0/os/IO_and_Zero-copy/0007.png"/>
|
||||
>
|
||||
> 从时间线中可以看到,数据的拷贝工作都是由DMA控制器来完成的。因为CPU在此时是空闲的,所以操作系统可以让它做一些其他事情,比如此处调度进程2到CPU来运行。因此进程2在进程1再次运行之前可以使用更多的CPU。
|
||||
|
||||
为了更好理解,看图:
|
||||
|
||||
<img src="https://cdn.jsdelivr.net/gh/youthlql/lqlp@master/os/IO_and_Zero-copy/0008.png"/>
|
||||
<img src="https://unpkg.zhimg.com/youthlql@1.0.0/os/IO_and_Zero-copy/0008.png"/>
|
||||
|
||||
过程:
|
||||
|
||||
@@ -302,7 +302,7 @@ IO过程简述:
|
||||
|
||||
场景:将磁盘上的文件读取出来,然后通过网络协议发送给客户端。
|
||||
|
||||
<img src="https://cdn.jsdelivr.net/gh/youthlql/lqlp@master/os/IO_and_Zero-copy/0009.png"/>
|
||||
<img src="https://unpkg.zhimg.com/youthlql@1.0.0/os/IO_and_Zero-copy/0009.png"/>
|
||||
|
||||
1. 很明显发生了4次拷贝
|
||||
|
||||
@@ -326,7 +326,7 @@ IO过程简述:
|
||||
|
||||
`read()` 系统调用的过程中会把内核缓冲区的数据拷贝到用户的缓冲区里,为了减少这一步开销,我们可以用 `mmap()` 替换 `read()` 系统调用函数。`mmap()` 系统调用函数会直接把内核缓冲区里的数据映射到用户空间,这样,操作系统内核与用户空间共享缓冲区,就不需要再进行任何的数据拷贝操作。
|
||||
|
||||
<img src="https://cdn.jsdelivr.net/gh/youthlql/lqlp@master/os/IO_and_Zero-copy/0010.png"/>
|
||||
<img src="https://unpkg.zhimg.com/youthlql@1.0.0/os/IO_and_Zero-copy/0010.png"/>
|
||||
|
||||
总的来说mmap减少了一次数据拷贝,总共4次上下文切换,3次数据拷贝
|
||||
|
||||
@@ -336,7 +336,7 @@ IO过程简述:
|
||||
|
||||
`Linux2.1` 版本提供了 `sendFile` 函数,其基本原理如下:数据根本不经过用户态,直接从内核缓冲区进入到 `SocketBuffer`
|
||||
|
||||
<img src="https://cdn.jsdelivr.net/gh/youthlql/lqlp@master/os/IO_and_Zero-copy/0011.png"/>
|
||||
<img src="https://unpkg.zhimg.com/youthlql@1.0.0/os/IO_and_Zero-copy/0011.png"/>
|
||||
|
||||
总的来说有2次上下文切换,3次数据拷贝。
|
||||
|
||||
@@ -344,7 +344,7 @@ IO过程简述:
|
||||
|
||||
`Linux在2.4` 版本中,做了一些修改,避免了从内核缓冲区拷贝到 `Socketbuffer` 的操作,直接拷贝到协议栈,从而再一次减少了数据拷贝
|
||||
|
||||
<img src="https://cdn.jsdelivr.net/gh/youthlql/lqlp@master/os/IO_and_Zero-copy/0012.png" />
|
||||
<img src="https://unpkg.zhimg.com/youthlql@1.0.0/os/IO_and_Zero-copy/0012.png" />
|
||||
|
||||
|
||||
|
||||
|
||||
1040
docs/rpc/dubbo/Dubbo源码系列V1-01和02.Dubbo第一二节-基本应用与高级应用.md
Normal file
1040
docs/rpc/dubbo/Dubbo源码系列V1-01和02.Dubbo第一二节-基本应用与高级应用.md
Normal file
File diff suppressed because it is too large
Load Diff
1179
docs/rpc/dubbo/Dubbo源码系列V1-03.Dubbo第三节-可扩展机制SPI源码解析.md
Normal file
1179
docs/rpc/dubbo/Dubbo源码系列V1-03.Dubbo第三节-可扩展机制SPI源码解析.md
Normal file
File diff suppressed because it is too large
Load Diff
@@ -435,7 +435,7 @@ public class JZ_056 {
|
||||
}
|
||||
```
|
||||
|
||||
<img src="https://cdn.jsdelivr.net/gh/youthlql/lqlp@master/xiaozhao/shuiwen/0001.png"/>
|
||||
<img src="https://unpkg.zhimg.com/youthlql@1.0.0/xiaozhao/shuiwen/0001.png"/>
|
||||
|
||||
由上面代码可以看出来,this()方法可以调用本类的无参构造函数。如果本类继承的有父类,那么无参构造函数会有一个隐式的super()的存在。所以在同一个构造函数里面如果this()之后再调用super(),那么就相当于调用了两次super(),也就是调用了两次父类的无参构造,就失去了语句的意义,编译器也不会通过。
|
||||
|
||||
@@ -525,7 +525,7 @@ https://blog.csdn.net/striner/article/details/86375684
|
||||
2. 遍历链表或调用红黑树相关的删除方法
|
||||
3. 从 LinkedHashMap 维护的双链表中移除要删除的节点
|
||||
|
||||
<img src="https://cdn.jsdelivr.net/gh/youthlql/lqlp@master/xiaozhao/shuiwen/0002.png" />
|
||||
<img src="https://unpkg.zhimg.com/youthlql@1.0.0/xiaozhao/shuiwen/0002.png" />
|
||||
|
||||
|
||||
|
||||
@@ -537,7 +537,7 @@ https://blog.csdn.net/qq_36610462/article/details/83277524
|
||||
|
||||
## 集合继承结构
|
||||
|
||||
<img src="https://cdn.jsdelivr.net/gh/youthlql/lqlp@master/xiaozhao/shuiwen/0010.jpeg">
|
||||
<img src="https://unpkg.zhimg.com/youthlql@1.0.0/xiaozhao/shuiwen/0010.jpeg">
|
||||
|
||||
|
||||
|
||||
@@ -675,11 +675,11 @@ https://blog.csdn.net/puppylpg/article/details/80433271
|
||||
|
||||
这里我只截图下我准备的面试题目录,因为所有答案你都能在笔者的JVM文章里找到答案
|
||||
|
||||
<img src="https://cdn.jsdelivr.net/gh/youthlql/lqlp@master/xiaozhao/shuiwen/0003.png"/>
|
||||
<img src="https://unpkg.zhimg.com/youthlql@1.0.0/xiaozhao/shuiwen/0003.png"/>
|
||||
|
||||
|
||||
|
||||
<img src="https://cdn.jsdelivr.net/gh/youthlql/lqlp@master/xiaozhao/shuiwen/0004.png"/>
|
||||
<img src="https://unpkg.zhimg.com/youthlql@1.0.0/xiaozhao/shuiwen/0004.png"/>
|
||||
|
||||
|
||||
|
||||
@@ -1335,7 +1335,7 @@ redis cluster 功能强大,直接集成了 replication 和 sentinel 的功能
|
||||
|
||||
## 存储引擎Innodb和Myisam的区别以及使用场景
|
||||
|
||||
<img src="https://cdn.jsdelivr.net/gh/youthlql/lqlp@master/xiaozhao/shuiwen/0005.png"/>
|
||||
<img src="https://unpkg.zhimg.com/youthlql@1.0.0/xiaozhao/shuiwen/0005.png"/>
|
||||
|
||||
|
||||
|
||||
@@ -2039,7 +2039,7 @@ snowflake 算法是 twitter 开源的分布式 id 生成算法,采用 Scala
|
||||
|
||||
## Bean生命周期
|
||||
|
||||
<img src= "https://cdn.jsdelivr.net/gh/youthlql/lqlp@master/xiaozhao/shuiwen/0006.png">
|
||||
<img src= "https://unpkg.zhimg.com/youthlql@1.0.0/xiaozhao/shuiwen/0006.png">
|
||||
|
||||
|
||||
|
||||
@@ -2199,7 +2199,7 @@ https://github.com/Snailclimb/springboot-guide/blob/master/docs/interview/spring
|
||||
|
||||
|
||||
|
||||
<img src="https://cdn.jsdelivr.net/gh/youthlql/lqlp@master/xiaozhao/shuiwen/0007.png">
|
||||
<img src="https://unpkg.zhimg.com/youthlql@1.0.0/xiaozhao/shuiwen/0007.png">
|
||||
|
||||
|
||||
|
||||
@@ -2331,7 +2331,7 @@ private final Map<String, Object> earlySingletonObjects = new HashMap<String, Ob
|
||||
|
||||
11、DispatcherServlet 类向用户返回响应。
|
||||
|
||||
<img src="https://cdn.jsdelivr.net/gh/youthlql/lqlp@master/xiaozhao/shuiwen/0008.png" />
|
||||
<img src="https://unpkg.zhimg.com/youthlql@1.0.0/xiaozhao/shuiwen/0008.png" />
|
||||
|
||||
|
||||
|
||||
@@ -2416,7 +2416,7 @@ https://blog.csdn.net/j04110414/article/details/78914787
|
||||
|
||||
|
||||
|
||||
<img src="https://cdn.jsdelivr.net/gh/youthlql/lqlp@master/xiaozhao/shuiwen/0009.png" >
|
||||
<img src="https://unpkg.zhimg.com/youthlql@1.0.0/xiaozhao/shuiwen/0009.png" >
|
||||
|
||||
从整个秒杀系统的架构其实和一般的互联网系统架构本身没有太多的不同,核心理念还是通过缓存、异步、限流来保证系统的高并发和高可用。下面从一笔秒杀交易的流程来描述下秒杀系统架构设计的要点:
|
||||
|
||||
|
||||
Reference in New Issue
Block a user