Netty快速入门
异步非阻塞IO
什么是Netty
Netty 是一个基于 JAVA NIO 类库的异步通信框架,它的架构特点是:异步非阻塞、基于事件驱动、高性能、高可靠性和高可定制性。
Netty应用场景
1.分布式开源框架中dubbo、Zookeeper,RocketMQ,底层rpc通讯使用就是netty。
2.游戏开发中,底层使用netty通讯。
为什么选择netty
因为可以解决NIO代码复杂问题,以及有更好的容错机制!
那么为什么不建议开发者直接使用JDK的NIO类库进行开发?
1) NIO的类库和API繁杂,使用麻烦,你需要熟练掌握Selector、ServerSocketChannel、SocketChannel、ByteBuffer等;
2) 需要具备其它的额外技能做铺垫 。
例如:熟悉Java多线程编程,因为NIO编程涉及到Reactor模式,你必须对多线程和网路编程非常熟悉,才能编写出高质量的NIO程序;
3) 可靠性能力补齐,工作量和难度都非常大。
例如:客户端面临断连重连、网络闪断、半包读写、失败缓存、网络拥塞和异常码流的处理等等,NIO编程的特点是功能开发相对容易,但是可靠性能力补齐工作量和难度都非常大;
4) JDK NIO的BUG。
例如:臭名昭著的epoll bug,它会导致Selector空轮询,最终导致CPU 100%。官方声称在JDK1.6版本的update18修复了该问题,但是直到JDK1.7版本该问题仍旧存在,只不过该bug发生概率降低了一些而已,它并没有被根本解决。
代码示例
Maven坐标
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty</artifactId>
<version>3.10.5.Final</version>
</dependency>
Netty服务器端
public class NettyServer {
public static void main(String[] args) {
//创建服务类对象
ServerBootstrap serverBootstrap = new ServerBootstrap();
//创建两个线程池,分别为监听端口,监听NIO
ExecutorService boos = Executors.newCachedThreadPool();
ExecutorService worker = Executors.newCachedThreadPool();
//设置工程,并把两个线程池加入其中
serverBootstrap.setFactory(new NioServerSocketChannelFactory(boos,worker));
//设置管道工程
serverBootstrap.setPipelineFactory(new ChannelPipelineFactory() {
public ChannelPipeline getPipeline() throws Exception {
ChannelPipeline pipeline = Channels.pipeline();
//将数据转换为String类型
pipeline.addLast("decoder",new StringDecoder());
pipeline.addLast("encoder",new StringEncoder());
pipeline.addLast("serverHandler",new ServerHandler());
return pipeline;
}
});
//绑定端口号
serverBootstrap.bind(new InetSocketAddress(8080));
System.out.println("Netty服务器端已经启动...");
}
}
class ServerHandler extends SimpleChannelHandler{
/**
* 通道关闭的时候触发
*/
@Override
public void channelClosed(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception {
System.out.println("通道关闭的时候触发:channelClosed");
}
/**
* 必须是连接已经建立,关闭通道的时候才会触发
*/
@Override
public void channelDisconnected(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception {
System.out.println("必须是连接已经建立,关闭通道的时候才会触发:channelDisconnected");
}
/**
* 捕获异常
*/
@Override
public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) throws Exception {
System.out.println("捕获异常:exceptionCaught");
}
/**
* 接受消息
*/
@Override
public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception {
System.out.println("服务器端接受到客户端的消息:"+e.getMessage());
//回复内容
ctx.getChannel().write("您好,主人暂时不在,有事请留言!!!");
}
}
Netty客户端
public class NettyClient {
public static void main(String[] args) {
System.out.println("Netty客户端已经启动...");
//创建服务类对象
ClientBootstrap clientBootstrap = new ClientBootstrap();
//创建两个线程池,分别为监听端口,监听NIO
ExecutorService boos = Executors.newCachedThreadPool();
ExecutorService worker = Executors.newCachedThreadPool();
//设置工程,并把两个线程池加入其中
clientBootstrap.setFactory(new NioClientSocketChannelFactory(boos,worker));
//设置管道工程
clientBootstrap.setPipelineFactory(new ChannelPipelineFactory() {
public ChannelPipeline getPipeline() throws Exception {
ChannelPipeline pipeline = Channels.pipeline();
//将数据转换为String类型
pipeline.addLast("decoder",new StringDecoder());
pipeline.addLast("encoder",new StringEncoder());
pipeline.addLast("clientHandler",new ClientHandler());
return pipeline;
}
});
//连接服务端
ChannelFuture connect = clientBootstrap.connect(new InetSocketAddress("127.0.0.1", 8080));
Channel channel = connect.getChannel();
System.out.println("Client Start");
Scanner scanner = new Scanner(System.in);
while (true){
System.out.println("请输入内容:");
String str = scanner.next();
channel.write(str);
}
}
}
class ClientHandler extends SimpleChannelHandler {
/**
* 通道关闭的时候触发
*/
@Override
public void channelClosed(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception {
System.out.println("通道关闭的时候触发:channelClosed");
}
/**
* 必须是连接已经建立,关闭通道的时候才会触发
*/
@Override
public void channelDisconnected(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception {
System.out.println("必须是连接已经建立,关闭通道的时候才会触发:channelDisconnected");
}
/**
* 捕获异常
*/
@Override
public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) throws Exception {
System.out.println("捕获异常:exceptionCaught");
}
/**
* 接受消息
*/
@Override
public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception {
System.out.println("服务器端向客户端回复内容:"+e.getMessage());
//回复内容
// ctx.getChannel().write("您好,主人暂时不在,有事请留言!!!");
}
}
运行结果
服务器端:
客户端: