netty的快速入门

netty介绍
-Netty 是由 JBOSS 提供的一个 Java 开源框架,现为 Github 上的独立项目。
-Netty 是一个异步的、基于事件驱动的网络应用框架,用以快速开发高性能、高可靠性的网络 IO 程序。
-Netty 主要针对在 TCP 协议下,面向 Client 端的高并发应用,或者 Peer-to-Peer 场景下的大量数据持续传输的应用。
-Netty 本质是一个 NIO 框架,适用于服务器通讯相关的多种应用场景。
-要透彻理解 Netty,需要先学习 NIO,这样我们才能阅读 Netty 的源码。

pom依赖

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

HelloServer

package com.example.netty.simple;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.ByteToMessageCodec;
import io.netty.handler.codec.http.websocketx.WebSocket00FrameDecoder;
import io.netty.handler.codec.http.websocketx.WebSocketDecoderConfig;
import io.netty.handler.codec.http.websocketx.WebSocketFrameDecoder;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;

import java.nio.charset.Charset;

public class HelloServer {
    public static void main(String[] args) throws InterruptedException {

        ServerBootstrap serverBootstrap = new ServerBootstrap();
        //这里创建两个线程组 bossGroup 和 workerGroup,bossGroup 只是处理连接请求 , 真正的和客户端业务处理,会交给 workerGroup完成
        serverBootstrap.group(new NioEventLoopGroup(1), new NioEventLoopGroup(10));
        //使用NioServerSocketChannel作为服务器的通道实现
        serverBootstrap.channel(NioServerSocketChannel.class);
        //
        serverBootstrap.childHandler(new ChannelInitializer<NioSocketChannel>() {

            @Override
            //创建一个通道初始化对象
            protected void initChannel(NioSocketChannel nioSocketChannel) throws Exception {

                //给pipeline 设置处理器
                ChannelPipeline pipeline = nioSocketChannel.pipeline();//
                pipeline.addLast(new StringDecoder());
                pipeline.addLast(new StringEncoder());
                //
                pipeline.addLast(new ChannelInboundHandlerAdapter() {
                    @Override
                    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
                        System.out.println("handler1 || ");
                        msg = "你的消息为: "+msg.toString()+" handler1 || ";
                        ctx.fireChannelRead(msg);
                    }
                });
                pipeline.addLast(new ChannelInboundHandlerAdapter() {
                    @Override
                    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
                        System.out.println("handler2 || ");
                        msg = msg.toString()+"handler2 || ";
                        ctx.channel().write(msg);
                    }
                });

                pipeline.addLast(new ChannelOutboundHandlerAdapter() {
                    @Override
                    public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
                        System.out.println("handler3 || ");
                        msg = msg.toString()+"handler3 || ";

                        ByteBuf buf = ctx.alloc().buffer();
                        buf.writeBytes(msg.toString().getBytes());

                        ctx.writeAndFlush(buf);
                    }
                });

                pipeline.addLast(new ChannelOutboundHandlerAdapter() {
                    @Override
                    public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
                        //handler4 || 经过了这个handler
                        System.out.println("handler4 || ");
                        msg = msg.toString()+"handler4 || ";
                        ctx.writeAndFlush(msg);
                    }
                });

            }
        });
        //
        serverBootstrap.bind(8080);
    }
}

HelloCilent

package com.example.netty.simple;

import io.netty.bootstrap.Bootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;

import java.util.Scanner;

public class HelloClient {
    public static void main(String[] args) throws InterruptedException {
        //
        NioEventLoopGroup group = new NioEventLoopGroup();
        //注意客户端使用的不是 ServerBootstrap 而是 Bootstrap
        Bootstrap bootstrap = new Bootstrap();
        bootstrap.group(group);
        // 设置客户端通道的实现类(反射)
        bootstrap.channel(NioSocketChannel.class);
        bootstrap.handler(new ChannelInitializer<NioSocketChannel>() {
            @Override
            protected void initChannel(NioSocketChannel ch) throws Exception {
                ChannelPipeline pipeline = ch.pipeline();
                pipeline.addLast(new StringDecoder());
                pipeline.addLast(new StringEncoder());
                pipeline.addLast(new ChannelInboundHandlerAdapter() {
                    @Override
                    public void channelRead(ChannelHandlerContext ctx, Object msg) {
//                        ByteBuf buffer = (ByteBuf) msg;
                        System.out.println(msg.toString());

                    }
                });
            }
        });
        Channel channel = bootstrap.connect("127.0.0.1", 8080).sync().channel();

        channel.closeFuture().addListener(future -> {
            group.shutdownGracefully();
        });

        new Thread(() -> {
            Scanner scanner = new Scanner(System.in);
            while (true) {
                String line = scanner.nextLine();
                if ("q".equals(line)) {
                    channel.close();
                    break;
                }
//                channel.write(line);
                channel.writeAndFlush(line);
            }
        }).start();

    }
}

