netty网络编程(二)之netty入门以及主要组件介绍

NIO

NIO优点

  • 事件驱动模型
    • 避免多线程
    • 单线程处理多任务
  • 非阻塞IO,IO读写不再阻塞,而是返回0
  • 基于block的传输,通常比基于流的传输更加高效
  • 更高级的IO函数,zero-copy
  • IO多路复用大大提高了java网络应用的可伸缩性和实用性

netty

有了NIO,为什么还要学习netty

  • 并发量大
    Netty是一款基于NIO(Nonblocking I/O,非阻塞IO)开发的网络通信框架,对比于BIO(Blocking I/O,阻塞IO),他的并发性能得到了很大提高
  • 传输快
    Netty的传输快其实也是依赖了NIO的一个特性——零拷贝。我们知道,Java的内存有堆内存、栈内存和字符串常量池等等,其中堆内存是占用内存空间最大的一块,也是Java对象存放的地方,一般我们的数据如果需要从IO读取到堆内存,中间需要经过Socket缓冲区,也就是说一个数据会被拷贝两次才能到达他的的终点,如果数据量大,就会造成不必要的资源浪费。
    Netty针对这种情况,使用了NIO中的另一大特性——零拷贝,当他需要接收数据的时候,他会在堆内存之外开辟一块内存,数据就直接从IO读到了那块内存中去,在netty里面通过ByteBuf可以直接对这些数据进行直接操作,从而加快了传输速度。
  • NIO做了进一步的封装
    简化了编程。

netty组件调用关系图

在这里插入图片描述

netty主要组件介绍

NIO编程回顾

在这里插入图片描述
在这里插入图片描述

Netty主要组件

  • 1.Transport Channel ---- 对应NIO中的channel
  • 2.EventLoop ---- 对应于NIO中的while循环
    • EventLoopGroup: 多个EventLoop
  • 3.ChannelHandler和ChannelPipeline — 对应于NIO中的客户逻辑实现handleRead/handleWrite (interceptor pattern)
  • 4.ByteBuf ---- 对应于NIO 中的ByteBuffer
  • 5.Bootstrap 和 ServerBootstrap — 对应NIO中的Selector、ServerSocketChannel等的创建、配置、启动等

Netty Server启动主要流程

  • 设置服务端ServerBootStrap启动参数
    • group(parentGroup, childGroup):
    • channel(NioServerSocketChannel): 设置通道类型
    • handler():设置NioServerSocketChannel的ChannelHandlerPipeline
    • childHandler(): 设置NioSocketChannel的ChannelHandlerPipeline
  • 通过ServerBootStrap的bind方法启动服务端,bind方法会在parentGroup中注册NioServerScoketChannel,监听客户端的连接请求
    • 会创建一个NioServerSocketChannel实例,并将其在parentGroup中进行注册

Netty Server执行主要流程

  • Client发起连接CONNECT请求,parentGroup中的NioEventLoop不断轮循是否有新的客户端请求,如果有,ACCEPT事件触发。
  • ACCEPT事件触发后,parentGroup中NioEventLoop会通过NioServerSocketChannel获取到对应的代表客户端的NioSocketChannel,并将其注册到childGroup中。
  • childGroup中的NioEventLoop不断检测自己管理的NioSocketChannel是否有读写事件准备好,如果有的话,调用对应的ChannelHandler进行处理。
    在这里插入图片描述

Netty Transport-传输层

  • 提供了统一的API,支持不同类型的传输层
    • OIO-阻塞IO
    • NIO-Java nio
    • Epoll - Linux Epoll(JNI)
    • Local Transport - IntraVM调用
    • Embedded Transport - 供测试使用的嵌入传输
    • UDS - Unix套接字的本地传输

在这里插入图片描述

Netty EventLoop——对应于NIO中的while循环

  • ServerBootstrap:包括2个不同类型的EventLoopGroup:
    • Parent EventLoop:负责处理Accept事件,接收请求
    • Child EventLoop:负责处理读写事件
  • EventLoopGroup
    • 包括多个EventLoop
    • 多个EventLoop之间不交互
  • EventLoop:
    • 每个EventLoop对应一个线程
    • 所有连接(channel)都将注册到一个EventLoop,并且只注册到一个,整个生命周期中都不会变化
    • 每个EventLoop管理着多个连接(channel)
    • EventLoop来处理连接(Channel)上的读写事件
      在这里插入图片描述

