IO模型Netty

news/2024/5/19 17:37:01/文章来源:https://blog.csdn.net/xhom_w/article/details/128041439

一、IO模型

对于一次IO操作,数据会先拷贝到内核空间中,然后再从内核空间拷贝到用户空间中,所以一次read操作,会经历以下两个阶段,基于这两个阶段就产生了五种不同的IO模式。

为了避免用户进程直接操作内核,保证内核安全,操作系统将内存(虚拟内存)划分为两部分:一部分是内核空间(Kernel-Space),另一部分是用户空间(User-Space)。在Linux系统中,内核模块运行在内核空间,对应的进程处于内核态;用户程序运行在用户空间,对应的进程处于用户态。

操作系统的核心是内核程序,它独立于普通的应用程序,既有权限访问受保护的内核空间,也有权限访问硬件设备,而普通的应用程序并没有这样的权限。内核空间总是驻留在内存中,是为操作系统的内核保留的。应用程序不允许直接在内核空间区域进行读写,也不允许直接调用内核代码定义的函数。每个应用程序进程都有一个单独的用户空间,对应的进程处于用户态,用户态进程不能访问内核空间中的数据,也不能直接调用内核函数,因此需要将进程切换到内核态才能进行系统调用(System Call)。

  • 等待数据准备
  • 数据从内核空间拷贝到用户空间
1.阻塞IO(同步阻塞IO Blocking IO, BIO)

在这里插入图片描述

从进程发起IO操作,一直等待上述两个阶段完成。两阶段一起阻塞

2.非阻塞IO(同步非阻塞IO Non-Blocking IO,NIO)

在这里插入图片描述

进程一直询问IO准备好了没有,准备好了再发起读取操作,这时才把数据从内核空间拷贝到用户空间。第一阶段不阻塞但要轮询,第二阶段阻塞

整个IO请求过程中,虽然用户线程每次发起IO请求后可以立即返回,但是为了等到数据。仍需要不断地轮询、重复请求、消耗了大量的CPU资源;是比较浪费CPU的方式,一般很少用这种模型,而是在其他模型中使用非阻塞IO这一特性。

同步非阻塞IO也可以简称为NIO,但是它不是Java编程中的NIO。Java编程中的NIO(New IO)类库组件所归属的不是基础IO模型中的NIO模型,而是IO多路复用模型。

3.多路复用IO(IO Multiplexing)

在这里插入图片描述

多个连接使用同一个select去询问IO准备好了没有,如果有准备好了的,就返回有数据准备好了,然后对应的连接再发起读取操作,把数据从内核空间拷贝到用户空间。两阶段分开阻塞

为了提高性能,操作系统引入了一种新的系统调用,专门用于查询IO文件描述符(含socket连接)的就绪状态。在Linux系统中,新的系统调用为select/epoll系统调用。通过该系统调用,一个用户进程(或者线程)可以监视多个文件描述符,一旦某个描述符就绪(一般是内核缓冲区可读/可写),内核就能够将文件描述符的就绪状态返回给用户进程(或者线程),用户空间可以根据文件描述符的就绪状态进行相应的IO系统调用。

I/O复用模型会用到select或poll函数,在I/O复用模型中,并不是阻塞到I/O操作过程中,而是阻塞到select或者poll函数中; 以select为例:进程在select处阻塞,等待几个描述符中的一个变为可操作,如果没等待到就继续阻塞在第一阶段,如果等到了一个描述符变为了可操作,则调用recvfrom函数将数据拷贝到应用缓冲区。

IO多路复用模型的特点是:IO多路复用模型的IO涉及两种系统调用,一种是IO操作的系统调用,另一种是select/epoll就绪查询系统调用。IO多路复用模型建立在操作系统的基础设施之上,即操作系统的内核必须能够提供多路分离的系统调用select/epoll。

IO多路复用模型的优点是一个选择器查询线程可以同时处理成千上万的网络连接,所以用户程序不必创建大量的线程,也不必维护这些线程,从而大大减少了系统的开销。与一个线程维护一个连接的阻塞IO模式相比,这一点是IO多路复用模型的最大优势。

IO多路复用模型的缺点是,本质上select/epoll系统调用是阻塞式的,属于同步IO,需要在读写事件就绪后由系统调用本身负责读写,也就是说这个读写过程是阻塞的。要彻底地解除线程的阻塞,就必须使用异步IO模型。

