Netty学习笔记之WorkerGroup线程模型、任务队列、HTTP服务实例

系列文章目录

一、Netty编程实例


提示:写完文章后,目录可以自动生成,如何生成可参考右边的帮助文档


提示:以下是本篇文章正文内容,下面案例可供参考

一、WorkerGroup线程模型

BossGroup和WorkerGroup是如何确定自己有多少个EventGroup的?
bg和wg含有的子线程(NioEvent Group)的个数是默认CPU核数*2的 , 比如一台计算机是双CPU4核的,那么这个值就是8

NettyRuntime.availableProcessors()即可获取CPU的核数

假设创建有16个子线程 , 那么当第17个客户端连接的时候 , 是使用1号线程去处理的 , 也就是循环的处理

// bossGroup仅处理连接请求
NioEventLoopGroup bossGroup = new NioEventLoopGroup(1);
// workerGroup和客户端做业务处理
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 {
        /**
         * 解决阻塞问题,存在长时间的操作的业务代码的时候 ,异步执行
         * */
        // 用户程序自定义的普通任务
        // 下面的任务会被提交到NIOEventLoop的taskQueue中去,"go on..."会被立即输出,然后再去执行execute方法
        // 也就是先执行后续的业务代码,再去回复客户端消息
        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机制

  1. 当Future对象刚刚创建时, 处于非完成状态, 调用者可以通过返回的ChannelFutrue来获取操作执行的状态, 注册监听函数来执行完成后的操作
  2. 常见操作
    • 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 {
        // 向管道加入处理器
        // 1. 得到管道
        ChannelPipeline pipeline = socketChannel.pipeline();
        // 2. 加入一个netty提供的httpServerCodec编解码器HttpServerCodec
        pipeline.addLast("myHttpServerCodec",new HttpServerCodec());
        // 3. 增加一个自定义的handler
        pipeline.addLast("myTestHttpServerHandler",new TestHttpServerHandler());
    }
}
public class TestHttpServerHandler extends SimpleChannelInboundHandler<HttpObject> {
    // 当有读取事件时,就会触发 , 用于读取客户端数据
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, HttpObject msg) throws Exception {
        // 判断msg 是不是http request请求
        if (msg instanceof HttpRequest){
            // msg真实类型是DefaultHttpRequest
            System.out.println("msg 类型 "+msg.getClass());
            System.out.println("客户端地址 "+ctx.channel().remoteAddress());

            // 通过uri过滤特定资源
            HttpRequest httpRequest = (HttpRequest) msg;
            // 获取uri
            URI uri = new URI((httpRequest.uri()));
            if ("/favicon.ico".equals(uri.getPath())){
                System.out.println("请求图标,不做响应");
                return;
            }
            // 回复信息给浏览器[要求满足http协议]
            ByteBuf content = Unpooled.copiedBuffer("hello world!", CharsetUtil.UTF_8);

            //构造http的响应
            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());

            // 将构建好的response返回
            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 uri = new URI((httpRequest.uri()));
if ("/favicon.ico".equals(uri.getPath())){
    System.out.println("请求图标,不做响应");
    return;
}
* 每个浏览器对应的pipeline和handler是独立的

http协议不是长连接 , 用完就会断掉 , 所以同一个浏览器第二次请求时 , 会被认为是一个新的浏览器

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值