前言
上一章讨论的是Netty的线程模型,这一章我们基于上一章的线程模型来实战一把。由于Netty5出现重大BUG被官方废弃,所以我们使用Netty4进行学习。
Netty入门实战在开始之前再来看一下Netty的线程模型
我们需要有GoosGroup来循环监听请求事件,需要有WorkGroup来处理事件,而这两个角色都通过来就NioEventLoopGroup来进行事件监听,我们还需要创建事件处理器ChannelHandler,通过 Channel的ChannelPipeline把ChannelHandler进行关联。
导入依赖我们来写一个简单的服务端和客户端通信的案例,首先创建Maven项目,导入Maven依赖
io.netty
netty-all
4.1.42.Final
服务端代码
对于服务端我们要做如下事情
- 创建两个NioEventLoopGroup ,一个作为BossGroup ,一个作为 WorkGroup
- 创建 ServerBootstrap 加入两个NioEventLoopGroup
- 通过 ServerBootstrap 添加Handler ,需要自己创建Handler
- 编写Handler ,通过继承ChannelInboundHandlerAdapter 来对不同的事件做不同的处理
服务端代码如下
public class NettyServer {
public static void main(String[] args) throws InterruptedException {
//创建Boss Group :负责处理链接
NioEventLoopGroup bossGroup = new NioEventLoopGroup();
//创建 workGroup :负责处理读写事件
NioEventLoopGroup workGroup = new NioEventLoopGroup();
//创建服务端启动对象
ServerBootstrap bootstrap = new ServerBootstrap();
try{
//把 bossGroup 和 workGroup 加入启动对象
bootstrap
.group(bossGroup,workGroup)
//保存活动链接状态,启用该功能时,TCP会主动探测空闲连接的有效性
.childOption(ChannelOption.SO_KEEPALIVE,true)
//多个客户端过来,处理不过来的请求在队列排队,指定存放请求的队列的大小
.option(ChannelOption.SO_BACKLOG,64)
//指定服务器使用什么样的通道
.channel(NioServerSocketChannel.class)
//添加事件处理器Handler
.childHandler(new ChannelInitializer(){
@Override
protected void initChannel(SocketChannel ch) throws Exception {
//把处理器添加进去 , 每个channel都对应一个pieline
//pieline通道和Handler进行绑定,通过Pieline可以获取到Handler
ch.pipeline().addLast(new MyServerHandler());
}
});
//绑定端口,开始接收客户端请求
//bind方法中会创建 Channel,然后使用 EventLoop注册Channel
ChannelFuture future = bootstrap.bind(new InetSocketAddress("127.0.0.1", 6000)).sync();
//对关闭通道进行监听
//当此通道关闭时将收到通知
future.channel().closeFuture().sync();
}catch (Exception e){
e.printStackTrace();
}finally {
//关闭资源
workGroup.shutdownGracefully();
bossGroup.shutdownGracefully();
}
}
}
- ChannelOption.SO_BACKLOG:服务端处理客户端连接请求是顺序处理的,所以同一事件只能处理一个客户端连接,多个客户端来的时候,服务端将不能处理的客户端连接请求放在队列中等待处理,backlog参数指定了队列的大小
- ChannelOption.SO_KEEPALIVE:当设置该选项以后,如果在两小时内没有数据的通信时,TCP会自动发送一个活动探测数据报文。
handler代码如下
//这个Handler是用来处理客户端channel的IO事件
public class MyServerHandler extends ChannelInboundHandlerAdapter {
//处理读事件 ChannelHandlerContext :上下文,含有通道,pipline ,地址 ; msg就是数据
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
ByteBuf buffer = (ByteBuf) msg;
System.out.println("MyServerHandler,接收到:"+ctx.channel().remoteAddress()+"发来的消息:"+buffer.toString(CharsetUtil.UTF_8));
}
//数据读取完毕
@Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
//给客户端恢复消息
ctx.writeAndFlush(Unpooled.copiedBuffer("你好,我收到了消息",CharsetUtil.UTF_8));
}
}
上面有几个陌生的类
- ChannelInboundHandlerAdapter :这个是为Channel通道绑定处理事件的Handler,用来接收请求。
- ByteBuf :Netty提供的byte缓冲区
- Unpooled.copiedBuffer :工具,把数据拷贝到ByteBuf
客户端要做如下事情
- 创建 NioEventLoopGroup 用于监听服务端的事件
- 创建 Bootstrap启动对象 ,加入 NioEventLoopGroup
- 一样要通过 Bootstrap 添加Handler ,需要自己创建Handler
- 编写Handler ,通过继承ChannelInboundHandlerAdapter 来对不同的事件做不同的处理
客户端代码如下
public class NettyClient {
public static void main(String[] args) throws InterruptedException {
//客户端线程循环组
NioEventLoopGroup eventExecutors = new NioEventLoopGroup();
//客户端启动对象
Bootstrap bootstrap = new Bootstrap();
try {
bootstrap.group(eventExecutors)
//指定客户端使用的通道类型
.channel(NioSocketChannel.class)
//添加客户端处理器
.handler(new ChannelInitializer() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
//添加客户端处理器
ch.pipeline().addLast(new MyClientHandler());
}
});
//链接服务端
ChannelFuture channelFuture = bootstrap
.connect(new InetSocketAddress("127.0.0.1", 6000)).sync();
//监听关闭事件
channelFuture.channel().closeFuture().sync();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
eventExecutors.shutdownGracefully();
}
}
}
客户端handler代码如下
//这个Handler是用来处理客户端channel的IO事件
public class MyClientHandler extends ChannelInboundHandlerAdapter {
//链接激活
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
//给服务端发送消息
ctx.writeAndFlush(Unpooled.copiedBuffer("你好服务端",CharsetUtil.UTF_8));
}
//处理读事件 ChannelHandlerContext :上下文,含有通道,pipline ,地址 ; msg就是数据
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
ByteBuf buffer = (ByteBuf) msg;
System.out.println("MyClientHandler,接收到:"+ctx.channel().remoteAddress()+"发来的消息:"+buffer.toString(CharsetUtil.UTF_8));
}
//处理异常
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
ctx.close();
}
}
分别启动服务端和客户端,最终效果如下
- 服务端
- 客户端
在上面的入门案例中涉及到一些类,这里做一个解释
NioEventLoopGroupNioEventLoopGroup 继承于 EventLoopGroup ,代表Nio 事件循环组,用来循环监听注册的Channel的IO事件,如上图,我们需要两个NioEventLoopGroup,一个为BossGroup做事件监听 ,一个为 WorkGroup做事件监听
ServerBootstrap,BootstrapServerBootstrap是Netty服务端启动引导对象 ,Bootstrap是客户端的启动引导对象,作用都差不多,主要是用来配置整个netty程序,整合各个组件。
ServerBootstrap 常用方法如下:
- group(EventLoopGroup parentGroup, EventLoopGroup childGroup) : 该方法用于服务端,主要是为 BossGroup(Acceptor) 设置 EventLoopGroup 来监听连接事件,以及为 WorkGroup 设置EventLoopGroup来监听读写事件。
- channel(Class
关注打赏
热门博文
- 十九.SpringCloud极简入门-基于Docker整合ELK分布式日志解决方案
- 八.全文检索ElasticSearch经典入门-深入理解ElasticSearch核心原理
- 七.全文检索ElasticSearch经典入门-聚合查询
- 六.全文检索ElasticSearch经典入门-高亮
- 招人啦招人啦快来成为我的同事
- 四.全文检索ElasticSearch经典入门-字符串查询&批量查询&DSL查询过滤&乐观锁
- 三.全文检索ElasticSearch经典入门-索引CRUD&分词器&文档映射&文档CRUD
- 二.全文检索ElasticSearch经典入门-倒排索引&核心概念&集群概念&分片机制
- 使用canal解决Mysql和ElasticSearch数据同步问题
- 五.MongoDB入门-SpringData操作MongoDB