netty之入门篇

什么是netty

netty是一个nio的客户端服务器rpc框架,开发者可以很快很容易和开发出协议服务器和客户端等网络应用。它是一个高性能的基于socket的网络通信框架。它在高并发的业务场景下常常有很好的性能表现。

netty核心组件

  • Channel
  • 回调
  • Future
  • 事件和ChannelHandler

这些构建代表了不同类型的构造:资源、逻辑以及通知。你的应用将使用它们来访问网络以及流经网络的数据。

Channel

Channel是jave nio的一个基本构造。它代表一个到实体的开放链接,如读操作和写操作,即可将channel看作是一个输入输出的载体。因此它应该具有打开或者关闭,连接或者断开连接的功能。

回调

一个回调其实就是一个方法,一个指向已经被提供给另外一个方法的方法的引用。这使得后者(接受回调的方法)可以在适当的时候调用前者。

Future

Future提供了另外一种在操作完成时通知应用程序的方式,这个对象可以看做是一个异步操作的结果的占位符:他将在未来的某个时刻完成,并提供对其结果的访问。

事件和ChannelHandler

Netty使用不同的事件来通知我们状态的改变或者是操作的状态,这使得我们能够基于已经发生的事件来触发适当的动作。这些动作可能是:

  • 记录日志
  • 数据转换
  • 流控制
  • 应用程序逻辑

也就是说我们对数据的处理逻辑一般都会定义在ChannelHandler中。

因此,在开发netty程序时,请注意以下3个关键点:

  1. 针对不同类型的时间来调用ChannelHandler
  2. 应用程序通过文件或者扩展ChannelHandler来挂钩到时间的生命周期,并且提供自定义的应用程序逻辑
  3. 在架构上,ChannelHandler有助于保持业务逻辑与网络处理代码的分离。这简化了开发过程,因为代码必须不断地演化以响应不断变化的需求

第一个netty程序

使用netty开发了一个简单的http程序,使用浏览器作为客户端,我们只需要实现服务端逻辑。

netty的编程模型一般分为三步,首先是定义一个程序启动入口,其次是定义一个初始化器,最后需要指定一个ChannelHandler。

首先需要定义一个程序的启动入口,对于代码中每个组件的详细解释此处暂时不做说明,代码清单如下:

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;

public class TestServer {
    public static void main(String[] args) throws Exception {

        //不断从客户端接受连接,但是不对连接作出处理,然后将连接转给worker
        EventLoopGroup bossGroup = new NioEventLoopGroup();
        //处理连接
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        try {
            ServerBootstrap bootstrap = new ServerBootstrap();
            bootstrap.group(bossGroup, workerGroup)
                    .channel(NioServerSocketChannel.class)//指定所使用的nio传输channel
                    .childHandler(new TestServerInitializer());

            ChannelFuture channelFuture = bootstrap.bind(8899).sync();
            channelFuture.channel().closeFuture().sync();
        }finally {
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }

}

netty每次启动时,都会首先执行初始化方法,在初始化器中我们可以添加一些自定义或者已经实现的处理器,比如编解码器等等。如下所示,TestServerInitializer继承自ChannelInitializer并且实现了initChannel方法,该方法在server创建时就会被执行,代码清单如下:

import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.http.HttpServerCodec;


public class TestServerInitializer extends ChannelInitializer<SocketChannel> {

    //server 一旦被创建就会执行这个方法
    protected void initChannel(SocketChannel socketChannel) throws Exception {
        ChannelPipeline pipeline = socketChannel.pipeline();
        pipeline.addLast("httpServerCodec", new HttpServerCodec());
        //添加自定义的处理器
        pipeline.addLast("testHttpServerHandler", new TestHttpServerHandler());

    }
}

对于我们自定义的处理器,通常通过ChannelInitializerinitChannel方法添加,比如在我们实现的代码中就添加了一个名为testHttpServerHandler的自定义处理器,该处理器会读取客户端的请求,并返回客户端请求响应。定义一个TestHttpServerHandler 类,该类继承自抽象类SimpleChannelInboundHandler,并需要实现channelRead0方法,该方法定义了定义了客户端的请求的读取和返回逻辑。代码清单如下:

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.*;
import io.netty.util.CharsetUtil;


public class TestHttpServerHandler extends SimpleChannelInboundHandler<HttpObject> {

    //读取客户端发送的请求,并返回客户端请求响应
    protected void channelRead0(ChannelHandlerContext ctx, HttpObject msg) throws Exception {
        if (msg instanceof HttpRequest) {
            HttpRequest httpRequest=(HttpRequest)msg;
            System.out.println("请求方法名:"+httpRequest.method().name());

            ByteBuf content = Unpooled.copiedBuffer("hello world", CharsetUtil.UTF_8);
            FullHttpResponse 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);
            ctx.channel().close();
        }

    }

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        System.out.println("channelActive");
        super.channelActive(ctx);
    }

    @Override
    public void channelRegistered(ChannelHandlerContext ctx) throws Exception {
        System.out.println("channelRegistered");
        super.channelRegistered(ctx);
    }

    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        System.out.println("channelInactive");
        super.channelInactive(ctx);
    }

    @Override
    public void channelUnregistered(ChannelHandlerContext ctx) throws Exception {
        System.out.println("channelUnregistered");
        super.channelUnregistered(ctx);
    }

    @Override
    public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
        System.out.println("handlerAdded");
        super.handlerAdded(ctx);
    }
}

在server启动时,netty首先会监听到handler的add,然后会注册该handler,将该handler的状态更改为active,然后会处理该handler的msg方法,即channelRead0方法中我们自定义的处理逻辑,处理完以后会将该handler置为inactive,最终将该handler的注册状态更改为unregistered。

为了验证以上的handler的生命周期,我们重写了SimpleChannelInboundHandler的生命周期方法,并添加了打印字段。

启动TestServer,并在浏览器输入localhost:8899,浏览器会返回hello world字符串,在终端中,我们可以看到如下输入:

  • handlerAdded
  • channelRegistered
  • channelActive
  • 请求方法名:GET
  • channelInactive
  • channelUnregistered
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值