目录
目标
Netty版本
Netty官方API
NioEventLoopGroup和DefaultEventLoop的区别
EventLoopGroup实现对内部EventLoop的轮询
EventLoop对普通任务和定时任务的实现
执行普通任务
执行定时任务
划分EventLoopGroup职责
简言
实现
指定EventLoopGroup操作ChannelHandler
简言
实现
io.netty
netty-all
4.1.87.Final
Netty API Reference (4.1.87.Final)https://netty.io/4.1/api/index.html
io.netty.channel.nio.NioEventLoopGroup和io.netty.channel.DefaultEventLoop是较为常用的EventLoop,最大的区别在于前者可以处理IO事件、普通任务、定时任务;后者只能处理普通任务和定时任务。
package com.ctx.netty;
import io.netty.channel.DefaultEventLoopGroup;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class EventLoopTest {
public static void main(String[] args) {
//设置组内2个EventLoop
DefaultEventLoopGroup defaultEventLoop = new DefaultEventLoopGroup(2);
//循环输出5次
for(int i=0;i<5;i++){
//根据打印的结果,发现组内的2个对象被轮询了。
log.info(String.valueOf(defaultEventLoop.next()));
}
}
}
package com.ctx.netty;
import io.netty.channel.DefaultEventLoopGroup;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class EventLoopTest {
public static void main(String[] args) {
DefaultEventLoopGroup defaultEventLoop = new DefaultEventLoopGroup(2);
//执行普通任务
//或者defaultEventLoop.next().submit(()->{});
defaultEventLoop.next().execute(()->{
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("Hello World!");
});
System.out.println("main");
}
}
结果
package com.ctx.netty;
import io.netty.channel.DefaultEventLoopGroup;
import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.TimeUnit;
@Slf4j
public class EventLoopTest {
public static void main(String[] args) {
DefaultEventLoopGroup defaultEventLoop = new DefaultEventLoopGroup(2);
log.info(String.valueOf(System.currentTimeMillis()));
//执行普通任务
defaultEventLoop.next().scheduleAtFixedRate(
//任务对象
() -> {
log.info(String.valueOf(System.currentTimeMillis()));
},
//初始延迟时间,0表示不延迟(受到时间单位的影响)。
1,
//间隔执行时间(受到时间单位的影响)。
2,
//时间单位
TimeUnit.MINUTES
);
System.out.println("main");
}
}
我在之前的一篇文章《初识Netty并用Netty搭建最基本的网络服务器和客户端_我的身前一尺是我的世界的博客-CSDN博客》中例举了一组简单的服务端和客户端的实现。文章中的服务端我只用了一个EventLoopGroup,但是Netty更推荐将不同的事件划分给不同的EventLoopGroup。
package com.ctx.netty;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.string.StringDecoder;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class NettyServer {
public static void main(String[] args) {
//Netty服务端启动器
ServerBootstrap serverBootstrap = new ServerBootstrap();
/**
* 官方API对group(EventLoopGroup parentGroup,EventLoopGroup childGroup)法的描述:
* Set the EventLoopGroup for the parent (acceptor) and the child (client).
* These EventLoopGroup's are used to handle all the events and IO for ServerChannel and Channel's.
*
* 译文:
* parentGroup对ServerChannel负责,它处理accept事件;默认1个线程数。
* childGroup对Channel负责,它处理读写操作;默认cpu核心数*2个线程。
*/
serverBootstrap.group(new NioEventLoopGroup(),new NioEventLoopGroup(2));
//选择服务器的ServerSocketChannel实现,这里我选择NIO。
serverBootstrap.channel(NioServerSocketChannel.class);
//设置事件类型
serverBootstrap.childHandler(new ChannelInitializer() {
@Override
protected void initChannel(NioSocketChannel nioSocketChannel) throws Exception {
//把ByteBuf解码为String
nioSocketChannel.pipeline().addLast(new StringDecoder());
//pipeline是流水线,Handler是工序,流水线可以有多个工序。
//Handler分为入栈(Inbound)和出栈(Outbound)
//自定义Handler
nioSocketChannel.pipeline().addLast(new ChannelInboundHandlerAdapter(){
//处理读事件
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
log.info(msg.toString());
}
});
}
});
//设置服务器的监听端口
serverBootstrap.bind(8999);
}
}
我们知道,pipeline是流水线,ChannelHandler相当于工序,一个或多个工序组成了流水线。不同的工序耗时可能不同,如果有的工序耗时太久,则会对其他工序的执行产生影响,为此,Netty为我们提供了指定EventLoopGroup操作ChannelHandler的API。
package com.ctx.netty;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.DefaultEventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.string.StringDecoder;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class NettyServer {
public static void main(String[] args) {
DefaultEventLoopGroup defaultEventLoopGroup = new DefaultEventLoopGroup();
//Netty服务端启动器
ServerBootstrap serverBootstrap = new ServerBootstrap();
serverBootstrap.group(new NioEventLoopGroup(),new NioEventLoopGroup(2));
//选择服务器的ServerSocketChannel实现,这里我选择NIO。
serverBootstrap.channel(NioServerSocketChannel.class);
//设置事件类型
serverBootstrap.childHandler(new ChannelInitializer() {
@Override
protected void initChannel(NioSocketChannel nioSocketChannel) throws Exception {
//把ByteBuf解码为String
nioSocketChannel.pipeline().addLast(new StringDecoder());
nioSocketChannel.pipeline().addLast("Handler",new ChannelInboundHandlerAdapter(){
//处理读事件
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
log.info(msg.toString());
//把msg传给下一个Handler
ctx.fireChannelRead(msg);
}
}).addLast(defaultEventLoopGroup,"Handler2",new ChannelInboundHandlerAdapter(){
//处理读事件
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
log.info(msg.toString());
}
});
}
});
//设置服务器的监听端口
serverBootstrap.bind(8999);
}
}