Netty之入门

在上篇《Netty之引题》中,分别对AIO,BIO,PIO,NIO进行了简单的阐述,并写了简单的demo。但是这里说的简单,我也只能呵呵了,特别是NIO、AIO(我全手打的,好麻烦)。
在开始netty开发TimeServer之前,先回顾下NIO进行服务端开发的步骤:

  • 1.创建ServerSocketChannel,配置它为非阻塞;
  • 2.绑定监听,配置TCP参数,如backlog大小;
  • 3.创建独立的IO线程,用于轮询多路复用器Selector;
  • 4.创建Selector,将之前创建的ServerSocketChannel注册到Selector上,监听SelectionKey.ACEPT;
  • 5.启动IO线程,在循环体中执行Selector.select()方法,轮询就绪的Channel;
  • 6.当轮询到处于就绪状态的Channel时,需要对其进行判断,如果是OP_ACCEPT状态,说明是新的客户端接入,则调用ServerSocketChannel.accept()方法接受新的客户端;
  • 7.设置新接入的客户端链接SocketChannel为非阻塞模式,配置其他的一些TCP参数;
  • 8.将SocketChannel注册到Selector,监听OP_READ操作位;
  • 9.如果轮询的Channel为OP_READ,则说明SocketChannel中有新的就绪的数据包需要读取,则构造ByteBuffer对象,读取数据包;
  • 10.如果轮询的Channel为OP_WRITE,说明还有数据没有发送完成,需要继续发送。

一个简单的NIO程序,需要经过繁琐的十多步操作才能完成最基本的消息读取和发送,这也是我学netty的原因,下面就看看使用netty是如何轻松写服务器的。
在这里,我使用IDEA 14 + Maven用netty写上篇中TimeServer的程序。这里我直接用Maven的pom.xml来直接下载netty的包(Maven是对依赖进行管理,支持自动化的测试、编译、构建的项目管理工具,具体的Maven请读者自行百度、google搜索)。

/* TimeServer */

 1 public class TimeServer {
 2     public void bind(int port)throws Exception{
 3         /* 配置服务端的NIO线程组 */
 4         // NioEventLoopGroup类 是个线程组,包含一组NIO线程,用于网络事件的处理
 5         // (实际上它就是Reactor线程组)。
 6         // 创建的2个线程组,1个是服务端接收客户端的连接,另一个是进行SocketChannel的
 7         // 网络读写
 8         EventLoopGroup bossGroup = new NioEventLoopGroup();
 9         EventLoopGroup WorkerGroup = new NioEventLoopGroup();
10
11         try {
12             // ServerBootstrap 类,是启动NIO服务器的辅助启动类
13             ServerBootstrap b = new ServerBootstrap();
14             b.group(bossGroup,WorkerGroup)
15                     .channel(NioServerSocketChannel.class)
16                     .option(ChannelOption.SO_BACKLOG,1024)
17                     .childHandler(new ChildChannelHandler());
18
19             // 绑定端口,同步等待成功
20             ChannelFuture f= b.bind(port).sync();
21
22             // 等待服务端监听端口关闭
23             f.channel().closeFuture().sync();
24         }finally {
25             // 释放线程池资源
26             bossGroup.shutdownGracefully();
27             WorkerGroup.shutdownGracefully();
28         }
29     }
30
31     private class ChildChannelHandler extends ChannelInitializer<SocketChannel>{
32         @Override
33         protected  void initChannel(SocketChannel arg0)throws Exception{
34             arg0.pipeline().addLast(new TimeServerHandler());
35         }
36     }
37
38     public static void main(String[]args)throws Exception{
39         int port = 8080;
40         if(args!=null && args.length>0){
41             try {
42                 port = Integer.valueOf(args[0]);
43             }
44             catch (NumberFormatException ex){}
45         }
46         new TimeServer().bind(port);
47     }
48 }
 1 public class TimeServerHandler extends ChannelHandlerAdapter{
 2     // 用于网络的读写操作
 3     @Override
 4     public void channelRead(ChannelHandlerContext ctx,Object msg)
 5             throws Exception{
 6         ByteBuf buf = (ByteBuf)msg;
 7         byte[]req = new byte[buf.readableBytes()];
 8         buf.readBytes(req);
 9         String body = new String(req,"UTF-8");
10         System.out.println("the time server order : " + body);
11
12         String currentTime = "QUERY TIME ORDER".equalsIgnoreCase(body)?new Date(
13                 System.currentTimeMillis()).toString():"BAD ORDER";
14         ByteBuf resp = Unpooled.copiedBuffer(currentTime.getBytes());
15         ctx.write(resp);
16     }
17
18     @Override
19     public void channelReadComplete(ChannelHandlerContext ctx)throws Exception{
20         ctx.flush();   // 它的作用是把消息发送队列中的消息写入SocketChannel中发送给对方
21         // 为了防止频繁的唤醒Selector进行消息发送,Netty的write方法,并不直接将消息写入SocketChannel中
22         // 调用write方法只是把待发送的消息发到缓冲区中,再调用flush,将发送缓冲区中的消息
23         // 全部写到SocketChannel中。
24     }
25
26     @Override
27     public void exceptionCaught(ChannelHandlerContext ctx,Throwable cause){
28         ctx.close();
29     }
30 }