运行结果
在这里插入图片描述

核心组件说明

核心serverBootstrap
常用于服务端,服务端启动引导类

核心bootstrap
常用于客户端,客户端的启动引导类

核心eventLoop
eventLoop 本质是一个单线程执行器(同时维护了一个 Selector),里面有 run 方法处理 Channel 上源源不断的 io 事件。

核心eventLoopGroup
eventLoopGroup 是一组 eventLoop,Channel 一般会调用 EventLoopGroup 的 register 方法来绑定其中一个 EventLoop,后续这个 Channel 上的 io 事件都由此 EventLoop 来处理。其中BossGroup 专门负责接收客户端的连接,WorkerGroup 专门负责网络的读写

核心channel
channel 有一点类似于 stream,它就是读写数据的双向通道。一个客户端对应一个channel

核心Pipeline 和 ChannelPipeline
Channel 都有且仅有一个 ChannelPipeline 与之对应。ChannelPipeline 是一个 Handler 的集合,它负责处理和拦截 inbound 或者 outbound 的事件和操作。ChannelHandler 被连成一串,就是 Pipeline

核心ChannelHandler
ChannelHandler 是一个接口,处理 I/O 事件或拦截 I/O 操作
入站处理器通常是 ChannelInboundHandlerAdapter 的子类,主要用来读取客户端数据,写回结果
出站处理器通常是 ChannelOutboundHandlerAdapter 的子类,主要对写回结果进行加工

核心ChannelHandlerContext
保存 Channel 相关的所有上下文信息,同时关联一个 ChannelHandler 对象。即 ChannelHandlerContext 中包含一个具体的事件处理器 ChannelHandler,同时 ChannelHandlerContext 中也绑定了对应的 pipeline 和 Channel 的信息,方便对 ChannelHandler 进行调用。

ctx.channel().write(msg) vs ctx.write(msg)
ctx.channel().write(msg) 从尾部开始查找出站处理器
ctx.write(msg) 是从当前节点找上一个出站处理器

示意图如下
在这里插入图片描述

核心bytebuf
是对字节数据的封装

下面代码创建了一个默认的 ByteBuf(池化基于直接内存的 ByteBuf),初始容量是 10

ByteBuf buffer = ByteBufAllocator.DEFAULT.buffer(10);
log(buffer);
        int length = buffer.readableBytes();
        int rows = length / 16 + (length % 15 == 0 ? 0 : 1) + 4;
        StringBuilder buf = new StringBuilder(rows * 80 * 2)
                .append("read index:").append(buffer.readerIndex())
                .append(" write index:").append(buffer.writerIndex())
                .append(" capacity:").append(buffer.capacity())
                .append(NEWLINE);
        appendPrettyHexDump(buf, buffer);
        System.out.println(buf.toString());

直接内存 VS 堆内存

netty默认使用的是直接内存

//可以使用下面的代码来创建池化基于堆的 ByteBuf
ByteBuf buffer = ByteBufAllocator.DEFAULT.heapBuffer(10); 
//也可以使用下面的代码来创建池化基于直接内存的 ByteBuf
ByteBuf buffer = ByteBufAllocator.DEFAULT.directBuffer(10); 

池化 VS 非池化

netty默认池化

池化的最大意义在于可以重用 ByteBuf,有了池化,则可以重用池中 ByteBuf 实例,并且采用了与 jemalloc 类似的内存分配算法提升分配效率,总的来说池化优于非池化

ByteBuf 由四部分组成
在这里插入图片描述
查看
通过getClass();来查看是否池化和用的直接内存还是堆内存
在这里插入图片描述

ByteBuf的回收

由于 Netty 中有堆外内存的 ByteBuf 实现,堆外内存最好是手动来释放,而不是等 GC 垃圾回收。

  • UnpooledHeapByteBuf 使用的是 JVM 内存,只需等 GC 回收内存即可
  • UnpooledDirectByteBuf 使用的就是直接内存了,需要特殊的方法来回收内存
  • PooledByteBuf 和它的子类使用了池化机制,需要更复杂的规则来回收内存

Netty 这里采用了引用计数法来控制回收内存,每个 ByteBuf 都实现了 ReferenceCounted 接口

  • 每个 ByteBuf 对象的初始计数为 1
  • 调用 release 方法计数减 1,如果计数为 0,ByteBuf 内存被回收
  • 调用 retain 方法计数加 1,表示调用者没用完之前,其它 handler 即使调用了 release 也不会造成回收
  • 当计数为 0 时,底层内存会被回收,这时即使 ByteBuf 对象还在,其各个方法均无法正常使用

netty会自动的帮我们释放bytebuf
handler中有默认的headHandler和tailHandler帮我们释放bytebuf对象
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值