源码地址:https://gitee.com/pidaner/netty-class
官网:https://netty.io/
Netty is an asynchronous event-driven network application framework for rapid development of maintainable high performance protocol servers & clients
Netty是一个异步事件驱动的网络应用程序框架
快速开发可维护的高性能协议服务器和客户端
netty分为三部分
- core:零拷贝、通用api库、可拓展事件模型
- 支持协议Protocol Support:
webSocket协议阮一峰详解 - transport services
netty的自我介绍
- Netty 是由 JBOSS 提供的一个 Java 开源框架。Netty
提供异步的、基于事件驱动的网络应用程序框架,用以快速开发高性能、高可靠性的网络 IO 程序 - Netty 可以帮助你快速、简单的开发出一个网络应用,相当于简化和流程化了 NIO 的开发过程
- Netty 是目前最流行的 NIO 框架,Netty 在互联网领域、大数据分布式计算领域、游戏行业、通信行业等获得了广泛的应用,知名的Elasticsearch 、Dubbo 框架内部都采用了 Netty。
- netty 下载地址: https://bintray.com/netty/downloads/netty/
Reactor理解
- 单 Reactor 单线程,前台接待员和服务员是同一个人,全程为顾客服务
- 单 Reactor 多线程,1 个前台接待员,多个服务员,接待员只负责接待
- 主从 Reactor 多线程,多个前台接待员,多个服务生
reactor架构图
结构图解释
Netty
抽象出两组线程池BossGroup
专门负责接收客户端的连接,WorkerGroup
专门负责网络的读写- BossGroup 和 WorkerGroup 类型都是
NioEventLoopGroup
- NioEventLoopGroup 相当于一个
事件循环组
, 这个组中含有多个事件循环
,每一个事件循环是NioEventLoop
- NioEventLoop 表示一个
不断循环的执行处理任务的线程
, 每个NioEventLoop 都有一个selector
,
用于监听绑定在其上的socket
的网络通讯 - NioEventLoopGroup 可以有多个线程, 即可以含有多个NioEventLoop
- 每个Boss NioEventLoop 循环执行的步骤有3步
- 轮询accept 事件
- 处理accept 事件 , 与client建立连接 , 生成
NioScocketChannel
, 并将其注册到某个worker
,NIOEventLoop 上的 selector - 处理任务队列的任务 , 即 runAllTasks
- 每个 Worker NIOEventLoop 循环执行的步骤
- 轮询read, write 事件
- 处理i/o事件, 即read , write 事件,在对应NioScocketChannel 处理
- 处理任务队列的任务 , 即 runAllTasks
- 每个
Worker NIOEventLoop
处理业务时,会使用pipeline(管道)
, pipeline 中包含了channel
, 即通过pipeline 可以获取到对应通道, 管道中维护了很多的处理器
Netty入门实例:TCP服务
实例要求:使用IDEA 创建Netty项目
Netty 服务器在 6668 端口监听,客户端能发送消息给服务器 “hello, 服务器~”
服务器可以回复消息给客户端 “hello, 客户端~”
目的:对Netty 线程模型 有一个初步认识, 便于理解Netty 模型理论
编写服务端、编写客户端,对netty 程序进行分析,看看netty模型特点
说明: 创建Maven项目,并引入Netty 包
1、创建maven项目,导入netty依赖
2、服务端
package com.lian.simple;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
public class Server {
public static void main(String[] args) throws Exception {
/**
* 1、创建两个线程组 BossGroup 和 workerGroup
* 2、bossGroup 只处理连接请求,workerGroup负责客户端业务处理
* 3、两个都是无限循环
*/
//门口迎宾的妹子们
NioEventLoopGroup bossGroup = new NioEventLoopGroup();
//店内负责端菜倒水的伙计们
NioEventLoopGroup workerGroup = new NioEventLoopGroup();
try {
//创建服务器端的启动对象,配置参数
ServerBootstrap bootstrap = new ServerBootstrap();
//使用链式编程来进行设置
bootstrap
//设置两个线程组
.group(bossGroup, workerGroup)
//使用NioServerSocketChannel作为通道类型实现
.channel(NioServerSocketChannel.class)
//设置线程队列得到连接个数
.option(ChannelOption.SO_BACKLOG,128)
//设置保持活动连接状态
.childOption(ChannelOption.SO_KEEPALIVE,true)
//给workerGroup的eventLoop线程对应的管道设置处理器
.childHandler(new ChannelInitializer<SocketChannel>() {
//初始化一个通道
@Override
protected void initChannel(SocketChannel ch) throws Exception {
//通道上有很多处理器,都由pipeline管道统一管理
ChannelPipeline pipeline = ch.pipeline();
//管道里添加处理器
pipeline.addLast(new ServerHandler());
}
});
System.out.println("server is prepare...");
//绑定一个端口,并且同步,作用是启动服务器
ChannelFuture cf = bootstrap.bind(6666).sync();
//对关闭通道进行监听
cf.channel().closeFuture().sync();
}finally {
//优雅的关闭线程
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
}
3、服务端处理器类
package com.lian.simple;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.util.CharsetUtil;
/**
* 自定义一个handler,需要继承netty 绑定好的某个 handlerAdapter
* 这时我们自定义一个handler,才能称位一个hanler
*/
public class ServerHandler extends ChannelInboundHandlerAdapter {
/**
* 2、服务器端读取到客户端发送过来的消息
*
* 读取客户端发送的消息
* @param ctx 上下文对象,含有 管道pipeline、通道channel
* @param msg 客户端发送的数据
* @throws Exception
*/
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
System.out.println("server ctx =" + ctx);
//将客户端消息msg 转换为ByteBuf类型,此bytebuf是netty提供的,性能更高
ByteBuf buf = (ByteBuf) msg;
//
System.out.println("client send message: " + buf.toString(CharsetUtil.UTF_8));
System.out.println("client send address: " + ctx.channel().remoteAddress());
}
/**
* 3、服务器端读取完毕客户端发过来的消息后,也回复了客户端的消息
*
* 读取客户端数据后,给客户端回复消息
* @param ctx
* @throws Exception
*/
@Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
//将数据写到缓存区ButeBuf中,并且传到通道里,write+flush
//unPooled: Netty 提供一个专门用来操作缓冲区(即 Netty 的数据容器)的工具类
ctx.writeAndFlush(Unpooled.copiedBuffer("hello client",CharsetUtil.UTF_8));
}
/**
* 发生异常时,需要关闭通道
* @param ctx
* @param cause
* @throws Exception
*/
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
//关闭通道,拿到通道再关闭
ctx.channel().close();
}
}
4、客户端
package com.lian.simple;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
public class Client {
public static void main(String[] args) throws Exception {
NioEventLoopGroup clientEventLoop = new NioEventLoopGroup();
try{
//创建客户端启动对象
Bootstrap bootstrap = new Bootstrap();
//设置相关参数
bootstrap
//设置线程组
.group(clientEventLoop)
//设置客户端通道的实现类
.channel(NioSocketChannel.class)
//处理器
.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline pipeline = ch.pipeline();
pipeline.addLast(new ClientHandler());
}
});
System.out.println("client is ok");
//启动客户端连接服务器端
ChannelFuture cf = bootstrap.connect("127.0.0.1", 6666).sync();
//监听关闭通道,监听啥时候关闭
cf.channel().closeFuture().sync();
}finally {
//优雅的关闭
clientEventLoop.shutdownGracefully();
}
}
}
5、客户端处理器
package com.lian.simple;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.util.CharsetUtil;
/**
* 处理器必须要继承 ChannelInboundHandlerAdapter
*/
public class ClientHandler extends ChannelInboundHandlerAdapter {
/**
* 1、客户端是active的,主动先和服务器端打招呼
*
* Http协议是只能客户端和服务器联系,服务器然后回复,服务器不能主动联系到客户端
* websocket协议是客户端和服务器端可互相联系到对方,实现平等通信
*
* 当通道就绪就会触发该方法
* @param ctx
* @throws Exception
*/
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
System.out.println("client" + ctx);
//将数据写到缓存区,再传到通道里
ctx.writeAndFlush(Unpooled.copiedBuffer("hello server", CharsetUtil.UTF_8));
}
/**
* 4、客户端收到服务器端的消息
*
* 当通道有读取事件时,会触发
* @param ctx
* @param msg
* @throws Exception
*/
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
ByteBuf buf = (ByteBuf) msg;
System.out.println("server answer: "+buf.toString(CharsetUtil.UTF_8));
System.out.println("server address: "+ctx.channel().remoteAddress());
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
//打印异常信息
cause.printStackTrace();
//关闭通道
ctx.channel().close();
}
}