4.信号驱动IO(SIGIO)

在这里插入图片描述

进程发起读取操作会立即返回,当数据准备好了会以通知的形式告诉进程,进程再发起读取操作,把数据从内核空间拷贝到用户空间。第一阶段不阻塞,第二阶段阻塞

5.异步IO(Asynchronous IO,AIO)

在这里插入图片描述

进程发起读取操作会立即返回,等到数据准备好且已经拷贝到用户空间了再通知进程拿数据。两个阶段都不阻塞

6.五种IO模型的对比

在这里插入图片描述

同步非同步的区别在于调用操作系统的recvfrom()的时候是否阻塞,可见除了最后的异步IO其它都是同步IO。

7.阻塞与非阻塞

阻塞IO指的是需要内核IO操作彻底完成后才返回到用户空间执行用户程序的操作指令。“阻塞”指的是用户程序(发起IO请求的进程或者线程)的执行状态。可以说传统的IO模型都是阻塞IO模型,并且在Java中默认创建的socket都属于阻塞IO模型。

8.同步与异步

简单来说,可以将同步与异步看成发起IO请求的两种方式。同步IO是指用户空间(进程或者线程)是主动发起IO请求的一方,系统内核是被动接收方。异步IO则反过来,系统内核是主动发起IO请求的一方,用户空间是被动接收方。

二、IO基础

1.IO读写的基本原理

为了避免用户进程直接操作内核,保证内核安全,操作系统将内存(虚拟内存)划分为两部分:一部分是内核空间(Kernel-Space),另一部分是用户空间(User-Space)。在Linux系统中,内核模块运行在内核空间,对应的进程处于内核态用户程序运行在用户空间,对应的进程处于用户态

操作系统的核心是内核程序,它独立于普通的应用程序,既有权限访问受保护的内核空间,也有权限访问硬件设备,而普通的应用程序并没有这样的权限。内核空间总是驻留在内存中,是为操作系统的内核保留的。应用程序不允许直接在内核空间区域进行读写,也不允许直接调用内核代码定义的函数。每个应用程序进程都有一个单独的用户空间,对应的进程处于用户态,用户态进程不能访问内核空间中的数据,也不能直接调用内核函数,因此需要将进程切换到内核态才能进行系统调用。

内核态进程可以执行任意命令,调用系统的一切资源,而用户态进程只能执行简单的运算,不能直接调用系统资源,那么问题来了:用户态进程如何执行系统调用呢?答案是:用户态进程必须通过系统调用(System Call)向内核发出指令,完成调用系统资源之类的操作。

用户程序进行IO的读写依赖于底层的IO读写,基本上会用到底层的read和write两大系统调用。虽然在不同的操作系统中read和write两大系统调用的名称和形式可能不完全一样,但是它们的基本功能是一样的。

操作系统层面的read系统调用并不是直接从物理设备把数据读取到应用的内存中,write系统调用也不是直接把数据写入物理设备。上层应用无论是调用操作系统的read还是调用操作系统的write,都会涉及缓冲区。 具体来说,上层应用通过操作系统的read系统调用把数据从内核缓冲区复制到应用程序的进程缓冲区,通过操作系统的write系统调用把数据从应用程序的进程缓冲区复制到操作系统的内核缓冲区

简单来说,应用程序的IO操作实际上不是物理设备级别的读写,而是缓存的复制。read和write两大系统调用都不负责数据在内核缓冲区和物理设备(如磁盘、网卡等)之间的交换。这个底层的读写交换操作是由操作系统内核(Kernel)来完成的。所以,在应用程序中,无论是对socket的IO操作还是对文件的IO操作,都属于上层应用的开发,它们在输入(Input)和输出(Output)维度上的执行流程是类似的,都是在内核缓冲区和进程缓冲区之间进行数据交换。

2.内核缓冲区与进程缓冲区

为什么设置那么多的缓冲区,导致读写过程那么麻烦呢?

缓冲区的目的是减少与设备之间的频繁物理交换。计算机的外部物理设备与内存和CPU相比,有着非常大的差距,外部设备的直接读写涉及操作系统的中断。发生系统中断时,需要保存之前的进程数据和状态等信息,结束中断之后,还需要恢复之前的进程数据和状态等信息。为了减少底层系统的频繁中断所导致的时间损耗、性能损耗,出现了内核缓冲区。

