初探netty

什么是netty

netty是jboss提供的一个java开源框架,netty提供异步的、事件驱动的网络应用程序框架和工具,用以快速开发高性能、高可用性的网络服务器和客户端程序。也就是说netty是一个基于nio的编程框架,使用netty可以快速的开发出一个网络应用。
由于java 自带的nio api使用起来非常复杂,并且还可能出现 Epoll Bug,这使得我们使用原生的nio来进行网络编程存在很大的难度且非常耗时。但是netty良好的设计可以使开发人员快速高效的进行网络应用开发。

核心架构

如下图所示:netty的核心是支持零拷贝的bytebuf缓冲对象、通用通信api和可扩展的事件模型;它支持多种传输服务并且支持HTTP、Protobuf、二进制、文本、WebSocket 等一系列常见协议,也支持自定义协议。

netty中的一些核心概念

1、bootstrap、serverBootstrap:bootstrap的意思是引导,其主要作用是配置整个netty程序,将各个组件整合起来。serverBootstrap是服务器端的引导类。bootstrap用于连接远程主机它有一个EventLoopGroup ;serverBootstrap用于监听本地端口有两个EventLoopGroup。

2、eventLoop:eventLoop维护了一个线程和任务队列,支持异步提交执行任务。

3、eventLoopGroup:eventLoopGroup 主要是管理eventLoop的生命周期,可以将其看作是一个线程池,其内部维护了一组eventLoop,每个eventLoop对应处理多个Channel,而一个Channel只能对应一个eventLoop。

4、channelPipeLine:是一个包含channelHandler的list,用来设置channelHandler的执行顺序。

5、Channel:Channel代表一个实体(如一个硬件设备、一个文件、一个网络套接字或者一个能够执行一个或者多个不同的IO操作的程序组件)的开放链接,如读操作和写操作。

6、Futrue、ChannelFuture :Future提供了另一种在操作完成时通知应用程序的方式。这个对象可以看作是一个异步操作结果的占位符;它将在未来的某个时刻完成,并提供对其结果的访问。netty的每一个出站操作都会返回一个ChannelFuture。future上面可以注册一个监听器,当对应的事件发生后会出发该监听器。

7、ChannelInitializer:它是一个特殊的ChannelInboundHandler,当channel注册到eventLoop上面时,对channel进行初始化

*8、ChannelHandler:用来处理业务逻辑的代码,ChannelHandler是一个父接口,ChannelnboundHandler和ChannelOutboundHandler都继承了该接口,它们分别用来处理入站和出站。

9、ChannelHandlerContext:允许与其关联的ChannelHandler与它相关联的ChannlePipeline和其它ChannelHandler来进行交互。它可以通知相同ChannelPipeline中的下一个ChannelHandler,也可以对其所属的ChannelPipeline进行动态修改。

快速构建一个hello world 程序

环境搭建

我这里使用的springboot,内置集成了netty依赖,当然也可以建立一个空的maven项目,再去导入单独的netty依赖也是可以实现的。
springboot下pom文件:

单独使用netty项目下pom文件:

代码编写

服务类,主要用于初始化和运行ServerBootstrap对象

@Component
public class MyServer {
    //设置 boss线程池和worker线程组,这两个线程组相当于死循环的不停监听,boss线程组给worker分配任务
    EventLoopGroup bossGroup = new NioEventLoopGroup();
    EventLoopGroup workerGroup = new NioEventLoopGroup();
    //启动对象
    ServerBootstrap serverBootstrap = new ServerBootstrap();
    private static class SingletionServer{
        static final MyServer instance = new MyServer();
    }
    public static MyServer getInstance(){
        return SingletionServer.instance;
    }

    public void start()  {
       try{
           System.out.println("启动了家人们!");
           //为启动对象绑定线程池。绑定channel(通道),这里使用的是NIO实现(非阻塞异步)
           serverBootstrap.group(bossGroup,workerGroup).channel(NioServerSocketChannel.class)
                   .childHandler(new ServerInitializer());//绑定我们写好的初始化类
            //设置开启和关闭
           ChannelFuture channelFuture = serverBootstrap.bind(9999).sync();
           channelFuture.channel().closeFuture().sync();
       } catch (Exception e) {

       } finally {
           bossGroup.shutdownGracefully();
           workerGroup.shutdownGracefully();
       }

    }
}

编写初始化类

//初始化类,主要用于绑定我们的handler
public class ServerInitializer extends ChannelInitializer<SocketChannel> {
    //channel被注册后立马回调用
    @Override
    protected void initChannel(SocketChannel socketChannel) throws Exception {
        ChannelPipeline pipeline = socketChannel.pipeline();
        pipeline.addLast("HttpServerCodec",new HttpServerCodec())
                .addLast("HelloHandler",new HelloHandler());
    }
}

编写handler类

handler类是我们处理业务逻辑的类

//我们的业务处理handler
public class HelloHandler extends SimpleChannelInboundHandler<HttpObject> {
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, HttpObject httpObject) throws Exception {
        //只处理 HttpRequest ,若非HttpRequest 不写IF会报异常
       if(httpObject instanceof HttpRequest){
            //定义我们的返回结果
            ByteBuf content = Unpooled.copiedBuffer("Hello world!!", CharsetUtil.UTF_8);
            //定义我们的response
            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);
        }
    }
}

springboot配置

由于我使用的springboot,MyServer的启动如果写在springboot的启动类中显然是不合适的,所以我们写一个配置类,ApplicationListener接口,使其启动时启动我们的Myserver。

@Component
public class NettybootServerInitConfig implements ApplicationListener<ContextRefreshedEvent> {
    @Override
    public void onApplicationEvent(ContextRefreshedEvent event) {
        if(event.getApplicationContext().getParent() == null){
            MyServer.getInstance().start();
        }
    }
}

测试

启动程序

使用cmd命令行执行curl命令发送一个http请求给localhost:9999

可以看到我们的服务端确实接收到了请求,并返回了一个长度为13的字符串数据,并且code为200 请求成功

posted @ 2020-12-17 16:21  DirtyShady  阅读(68)  评论(0)    收藏  举报