【Netty系列】Netty/BIO/NIO/AIO


一、Netty介绍

  1. Netty是一个开源的异步的事件驱动的网络应用框架,用于快速开发高性能的网络服务器和客户端。它主要用于构建可伸缩的网络应用程序,如协议服务器和客户端。
  2. Netty的设计目标是提供一个简单、快速、稳定的网络应用程序框架,同时保持灵活性和可扩展性。
  3. Netty基于NIO(非阻塞I/O)的事件驱动模型,使用了高效的线程池和优化的内存分配机制,使得它在处理大量并发连接时表现出色。
  4. Netty广泛用于构建各种类型的网络应用,包括Web服务器、游戏服务器、即时通讯系统等。

 Netty本质是一个NIO框架适用于服务器通讯相关的多种应用场景,要想透彻理解Netty,需要先学习NIO。

二、Java BIO/NIO/AIO

什么是I/O? 通常是指数据在内部存储器和外部存储器或其他周边设备之间的输入和输出。是信息处理系统与外部世界之间的通信。输入是系统接收的信号或数据,输出则是从其发送的信号或数据。I/O模型简单的理解就是用什么样的通道进行数据的发送和接收,很大程度上决定了程序通信的性能。

 Java支持三种网络编程I/O模型:BIONIOAIO

  1. Java BIO(Blocking IO):也称为同步阻塞I/O模型。应用程序发起I/O系统调用,在获得结果之前,应用程序进程会一直阻塞,直到获得到结果。其I/O过程是同步并阻塞的,服务器实现模式为一个连接一个线程,也就是说客户端有连接请求时服务器端就需要启动一个线程进行处理,如果这个连接不做任何事情的话,会造成不必要的线程开销。

在这里插入图片描述
 该模型最大的问题就是缺乏弹性伸缩能力,当客户端并发访问量增加后,服务端的线程个数和客户端并发访问数呈1:1的正比关系,由于线程是JAVA虚拟机非常宝贵的系统资源,当线程数膨胀之后,系统的性能将急剧下降,随着并发访问量的继续增大,系统会发生线程堆栈溢出、创建新线程失败等问题,并最终导致进程宕机或者僵死,不能对外提供服务。
 弊端有很多:① 性能问题:一连接一线程模型导致服务端的并发接入数和系统吞吐量受到极大限制;② 可靠性问题:由于I/O操作采用同步阻塞模式,当网络拥塞或者通信对端处理缓慢会导致I/O线程被挂住,阻塞时间无法预测;③ 可维护性问题:I/O线程数无法有效控制、资源无法有效共享(多线程并发问题),系统可维护性差;

服务端:

import java.io.IOException;
import java.io.InputStream;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
public class BioServer {
    public static void main(String[] args) throws IOException {
        ServerSocket serverSocket = new ServerSocket();
        //绑定端口9090
        serverSocket.bind(new InetSocketAddress(9090));
        //阻塞,接收外界的连接
        Socket socket = serverSocket.accept();
        while (true) {
            InputStream inputStream = socket.getInputStream();
            byte[] bytes = new byte[10];
            //阻塞调用
            inputStream.read(bytes);
            System.out.println("服务端收到的数据是:" + new String(bytes));
        }
    }
}

客户端:

import java.io.IOException;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.net.Socket;
public class BioClient {
    public static void main(String[] args) throws IOException, InterruptedException {
        // 连接bio server
        Socket socket = new Socket();
        socket.connect(new InetSocketAddress(9090));
        OutputStream outputStream = socket.getOutputStream();
        // 发送数据
        while(true){
            outputStream.write("test".getBytes());
            outputStream.flush();
            System.out.println("发送数据");
            Thread.sleep(1000);
        }
    }
}