/* TimeClient */

 1 public class TimeClient {
 2     public void connect(String host,int port)throws Exception{
 3         // 配置服务端的NIO线程组
 4         EventLoopGroup group = new NioEventLoopGroup();
 5
 6         try {
 7             // Bootstrap 类,是启动NIO服务器的辅助启动类
 8             Bootstrap b = new Bootstrap();
 9             b.group(group).channel(NioSocketChannel.class)
10                     .option(ChannelOption.TCP_NODELAY,true)
11                     .handler(new ChannelInitializer<SocketChannel>() {
12                         @Override
13                         public void initChannel(SocketChannel ch)
14                                 throws Exception{
15                             ch.pipeline().addLast(new TimeClientHandler());
16                         }
17                     });
18
19             // 发起异步连接操作
20             ChannelFuture f= b.connect(host,port).sync();
21
22             // 等待客服端链路关闭
23             f.channel().closeFuture().sync();
24         }finally {
25             group.shutdownGracefully();
26         }
27     }
28
29     public static void main(String[]args)throws Exception{
30         int port = 8080;
31         if(args!=null && args.length>0){
32             try {
33                 port = Integer.valueOf(args[0]);
34             }
35             catch (NumberFormatException ex){}
36         }
37         new TimeClient().connect("127.0.0.1",port);
38     }
39 }
 1 public class TimeClientHandler extends ChannelHandlerAdapter{
 2
 3     // 写日志
 4     private static final Logger logger =
 5             Logger.getLogger(TimeClientHandler.class.getName());
 6
 7     private final ByteBuf firstMessage;
 8
 9     public TimeClientHandler(){
10         byte[] req = "QUERY TIME ORDER".getBytes();
11         firstMessage = Unpooled.buffer(req.length);
12         firstMessage.writeBytes(req);
13     }
14
15     @Override
16     public void channelRead(ChannelHandlerContext ctx,Object msg)
17             throws Exception{
18         ByteBuf buf = (ByteBuf)msg;
19         byte[]req = new byte[buf.readableBytes()];
20         buf.readBytes(req);
21         String body = new String(req,"UTF-8");
22         System.out.println("Now is : " + body);
23     }
24
25     @Override
26     public void channelActive(ChannelHandlerContext ctx){
27         // 当客户端和服务端建立tcp成功之后,Netty的NIO线程会调用channelActive
28         // 发送查询时间的指令给服务端。
29         // 调用ChannelHandlerContext的writeAndFlush方法,将请求消息发送给服务端
30         // 当服务端应答时,channelRead方法被调用
31         ctx.writeAndFlush(firstMessage);
32     }
33
34     @Override
35     public void exceptionCaught(ChannelHandlerContext ctx,Throwable cause){
36         logger.warning("message from:"+cause.getMessage());
37         ctx.close();
38     }
39 }

本例子没有考虑读半包的处理,对于功能演示和测试,本例子没问题,但是如果进行性能或者压力测试,就不能正常工作了。在下一节会弄正确处理半包消息的例子。

项目在源码在src/main/java/Netty/下,分为客户端和服务端。

源码下载:GitHub地址:https://github.com/orange1438/Netty_Course

题外话:虽然文章全是我纯手打,没任何复制,但是文章大多数内容来自《Netty权威指南》,我也是顺便学习的。之前我做C++服务端,因为狗血的面试C++,结果公司系统居然是java的,无耐我所在的重庆,C++少得可怜,所以只有在公司里学java了。当然,有epoll,select,事件驱动,TCP/IP概念的小伙伴来说,学这个netty,还是挺简单的。

时间: 2024-10-24 13:15:46

Netty之入门的相关文章

Netty快速入门(08)ByteBuf组件介绍

前面的内容对netty进行了介绍,写了一个入门例子.作为一个netty的使用者,我们关注更多的还是业务代码.也就是netty中这两种组件: ChannelHandler和ChannelPipeline---对应于NIO中的客户逻辑实现handleRead/handleWrite(interceptor pattern) ByteBuf---- 对应于NIO 中的ByteBuffer 我们的业务逻辑要放在handler里面,读写数据用的是ByteBuf.其余的Transport.ServerBoo

netty从入门到深入

