Netty Mina
高性能和高可伸缩性网络应用程序的网络应用框架
传统io缺点
- NIO的类库和API还是有点复杂,比如Buffer的使用
- Selector编写复杂,如果对某个事件注册后,业务代码过于耦合
- 需要了解很多多线程的知识,熟悉网络编程
- 面对断连重连、保丢失、粘包等,处理复杂
- NIO存在BUG,根据网上言论说是selector空轮训导致CPU飙升
创建步骤
- 即包含一个接收连接的线程池(也有可能是单个线程,boss线程池)以及一个处理连接的线程池(worker线程池)。boss负责接收连接,并进行IO监听;worker负责后续的处理。
- 流程
- 1、创建两个NIO线程组,一个专门用来网络事件处理(接受客户端连接),另一个则进行网络通讯读写
- 2、创建一个ServerBootstrap对象,配置Netty的一系列参数,例如接受传入数据的缓存大小等。
- 3、创建一个实际处理数据的类ChannelInitializer,进行初始化的准备工作,比如设置传入数据的字符集,格式,实现实际处理数据的接口。
- 4、绑定端口,执行同步阻塞方法等待服务器启动即可。
- 流程
通讯方式
-
使用长连接通道不断开的形式进行通信,也就是服务器和客户端的通道一直处于开启状态,如果服务器的性能比较好,而且客户端的数量也不多的情况下,可以考虑这种方式
-
一次性批量提交数据,采用短连接的方式,也就是我们把数据保存在本地临时缓冲区或者临时表中,
当达到临界值时进行一次性批量提交,又或者根据定时任务轮询提交,这种情况下弊端是做不到
实时性传输,在实时性要求不高的程序中可以采用 -
采用一种特殊的长连接,在指定某一段时间之内,服务端和某台客户端没有任何通讯,则断开连接,下次如果客户端要向服务端发送数据时,再次建立连接。
组件
- Bootstrap:netty的组件容器,用于把其他各个部分连接起来;如果是TCP的Server端,则为ServerBootstrap.
- Channel:代表一个Socket的连接
- EventLoopGroup:一个Group包含多个EventLoop,可以理解为线程池
- EventLoop:处理具体的Channel,一个EventLoop可以处理多个Channel
- ChannelPipeline:每个Channel绑定一个pipeline,在上面注册处理逻辑handler
- Handler:具体的对消息或连接的处理,有两种类型,Inbound和Outbound。分别代表消息接收的处理和消息发送的处理。
- 当接收消息的时候,会从链表的表头开始遍历,如果是inbound就调用对应的方法;如果发送消息则从链表的尾巴开始遍历
- ChannelFuture:注解回调方法
netty服务端代码
import java.net.InetSocketAddress;
import java.nio.charset.Charset;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOutboundHandlerAdapter;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.ChannelPromise;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
/**
* 注意:
*
* 1 ChannelOutboundHandler要在最后一个Inbound之前
*
*/
public class NettyNioServerHandlerTest {
final static ByteBuf buffer = Unpooled.unreleasableBuffer(Unpooled.copiedBuffer("Hi\r\n", Charset.forName("UTF-8")));
public void serve(int port) throws InterruptedException {
// 1、创建两个NIO线程组,一个专门用来网络事件处理(接受客户端连接),另一个则进行网络通讯读写
EventLoopGroup bossGroup = new NioEventLoopGroup(1);
EventLoopGroup workerGroup = new NioEventLoopGroup();
try{
// 2、创建一个ServerBootstrap对象,配置Netty的一系列参数,例如接受传入数据的缓存大小等。
// 3、创建一个实际处理数据的类ChannelInitializer,进行初始化的准备工作,比如设置传入数据的字符集,格式,实现实际处理数据的接口ChannelHandlerAdapter。
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.localAddress(new InetSocketAddress(port))
.childHandler(
new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
ChannelPipeline pipeline = socketChannel.pipeline();
pipeline.addLast("1",new InboundA());
pipeline.addLast("2",new OutboundA());
pipeline.addLast("3",new InboundB());
pipeline.addLast("4",new OutboundB());
pipeline.addLast("5",new OutboundC());
pipeline.addLast("6",new InboundC());
}
});
ChannelFuture f = b.bind().sync();
f.channel().closeFuture().sync();
} finally {
bossGroup.shutdownGracefully().sync();
workerGroup.shutdownGracefully().sync();
}
}
public static void main(String[] args) throws InterruptedException {
NettyNioServerHandlerTest server = new NettyNioServerHandlerTest();
server.serve(5555);
}
//实现实际处理数据的接口
private static class InboundA extends ChannelInboundHandlerAdapter {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
ByteBuf buf = (ByteBuf)msg;
System.out.println("InboundA read"+buf.toString(Charset.forName("UTF-8")));
super.channelRead(ctx, msg);
}
}
//实现实际处理数据的接口
private static class InboundB extends ChannelInboundHandlerAdapter {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
ByteBuf buf = (ByteBuf)msg;
System.out.println("InboundB read"+buf.toString(Charset.forName("UTF-8")));
super.channelRead(ctx, msg);
// 从pipeline的尾巴开始找outbound
ctx.channel().writeAndFlush(buffer);
}
}
//实现实际处理数据的接口
private static class InboundC extends ChannelInboundHandlerAdapter {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
ByteBuf buf = (ByteBuf)msg;
System.out.println("InboundC read"+buf.toString(Charset.forName("UTF-8")));
super.channelRead(ctx, msg);
// 这样会从当前的handler向前找outbound
//ctx.writeAndFlush(buffer);
}
}
private static class OutboundA extends ChannelOutboundHandlerAdapter {
@Override
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
System.out.println("OutboundA write");
super.write(ctx, msg, promise);
}
}
private static class OutboundB extends ChannelOutboundHandlerAdapter {
@Override
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
System.out.println("OutboundB write");
super.write(ctx, msg, promise);
}
}
private static class OutboundC extends ChannelOutboundHandlerAdapter {
@Override
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
System.out.println("OutboundC write");
super.write(ctx, msg, promise);
}
}
}
简单客户端:
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;
public class WebClient {
public static void main(String[] args) throws IOException {
try {
SocketChannel socketChannel = SocketChannel.open();
socketChannel.connect(new InetSocketAddress("127.0.0.1", 5555));
ByteBuffer writeBuffer = ByteBuffer.allocate(32);
ByteBuffer readBuffer = ByteBuffer.allocate(32);
writeBuffer.put("hello".getBytes());
writeBuffer.flip();
int i=0;
while (i<10) {
writeBuffer.rewind();
socketChannel.write(writeBuffer);
readBuffer.clear();
socketChannel.read(readBuffer);
i++;
}
} catch (IOException e) {
}
}
}
XMind: ZEN - Trial Version