作者:vivo 互联网服务器团队- Jin KAI
本文从JAVA NIO网络编程的基础知识讲到了Tars框架使用NIO进行网络编程的源码分析 。
一、Tars框架基本介绍
Tars是腾讯开源的支持多语言的高性能RPC框架,起源于腾讯内部2008年至今一直使用的统一应用框架TAF(Total Application Framework),目前支持C++、Java、php、Nodejs、Go语言 。
【Tars-Java网络编程源码分析】该框架为用户提供了涉及到开发、运维、以及测试的一整套解决方案,帮助一个产品或者服务快速开发、部署、测试、上线 。它集可扩展协议编解码、高性能RPC通信框架、名字路由与发现、发布监控、日志统计、配置管理等于一体,通过它可以快速用微服务的方式构建自己的稳定可靠的分布式应用,并实现完整有效的服务治理 。
官方仓库地址:
https://Github.com/TarsCloud/Tars
vivo推送平台也深度使用了该框架,部署服务节点超过一千个,经过线上每日一百多亿消息推送量的考验 。
此前已在vivo互联网技术公众号发布过《 Tars Java 客户端源码分析 》此篇文章为续集 。
Tars-java 最新稳定版1.7.2以及之前的版本都使用Java NIO进行网络编程;本文将分别详细介绍java NIO的原理和Tars 使用NIO进行网络编程的细节 。
二、Java NIO原理介绍
从1.4版本开始,Java提供了一种新的IO处理方式:NIO (New IO 或 Non-blocking IO) 是一个可以替代标准Java IO 的API,它是面向缓冲区而不是字节流,它是非阻塞的,支持IO多路复用 。
2.1 Channels (通道) and Buffers (缓冲区)
标准的IO基于字节流进行操作的,而NIO是基于通道(Channel)和缓冲区(Buffer)进行操作 。数据总是从通道读取到缓冲区中,或者从缓冲区写入到通道中,下图是一个完整流程 。
Channel类型:
- 支持文件读写数据的FileChannel
- 能通过UDP读写网络中的数据的DatagramChannel
- 能通过TCP读写网络数据的SocketChannel
- 可以监听新进来的TCP连接,对每一个新进来的连接都会创建一个SocketChannel的ServerSocketChannel。
- 打开 SocketChannel: SocketChannel socketChannel = SocketChannel.open;
- 关闭 SocketChannel: socketChannel.close;
- 从Channel中读取的数据放到Buffer: int bytesRead = inChannel.read(buf);
- 将Buffer中的数据写到Channel: int bytesWritten = inChannel.write(buf);
通过 ServerSocketChannel.accept 方法监听新进来的连接,当accept方法返回的时候,它返回一个包含新进来的连接的SocketChannel,因此accept方法会一直阻塞到有新连接到达 。
通常不会仅仅只监听一个连接,在while循环中调用 accept方法. 如下面的例子:
代码1:
while( true){ SocketChannel socketChannel = serverSocketChannel.accept; //do something with socketChannel... }ServerSocketChannel可以设置成非阻塞模式 。在非阻塞模式下,accept 方法会立刻返回,如果还没有新进来的连接,返回的将是null 。因此,需要检查返回的SocketChannel是否是null 。
代码2:
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open; serverSocketChannel.socket.bind( new.NETSocketAddress( 8888)); serverSocketChannel.configureBlocking( false); while( true){ SocketChannel socketChannel = serverSocketChannel.accept; if(socketChannel != null){ //do something with socketChannel... } }Buffer类型:
- ByteBuffer
- CharBuffer
- DoubleBuffer
- FloatBuffer
- IntBuffer
- LongBuffer
- ShortBuffer
ByteBuffer buf = ByteBuffer.allocate(2048);
Buffer的读写:
一般是以下四个步骤:
- 写入数据到Buffer,最大写入量是capacity,写模式下limit值即为capacity值,position即为写到的位置 。
- 调用flip方法将Buffer从写模式切换到读模式,此时position移动到开始位置0,limit移动到position的位置 。
- 从Buffer中读取数据,在读模式下可以读取之前写入到buffer的所有数据,即为limit位置 。
- 调用clear方法或者compact方法 。clear方法将position设为0,limit被设置成capacity的值 。compact方法将所有未读的数据拷贝到Buffer起始处,然后将position设到最后一个未读元素后面 。
文章插图
mark 与 reset方法
通过调用Buffer.mark方法,可以标记Buffer中的一个特定position,之后可以通过调用Buffer.reset方法恢复到这个position 。
duplicate
此方法返回承载先前字节缓冲区内容的新字节缓冲区 。
推荐阅读
- 长线牛股指标源码 长线牛股
- MySQL 驱动中虚引用 GC 耗时优化与源码分析
- 带你读 MySQL 源码:Where 条件怎么过滤记录?
- 谷歌开源 Rust Crate 审查结果:便于 Rust 开发者验证源码安全
- Spring/SpringBoot中的声明式事务和编程式事务源码、区别、优缺点、适用场景、实战
- 从Java源码来看Native命令执行方法
- 带你读 MySQL 源码:Select *
- 深扒RocketMQ源码之后,我找出了RocketMQ消息重复消费的7种原因
- 一文看懂Java中的ThreadLocal源码和注意事项
- 一文看懂Redisson分布式锁的Watchdog机制源码实现