操作系统会对内核缓冲区进行监控,等待缓冲区达到一定数量的时候,再进行IO设备的中断处理,集中执行物理设备的实际IO操作,通过这种机制来提升系统的性能。至于具体什么时候执行系统中断(包括读中断、写中断)则由操作系统的内核来决定,应用程序不需要关心。

上层应用使用read系统调用时,仅仅把数据从内核缓冲区复制到应用的缓冲区(进程缓冲区);上层应用使用write系统调用时,仅仅把数据从应用的缓冲区复制到内核缓冲区。

内核缓冲区与应用缓冲区在数量上也不同。在Linux系统中,操作系统内核只有一个内核缓冲区。每个用户程序(进程)都有自己独立的缓冲区,叫作用户缓冲区或者进程缓冲区。在大多数情况下,Linux系统中用户程序的IO读写程序并没有进行实际的IO操作,而是在用户缓冲区和内核缓冲区之间直接进行数据的交换。

三、Reactor模式简介

Reactor模式由Reactor线程、Handlers处理器两大角色组成,两大角色的职责分别如下:

  • Reactor线程的职责:负责响应IO事件,并且分发到Handlers处理器。
  • Handlers处理器的职责:非阻塞的执行业务处理逻辑。
1.OIO模式

在Java的OIO编程中,原始的网络服务器程序一般使用一个while循环不断地监听端口是否有新的连接。如果有,就调用一个处理函数来完成传输处理。

如果前一个网络连接的handle(socket)没有处理完,那么后面的新连接无法被服务端接收,于是后面的请求就会被阻塞,导致服务器的吞吐量太低。为了解决这个严重的连接阻塞问题,出现了一个极为经典的模式:Connection Per Thread(一个线程处理一个连接)模式。

Connection Per Thread模式的缺点是对应于大量的连接,需要耗费大量的线程资源,对线程资源要求太高。
而且,线程的反复创建、销毁、切换也需要代价。因此,在高并发的应用场景下,多线程OIO的缺陷是致命的。

2.单线程Reactor模式

在Reactor模式中有Reactor和Handler两个重要的组件:
(1)Reactor:负责查询IO事件,当检测到一个IO事件时将其发送给相应的Handler处理器去处理。这里的IO事件就是NIO中选择器查询出来的通道IO事件。
(2)Handler:与IO事件(或者选择键)绑定,负责IO事件的处理,完成真正的连接建立、通道的读取、处理业务逻辑、负责将结果写到通道等。

Reactor和Handlers处于一个线程中执行。

在这里插入图片描述

3.多线程Reactor模式

多线程Reactor的演进分为两个方面:
(1)升级Handler。既要使用多线程,又要尽可能高效率,则可以考虑使用线程池。具体操作是将负责数据传输处理的IOHandler处理器的执行放入独立的线程池中。这样,业务处理线程与负责新连接监听的反应器线程就能相互隔离,避免服务器的连接监听受到阻塞。
(2)升级Reactor。可以考虑引入多个Selector(选择器),提升选择大量通道的能力。具体操作是将反应器线程拆分为多个子反应器(SubReactor)线程;同时,引入多个选择器,并且为每一个SubReactor引入一个线程,一个线程负责一个选择器的事件轮询。

在这里插入图片描述

四、Netty

1.Netty的Reactor模式

Reactor模式中IO事件的处理流程:

在这里插入图片描述

Reactor模式中IO事件的处理流程大致分为4步,具体如下:
第1步:通道注册。IO事件源于通道(Channel),IO是和通道(对应于底层连接而言)强相关的。一个IO事件一定属于某个通道。如果要查询通道的事件,首先就要将通道注册到选择器。
第2步:查询事件。在Reactor模式中,一个线程会负责一个反应器(或者SubReactor子反应器),不断地轮询,查询选择器中的IO事件(选择键)。
第3步:事件分发。如果查询到IO事件,则分发给与IO事件有绑定关系的Handler业务处理器。
第4步:完成真正的IO操作和业务处理,这一步由Handler业务处理器负责。

在这里插入图片描述

2.server端工作原理

在这里插入图片描述

