Netty

Netty

Netty是一个异步的,基于事件驱动的NIO网络应用框架。主要针对在TCP协议下,面向Client端的高并发应用,或者Peer-to-Peer场景下的大数据持续传输的应用。

Netty is an asynchronous event-driven network application framework for rapid development of maintainable high performance protocol servers & clients.

GitHub:https://github.com/netty/netty

20210731004327662304

线程模型

  1. 传统阻塞I/O服务模型

    特点:采用阻塞I/O模式获取输入的数据,每个连接都需要独立的线程完成数据的输入、业务处理、数据返回。

    瓶颈:当并发数很大时,会创建大量的线程,导致CPU上下文频繁切换,占用系统资源。连接创建后,如果当前线程暂时没有数据可读时,该线程会阻塞在Read操作上,造成线程资源浪费。

    传统阻塞I/O服务模型

  2. Reactor模型

    根据Reactor的数量和处理资源池线程数量的不同,有3种典型的实现。

    基于I/O复用模型:多个连接公用一个阻塞对象,应用程序只需要在一个阻塞对象等待,无需阻塞等待所有连接。当某个连接有新的数据可以处理时,操作系统通知应用程序,线程从阻塞态返回。

    基于线程池复用线程资源:不必再为每个连接创建线程,将连接完成后的业务处理任务分配给线程进行处理,一个线程可以处理多个连接的业务。

    iShot2022-03-14 19.41.40

    • 单Reactor单线程

      单Reactor单线程

    • 单Reactor多线程

      单Reactor多线程

    • 主从Reactor多线程

      主从Reactor多线程

  3. Netty线程模型(基于主从Reactor多线程模型,其中主从Reactor多线程模型有多个Reactor)

    Netty抽象出两组线程池BoosGroup负责接收客户端的连接,WorkGroup负责网络的读写。

    BoosGroupWorkGroup类型都是NioEventLoopGroup,相当于一个事件循环组,组中含有多个事件循环,每个事件循环是NioEventGroup

    NioEventLoop表示一个不断循环的执行处理任务的线程,每个NioEventLoop都有一个Selector,用于监听绑定在其上的Socket的网络通信。

    NioEventLoopGroup可以有多个线程,即可以含有多个NioEventLoop

    • 每个Boos NioEventLoop执行
      1. 轮训accept事件
      2. 处理accept事件,与clinet建立连接,生成NioSocketChannel,并将其注册到某个Worker NioEventLoop上的selector
      3. 处理任务队列,即runAllTasks
    • 每个Worker NioEventLoop执行
      1. 轮询read/write事件,即在对应的NoiSocketChannel处理I/O事件
      2. 处理任务队列,即runAllTasks

    每个Worker NioEventLoop处理业务是,会使用PipeLine,其中维护了多种处理器

    Netty线程模型

入门案例

Server端

package org.study.netty.simple.server;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;

/**
 * Netty 服务端
 * @author harlan
 */
public class Server {

    public static void main(String[] args) throws InterruptedException {
        //创建 BossGroup 和 WorkGroup
        //BoosGroup处理连接
        NioEventLoopGroup boosGroup = new NioEventLoopGroup();
        //WorkGroup处理业务
        NioEventLoopGroup workGroup = new NioEventLoopGroup();
        try {
            //创建服务端的启动对象,并配置启动参数
            ServerBootstrap serverBootstrap = new ServerBootstrap();
            //设置线程组
            serverBootstrap.group(boosGroup, workGroup)
                    //设置通道类型
                    .channel(NioServerSocketChannel.class)
                    //设置线程队列等待连接个数
                    .option(ChannelOption.SO_BACKLOG, 128)
                    //设置保持活动连接状态
                    .childOption(ChannelOption.SO_KEEPALIVE, true)
                    //设置PipeLine处理器
                    .childHandler(new ChannelInitializer<SocketChannel>() {

                        //创建一个通道初始化对象
                        @Override
                        protected void initChannel(SocketChannel ch) throws Exception {
                            //向PipeLine设置处理器
                            ch.pipeline().addLast(new ServerHandler());
                        }
                    });
            System.out.println("Server is ready");
            //绑定端口并进行同步处理
            ChannelFuture cf = serverBootstrap.bind(19999).sync();
            //对关闭通道进行监听
            cf.channel().closeFuture().sync();
        } finally {
            //关闭线程组
            boosGroup.shutdownGracefully();
            workGroup.shutdownGracefully();
        }

    }
}

