netty入门,HelloNetty服务器

1 什么是netty?

是一个提供异步的、事件驱动的网络应用程序框架和工具,用以快速开发高性能、高可靠性的网络服务器和客户端程序。

1.1 netty的特点
  1. 提供易于使用的API,且可实现多种协议方式;
  2. 并发高:netty是基于NIO(同步非阻塞)的编程框架;
  3. 传输快,零拷贝:传统从I/O中读取数据到堆中,需要先从I/O流中读取放到缓冲区,再从缓冲区读取到堆内存,需要两次拷贝,若数据量大则造成资源浪费,netty会开辟一个新的堆内存,直接从I/O读取到堆中,实现零拷贝。
1.2 BIO/NIO/AIO

阻塞与非阻塞:是线程访问资源,该资源是否准备就绪的一种方式。

同步和异步:访问数据的一种机制,异步表示未返回处理结果也可以访问一下个内容,同步没有被提醒功能,异步有被提醒功能。

  BIO:同步阻塞,会主动观察数据是否准备就绪,一旦准备好则进行处理。传统的BIO是一应一答模式,会频繁创建和销毁线程,造成资源浪费。
  NIO:同步非阻塞(Non-Block-IO或者New IO),同样会主动观察数据是否准备就绪,但不会一直原地等待,会同时处理其他数据,并轮询查看该阻塞数据是否准备就绪,准备好了再进行处理。

一个selector是单线程的,会轮询查看客户端是否有请求,若有请求,则创建一个channel,channel直接从buffer读取数据,读完之后还有,不想stream读完就没了。
  AIO:异步非阻塞。异步阻塞IO是指,数据阻塞后,不用主动查看数据是否准备完毕,不轮询数据状态,原地等待,不处理其他数据,当前线程挂起,等待该数据准备完毕后通知,再处理。而异步非阻塞IO不用主动查看数据是否准备完毕,当前线程不挂起,直接处理其他数据,当该阻塞数据准备完毕后主动通知,再进行处理。

  1. 同步,就是我调用一个功能,该功能没有结束前,我死等结果,无回调,轮询等结果
  2. 异步,就是我调用一个功能,不需要知道该功能结果,该功能有结果后通知我(回调通知),不轮询,等回调
  3. 阻塞,就是调用我(函数),我(函数)没有接收完数据或者没有得到结果之前,我不会返回。
  4. 非阻塞,就是调用我(函数),我(函数)立即返回,通过select通知调用者

同步IO和异步IO的区别就在于:数据拷贝的时候进程是否阻塞
阻塞IO和非阻塞IO的区别就在于:应用程序的调用是否立即返回

2 为什么要用netty?

原生NIO的实现非常复杂,且存在不少Bug,而netty在修复了这些Bug的同时,封装了很多API,易于使用,提高了开发效率,几行代码便可以实现一个通信服务器。

3 netty的线程模型(Reactor线程模型)
  1. 单线程模型:所有的IO都由同一个NIO线程处理。但在高负载,大并发场景下,一个单线程在性能上无法满足,可能会导致客户端请求超时
  2. 多线程模型:由一组NIO线程处理IO操作,但当并发非常高时,请求处理能力仍然有限。
    在这里插入图片描述
  3. 主从线程模型:一组线程池接受请求(客户端登陆,握手,安全认证等),当整体链路建立完毕后,交付从线程池处理,一组线程池处理IO(编解码,读写等)
    在这里插入图片描述
3 HelloNetty服务器
  1. 构建一对主从线程
  2. 定义服务器启动类
  3. 为服务器添加channel(设置NIO类型)
  4. 设置处理从线程池的助手类初始化器:可理解为拦截器,内含很多助手类,即多个拦截器
  5. 监听启动和关闭服务器

添加pom.xml依赖

<dependency>
    <groupId>io.netty</groupId>
    <artifactId>netty-all</artifactId>
    <version>4.1.42.Final</version>
</dependency>

构建主从线程组、服务器启动类、通道服务器Socket类型和初始化器,绑定端口,并同步等待服务器启动,同时同步监听关闭的channel。

public class HelloNetty {
    public static void main(String[] args) throws Exception {
        // 定义一个线程组
        // 主线程组,用于接受客户端连接,不做处理
        EventLoopGroup bossGroup = new NioEventLoopGroup();
        // 从线程组,从主线程组接受任务
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        try {
            // netty服务器创建,ServerBootstrap 是一个服务器启动类
            ServerBootstrap serverBootstrap = new ServerBootstrap();
            serverBootstrap.group(bossGroup, workerGroup)           // 设置主从线程组
                           .channel(NioServerSocketChannel.class)   // 设置nio双向通道
                           .childHandler(new HelloNettyInitializer()); // 子处理器       
            // 启动server,设置端口为8088,且为同步方式
            ChannelFuture channelFuture = serverBootstrap.bind(8088).sync();
            // 监听关闭的channel,设置同步方式
            channelFuture.channel().closeFuture().sync();
        } finally {
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }
}

每一个channel由多个handler(助手类,即处理器)共同组成管道(pipeline),可理解成拦截器

创建初始化器,继承ChannelInitializer类,通过SocketChannel获取对应管道,然后向管道中添加助手类(handler),即处理函数。

/**
 * @@description 初始化器,channel注册后,会执行里面响应的初始化方法
 */
public class HelloNettyInitializer extends ChannelInitializer<SocketChannel> {
    @Override
    protected void initChannel(SocketChannel socketChannel) throws Exception {
        // 通过socketChannel获取对应的管道
        ChannelPipeline pipeline = socketChannel.pipeline();
        // 通过管道添加Handler
        // HttpServerCodec是由Netty提供的助手类
        // 当请求服务器,需要解码,响应到客户端做编码
        pipeline.addLast("HttpServerCodec", new HttpServerCodec());
        // 添加自定义助手类, 返回hello netty字符串
        pipeline.addLast("customHandler", new CustomHandler());
    }
}

创建自定义助手类,继承SimpleChannelInboundHandler类,该类主要用来处理接收数据的一些事件,如解析等,数据类型自定义,本案例处理http请求则选择为HttpObject,ChannelHandlerContext是管道中的channel的上下文对象,每个在pipeline中的ChannelHandler (入栈 或者 出栈),都对应一个ChannelHandlerContext与其绑定,channelRead0用于在缓冲区读取数据。

// SimpleChannelInboundHandler:对于请求来说,相当于[入站,入境]
public class CustomHandler extends SimpleChannelInboundHandler<HttpObject> {
    @Override
    protected void channelRead0(ChannelHandlerContext channelHandlerContext, HttpObject httpObject)
            throws Exception {
        // 获取channel
        Channel channel = channelHandlerContext.channel();
        if (httpObject instanceof HttpRequest) {
            // 显示客户端远程地址
            System.out.println(channel.remoteAddress());
            // 定义发送消息
            ByteBuf content = Unpooled.copiedBuffer("hello netty~", CharsetUtil.UTF_8);
            // 构建http response
            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());
            // 把响应刷到客户端
            channelHandlerContext.writeAndFlush(response);
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值