在这里插入图片描述
客户端发送数据,服务端接收到了数据
在这里插入图片描述

  1. Java NIO: 同步非阻塞,服务器实现模式为一个线程处理多个请求(连接),即客户端发送的连接请求都会注册到多路复用器上,多路复用器轮询到连接有 I/O 请求就进行处理。Java NIO的实现关键是通过多路复用IO技术实现的,多路复用的核心就是通过Selector来轮询注册在其上的Channel,当发现某个或者多个Channel处于就绪状态后,从阻塞状态返回就绪的Channel的选择键集合,进行IO操作。由于多路复用器是NIO实现非阻塞IO的关键,它又是主要通过Selector实现。

在这里插入图片描述

  1. Java AIO: 异步非阻塞,AIO 引入异步通道的概念,采用了 Proactor 模式,简化了程序编写,有效的请求才启动线程,它的特点是先由操作系统完成后才通知服务端程序启动线程去处理,一般适用于连接数较多且连接时间较长的应用。

在这里插入图片描述

  使用场景:

  • BIO 方式适用于连接数目比较小且固定的架构,这种方式对服务器资源要求比较高,并发局限于应用中,JDK1.4 以前的唯一选择,但程序简单易理解。
  • NIO 方式适用于连接数目多且连接比较短(轻操作)的架构,比如聊天服务器,弹幕系统,服务器间通讯等。编程比较复杂,JDK1.4 开始支持。
  • AIO 方式使用于连接数目多且连接比较长(重操作)的架构,比如相册服务器,充分调用 OS 参与并发操作,编程比较复杂,JDK7 开始支持。