Netty Buffers——ByteBuf

  • 相比JDK ByteBuffer,更加的易于使用
    • 为读写分别维护单独的指针,不需要通过flip()进行读写模式切换
    • 容量自动伸缩(类似ArrayList,StringBuilder)
    • Fluent API(链式调用)
  • 更好地性能
    • 通过内置的CompositeBuffer来减少数据拷贝(Zero copy)
    • 支持内存池,减少GC压力

ByteBuf分配

  • 不直接通过new来创建,而是通过ByteBufAllocator来创建
    在这里插入图片描述
    在这里插入图片描述
  • Unpooled的工具类,它提供了静态的辅助方法来创建未池化的ByteBuf实例
    在这里插入图片描述

Netty ChannelHandler——对应于NIO中的客户逻辑实现handleRead/handleWrite (interceptor pattern)

ChannelHandler——业务处理核心逻辑,用户自定义

ChannelHandler介绍

Netty 提供2个重要的 ChannelHandler 子接口:
ChannelInboundHandler - 处理进站数据和所有状态更改事件
ChannelOutboundHandler - 处理出站数据,允许拦截各种操作
在这里插入图片描述
注意:我们一般继承ChannelInboundHandlerAdapter,就可以不用实现所有的方法,只处理我们需要的方法,其余方法采用默认实现

Netty Channel状态及转换

  • Channel的状态及其转换如下图所示:
    在这里插入图片描述
    在这里插入图片描述
  • 当 ChannelHandler 添加到 ChannelPipeline,或者从ChannelPipeline 移除后,对应的方法将会被调用
    在这里插入图片描述

Netty ChannelInboundHandler

ChannelInboundHandler 回调方法在下表中:

  • 当接收到数据或者与之关联的 Channel 状态改变时调用
  • 与 Channel 的生命周期接近

在这里插入图片描述

Netty ChannelOutboundHandler

在这里插入图片描述

Netty——ChannelPipeline

ChannelPipeline 是ChannelHandler容器

  • 包括一系列的ChannelHandler 实例,用于拦截流经一个 Channel 的入站和出站事件
  • 每个Channel都有一个其ChannelPipeline
  • 可以修改 ChannelPipeline 通过动态添加和删除 ChannelHandler
  • 定义了丰富的API调用来回应入站和出站事件

在这里插入图片描述

Netty—— ChannelHandlerContext

ChannelHandlerContext表示 ChannelHandler 和ChannelPipeline 之间的关联
在 ChannelHandler 添加到 ChannelPipeline 时创建
在这里插入图片描述

netty编程示例一

pom文件中引入

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

服务器端代码:

package test13;

 import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.util.CharsetUtil;

public class EchoServerHandler extends ChannelInboundHandlerAdapter {
   @Override
   public void channelRead(ChannelHandlerContext ctx, Object msg) {
       ByteBuf in = (ByteBuf) msg;
       System.out.println(
               "服务器接收到消息:" + in.toString(CharsetUtil.UTF_8));
       ctx.write(in);
   }

   @Override
   public void channelReadComplete(ChannelHandlerContext ctx)
           throws Exception {
       ctx.writeAndFlush(Unpooled.EMPTY_BUFFER)
               .addListener(ChannelFutureListener.CLOSE);
   }

   @Override
   public void exceptionCaught(ChannelHandlerContext ctx,
       Throwable cause) {
       cause.printStackTrace();
       ctx.close();
   }
}
package test13;

import io.netty.bootstrap.ServerBootstrap;
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.NioServerSocketChannel;

public class EchoServer {

    public static void main(String[] args) throws Exception {
        int port = 8080;
        if (args != null && args.length > 0) {
            try {
                port = Integer.valueOf(args[0]);
            } catch (NumberFormatException e) {
                // 采用默认值
            }
        }
        new EchoServer().bind(port);
    }

