Skip to content

Commit

Permalink
doc:
Browse files Browse the repository at this point in the history
1、备份文章;
  • Loading branch information
01Petard committed Nov 14, 2024
1 parent 90ff44b commit e43bebf
Show file tree
Hide file tree
Showing 2 changed files with 116 additions and 19 deletions.
2 changes: 1 addition & 1 deletion docs/.vitepress/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ export default {
items: [
{text: "命令行系统信息工具", link: "/软件/Linux/命令行系统信息工具"},
{text: "在服务器上部署hexo博客指南", link: "/软件/Linux/在服务器上部署hexo博客指南"},
{text: "Unix系统下的常用命令", link: "/软件/Linux/Unix系统下的常用命令"},
{text: "Unix系统下的Shell命令", link: "/软件/Linux/Unix系统下的Shell命令"},
{text: "Linux常用命令", link: "/软件/Linux/Linux常用命令"},
{text: "CentOS7安装oh-my-zsh", link: "/软件/Linux/CentOS7安装oh-my-zsh"},
{text: "zsh与bash的切换", link: "/软件/Linux/zsh与bash的切换"},
Expand Down
133 changes: 115 additions & 18 deletions docs/开发/My Java Guide/My Java Guide - 分布式.md
Original file line number Diff line number Diff line change
Expand Up @@ -2236,6 +2236,17 @@ Kafka 处理请求的全流程是一个复杂的多步骤过程,涉及到网
3. **多路复用(Multiplexing I/O**:使用`select`、`poll`、`epoll`等机制,同时监视多个I/O操作,适合高并发场景。
4. **异步I/OAsynchronous I/O**:操作完成时会通知应用程序,避免了轮询。

# NIO产生的背景

对于每一个新的网络连接都分配给一个线程。每个线程都独自处理自己负责的socket连接的输入和输出。当然,服务器的监听线程也是独立的,任何socket连接的输入和输出处理都不会阻塞到后面新socket连接的监听和建立,这样服务器的吞吐量就得到了提升。**早期版本的Tomcat服务器**就是这样实现的。

- ConnectionPerThread模式(一个线程处理一个连接)的优点是解决了前面的新连接被严重阻塞的问题,在一定程度上较大地提高了服务器的吞吐量。
- ConnectionPer Thread模式的缺点是对应于大量的连接,需要耗费大量的线程资源,对线程资源要求太高。在系统中,线程是比较昂贵的系统资源。如果线程的数量太多,系统将无法承受。而且,线程的反复创建、销毁、切换也需要代价。

**JavaNlo(非阻塞IO**其工作原理是通过使用**通道(Channels****缓冲区(Buffers**来实现数据的传输。与传统的面向流的I/O不同,NIO采用事件驱动的模型,允许单个线程监听多个通道上的事件,通过选择器(Selectors)来处理多个网络连接,从而实现高效率的数据读写操作,特别适用于高并发网络应用场景。

<img src="https://cdn.jsdelivr.net/gh/01Petard/imageURL@main/img/202411141405231.png" alt="image-20241114140513097" style="zoom: 55%;" />

# NIOBIO的区别

NIONew IO)和BIOBlocking IO)是Java编程语言中用于处理输入输出(IO)操作的两种不同机制,它们之间存在一些显著的区别。
Expand Down Expand Up @@ -2304,7 +2315,7 @@ Netty相比与直接使用JDK自带的api更简单,因为它具有这样一些

我们之所以要使用Netty,核心的点是要去解决服务器如何去承载更多的用户同时访问的问题,传统的BIO模型由于阻塞的特性使得在高并发的环境种很难去支持更高的吞吐量,尽管用NIO的多路复用模型可以在阻塞方面进行优化,但是它的api使用较为复杂,而Netty是基于NIO的封装,提供了成熟简单易用的api,降低了使用成本和学习成本,本质上来说NettyNIO所扮演的角色是相同的,都是是去为了提升服务端的吞吐量,让用户获得更好的产品体验。

# Netty的核心组件
# Netty核心组件

Netty有三层结构构成的,分别是:

Expand Down Expand Up @@ -2354,42 +2365,94 @@ Netty有三层结构构成的,分别是:
- `ChannelHandler` 主要是针对10数据的一个处理器,数据接收后,就通过指定的一个上Handler进行处理
- `ChannelHandlerContext` 是用来去保存ChannelHandler的一个上下文信息的。

# Reactor 线程模型、其原理和作用
# Reactor 模型、原理、作用

> Reactor模式在高性能网络编程中至关重要。许多知名软件如NginxRedisNetty都基于此模式实现。无论是为了开发还是学习,掌握Reactor模式都是理解和运用这些技术的基础。在大厂面试中,Reactor模式是一个常见的考察点。简而言之,对于高性能网络编程来说,了解并掌握Reactor模式是必不可少的。

Reactor线程模型是基于事件驱动的模型,主要分为三个角色
Reactor模型中的几个重要的概念

1. **Reactor**:负责监视I/O事件并分发事件。
2. **Handler**:处理具体的业务逻辑。
- `Acceptor` 处理客户端的连接请求
- `Reactor` 负责将IO事件分派给对应的Handler
- `Handlers` 负责执行我们的业务逻辑的读写操作
- `Worker` 负责执行I/O操作

Reactor线程模型是基于事件驱动的模型,在Reactor模式中有三个重要的组件:

1. **Reactor**:负责监视I/O事件并分发事件。当检测到一个lO事件时将其发送给相应的Handler处理器去处理。这里的IO事件就是NIO中选择器查询出来的通道IO事件。
2. **Handler**:处理具体的业务逻辑。与lO事件(或者选择键)绑定,负责lO事件的处理,完成真正的连接建立、通道的读取、处理业务逻辑、负责将结果写到通道等。
3. **Worker**:执行I/O操作。可以使用多个Worker线程处理具体的请求,提高并发性能。

Netty提供了三种Reactor模型的支持:

1. **单线程单Reactor模型**单线程单Reactor模型也有缺点:如果其中一个Handler的出现阻塞,就会导致后续的客户端无法被处理,因为它们是同一个线程,所以就导致无法接受新的请求。为了解决这个问题,就提出了使用多线程的方式,也就是说在业务处理的时候加入线程池去异步处理,这样就可以解决handlers阻塞的一个问题
**单线程单Reactor模型**在单线程Reactor模型中,所有的I/O操作(如读取请求、处理请求、发送响应)都**在同一个线程中完成**。这种模型适用于连接数量较少、处理逻辑较简单的场景

<img src="https://cdn.jsdelivr.net/gh/01Petard/imageURL@main/img/202409291709540.png" alt="image-20240929170912316" style="zoom:60%;" />
**执行流程**

2. **多线程单Reactor模型**。为了解决单线程中handlers阻塞的问题,我们引入了线程池去异步处理,这意味着我们把Reactor和handlers放在不同的线程里面去处理。在多线程单Reactor模型中,所有的IO操作都是由一个Reactor来完成的,这导致单个Reactor会存在一个性能瓶颈,对于小容量的场景影响不是很大,但是对于高并发的一些场景来说,很容易会因为单个Reactor线程的性能瓶颈,导致整个吞吐量会受到影响,所以当这个线程超过负载之后,处理的速度变慢,就会导致大量的客户端连接超时,超时之后往往会进行重发,这反而加重了这个线程的一个负载,最终会导致大量的消息积压和处理的超时,成为整个系统的一个性能瓶颈,所以我们还可以进行进一步的优化,也就是引入多线程多Reactor模型。
1. 事件循环初始化:创建一个单线程的Reactor,通常会注册一个Selector或epoll来监听l/O事件。
2. 事件注册:服务器启动后,将自已绑定到某个端口,并注册为“accept事件”,等待客户端连接。
3. 事件分发:一旦有l/O事件发生(如客户端连接、读写操作),Reactor会从Selector中获取事件,并调用相应的处理器(Handler)。
4. 处理请求:Handler执行处理逻辑,包括读取客户端数据、处理业务逻辑和发送响应。因为是单线程,所有操作都必须同步进行。
5. 返回结果:将处理结果返回给客户端,处理完一个事件后,再去处理下一个事件。

<img src="https://cdn.jsdelivr.net/gh/01Petard/imageURL@main/img/202409291711995.png" alt="image-20240929171018421" style="zoom:60%;" />
**优点**:结构简单,适用于处理量不大、请求处理时间短的场景。

3. **多线程多Reactor模型**,也叫**主从多线程Reactor模型**Main Reactor负责接收客户的连接请求,然后把接收的请求传递给Sub ReactorSub Reactorl我们可以配置多个,这样我们可以去进行灵活的扩容和缩容,具体的业务处理由Sub Reactor去完成,由它最终去绑定给对应的handler。Main Reactor扮演请求接收者,它会把接收的请求转发到Sub Reactor来处理,由Sub Reactor去进行真正意义上的分发
**缺点**:因为所有I/O事件都在一个线程中完成,若某个事件的处理时间较长,会阻塞其他事件的处理,导致性能瓶颈

<img src="https://cdn.jsdelivr.net/gh/01Petard/imageURL@main/img/202409291719179.png" alt="image-20240929171910957" style="zoom:60%;" />
<img src="https://cdn.jsdelivr.net/gh/01Petard/imageURL@main/img/202409291709540.png" alt="image-20240929170912316" style="zoom:55%;" />

Reactor模型有三个重要的组件:
单线程单Reactor模型也有缺点:如果其中一个Handler的出现阻塞,就会导致后续的客户端无法被处理,因为它们是同一个线程,所以就导致无法接受新的请求。为了解决这个问题,就提出了**使用多线程的方式(如下)**,也就是说在业务处理的时候加入线程池去异步处理,这样就可以解决handlers阻塞的一个问题。

- `Reactor` 负责将IO事件分派给对应的Handler
- `Acceptor` 处理客户端的连接请求
- `handlers` 负责执行我们的业务逻辑的读写操作
***多线程单Reactor模型***。为了解决单线程中handlers阻塞的问题,我们引入了线程池去异步处理,这意味着我们把Reactor和handlers放在不同的线程里面去处理。在多线程单Reactor模型中,所有的IO操作都是由一个Reactor来完成的,这导致单个Reactor会存在一个性能瓶颈,对于小容量的场景影响不是很大,但是对于高并发的一些场景来说,很容易会因为单个Reactor线程的性能瓶颈,导致整个吞吐量会受到影响,所以当这个线程超过负载之后,处理的速度变慢,就会导致大量的客户端连接超时,超时之后往往会进行重发,这反而加重了这个线程的一个负载,最终会导致大量的消息积压和处理的超时,成为整个系统的一个性能瓶颈,所以我们还可以进行进一步的优化,也就是引入多线程多Reactor模型。

<img src="https://cdn.jsdelivr.net/gh/01Petard/imageURL@main/img/202409291711995.png" alt="image-20240929171018421" style="zoom:55%;" />

多线程Reactor模型是对单线程模型的扩展,使用了一个**主线程(MainReactor**和多个**工作线程(SubReactors**,主线程负责事件分发,工作线程负责具体事件的处理。这种模型适用于高并发、大量连接的场景。

**多线程多Reactor模型**,也叫**主从多线程Reactor模型**Main Reactor负责接收客户的连接请求,然后把接收的请求传递给Sub ReactorSub Reactorl我们可以配置多个,这样我们可以去进行灵活的扩容和缩容,具体的业务处理由Sub Reactor去完成,由它最终去绑定给对应的handler。Main Reactor扮演请求接收者,它会把接收的请求转发到Sub Reactor来处理,由Sub Reactor去进行真正意义上的分发。

**执行流程**

1. 事件循环初始化:创建一个主Reactor线程,用于监听accept事件。主ReactorSelector负责监听客户端的连接请求。
2. 事件注册:主Reactor监听客户端连接请求,并将连接的l/O事件注册到对应的工作线程的Selector上。
3. 事件分发:当客户端连接建立后,主Reactor将l/O操作交给工作线程(SubReactor)处理,避免阻塞主线程。
4. 处理请求:每个工作线程都有自己的Selector,并独立处理其上注册的读写事件。这样多个请求可以并行处理。
5. 返回结果:处理完成后,工作线程将结果返回给客户端,同时继续监听新的/O事件。

**优点**

1.Reactor只负责连接分发,减少了阻塞,工作线程并行处理l/O事件,大大提高了处理效率。
2. 支持高并发,并能有效避免单个事件处理时间过长对系统性能的影响。

**缺点**

1. 需要协调多个线程的资源,会增加一些开发复杂性。
2. 若线程数配置不合理,可能导致频繁的上下文切换,增加系统开销。

# 高性能设计
**适用场景总结**

1. 单线程Reactor适用于连接数量少、处理时间短的场景。
2. 多线程Reactor更适合高并发、业务处理耗时较长的场景。

<img src="https://cdn.jsdelivr.net/gh/01Petard/imageURL@main/img/202409291719179.png" alt="image-20240929171910957" style="zoom:55%;" />

# 有了NlO为什么还需要Reactor?
JavaNIO出现之后,为什么还需要Reactor模式?这主要是因为Reactor模式提供了一种高效的事件处理机制,它能够更好地利用NIO的特性,提高应用程序的性能和可扩展性。

以下是几个具体的理由:

1. **可扩展性**:通常情况下,我们会把NIO理解成**单线程处理多请求**的情况,**对多线程的支持不太好**,而Reactor模式使得系统更加容易扩展。随着并发连接数的增加,Reactor模式可以通过增加处理事件的线程数(基于线程池)来应对更高的负载。
2. **事件驱动**Reactor模式也是一种基于事件驱动的设计模式,它**能够处理多个并发的事件或请求**,并且可以在单个或少量线程中管理这些事件。这**NIO本身的事件通知机制相辅相成**
3. **解耦**Reactor模式**将事件接收、事件分发和事件处理分离开来****降低**了各部分之间的**耦合度**,使得系统更加模块化,易于维护和扩展。
4. **异步处理**Reactor模式天然**支持非阻塞处理**,这意味着应用程序**可以在不等待IO操作完成的情况下继续执行**,从而提高了系统的响应速度。

# Netty高性能设计

1. **非阻塞IO模型**Netty基于NIO实现,使用非阻塞IO模型,减少了线程的使用,从而减少了上下文切换的开销。
2. **事件驱动**Netty采用了事件驱动的设计模式,当有IO事件发生时,才会被处理,这样可以有效地利用CPU资源。
3. **零拷贝技术**Netty支持直接缓冲区(DirectByteBuffer),在数据传输中减少了数据的拷贝次数,提高了数据传输的效率。具体做法是:使用`FileChannel`的`transferTo()`和`transferFrom()`等方法实现文件传输时,避免了将数据从用户空间复制到内核空间的过程,提高了性能。
4. **线程模型**Netty提供了高效的线程模型,如Boss/Worker模型,使得任务的分配更加合理,充分利用多核CPU的计算能力。

# Netty 中的设计模式
# Netty设计模式

- **单例模式**:如EventLoop
- **观察者模式**:事件的注册和触发。
Expand Down Expand Up @@ -2804,7 +2867,41 @@ docker build -t my_custom_image:latest .
docker run --cpus=1 --memory=512m [container_name]
```

# 如何使用 JenkinsDocker 集成?
# Jenkins

## 自动发布的好处

**CI/CD**:持续集成(CI)、持续交付(CD)

**持续交付**:指开发人员频繁地将代码更改合并到主代码库中。每次提交后,自动化工具会执行构建和测试,以确保新代码不会引入错误。

**持续交付**:旨在通过自动化构建、测试和部署过程,使得软件能够在任何时间点都可以安全地发布到生产环境中。

**一般认为**:自动发布可以节省人工编译、部署的时间

那是否?只有项目较大、服务器较多的情况下才需要自动发布呢?

**无论项目大小、服务器多少都应该采用自动发布进行编译、部署**,包括开发环境、测试环境、生产环境,因为在实际发布的环境下除了替换程序、重启服务,还需要人工修改配置、替换某些文件等,一些服务的启动顺序也是有特殊要求的,而且如果发布测试时发现了问题,则发布行为需要循环多次,如果是人工发布,则会产生很多人为失误,一来二去,会花掉很多不必要的时间。

**自动发布真正的好处**:并不是单单**节省了多服务器的部署时间****免去人工编译、部署时可能发生的人为失误**,从而节省无意间浪费的成本。

## Jenkins介绍

Jenkins是一个自动发布部署软件的系统,可以简单地将Jenkins理解为一个网站系统,在管理网站上,用户可以编排自动发布任务,可以实现代码下载-代码编译-文件发送-执行远程命令等场景,当条件触发时,Jenkins就会自动按预设任务进行编译部署。

例如:Git代码仓库创建了新tag标识后,Jenkins可以自动下载代码并编译发布到运行的服务器,实现自动发布。

<img src="https://cdn.jsdelivr.net/gh/01Petard/imageURL@main/img/202411141353929.png" alt="image-20241114135335847" style="zoom: 60%;" />

## Jenkins安装

使用[1Panel](https://1panel.cn/)一键安装

## Jenkins发布流程配置

TODO

## 如何使用 JenkinsDocker 集成?

1. 安装必要的插件:在 Jenkins 中,安装 Docker plugin 和 Pipeline plugin 等必要插件。
2. 配置 Jenkins:配置环境,确保 Jenkins 可以访问 Docker 命令。
Expand Down

0 comments on commit e43bebf

Please sign in to comment.