server端启动时绑定本地某个端口,将自己NioServerSocketChannel注册到某个boss NioEventLoop的selector上。
server端包含1个boss NioEventLoopGroup和1个worker NioEventLoopGroup,

NioEventLoopGroup相当于1个事件循环组,这个组里包含多个事件循环NioEventLoop,
每个NioEventLoop包含1个selector和1个事件循环线程。

每个boss NioEventLoop循环执行的任务包含3步:

  • 第1步:轮询accept事件
  • 第2步:处理io任务,即accept事件,与client建立连接,生成NioSocketChannel,并将NioSocketChannel注册到某个worker NioEventLoop的selector上
  • 第3步:处理任务队列中的任务,runAllTasks。任务队列中的任务包括用户调用eventloop.execute或schedule执行的任务,或者其它线程提交到该eventloop的任务

每个worker NioEventLoop循环执行的任务包含3步:

  • 第1步:轮询read、write事件
  • 第2步:处理io任务,即read、write事件,在NioSocketChannel可读、可写事件发生时进行处理
  • 第3步:处理任务队列中的任务,runAllTasks

服务端创建示例代码:

//1.创建一个服务端的引导类
ServerBootstrap bootstrap = new ServerBootstrap();//2.创建反应器事件轮询组
//boss轮询组(负责处理父通道:连接/接收监听(如NioServerSocketChannel))
EventLoopGroup bossGroup = new NioEventLoopGroup(2);//worker轮询组(负责处理子通道:读/写监听(如NioSocketChannel))
//线程数可以不配置,默认为cpu核心数的2倍
EventLoopGroup workerGroup = new NioEventLoopGroup(4);//3.设置父子轮询组
bootstrap.group(bossGroup, workerGroup);//如果不需要分开监听新连接事件和输出事件,就不一定非得配置两个轮询组,可以仅配置一个EventLoopGroup反应器轮询组。
//在这种模式下,新连接监听IO事件和数据传输IO事件可能被挤在了同一个线程中处理。
//b.group(workerGroup);//4.设置传输通道类型,Netty不仅支持Java NIO,也支持阻塞式的OIO
bootstrap.channel(NioServerSocketChannel.class);//5.设置监听端口
bootstrap.localAddress(new InetSocketAddress(8000));//6.设置通道参数
//option方法的作用是给父通道(Parent Channel)设置一些与传输协议相关的选项。
//如果要给子通道(Child Channel)设置一些通道选项,则需要调用childOption()设置方法。
//该选项表示是否开启TCP底层心跳机制,true为开启,false为关闭
bootstrap.option(ChannelOption.SO_KEEPALIVE, true);
bootstrap.option(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT);//7.装配子通道的Pipeline流水线
//每一个通道都用一条ChannelPipeline流水线,它的内部有一个双向的链表。
//装配流水线的方式是:将业务处理器ChannelHandler实例包装之后加入双向链表中。
//ChannelInitializer处理器有一个泛型参数SocketChannel,它代表需要初始化的通道类型,
//这个类型需要和前面的引导类(bootstrap.channel)中设置的传输通道类型一一对应起来。
bootstrap.childHandler(new ChannelInitializer<SocketChannel>() {   //有连接到达时,会创建一个通道的子通道,并初始化    @Override    protected void initChannel(SocketChannel ch) throws Exception {//这里可以管理子通道中的Handler业务处理器       //向子通道流水线添加一个Handler业务处理器        //ch.pipeline().addLast(new MyHandler());    }
});//父通道(NioServerSocketChannel)的内部业务处理是固定的:接收新连接后,创建子通道,然后初始化子通道,
//所以不需要特别的配置,由Netty自行进行装配。如果需要完成特殊的父通道业务处理,
//可以类似地调用ServerBootstrap的handler(ChannelHandler handler)方法,为父通道设置ChannelInitializer初始化器。//8.开始绑定端口,并通过调用sync()同步方法阻塞直到绑定成功
ChannelFuture channelFuture = bootstrap.bind().sync();//在Netty中,所有的IO操作都是异步执行的
//Netty中的IO操作都会返回异步任务实例(如channelFuture实例)。
//通过该异步任务实例,既可以实现同步阻塞一直到channelFuture异步任务执行完成,
//也可以通过为其增加事件监听器的方式注册异步回调逻辑,以获得Netty中的IO操作的真正结果
//bootstrap.bind().addListener( future -> {    //..
//});
SocketAddress address = channelFuture.channel().localAddress();
System.out.println("服务器启动成功,监听端口:"+address);//9.自我阻塞,直到监听通道关闭
ChannelFuture closeFuture = channelFuture.channel().closeFuture();
closeFuture.sync();
System.out.println("监听关闭");//10.释放所有资源,包括创建的反应器线程
//关闭反应器轮询组,同时会关闭内部的子反应器线程,
//也会关闭内部的选择器、内部的轮询线程以及负责查询的所有子通道。
//在子通道关闭后,会释放掉底层的资源,如Socket文件描述符等。
workerGroup.shutdownGracefully();
bossGroup.shutdownGracefully();