Server Handler

package org.study.netty.simple.server;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import java.nio.charset.StandardCharsets;


/**
 * 自定义 Handler 需继承 Netty 规定好的某个 HandlerAdapter
 * @author harlan
 */
public class ServerHandler extends ChannelInboundHandlerAdapter {

    /**
     * 读取数据
     * @param ctx 通道上下文(管道、通道)
     * @param msg 数据
     * @throws Exception 异常
     */
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        System.out.println("server ctx::" + ctx);
        //将msg转为ByteBuf
        ByteBuf buf = (ByteBuf) msg;
        System.out.println("client msg::" + buf.toString(StandardCharsets.UTF_8));
        System.out.println("client ip::" + ctx.channel().remoteAddress());
    }

    /**
     * 读取数据完毕
     * @param ctx 通道上下文
     * @throws Exception 异常
     */
    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
        //将数据写入到缓存并刷新
        //对发送的数据进行编码
        ctx.writeAndFlush(Unpooled.copiedBuffer("Hello Client", StandardCharsets.UTF_8));
    }

    /**
     * 异常处理
     * @param ctx 通道上下文
     * @param cause 异常
     * @throws Exception 异常
     */
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        //关闭通道
        ctx.close();
    }
}

Clinet端

package org.study.netty.simple.client;

import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import java.net.InetSocketAddress;

/**
 * Netty 客户端
 * @author harlan
 */
public class Client {

    public static void main(String[] args) throws InterruptedException {
        //客户端需要一个事件循环组
        EventLoopGroup loopGroup = new NioEventLoopGroup();
        try {
            //创建客户端启动对象
            Bootstrap bootstrap = new Bootstrap();
            //设置参数
            bootstrap.group(loopGroup)
                    .channel(NioSocketChannel.class)
                    .handler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel ch) throws Exception {
                            ch.pipeline().addLast(new ClientHandler());
                        }
                    });
            System.out.println("Client is ready");
            //启动客户端
            ChannelFuture cf = bootstrap.connect(new InetSocketAddress(19999)).sync();
            //关闭通道进行监听
            cf.channel().closeFuture().sync();
        } finally {
            //关闭线程组
            loopGroup.shutdownGracefully();
        }
    }
}

Client Handler

package org.study.netty.simple.client;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;

import java.nio.charset.StandardCharsets;

/**
 * @author harlan
 */
public class ClientHandler extends ChannelInboundHandlerAdapter {

    /**
     * 通道就绪时触发
     * @param ctx 通道上下文
     * @throws Exception 异常
     */
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        System.out.println("client ctx::" + ctx);
        ctx.writeAndFlush(Unpooled.copiedBuffer("Hello Server", StandardCharsets.UTF_8));
    }

    /**
     * 当通道有读取事件时触发
     * @param ctx 通道上下文
     * @param msg 数据
     * @throws Exception 异常
     */
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        ByteBuf buf = (ByteBuf) msg;
        System.out.println("server msg::" + buf.toString(StandardCharsets.UTF_8));
        System.out.println("server ip::" + ctx.channel().remoteAddress());
    }

    /**
     * 异常处理
     * @param ctx 通道上下文
     * @param cause 异常
     * @throws Exception 异常
     */
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        ctx.close();
    }
}

Netty 核心组件

BootStrap & ServerBootStrap

BootStrap意思是引导,一个Netty应用通常由一个BootStrap开始,主要作用是配置整个Netty程序,串联各个组件,NettyBootStrap类是客户端程序的引导类,ServerBootStrap是服务端启动引导类。

Channel

ChannelNetty网络通信的组件,能够用于执行网络I/O操作。

通过Channel可以获得当前网络连接的通道状态,网络连接的配置参数。

Channel提供异步的网络I/O操作,异步调用意味着任何I/O调用都将立即返回,并且不保证在调用结束时所请求的I/O操作已完成。

调用立即返回一个ChannelFuture实例,通过注册监听器到ChannelFuture上,可以在I/O操作成功、失败、取消时回调通知调用方。

Selector

Netty基于Selector对象实现I/O多路复用,通过Selector一个线程可以监听多个连接的Channel事件。当向一个Selector中注册Channel后,Selector内部的机制就可以自动不断地查询这些注册的Channel是否有已就绪的I/O事件。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值