Netty是什么?
- Netty是一个高性能、一部驱动的NIO框架,同时也是基于jAVA NIO实现的;
- Netty作为异步NIO框架,可以提供对TCP、UDP和文件传输的支持;
- 基于Netty的的异步机制-Future-Listener,用户可以主动获取/消息通知 方式,来获取IO操作的结果
Netty的高性能?怎么个搞法?
在IO编程过程中,当需要同事处理多个客户端的接入需求时,可以利用多线程/IO多路复用来处理; IO多路复用可以把多个请求阻塞复用到一个selector上,这样一个单线程selector就可以处理多个客户需求,降低开销,系统不需要每个需求都去创建线程,节省资源;
进入netty正题
- 多路复用的通信方式
1.1 服务端通信序列图 1.2 客户端通信序列图
Netty的IO线程NioEventLoop由于聚合了多路复用器selector,可以并发处理成百上千的客户端channel,且读操作书非阻塞的,效率更高;
- netty的异步通讯NIO
由于Netty采用了异步通信模式,一个IO线程可以并发处理多个客户端线程(读/写),从根本上解决传统的阻塞IO模IO链接和线程1VS1的问题,架构性能、弹性伸缩能力和可靠性都有很大提升;
-
提前记录下两个点:直接内存,堆内存? NIO中缓冲区是数据传输的基础,Netty基于JDK原生的ByteBuffer构造了ByteBuf,并进行了大量的优化。
- 堆内存: 堆内存是由JVM管理的,相对于方法区/栈,对象的实例,数组等分配都存在堆上,GC要地;
- 直接内存: JVM可以使用native方法在堆外分配内存,之后使用DircetByterBuffer对象作为这块内存的引用进行操作,这样不会对JVM的堆造成影响;只有在JVM-GC的时候才会去顺便清理下直接内存的废弃对象;
-
关键点 2.1 零拷贝 -
Netty的发送嗯哼接收ByteBuffer采用Direct Buffers,使用堆外直接内存进行Socket的读写,不需要进行字节缓冲区的二次拷贝。传统的方式会先存入堆内存Heap Buffers进行Socket读写,JVM会将堆内存Buffer拷贝一份到直接内存中,然后进行Socket的读写,会多拷贝一次; -
Netty提供了组合Buffer对象,可以聚合多个buffer对象,可以像操作一个buffer对象一样操作一组buffer对象;这样避免了传统操作中先将多个buffer对象合并的操作 -
Netty采用了TransferTo方法,它可以直接将文件缓冲区数据发送到目标的Channel,避免了传统的方式:通过循环write方法导致的内存拷贝问题;
2.2 内存池.
- netty的内存池是不依赖于JVM的,基于内存池的缓冲区重用机制,【没找到相关资料】
2.3 高效的reactor线程模型
-
reactor单线程模型 所有的IO操作都在同一个NIO线程(异步非阻塞)上完成:Acceptor 接收客户端的TCP 连接请求消息,链路建立成功之后,通过Dispatch 将对应的ByteBuffer 派发到指定的Handler 上进行消息解码。用户Handler 可以通过NIO 线程将消息发送给客户端 -
reactor多线程模型 所有的IO操作都是由一组NIO线程池来完成的:有专门一个NIO 线程-Acceptor 线程用于监听服务端,接收客户端的TCP 连接请求; 网络IO 操作-读、写等由一个NIO 线程池负责,线程池可以采用标准的JDK 线程池实现,它包含一个任务队列和N个可用的线程,由这些NIO 线程负责消息的读取、解码、编码和发送 -
reactor主从多线程模型 服务端用于接收客户端连接的不再是个1 个单独的NIO 线程,而是一个独立的NIO 线程池。 Acceptor 接收到客户端TCP 连接请求处理完成后(可能包含接入认证等),将新创建的 SocketChannel 注册到IO 线程池(sub reactor 线程池)的某个IO 线程上,由它负责 SocketChannel 的读写和编解码工作。Acceptor 线程池仅仅只用于客户端的登陆、握手和安全 认证,一旦链路建立成功,就将链路注册到后端subReactor 线程池的IO 线程上,由IO 线程负 责后续的IO 操作
2.4 无锁设计、线程绑定 Netty采用无锁设计,串行操作。避免多线程的竞争;其实netty内部可以同时启动多个串行化线程 Neety的NIOEventLoop读取到消息后,直接调用ChannelPiple的FireChannelRead(msg),只要用户不主动切换线程。一直由NioEventLoop来处理用户的Handler,期间不会线程切换;
2.5 高性能的序列化框架 netty默认支持google protobuf,用户可以实现其他的高性能序列化框架,例如Thrift的压缩二进制编码框架
Netty的RPC实现
RPC(远程调用):Remote Producedure Call
3 关键技术
- 通信:使用Netty来做通信框架
- Spring:使用Spring 来做beand加载配置
- 动态处理:客户端使用代理模式,透明化服务调用
- 消息编解码:使用pb序列化和反序列化
3.1 核心流程
3.2 通信流程
netty一般使用channel.writeAndFlush()方法来发送二进制串,请求书异步请求; 服务端接收到请求处理完后会发送给客户端; 问题:当有多个线程请求到服务端,服务端怎么保证消息返回的准确性?
- requestID:会生成一个AtomicLong 全局唯一,并存储回掉对象到全局的ConCurrentHashMap
- sync获取回调对象的锁并自旋wait:
当线程调用channel.writeAndFlush()发送消息后,紧接着会执行callback的get方法,视图获取远程的返回结果,get()方法内部使用sync获取callback对象锁:获取成功-查询是否有结果-没有结果-wait后释放锁继续等待; - 当收到消息找到callback的锁唤醒线程-获取结果
- 接收到服务端返回结果,response中包含requsetID,发送给客户端;
客户端的socket上有专门线程监听线程收到消息,获取到requestID,从前面的ConCurrentHashMap中获取到callBack对象,在用sync获取callbakc的锁。将结果设置到callback对象中。在调用callback.notifyAll()唤醒处于等待的线程; 3.3 RMI实现方式
JAVA RMI是java原生的远程调用编程接口,具体步骤如下
- 编写远程服务接口。实现Remote接口
- 实现类编写。继承UniccastRemoteObject
- 运行RMI编译器,创建客户端stub类和服务端skeleton类
- 启动RMI注册表,驻留这些服务
- RMI注册表中注册服务
- 客户端查找远程对象,并调用远程方法;
//1:创建远程接口,继承java.rmi.Remote 接口
public interface GreetService extends java.rmi.Remote {
String sayHello(String name) throws RemoteException;
}
//2:实现远程接口,继承 java.rmi.server.UnicastRemoteObject 类
public class GreetServiceImpl extends java.rmi.server.UnicastRemoteObject
implements GreetService {
private static final long serialVersionUID = 3434060152387200042L;
public GreetServiceImpl() throws RemoteException {
super();
}
@Override
public String sayHello(String name) throws RemoteException {
return "Hello " + name;
}
}
//3:生成Stub 和Skeleton;
// 4:执行rmiregistry 命令注册服务
// 5:启动服务
LocateRegistry.createRegistry(1098);
Naming.bind("rmi://10.108.1.138:1098/GreetService",new GreetServiceImpl());
// 6.客户端调用
GreetService greetService=(GreetService)
Naming.lookup("rmi://10.108.1.138:1098/GreetService");
System.out.println(greetService.sayHello("Jobs"));
4 其他RPC对比 4.1 Protocol buffer
4.2 THrift
Apache Thrift,它采用接口描述语言定义并创建服务,支持可扩展的跨语言服务开发,所包含的代码 生成引擎可以在多种语言中,如 C++, Java, Python, PHP, Ruby, Erlang, Perl, Haskell, C#, Cocoa, Smalltalk 等创建高效的、无缝的服务,其传输数据采用二进制格式,相对 XML 和 JSON 体积更小, 对于高并发、大数据量和多语言的环境更有优势。
引用:
- netty内存分析[https://stor.51cto.com/art/201808/581286.htm]
|