    public void bind(int port) throws Exception {
        // 配置服务端的NIO线程组
    	// 主线程组, 用于接受客户端的连接,但是不做任何具体业务处理,像老板一样,负责接待客户,不具体服务客户
        EventLoopGroup bossGroup = new NioEventLoopGroup(1);
        // 工作线程组, 老板线程组会把任务丢给他,让手下线程组去做任务,服务客户
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        try {
        	 // 类ServerBootstrap用于配置Server相关参数,并启动Server
            ServerBootstrap b = new ServerBootstrap();
            
            //链式调用 
            //配置parentGroup和childGroup
            b.group(bossGroup, workerGroup)
                    //配置Server通道
                    .channel(NioServerSocketChannel.class)
                    //配置通道的ChannelPipeline
                     .childHandler(new ChildChannelHandler());
            
            // 绑定端口,并启动server,同时设置启动方式为同步
            ChannelFuture f = b.bind(port).sync();
            
            System.out.println(EchoServer.class.getName() +
                    " 启动成功,在地址[" + f.channel().localAddress() +"]上等待客户请求......");

            // 等待服务端监听端口关闭
            f.channel().closeFuture().sync();
        } finally {
            // 释放线程池资源
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }

    private class ChildChannelHandler extends ChannelInitializer<SocketChannel> {
        @Override
        protected void initChannel(SocketChannel channel) throws Exception {
        	channel.pipeline().addLast(new EchoServerHandler());
        }
    }
}

客户端代码

package test13;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.util.CharsetUtil;


public class EchoClientHandler extends SimpleChannelInboundHandler<ByteBuf> {
	@Override
	public void channelActive(ChannelHandlerContext ctx) {
		ctx.writeAndFlush(Unpooled.copiedBuffer("这是一个Netty示例程序!\n", CharsetUtil.UTF_8));
	}

	@Override
	public void channelRead0(ChannelHandlerContext ctx, ByteBuf in) {
		System.out.println("客户端接收到消息: " + in.toString(CharsetUtil.UTF_8));
	}

	@Override
	public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
		cause.printStackTrace();
		ctx.close();
	}
}
package test13;

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

public class EchoClient {

    public static void main(String[] args) throws Exception {
        int port = 8080;
        if (args != null && args.length > 0) {
            try {
                port = Integer.valueOf(args[0]);
            } catch (NumberFormatException e) {
             }
        }
        new EchoClient().connect(port, "127.0.0.1");
    }

    public void connect(int port, String host) throws Exception {
        // 工作线程组, 老板线程组会把任务丢给他,让手下线程组去做任务,服务客户
        EventLoopGroup group = new NioEventLoopGroup();
        try {
            Bootstrap b = new Bootstrap();
            b.group(group).channel(NioSocketChannel.class)
                    .option(ChannelOption.TCP_NODELAY, true)
                    .handler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        public void initChannel(SocketChannel ch) throws Exception {
                            ch.pipeline().addLast(new EchoClientHandler());
                        }
                    });

            // 发起异步连接操作
            ChannelFuture f = b.connect(host, port).sync();

            // 等待客户端链路关闭
            f.channel().closeFuture().sync();
        } finally {
            // 释放线程池资源
            group.shutdownGracefully();
        }
    }
}

运行结果如下

服务器端
在这里插入图片描述
在这里插入图片描述
客户端:
在这里插入图片描述

netty编程示例二

package com.demo.netty.bytebuf;

import io.netty.buffer.*;
import io.netty.util.ByteProcessor;
import java.nio.charset.Charset;

public class ByteBufExamples {
    public static void main(String[] args) {
    	byteBufSetGet();
        System.out.println("============");
    	byteBufWriteRead();
        System.out.println("============");
    	writeAndRead();
        System.out.println("============");
    	byteBufSlice();
        System.out.println("============");
    	byteBufCopy();
        System.out.println("============");
    	byteBufComposite();
        System.out.println("============");
    	directBuffer();
        System.out.println("============");
    	heapBuffer();
        System.out.println("============");
    	   
    }
    
