Netty介绍
Netty is an asynchronous event-driven network application framework
for rapid development of maintainable high performance protocol servers & clients.
netty 官网如是说,大概意思是netty 是一个异步的事件驱动的网络应用框架,可以快速开发可维护的高性能网络服务器、客户端应用。
asynchronous异步,netty中很大一部分方法都是异步的,配合事件驱动模型能够处理更多请求。netty的一致性API相比于JDK的API有很高的用户体验,
使用起来也很方便。netty使我们不用考虑太多底层问题和各种各样的bug,让开发者能够更专注于业务逻辑。
netty现状
这是netty在github的主页 https://github.com/netty/netty ,目前已经有5000+的star
很多知名公司和项目在使用netty,包括facebook、IBM、RedHat等大公司和Spark、Finagle、Nifty等项目。
更多的adaptor在http://netty.io/wiki/adopters.html。
目前netty的主要维护版本有3.x 、4.x 、5.x。我接触比较多的是5.x,很多框架是基于3.x开发的,3 4 5之间有一些差别,
我认为新的版本是在以往的经验和教训上开发出来的,用的主要的5。
netty做什么事情
http://netty.io/images/components.png
netty对JDK的NIO进行了封装,为开发者提供了一致性和良好的API。netty提供了很多更好的"JDK API"实现,比如它的ByteBuf。
快的定义是什么
快, 我想尽快得到一个东西和我想尽快得到所有的东西。
在ServerClient编程中,前者可以认为是low latency, 更低的延迟, 尽快完成一个请求; 而后者是high throughput,更大的系统吞吐量。
可扩展性scalability
我们需要系统在大量请求时能够平滑的降低性能并且当我们提升硬件配置时能够获得相应的性能提升。
不同paradigm的Server
1.单线程模式
handle如果没有在新线程中执行那么while循环将会block在handle处理上,一次只能处理一个请求。
</pre></h3><h3 style="margin:30px 0px 0px; font-size:16px; line-height:1.5; color:rgb(51,51,51); font-family:Arial,sans-serif"><pre name="code" class="java">ServerSocket serverSocket = new ServerSocket(port);
while(true){
Socket socket = serverSocket.accept();
handle(socket);
}
private void handle(Socket socket){
try(
InputStream inputStream = socket.getInputStream();
PrintWriter writer = new PrintWriter(socket.getOutputStream(), true);
BufferedReader br = new BufferedReader(new InputStreamReader(inputStream));
){
String line;
while((line = br.readLine()) != null){
System.out.println("Read line : " + line);
writer.println(line);
}
} catch (IOException e) {
e.printStackTrace();
}
}
</pre></h3><h3 style="margin:30px 0px 0px; font-size:16px; line-height:1.5; color:rgb(51,51,51); font-family:Arial,sans-serif"><pre name="code" class="java">ServerSocket serverSocket = new ServerSocket(port);
while(true){
Socket socket = serverSocket.accept();
handle(socket);
}
private void handle(Socket socket){
try(
InputStream inputStream = socket.getInputStream();
PrintWriter writer = new PrintWriter(socket.getOutputStream(), true);
BufferedReader br = new BufferedReader(new InputStreamReader(inputStream));
){
String line;
while((line = br.readLine()) != null){
System.out.println("Read line : " + line);
writer.println(line);
}
} catch (IOException e) {
e.printStackTrace();
}
}
2.多线程执行
一个请求对应一个线程,当涌现大量请求时线程的创建、销毁、ContextSwitch的overhead都回影响系统性能
ServerSocket serverSocket = new ServerSocket(port);
while(true){
Socket socket = serverSocket.accept();
new Thread(){
@Override
public void run(){
handle(socket);
}
}.start();
}
ServerSocket serverSocket = new ServerSocket(port);
while(true){
Socket socket = serverSocket.accept();
new Thread(){
@Override
public void run(){
handle(socket);
}
}.start();
}
3.线程池
线程池并没有解决一请求一线程的问题,只能有限减少线程创建的开销和控制线程的创建。
Executor executor = Executors.newFixedThreadPool(100);
ServerSocket serverSocket = new ServerSocket(port);
while(true){
Socket socket = serverSocket.accept();
executor.execute(new Runnable() {
@Override
public void run() {
handle(socket);
}
});
}
Executor executor = Executors.newFixedThreadPool(100);
ServerSocket serverSocket = new ServerSocket(port);
while(true){
Socket socket = serverSocket.accept();
executor.execute(new Runnable() {
@Override
public void run() {
handle(socket);
}
});
}
4.JDK NIO
思考一下,问题出在handle(socket)上,InputStream 和OutputStream的基于字节的读写方式,的read write操作都是block操作,当没有bytes可以read或者write时执行线程都会block。
graph from 《netty in action》
JDK1.4 提供了nio实现, nio当时有两种说法,new io 和non blocking io, 现在过去这么多年了,已经不再new了,大家都称之为non blocking io。
介绍几个核心java.nio包中包括一些Buffer,核心是ByteBuffer,程序与网络层交互还是以byte流的形式。ByteBuffer有heap buffer 和direct buffer(non heap buffer)两种,head buffer 在Java heap 堆中,
使用byte数组作为其内部数据结构,direct buffer 在Java 堆内存之外。java.nio.channels中有Channel和Selector两个比较重要的类,Channel代表了一个和可以读写的目标的通道,实现类有FileChannel、ServerSocketChannel、SocketChannel等,Selector用于注册Channel感兴趣的事件,这样我们就可以实现asynchronous event-driven了,实现一个线程处理多个请求,多路复用(multiplexing)
ServerSocketChannel serverChannel = ServerSocketChannel.open();
serverChannel.bind(new InetSocketAddress(port));
serverChannel.configureBlocking(false);
Selector selector = Selector.open();
serverChannel.register(selector, SelectionKey.OP_ACCEPT);
while(true){
selector.select();
Set<SelectionKey> selectionKeySet = selector.selectedKeys();
Iterator<SelectionKey> iterator = selectionKeySet.iterator();
while(iterator.hasNext()){
SelectionKey selectionKey = iterator.next();
iterator.remove();
if(selectionKey.isAcceptable()){
ServerSocketChannel server = (ServerSocketChannel) selectionKey.channel();
SocketChannel client = server.accept();
client.configureBlocking(false);
client.register(selector, SelectionKey.OP_READ|SelectionKey.OP_WRITE, ByteBuffer.allocate(BUFFER_SIZE));
}
if(selectionKey.isReadable()){
SocketChannel client = (SocketChannel) selectionKey.channel();
ByteBuffer buf = (ByteBuffer) selectionKey.attachment();
client.read(buf);
}
if(selectionKey.isWritable()){
SocketChannel client = (SocketChannel) selectionKey.channel();
ByteBuffer buf = (ByteBuffer) selectionKey.attachment();
buf.flip();
client.write(buf);
buf.compact();
}
}
}
这个CPU占用比较严重
5. netty nio
为了演示把功能放到了一个块里。netty中我们的byte解析业务实现都可以用ChannelHandler来实现,ChannelHandler串联在ChannelPipeline形成了一种类插件的形式,通过Filter chain使各个逻辑相互独立可复用。
int port = 8090;
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup();
ServerBootstrap serverBootstrap = new ServerBootstrap();
try{
serverBootstrap.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.childHandler(new ChannelInitializer<io.netty.channel.socket.SocketChannel>() {
@Override
protected void initChannel(io.netty.channel.socket.SocketChannel ch) throws Exception {
ch.pipeline().addLast("echoHandler", new ChannelHandlerAdapter() {
@Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
ctx.flush();
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
ctx.write(msg);
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
ctx.close();
}
});
}
});
ChannelFuture f = serverBootstrap.bind(new InetSocketAddress(port)).sync();
f.channel().closeFuture().sync();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
未完待续。。。
continuning...
更多推荐资料
netty in action