系列文章目录
一、Netty编程实例
提示:写完文章后,目录可以自动生成,如何生成可参考右边的帮助文档
提示:以下是本篇文章正文内容,下面案例可供参考
一、WorkerGroup线程模型
BossGroup和WorkerGroup是如何确定自己有多少个EventGroup的? bg和wg含有的子线程(NioEvent Group)的个数是默认CPU核数*2的 , 比如一台计算机是双CPU4核的,那么这个值就是8
NettyRuntime.availableProcessors() 即可获取CPU的核数
假设创建有16个子线程 , 那么当第17个客户端连接的时候 , 是使用1号线程去处理的 , 也就是循环的处理
NioEventLoopGroup bossGroup = new NioEventLoopGroup(1);
NioEventLoopGroup workerGroup = new NioEventLoopGroup();
这里通过1来限制BossGroup的线程数为1 , 因为BossGroup的线程数不需要有那么多
pipeline底层是一个双向链表,通过pipeline能获得channel(二者是相互包含的),还能反向拿到eventLoop
二、任务队列的Task定时和非定时两种使用场景
下面的两段代码都是基于 Netty编程实例做的修改 , 向NettyServerHandler 加入了异步任务
1.用户自定义的普通任务;
代码如下(示例):
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
ctx.channel().eventLoop().execute(new Runnable() {
@SneakyThrows
@Override
public void run() {
Thread.sleep(10*1000);
ctx.writeAndFlush(Unpooled.copiedBuffer("hello,cli",CharsetUtil.UTF_8));
}
});
ctx.channel().eventLoop().execute(new Runnable() {
@SneakyThrows
@Override
public void run() {
Thread.sleep(10*1000);
ctx.writeAndFlush(Unpooled.copiedBuffer("hello,cli",CharsetUtil.UTF_8));
}
});
System.out.println("go on...");
}
2.用户自定义定时任务
代码如下(示例):
ctx.channel().eventLoop().schedule(new Runnable() {
@SneakyThrows
@Override
public void run() {
Thread.sleep(10*1000);
ctx.writeAndFlush(Unpooled.copiedBuffer("hello,cli,3",CharsetUtil.UTF_8));
}
},5, TimeUnit.SECONDS);
Netty抽象除了两组线程池, BossGroup专门负责接收客户端的连接,WorkerGroup专门负责网络读写操作
三、Future-Listener机制
- 当Future对象刚刚创建时, 处于非完成状态, 调用者可以通过返回的ChannelFutrue来获取操作执行的状态, 注册监听函数来执行完成后的操作
- 常见操作
- isDone 判断当前操作是否完成
- isSuccess 判断已完成的操作是否成功
- getCause 获取已完成操作失败的原因
- isCancelled 判断已完成的当前操作是否被取消
- addListener 注册监听器
四、HTTP服务实例
代码实例
public class TestServer {
public static void main(String[] args) {
EventLoopGroup bossGroup = new NioEventLoopGroup(1);
EventLoopGroup workerGroup = new NioEventLoopGroup();
try{
ServerBootstrap serverBootstrap = new ServerBootstrap();
serverBootstrap.group(bossGroup,workerGroup)
.channel(NioServerSocketChannel.class)
.childHandler(new TestServerInitializer());
ChannelFuture cf = serverBootstrap.bind(4396).sync();
cf.channel().closeFuture().sync();
}catch (Exception e){
e.printStackTrace();
}finally {
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
}
public class TestServerInitializer extends ChannelInitializer<SocketChannel> {
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
ChannelPipeline pipeline = socketChannel.pipeline();
pipeline.addLast("myHttpServerCodec",new HttpServerCodec());
pipeline.addLast("myTestHttpServerHandler",new TestHttpServerHandler());
}
}
public class TestHttpServerHandler extends SimpleChannelInboundHandler<HttpObject> {
@Override
protected void channelRead0(ChannelHandlerContext ctx, HttpObject msg) throws Exception {
if (msg instanceof HttpRequest){
System.out.println("msg 类型 "+msg.getClass());
System.out.println("客户端地址 "+ctx.channel().remoteAddress());
HttpRequest httpRequest = (HttpRequest) msg;
URI uri = new URI((httpRequest.uri()));
if ("/favicon.ico".equals(uri.getPath())){
System.out.println("请求图标,不做响应");
return;
}
ByteBuf content = Unpooled.copiedBuffer("hello world!", CharsetUtil.UTF_8);
DefaultFullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK, content);
response.headers().set(HttpHeaderNames.CONTENT_TYPE,"text/plain");
response.headers().set(HttpHeaderNames.CONTENT_LENGTH,content.readableBytes());
ctx.writeAndFlush(response);
}
}
}
为什么浏览器只请求一次,但服务器显示两次请求
msg 类型 class io.netty.handler.codec.http.DefaultHttpRequest
客户端地址 /0:0:0:0:0:0:0:1:53907
msg 类型 class io.netty.handler.codec.http.DefaultHttpRequest
客户端地址 /0:0:0:0:0:0:0:1:53907
事实上,浏览器请求了两次服务器, 一次的请求浏览器图标, 一次是请求对应的接口
如何避免服务器响应除接口外的其它请求
HttpRequest httpRequest = (HttpRequest) msg;
URI uri = new URI((httpRequest.uri()));
if ("/favicon.ico".equals(uri.getPath())){
System.out.println("请求图标,不做响应");
return;
}
* 每个浏览器对应的pipeline和handler是独立的
http协议不是长连接 , 用完就会断掉 , 所以同一个浏览器第二次请求时 , 会被认为是一个新的浏览器
|