Netty-尚硅谷(6. 异步模型和HTTP示例)学习笔记

上一篇 :5. Netty模型

下一篇 :7. Netty 核心模块

下一篇 :

1. 基本介绍

  1. 异步的概念和同步相对。当一个异步过程调用发出后,调用者不能立刻得到结果。实际处理这个调用的组件在完成后,通过状态、通知和回调来通知调用者。
  2. Netty 中的 I/O 操作是异步的,包括 Bind、Write、Connect 等操作会简单的返回一个 ChannelFuture
  3. 调用者并不能立刻获得结果,而是通过 Future-Listener 机制,用户可以方便的主动获 取或者通过通知机制获得 IO 操作结果
  4. Netty 的异步模型是建立在 future 和 callback 的之上的。callback 就是回调。重点说 Future,它的核心思想是:假设一个方法 fun,计算过程可能非常耗时,等待 fun返回 显然不合适。那么可以在调用 fun 的时候,立马返回一个 Future,后续可以通过 Future去监控方法 fun 的处理过程(即 : Future-Listener 机制)

2. Future 说明

  1. 表示异步的执行结果, 可以通过它提供的方法来检测执行是否完成,比如检索计算等等.
  2. ChannelFuture 是一个接口 :
    在这里插入图片描述
    我们可以添加监听器,当监听的事件发生时,就会通知到监听器.

3. 工作原理示意图

在这里插入图片描述

  • 说明:
  1. 在使用 Netty 进行编程时,拦截操作和转换出入站数据只需要您提供 callback 或利用 future 即可。这使得链式操作简单、高效, 并有利于编写可重用的、通用的代码。
  2. Netty 框架的目标就是让你的业务逻辑从网络基础应用编码中分离出来

4. Future-Listener 机制

  1. 当 Future 对象刚刚创建时,处于非完成状态,调用者可以通过返回的 ChannelFuture 来获取操作执行的状态,注册监听函数来执行完成后的操作。
  2. 常见有如下操作

• 通过 isDone 方法来判断当前操作是否完成;
• 通过 isSuccess 方法来判断已完成的当前操作是否成功;
• 通过 getCause 方法来获取已完成的当前操作失败的原因;
• 通过 isCancelled 方法来判断已完成的当前操作是否被取消;
• 通过 addListener 方法来注册监听器,当操作已完成(isDone 方法返回完成),将会通知 指定的监听器;如果 Future 对象已完成,则通知指定的监听器

  1. 代码示例

    给一个 ChannelFuture 注册监听器,来监控我们关系的事件

    channelFuture.addListener(new ChannelFutureListener() {
         @Override
         public void operationComplete(ChannelFuture channelFuture) throws Exception {
              if (channelFuture.isSuccess()){
                   System.out.println("监听端口 6668 成功");
              }else {
                   System.out.println("监听端口 6668 失败");
              }
          }
    });
    

5. 小结

相比传统阻塞 I/O,执行 I/O 操作后线程会被阻塞住, 直到操作完成;
异步处理的好处是不会造成线程阻塞,线程在 I/O 操作期间可以执行别的程序,在高并发情形下会更稳定和更高的吞吐量

6. 快速入门实例-HTTP服务

  • 要求:
  1. Netty 服务器在 6668 端口监听,浏览器发出请求 "http://localhost:6668/ "
  2. 服务器可以回复消息给客户端 "Hello! 我是服务器 5 " , 并 对特定请求资源进行过滤.
  • 目的:

Netty 可以做Http服务开发,并且理解Handler实例 和客户端及其请求的关系
在这里插入图片描述

  • 编写代码 —— 服务端代码
  1. 编写 服务端 :HttpServer

    public class HttpServer {
        public static void main(String[] args) throws Exception{
            EventLoopGroup bossGroup = new NioEventLoopGroup(1);
            EventLoopGroup workerGroup = new NioEventLoopGroup(8);
    
            try {
                // 创建 服务端 启动对象,并配置参数
                ServerBootstrap serverBootstrap = new ServerBootstrap();
                serverBootstrap.group(bossGroup, workerGroup)
                        .channel(NioServerSocketChannel.class)
                        // 使用自己写的 ServerInitializer 完成初始化
                        .childHandler(new HttpServerInitializer());
                System.out.println("服务器准备好了……");
    
                ChannelFuture channelFuture = serverBootstrap.bind(6660).sync();
                channelFuture.channel().closeFuture().sync();
            }finally {
                bossGroup.shutdownGracefully();
                workerGroup.shutdownGracefully();
            }
        }
    }
    
  2. 编写 服务初始化器 :HttpServerInitializer

    public class HttpServerInitializer extends ChannelInitializer<SocketChannel> {
        @Override
        protected void initChannel(SocketChannel socketChannel) throws Exception {
            // 向管道加入处理器
            // 得到管道
            ChannelPipeline pipeline = socketChannel.pipeline();
    
            // 加入一个 Netty 提供的 httpServerCodec (CoDec => Coder + Decoder => 编解码器)
            pipeline.addLast("MyHttpServerCodec",new HttpServerCodec());
            // 增加一个自己的 Handler
            pipeline.addLast("MyServerHandler", new HttpServerHandler());
        }
    }
    
  3. 编写 服务处理器 :HttpServerHandler

    /*
        1. SimpleChannelInboundHandler 是之前使用的 ChannelInboundHandlerAdapter 的子类
        2. HttpObject 这个类型表示, 客户端、服务端 相互通信的数据需要被封装成什么类型
     */
    public class HttpServerHandler extends SimpleChannelInboundHandler<HttpObject> {
        /**
         * 读取客户端数据
         * @param channelHandlerContext 上下文
         * @param httpObject 传递过来的消息
         * @throws Exception
         */
        @Override
        protected void channelRead0(ChannelHandlerContext channelHandlerContext, HttpObject httpObject) throws Exception {
            // 判断 httpObject 是不是一个 HttpRequest 请求
            if (httpObject instanceof HttpRequest){
                System.out.println("httpObject 的类型 :"+ httpObject.getClass());
                System.out.println("客户端的地址 : "+ channelHandlerContext.channel().remoteAddress());
    
                // 回复信息给浏览器,需要把数据封装成 HttpObject 类型
                // 创建一个 ButeBuf
                ByteBuf byteBuf = Unpooled.copiedBuffer("Hello,我是服务器", CharsetUtil.UTF_8);
                // 构建一个 Http 的响应,即 httpResponse  ;  后面的三个参数 :(Http 协议的版本, Http 的状态码, 需要传输的内容)
                FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK, byteBuf);
                // 设置文本的类型,及字符编码
                response.headers().set(HttpHeaderNames.CONTENT_TYPE, "text/plain;charset=utf-8");
                // 文本的长度
                response.headers().set(HttpHeaderNames.CONTENT_LENGTH, byteBuf.readableBytes());
                // 将构建好的 response 返回
                channelHandlerContext.writeAndFlush(response);
            }
        }
    }
    
  • 启动测试
    在这里插入图片描述

  • 编写代码 —— 对特定资源的过滤

    上面的服务端启动后,在页面上不止接收到了文本,还接收到了一个网页的图标
    在这里插入图片描述
    现在把它过滤掉

    修改 HttpServerHandler

    // 获取请求的 URI
            HttpRequest httpRequest = (HttpRequest) httpObject;
            URI uri = new URI(httpRequest.uri());
            // 判断请求路径为 /favicon.ico,就不做处理
            if ("/favicon.ico".equals(uri.getPath())){
                System.out.println("请求了 图标 资源,不做响应");
                return;
            }
    

    在这里插入图片描述

  • 启动测试
    在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

yuan_404

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值