    public static void byteBufSetGet() {
        Charset utf8 = Charset.forName("UTF-8");
        ByteBuf buf = Unpooled.copiedBuffer("Netty in Action rocks!", utf8);
        System.out.println((char)buf.getByte(0));
        int readerIndex = buf.readerIndex();
        int writerIndex = buf.writerIndex();
        System.out.println("readerIndex = " + readerIndex + "; writerIndex = " + writerIndex);
        buf.setByte(0, (byte)'B');
        System.out.println((char)buf.getByte(0));
        System.out.println("readerIndex = " + buf.readerIndex() + "; writerIndex = " + buf.writerIndex());
    }
    
  
    public static void byteBufWriteRead() {
        Charset utf8 = Charset.forName("UTF-8");
        ByteBuf buf = Unpooled.copiedBuffer("Netty in Action rocks!", utf8);
        System.out.println((char)buf.readByte());
        int readerIndex = buf.readerIndex();
        int writerIndex = buf.writerIndex();
        System.out.println("readerIndex = " + readerIndex + "; writerIndex = " + writerIndex);

        buf.writeByte((byte)'?');
        System.out.println("readerIndex = " + buf.readerIndex() + "; writerIndex = " + buf.writerIndex());
        
        buf.readByte();
        System.out.println("readerIndex = " + buf.readerIndex() + "; writerIndex = " + buf.writerIndex());


    }
    
    public static void writeAndRead() {
         ByteBuf buffer = Unpooled.buffer(20); //get reference form somewhere
         int i = 0;
        while (buffer.writableBytes() >= 4) {
            buffer.writeInt(i++);
        }
        
        while (buffer.isReadable()) {
            System.out.println(buffer.readInt());
        }
    }
    
    public static void byteProcessor() {
        Charset utf8 = Charset.forName("UTF-8");

        ByteBuf buffer = Unpooled.copiedBuffer("Netty\r in Action rocks! ", utf8);
        int index = buffer.forEachByte(ByteProcessor.FIND_CR);
    }
    public static void byteBufSlice() {
        Charset utf8 = Charset.forName("UTF-8");
        ByteBuf buf = Unpooled.copiedBuffer("Netty in Action rocks!", utf8);
        ByteBuf sliced = buf.slice(0, 15);
        System.out.println(sliced.toString(utf8));
        buf.setByte(0, (byte)'J');
        System.out.println(sliced.toString(utf8));
    }

    public static void byteBufCopy() {
        Charset utf8 = Charset.forName("UTF-8");
        ByteBuf buf = Unpooled.copiedBuffer("Netty in Action rocks!", utf8);
        ByteBuf copy = buf.copy(0, 15);
        System.out.println(copy.toString(utf8));
        buf.setByte(0, (byte)'J');
        System.out.println(copy.toString(utf8));
    }

    public static void byteBufComposite() {
        CompositeByteBuf messageBuf = Unpooled.compositeBuffer();

        Charset utf8 = Charset.forName("UTF-8");
        ByteBuf headerBuf = Unpooled.copiedBuffer("Header", utf8);
        ByteBuf bodyBuf = Unpooled.copiedBuffer("This is body", utf8);
        messageBuf.addComponents(headerBuf, bodyBuf);
        for (ByteBuf buf : messageBuf) {
            System.out.println(buf.toString());
        }
        
        messageBuf.removeComponent(0); // remove the header
        for (ByteBuf buf : messageBuf) {
            System.out.println(buf.toString());
        }
    }

    public static void heapBuffer() {
        ByteBuf heapBuf = Unpooled.buffer(16);   
        if (heapBuf.hasArray()) {
            int i = 0;
            while (heapBuf.writableBytes() > 0) {
            	heapBuf.writeByte(i++);
            }
            byte[] array = heapBuf.array();
            int offset = heapBuf.arrayOffset() + heapBuf.readerIndex();
            int length = heapBuf.readableBytes();
            handleArray(array, offset, length);
        }
    }

  
    public static void directBuffer() {
        ByteBuf directBuf = Unpooled.directBuffer(16);    
        if (!directBuf.hasArray()) {
            int i = 0;
            while (directBuf.writableBytes() > 0) {
            	directBuf.writeByte(i++);
            }
            int length = directBuf.readableBytes();
            byte[] array = new byte[length];
            directBuf.getBytes(directBuf.readerIndex(), array);
            handleArray(array, 0, length);
        }
    }

 
 
   
    public static void byteBufCompositeArray() {
        CompositeByteBuf compBuf = Unpooled.compositeBuffer();
        int length = compBuf.readableBytes();
        byte[] array = new byte[length];
        compBuf.getBytes(compBuf.readerIndex(), array);
        handleArray(array, 0, array.length);
    }

  


    private static void handleArray(byte[] array, int offset, int len) {
    	for(int i = 0; i<len; i++) {
    		System.out.println(array[offset+i]);
    	}
    }
}

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章