channelOption常见配置项

  • SO_RCVBUF和SO_SNDBUF
    这两个为TCP传输选项,每个TCP socket(套接字)在内核中都有一个发送缓冲区和一个接收缓冲区,这两个选项就是用来设置TCP连接的两个缓冲区大小的。
  • TCP_NODELAY
    此为TCP传输选项,如果设置为true就表示立即发送数据。TCP_NODELAY用于开启或关闭Nagle算法。
    如果要求高实时性,有数据发送时就马上发送,就将该选项设置为true(关闭Nagle算法);
    如果要减少发送次数、减少网络交互,就设置为false(开启Nagle算法),等累积一定大小的数据后再发送。
    关于TCP_NODELAY的值,Netty默认为true,而操作系统默认为false。
  • SO_KEEPALIVE
    此为TCP传输选项,表示是否开启TCP的心跳机制。true为连接保持心跳,默认值为false。
    启用该功能时,TCP会主动探测空闲连接的有效性。
    需要注意的是:默认的心跳间隔是7200秒,即2小时。Netty默认关闭该功能。
  • SO_REUSEADDR
    此为TCP传输选项,为true时表示地址复用,默认值为false。
  • SO_LINGER
    此为TCP传输选项,可以用来控制socket.close()方法被调用后的行为,包括延迟关闭时间。
  • SO_BACKLOG
    此为TCP传输选项,表示服务端接收连接的队列长度,如果队列已满,客户端连接将被拒绝。
  • SO_BROADCAST
    此为TCP传输选项,表示设置为广播模式。

流水线配置

public class SimpleOutChannelInitializer extends ChannelInitializer<SocketChannel> {   @Override    protected void initChannel(EmbeddedChannel ch) throws Exception { ch.pipeline() //流水线,注意添加顺序.addLast(new SimpleInHandlerA()) //添加入站处理器.addLast(new SimpleInHandlerB()) //添加入站处理器.addLast(new SimpleInHandlerC()) //添加入站处理器.addLast(new SimpleOutHandlerA()) //添加出站处理器.addLast(new SimpleOutHandlerB()) //添加出站处理器.addLast(new SimpleOutHandlerC()); //添加出站处理器}
}

一个简单的入站处理器:

@Slf4j
public class SimpleInHandlerA extends ChannelInboundHandlerAdapter {    @Override    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {      log.info("入站处理器A,读取到数据: {}", msg);        Object o = msg+"-handlerA";        super.channelRead(ctx, o);   //注释后可截断流水线}
}

出站处理器执行顺序:

在这里插入图片描述

一个简单的出站处理器:

@Slf4j
public class SimpleOutHandlerA extends ChannelOutboundHandlerAdapter {@Overridepublic void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {log.info("出站处理器A,写数据.");super.write(ctx, msg, promise);}
}

出站处理器执行顺序:

在这里插入图片描述

解码/编码器:
现实情况下,所谓的入站和出站处理器一般都是用于数据的解码和编码,
所以Netty提供了一些常见的解码和编码器,也可自定义自己的解码/编码器
下面定义一个简单的解码器用于解码出int类型的数字:

@Slf4j
public class Byte2IntegerDecoder extends ByteToMessageDecoder {    @Override   protected void decode(ChannelHandlerContext ctx,ByteBuf in, List<Object> out) throws Exception {   while (in.readableBytes() >= 4){ int i = in.readInt();           log.info("解码出一个整数:{}", i);           out.add(i);       }   }}
3.client端工作原理

在这里插入图片描述

client端启动时connect到server,建立NioSocketChannel,并注册到某个NioEventLoop的selector上,client端只包含1个NioEventLoopGroup。

每个NioEventLoop循环执行的任务包含3步:

