深圳博纳网站建设,ssh框架可以做网站么,替别人做网站,上海最专业的集团网站建设如何实现高性能的异步网络传输#xff1f;
**异步与同步模型最大的区别是#xff0c;同步模型会阻塞线程等待资源#xff0c;而异步模型不会阻塞线程#xff0c;它是等资源准备好后#xff0c;再通知业务代码来完成后续的资源处理逻辑。**这种异步设计的方法#xff0c;…如何实现高性能的异步网络传输
**异步与同步模型最大的区别是同步模型会阻塞线程等待资源而异步模型不会阻塞线程它是等资源准备好后再通知业务代码来完成后续的资源处理逻辑。**这种异步设计的方法可以很好地解决 IO 等待的问题。我们开发的绝大多数业务系统都是 IO 密集型系统。 跟 IO 密集型系统相对的另一种系统叫计算密集型系统。IO 密集型系统大部分时间都在执行 IO 操作这个 IO 操作主要包括网络 IO 和磁盘 IO以及与计算机连接的一些外围设备的访问。与之相对的计算密集型系统大部分时间都是在使用 CPU 执行计算操作。我们开发的业务系统很少有非常耗时的计算更多的是网络收发数据读写磁盘和数据库这些 IO 操作。这样的系统基本上都是 IO 密集型系统特别适合使用异步的设计来提升系统性能。 应用程序最常使用的 IO 资源主要包括磁盘 IO 和网络 IO。 由于现在的 SSD 的速度越来越快对于本地磁盘的读写异步的意义越来越小。所以使用异步设计的方法来提升 IO 性能我们更加需要关注的问题是如何来实现高性能的异步网络传输。
理想的异步网络框架 Netty
大部分语言提供的网络通信基础类库都是同步的。 一个 TCP 连接建立后用户代码会获得一个用于收发数据的通道每个通道会在内存中开辟两片区域用于收发数据的缓存。发送数据的过程比较简单我们直接往这个通道里面写入数据就可以了。 用户代码在发送时写入的数据会暂存在缓存中然后操作系统会通过网卡把发送缓存中的数据传输到对端的服务器上。只要这个缓存不满或者说我们发送数据的速度没有超过网卡传输速度的上限那这个发送数据的操作耗时只不过是一次内存写入的时间这个时间是非常快的。所以发送数据的时候同步发送就可以了没有必要异步。 对于数据的接收方来说它并不知道什么时候会收到数据。 那我们能直接想到的方法就是用一个线程阻塞在那⼉等着数据当有数据到来的时候操作系统会先把数据写⼊接收缓存然后给接收数据的线程发一个通知线程收到通知后结束等待开始读取数据。处理完这一批数据后继续阻塞等待下一批数据到来这样周而复始地处理收到的数据。 同步网络 IO 的模型BIO 同步网络 IO 模型在处理少量连接的时候是没有问题的。但是如果要同时处理非常多的连接同步的网络 IO 模型就有点力不从心了。因为每个连接都需要阻塞一个线程来等待数据大量的连接数就会需要相同数量的数据接收线程。当这些 TCP 连接都在进行数据收发的时候会有大量的线程来抢占 CPU 时间造成频繁的 CPU 上下文切换导致 CPU 的负载升高整个系统的性能就会比较慢。 对于业务开发者来说一个好的异步网络框架它的 API 应该是什么样的呢 我们希望达到的效果无非就是只用少量的线程就能处理大量的连接有数据到来的时候能第一时间处理就可以了。对于开发者来说最简单的方式就是事先定义好收到数据后的处理逻辑把这个处理逻辑作为一个回调方法在连接建立前就通过框架提供的 API 设置好。当收到数据的时候由框架自动来执行这个回调方法就好了。 接下来我们看一下如何使用 Netty 实现异步接收数据。// 创建⼀组线性
EventLoopGroup group new NioEventLoopGroup();try{// 初始化 ServerServerBootstrap serverBootstrap new ServerBootstrap();serverBootstrap.group(group);serverBootstrap.channel(NioServerSocketChannel.class);serverBootstrap.localAddress(new InetSocketAddress(localhost, 9999));// 设置收到数据后的处理的 HandlerserverBootstrap.childHandler(new ChannelInitializerSocketChannel() {protected void initChannel(SocketChannel socketChannel) throws ExceptionsocketChannel.pipeline().addLast(new MyHandler());}});// 绑定端⼝开始提供服务ChannelFuture channelFuture serverBootstrap.bind().sync();channelFuture.channel().closeFuture().sync();
} catch(Exception e){e.printStackTrace();
} finally {group.shutdownGracefully().sync();
}首先我们创建了一个 EventLoopGroup 对象命名为 group这个 group 对象你可以简单把它理解为一组线程。这组线程的作用就是来执行收发数据的业务逻辑。然后使用 Netty 提供的 ServerBootstrap 来初始化⼀个 Socket Server绑定到本地 9999 端口上。在真正启动服务之前我们给 serverBootstrap 传入了一个 MyHandler 对象这个 MyHandler 是我们自己来实现的一个类它需要继承 Netty 提供的一个抽象类ChannelInboundHandlerAdapter在这个 MyHandler 里面我们可以定义收到数据后的处理逻辑。这个设置 Handler 的过程就是预先来定义回调方法的过程。最后就可以真正绑定本地端口启动 Socket 服务了。 真正需要业务代码来实现的就两个部分 一个是把服务初始化并启动起来。还有就是实现收发消息的业务逻辑 MyHandler。像线程控制、缓存管理、连接管理这些异步网络 IO 中通用的、比较复杂的问题Netty 已经自动帮你处理好了。
使用 NIO 来实现异步网络通信
在 Java 的 NIO 中它提供了一个 Selector 对象来解决一个线程在多个网络连接上的多路复用问题。 在 NIO 中每个已经建立好的连接用一个 Channel 对象来表示。 我们希望能实现在一个线程里接收来自多个 Channel 的数据。 Selecor 通过一种类似于事件的机制来解决这个问题。首先你需要把你的连接也就是 Channel 绑定到 Selector 上然后你可以在接收数据的线程来调用 Selector.select() 方法来等待数据到来。这个 select 方法是一个阻塞方法这个线程会一直卡在这儿直到这些 Channel 中的任意一个有数据到来就会结束等待返回数据。它的返回值是一个迭代器你可以从这个迭代器里面获取所有 Channel 收到的数据然后来执行你的数据接收的业务逻辑。