netty
1.netty介绍:
netty是由jboss提供的基于nio的网络编程开源框架,它采用异步,事件驱动的方式用来快速开发一个高性能高可靠的网络IO程序;elasticsearch,dubbox内部采用的就是netty
2.netty线程模型:
* 单线程模型:
单线程多路复用的方式来完成服务器端包括建立客户端连接,读写的所有操作,编码简单,但是无法满足大量客户端连接的需求,传统的nio编程便是这种方式
* 线程池模型:
服务器端使用单线程管理客户端的连接,同时使用一个线程池来管理其他的网络IO操作
* netty模型; NioEventLoopGroup
服务器端使用bossGroup线程池管理客户端连接请求,同时使用workerGroup管理其他的所有IO操作;bossGroup和workerGroup对应的实例是NioEventLoopGroup类,每个NioEventLoopGrou中对应有多个NioEventLoop,而每个NioEventLoop对应线程池中的单个线程,每个NioEventLoop都有自己的Selector并在Selector上注册各种事件和Channel,以及taskQueue;
bossGroup对应多个Selector和ServerSocketChannel,workerGroup对应多个Selector和SocketChannel;ChannelPipeline是贯穿整个netty的一条处理链,所有的入站出站操作都需要挂载到ChannelPipeline上才能生效
3.netty异步模型:
netty异步模型基于future和callback,它的核心思想:当需要调用方法时,直接返回一个future,使用future和callbackk来监控方法的处理过程
4.netty API:
* 业务逻辑处理类(workerGroup使用)
自定义一个Handler继承ChannelInboundHandlerAdapter类,重写通道入站处理器适配器中的默认空实现的方法
public void channelActive(ChannelHandlerContext ctx),通道就绪事件
public void channelRead(ChannelHandlerContext ctx, Object msg),通道读取数据事件
public void channelReadComplete(ChannelHandlerContext ctx) ,数据读取完毕事件
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause),通道发生异常
事件
* Pipeline 和 ChannelPipeline
ChannelPipeline是一个Handler集合,是贯穿整个netty的一条处理链
* ChannelHandlerContext
这是事件处理器上下文对象,代表ChannelPipeline中每个Handler的处理节点
ctx.writeAndFlush(Unpooled.copiedBuffer("你好",CharsetUtil.UTF_8));
* ChannelOption 设置socket标准参数
* ChannelFuture 监控nettyI/O操作的结果
* ServerBootstrap 服务器端启动助手类,用于设置参数
* Bootstrap 客户端启动助手类
* Unpooled netty提供的用来操作缓冲区的工具类,netty的字节缓冲区叫ByteBuf,不是ButeBuffer
ByteBuf byteBuf = Unpooled.copiedBuffer("你好",CharsetUtil.UTF_8);
5.netty坐标
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>4.1.8.Final</version>
</dependency>
6.编码和解码
* 方式一: 采用jdk自带的序列化方式
缺点:无法跨语言,且序列化后体积是二进制的5倍
* 方式二: netty提供的编码器解码器,ObjectDecoder/ObjectEncoder
缺点:内部使用的是jdk序列化方式,同上
* 方式三:google提供的Protobuf序列化
跨语言,且高性能高可靠
7.Protobuf序列化使用步骤
第一步:导入依赖
<dependency>
<groupId>com.google.protobuf</groupId>
<artifactId>protobuf-java</artifactId>
<version>3.6.1</version>
</dependency>
第二步:编写proto文件
# syntax 设置版本号
# BookMessage 设置生成java文件的类名
# Book 生成内部类的类名(实际需要使用的实体类)
# string name = 2 设置实体类属性,并指定序号为2(不是实体类属性)
syntax = "proto3";
option java_outer_classname = "BookMessage";
message Book{
int32 id = 1;
string name = 2;
}
第三步:使用protoc.exe工具软件,cmd输入命令,生成BookMessage.java文件,并拷贝到项目中
protoc --java_out=. Book.proto
第四步:在netty中使用
Client: socketChannel.addLast("encoder",new ProtobufEncoder());
MessageBook book = MessageBook.Book.newBuilder().setId(1).setName("张三").build();
Server: socketChannel.addLast("decoder",new ProtobufDecoder(BookMessage.Book.getDefaultInstance()));
netty demo
案例: 实现客户端向服务器端对话
1.服务器端
public class TestNettyServer {
public static void main(String[] args) throws Exception {
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup();
ServerBootstrap serverBootstrap = new ServerBootstrap();
serverBootstrap
.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.option(ChannelOption.SO_BACKLOG, 128)
.childOption(ChannelOption.SO_KEEPALIVE, true)
.childHandler(new ChannelInitializer<SocketChannel>() {
public void initChannel(SocketChannel socketChannel) {
socketChannel.pipeline().addLast(new TestNettyServerHandler());
}
});
System.out.println("......Server is ready......");
ChannelFuture future = serverBootstrap.bind(9999).sync();
System.out.println("......Server is starting......");
future.channel().closeFuture().sync();
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
2.服务器业务处理类
public class TestNettyServerHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
ByteBuf byteBuf = (ByteBuf) msg;
System.out.println("Client Say : " + byteBuf.toString(CharsetUtil.UTF_8));
}
@Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
ctx.writeAndFlush(Unpooled.copiedBuffer("hello Client ...", CharsetUtil.UTF_8));
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
ctx.close();
}
}
3.客户端
public class TestNettyClient {
public static void main(String[] args) throws Exception {
EventLoopGroup workerGroup = new NioEventLoopGroup();
Bootstrap bootstrap = new Bootstrap();
bootstrap.group(workerGroup)
.channel(NioSocketChannel.class)
.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel socketChannel) {
socketChannel.pipeline().addLast(new TestNettyClientHandler());
}
});
System.out.println("Client is ready......");
ChannelFuture future = bootstrap.connect("127.0.0.1", 9999).sync();
future.channel().closeFuture().sync();
}
}
4.客户端业务逻辑处理类
public class TestNettyClientHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
ctx.writeAndFlush(Unpooled.copiedBuffer("Hello Server ...", CharsetUtil.UTF_8));
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
ByteBuf byteBuf = (ByteBuf) msg;
System.out.println("Server Say : " + byteBuf.toString(CharsetUtil.UTF_8));
}
}