  • 第1步:轮询connect、read、write事件
  • 第2步:处理io任务,即connect、read、write事件,在NioSocketChannel连接建立、可读、可写事件发生时进行处理
  • 第3步:处理非io任务,runAllTasks

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.luyixian.cn/news_show_225679.aspx

如若内容造成侵权/违法违规/事实不符,请联系dt猫网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Android8.1 MTK 浏览器下载的apk点击无反应不能安装

最近测试人员发现用原生浏览器下载的apk点击安装时无反应&#xff0c;不能安装。 在/vendor/mediatek/proprietary/packages/apps/Browser/src/com/android/browser/DownloadHandler.java 中&#xff0c;发现下载的apk文件缺少了mime类型&#xff0c;如下图 mimetype null造…

RS编码译码误码率性能matlab仿真

目录 1.算法描述 2.仿真效果预览 3.MATLAB部分代码预览 4.完整MATLAB程序 1.算法描述 纠错编码技术在卫星通信、移动通信及数字存储等领域已获得了广泛的应用。RS码作为其中最重要的码类之一,具有优良的纠随机错误和突发错误的能力,被空间数据系统咨询委员会(CCSDS)作为一种…

计算机毕业设计——基于SpringBoot框架的网上购书系统的设计与实现

文章目录前言一、背景及意义选题背景选题目的二、系统设计主要功能运行环境三、系统实现部分页面截图展示部分代码展示四、源码获取前言 提示&#xff1a;这里可以添加本文要记录的大概内容&#xff1a; 二十一世纪是网络化&#xff0c;信息化的时代&#xff0c;为了满足广大…

植入“人工心脏”助患者重获“心”生

【同期】人工心脏移植患者 刘女士这要是在过去的时候也就放弃了&#xff0c;我再活20年&#xff0c;我还能看着我大孙子成家&#xff0c;这就是我最大的希望。【解说】11月22日&#xff0c;人工心脏移植患者和心脏移植患者在即将康复出院前&#xff0c;互相握手庆贺。据了解&am…

18.3 内存池概念、代码实现和详细分析

一&#xff1a;内存池的概念和实现原理概述 malloc&#xff1a;内存浪费&#xff0c;频繁分配小块内存&#xff0c;浪费更加明显。 “内存池”要解决什么问题&#xff1f; 1、减少malloc()的次数&#xff0c;减少malloc()调用次数就意味着减少对内存的浪费 2、减少malloc()的…

Wireshark Ethernet and ARP 实验—Wireshark Lab: Ethernet and ARP v7.0

Wireshark Lab: Ethernet and ARP v7.0 1. Capturing and analyzing Ethernet frames 清除浏览器缓存 使用wireshark抓包并请求网页 修改“捕获数据包列表”窗口&#xff0c;仅显示有关 IP 以下协议的信息。 抓包干扰较多&#xff0c;故分析作者的数据包回答下列问题 包含…

关于WEB端实现电子海图之Openlayers加载切片

记笔记&#xff0c;免忘记&#xff01; 关于WEB端实现电子海图研究之思路 关于WEB端实现电子海图研究二GeoServer GeoServer完成shp文件切矢量图后&#xff0c;我们需要加载GeoServer切片在web上展示。 vector-tiles-tutorial官方示例 以下示例使用openLayers来加载 D:\s…

Django Cookie 与 Session 对比

文章目录原理比较语法比较Cookie 示例创建 Cookie更新 Cookie删除 CookieSession 示例创建 session查询 session删除一组键值对删除 session参考文档本文通过示例演示 Django 中如何创建、查询、删除 Cookie 与 Session。 原理比较 在Web开发中&#xff0c;使用 session 来完成…

Docker-CentOS开启防火墙firewalled映射Docker端口

开启docker的Tomcat容器后&#xff0c;启动 docker run -d -p 8080:8080 tomcat 访问不了Tomcat 查看防火墙所有开放的端口 firewall-cmd --zonepublic --list-ports 一、需要防火墙开启8080 端口 1、通过systemctl status firewalld查看firewalld状态&#xff0c;发现当前…

流媒体传输 - RTSP 协议

概述 协议简介 RTSP RTSP (Real-Time Stream Protocol) 实时流传输协议是一种基于文本的应用层协议&#xff0c;常被用于 建立的控制媒体流的传输&#xff0c;该协议用于 C/S 模型 , 是一个 基于文本 的协议&#xff0c;用于在客户端和服务器端建立和协商实时流会话。 RTP …

qt 实现PDF阅读器

1、前言 查阅资料可发现&#xff0c;网上的几种pdf阅读器的实现&#xff0c;几乎大多依赖的第三方库&#xff0c;这里就不做介绍了。 qt 自带的关于pdf的显示&#xff1a;pdfwriter、printpreviewwidget&#xff0c;分别为创建pdf 和 打印预览pdf。由此&#xff0c;后者似乎满足…

你一定要知道的四个程序员接外包的网站,悄悄把技术变现!

说起程序员接外包的网站&#xff0c;你在网上一搜发现数不胜数&#xff0c;但真正有用的却很少。然后你想快速的找到几个靠谱的网站&#xff0c;去看了看接外包的攻略&#xff0c;你会发现排雷的又数不胜数。一时间你还真不知道要选哪一个。 接下来就为大家推荐几个我认为比较…

基于springboot的汽车租赁管理系统的设计与实现

项目描述 临近学期结束&#xff0c;还是毕业设计&#xff0c;你还在做java程序网络编程&#xff0c;期末作业&#xff0c;老师的作业要求觉得大了吗?不知道毕业设计该怎么办?网页功能的数量是否太多?没有合适的类型或系统?等等。这里根据疫情当下&#xff0c;你想解决的问…

【RuoYi-Vue-Plus】学习笔记 44 - XSS 过滤器以及 @Xss 注解简单分析

文章目录前言参考目录关于 XSS 攻击框架集成配置说明测试方法一&#xff1a;通过过滤器测试方法二&#xff1a;通过 Xss 注解功能调用流程分析XSS 过滤器启动初始化Form 表单请求过滤JSON 对象请求过滤Xss 注解校验前言 之前在对接口进行传参时发现富文本包含的标签全部被过滤…

linux NPS 服务端安装 +linux 客户端安装

本文参考博客&#xff1a; https://blog.csdn.net/m0_57776598/article/details/123674866 一、下载安装包 1、官方下载 官方下载地址&#xff1a;https://github.com/ehang-io/nps/releases 注意不要下错了&#xff0c;当前我下的版本为 v0.26.10 2、网盘下载 网盘下载&…

干货 | 如何获取Servlet内存马?

前言 对于回显的获取主要是在ApplicationFilterChain类的lastServicedRequest / lastServicedResponse两个属性&#xff0c;是使用的ThreadLocal进行修饰的&#xff0c;并且&#xff0c;在执行请求的过程中&#xff0c;通过反射修改属性值&#xff0c;能够记录下当前线程的req…

K8S部署后的使用:dashboard启动、使用+docker镜像拉取、容器部署

1、重置和清除旧工程&#xff1a;每个节点主机都要运行 kubeadm reset iptables -F && iptables -t nat -F && iptables -t mangle -F && iptables -X 2、部署新的k8s项目&#xff1a; 只在主节点运行&#xff0c;apiserver-advertise-address填写主…

LiveData源码分析

先放整理流程图&#xff1a; 1.postValue调2次只触发1次&#xff1f; postValue本质是把新值保存到LiveData的mPendingData成员变量里&#xff0c;版本号1&#xff0c;把执行Runnable post到主线程&#xff0c;在主线程setValue。 多次调用会更新mPendingData的值&#xff0c…

域名服务:域名迁移

摘要&#xff1a;这里记录一下西部数据域名的迁移步骤&#xff0c;迁移的域名为从西部数据一个账号迁移至另一个西数数据账号。域名的实体认证为同一主体&#xff0c;迁移后无需重新备案。此处为一家公立医院。 相关文章 域名服务&#xff1a;西部数据域名迁移阿里云Symantec免…

【Spring框架】一文带你吃透基于注解的DI技术详细教程

本文目录 文章目录本文目录&#x1f496;基于注解的DI✨概念✨[Component](https://so.csdn.net/so/search?qComponent&spm1001.2101.3001.7020)注解创建对象✨声明组件扫描器✨创建对象的四个注解✨扫描多个包的三种方式✨Value简单类型属性赋值✨Value使用外部属性配置文…