这段时间领导让我熟悉Socket开发,我花了三周时间左右去学习相关的知识,包括Java socket开发,重点学习了netty这个异步非阻塞通信框架. 在这里把我学习过程中遇到的有用资料整理了,供大家参考. 第一步:Java 原生的Socket网络开发学习 重点掌握CS通信的基本原理和那些常用类,以及如何实现通信的过程. 这个阶段练习的demo主要有 TCP服务端到客户端,UDP的单播,组播,广播.参考博文: 1.  http://blog.csdn.net/qq_23473123/articl

netty的入门

netty是什么? netty是一个基于NIO的通信框架,对于传统计算机,系统的瓶颈一直在输入输出设备上,计算速度超过IO速度,所以对于i o的性能提高异常重要. 什么是NIO? 非阻塞IO,N表示no-blocking io,简单来说就是不需要传统的IO 包中的阻塞的读写.对应jdk中的nio包, 简单而有趣的理解: 我们从应用服务器的角度,tomcat出发,我们可以每个用户连接都创建一个线程为它服务,如果有几万用户创建几万线程很恐怖,创建线程需要消耗资源的.在长期的观察中我们发现,很多用户连

Netty实战入门详解——让你彻底记住什么是Netty(看不懂你来找我)

一.Netty 简介 Netty 是基于 Java NIO 的异步事件驱动的网络应用框架,使用 Netty 可以快速开发网络应用,Netty 提供了高层次的抽象来简化 TCP 和 UDP 服务器的编程,但是你仍然可以使用底层的 API. Netty 的内部实现是很复杂的,但是 Netty 提供了简单易用的API从网络处理代码中解耦业务逻辑.Netty 是完全基于 NIO 实现的,所以整个 Netty 都是异步的. Netty 是最流行的 NIO 框架,它已经得到成百上千的商业.商用项目验证,许多

Netty从入门到放弃,从放弃在到入门

Nio的好文章:https://zhuanlan.zhihu.com/p/64537916?utm_source=wechat_session&utm_medium=social&utm_oi=660405125986914304 服务端Channel初始化过程1. 通过 BootstrapChannelFactory 实例化 NioServerSocketChannel(实例化 Channel 的具体类型又是和在初始化 ServerBootstrap 时传入的 channel() 方法的

Netty5快速入门及实例视频教程(整合Spring)

Netty5快速入门及实例视频教程+源码(整合Spring) https://pan.baidu.com/s/1pL8qF0J 01.传统的Socket分析02.NIO的代码分析03.对于NIO的一些疑惑04.Netty服务端HelloWorld入门05.Netty服务端入门补充06.Netty客户端入门07.如何构建一个多线程NIO系统08.Netty源码分析一09.Netty源码分析二10.Netty5服务端入门案例11.Netty5客户端入门案例12.单客户端多连接程序13.Netty学习

Netty系列之Netty高性能之道(转载InfoQ)

1. 背景 1.1. 惊人的性能数据 最近一个圈内朋友通过私信告诉我,通过使用Netty4 + Thrift压缩二进制编解码技术,他们实现了10W TPS(1K的复杂POJO对象)的跨节点远程服务调用.相比于传统基于Java序列化+BIO(同步阻塞IO)的通信框架,性能提升了8倍多. 事实上,我对这个数据并不感到惊讶,根据我5年多的NIO编程经验,通过选择合适的NIO框架,加上高性能的压缩二进制编解码技术,精心的设计Reactor线程模型,达到上述性能指标是完全有可能的. 下面我们就一起来看下N

Netty系列之Netty高性能之道

from:http://itindex.net/detail/49964-netty-%E7%B3%BB%E5%88%97-netty 1. 背景 1.1. 惊人的性能数据 最近一个圈内朋友通过私信告诉我,通过使用Netty4 + Thrift压缩二进制编解码技术,他们实现了10W TPS(1K的复杂POJO对象)的跨节点远程服务调用.相比于传统基于Java序列化+BIO(同步阻塞IO)的通信框架,性能提升了8倍多. 事实上,我对这个数据并不感到惊讶,根据我5年多的NIO编程经验,通过选择合适的

Netty高性能之道

1. 背景 1.1. 惊人的性能数据 最近一个圈内朋友告诉我,通过使用Netty4 + Thrift压缩二进制编解码技术,他们实现了10W TPS(1K的复杂POJO对象)的跨节点远程服务调用.相比于传统基于Java序列化+BIO(同步阻塞IO)的通信框架,性能提升了8倍多. 事实上,我对这个数据并不感到惊讶,根据我5年多的NIO编程经验,通过选择合适的NIO框架,加上高性能的压缩二进制编解码技术,精心的设计Reactor线程模型,达到上述性能指标是完全有可能的. 下面我们就一起来看下Netty