参考

  • 21
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
下面是一个简单的 Java Netty 实现的 HTTP/HTTPS 代理服务代码示例: ```java import io.netty.bootstrap.Bootstrap; import io.netty.bootstrap.ServerBootstrap; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.Channel; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelOption; import io.netty.channel.EventLoopGroup; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.SocketChannel; import io.netty.channel.socket.nio.NioSocketChannel; import io.netty.handler.codec.http.DefaultFullHttpResponse; import io.netty.handler.codec.http.DefaultHttpRequest; import io.netty.handler.codec.http.HttpClientCodec; import io.netty.handler.codec.http.HttpContent; import io.netty.handler.codec.http.HttpHeaders; import io.netty.handler.codec.http.HttpMethod; import io.netty.handler.codec.http.HttpObject; import io.netty.handler.codec.http.HttpResponse; import io.netty.handler.codec.http.HttpServerCodec; import io.netty.handler.codec.http.HttpVersion; import io.netty.handler.ssl.SslContext; import io.netty.handler.ssl.SslContextBuilder; import io.netty.handler.ssl.SslHandler; import io.netty.handler.ssl.util.InsecureTrustManagerFactory; import io.netty.util.CharsetUtil; import javax.net.ssl.SSLEngine; import java.net.InetSocketAddress; import java.util.HashMap; import java.util.Map; public class HttpsProxyServer { private final int port; public HttpsProxyServer(int port) { this.port = port; } public void start() throws Exception { EventLoopGroup bossGroup = new NioEventLoopGroup(1); EventLoopGroup workerGroup = new NioEventLoopGroup(); try { ServerBootstrap b = new ServerBootstrap(); b.group(bossGroup, workerGroup) .channel(NioServerSocketChannel.class) .localAddress(new InetSocketAddress(port)) .childHandler(new ChannelInitializer<SocketChannel>() { @Override public void initChannel(SocketChannel ch) throws Exception { ch.pipeline().addLast(new HttpServerCodec()); ch.pipeline().addLast(new HttpsProxyServerHandler()); } }) .childOption(ChannelOption.AUTO_READ, false) .bind().sync().channel().closeFuture().sync(); } finally { bossGroup.shutdownGracefully(); workerGroup.shutdownGracefully(); } } public static void main(String[] args) throws Exception { if (args.length != 1) { System.err.println("Usage: " + HttpsProxyServer.class.getSimpleName() + " <port>"); return; } int port = Integer.parseInt(args[0]); new HttpsProxyServer(port).start(); } private static final class HttpsProxyServerHandler extends SimpleChannelInboundHandler<HttpObject> { private Channel clientChannel; private Channel serverChannel; private SSLEngine sslEngine; private boolean isHttps; private final Map<String, String> headers = new HashMap<>(); @Override public void channelActive(ChannelHandlerContext ctx) throws Exception { clientChannel = ctx.channel(); } @Override public void channelRead0(ChannelHandlerContext ctx, HttpObject msg) throws Exception { if (msg instanceof DefaultHttpRequest) { DefaultHttpRequest request = (DefaultHttpRequest) msg; String requestUri = request.getUri(); isHttps = requestUri.startsWith("https://"); String[] hostParts = HttpHeaders.getHost(request).split(":"); String host = hostParts[0]; int port = isHttps ? 443 : (hostParts.length > 1 ? Integer.parseInt(hostParts[1]) : 80); Bootstrap b = new Bootstrap(); b.group(clientChannel.eventLoop()) .channel(NioSocketChannel.class) .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 5000) .handler(new ChannelInitializer<SocketChannel>() { @Override public void initChannel(SocketChannel ch) throws Exception { if (isHttps) { sslEngine = SslContextBuilder.forClient() .trustManager(InsecureTrustManagerFactory.INSTANCE) .build().newEngine(ch.alloc(), host, port); ch.pipeline().addLast(new SslHandler(sslEngine)); } ch.pipeline().addLast(new HttpClientCodec()); ch.pipeline().addLast(new HttpsProxyServerHandler()); } }) .connect(host, port) .addListener((ChannelFuture future) -> { if (future.isSuccess()) { serverChannel = future.channel(); } else { clientChannel.close(); } }); headers.clear(); for (Map.Entry<String, String> entry : request.headers()) { headers.put(entry.getKey(), entry.getValue()); } headers.remove(HttpHeaders.Names.HOST); b.channel(NioSocketChannel.class) .connect(host, port) .addListener((ChannelFuture future) -> { if (future.isSuccess()) { serverChannel = future.channel(); serverChannel.writeAndFlush(request.retain()); clientChannel.read(); } else { clientChannel.close(); } }); } else if (msg instanceof HttpResponse) { HttpResponse response = (HttpResponse) msg; response.headers().remove(HttpHeaders.Names.TRANSFER_ENCODING); response.headers().set(HttpHeaders.Names.CONNECTION, HttpHeaders.Values.CLOSE); clientChannel.writeAndFlush(response.retain()); } else if (msg instanceof HttpContent) { HttpContent content = (HttpContent) msg; clientChannel.writeAndFlush(content.retain()); if (content instanceof LastHttpContent) { if (isHttps) { sslEngine.closeOutbound(); } clientChannel.flush(); clientChannel.close(); serverChannel.close(); } } } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { if (clientChannel.isActive()) { clientChannel.writeAndFlush(new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.BAD_GATEWAY, Unpooled.copiedBuffer("Failure: " + cause.getMessage(), CharsetUtil.UTF_8))) .addListener(ChannelFutureListener.CLOSE); } } } } ``` 这是一个简单的 HTTP/HTTPS 代理服务,可以接收来自客户端的请求,并将其转发到目标服务器。如果请求是 HTTPS 请求,它还会与目标服务器建立安全连接。 在这个示例中,我们使用了 Netty 的 HTTP/HTTPS 编解码器和 SSL 处理程序来处理请求和响应,并将它们转发到目标服务器。在建立与目标服务器的连接时,我们还使用了 NettyBootstrap 类来创建客户端通道。 当客户端发送请求时,我们从请求中提取目标主机和端口,并使用 Bootstrap 类创建一个新的客户端通道,然后将请求发送到目标服务器。在接收到来自目标服务器的响应时,我们将响应转发给客户端。 如果请求是 HTTPS 请求,我们还需要使用 SSL 处理程序来建立与目标服务器的安全连接。我们使用 Netty 的 SslContextBuilder 类创建一个 SSL 引擎,并将其添加到客户端通道的管道中。在建立与目标服务器的连接时,我们还需要使用 SslHandler 将 SSL 引擎添加到客户端通道的管道中,以便进行 SSL 握手。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值