首页 > 其他分享 >Netty - 快速开始

Netty - 快速开始

时间:2023-11-16 18:48:19浏览次数:36  
标签:Netty netty 开始 服务端 io import 快速 public channel

一、为什么使用Netty

1. NIO的缺点

  • NIO的类库和API繁杂,学习成本高,你需要熟练掌握Selector、ServerSocketChannel、SocketChannel、ByteBuffer等。
  • 需要熟悉Java多线程编程。这是因为NIO编程涉及到Reactor模式,你必须对多线程和网络编程非常熟悉,才能写出高质量的NIO程序。
  • 臭名昭著的epoll bug。它会导致Selector空轮询,最终导致CPU 100%。直到JDK1.7版本依然没得到根本性的解决。

2. Netty的优点

  • API使用简单,学习成本低。
  • 功能强大,内置了多种解码编码器,支持多种协议。
  • 性能高,对比其他主流的NIO框架,Netty的性能最优。
  • 社区活跃,发现BUG会及时修复,迭代版本周期短,不断加入新的功能。
  • Dubbo、Elasticsearch都采用了Netty,质量得到验证。

二、Netty架构图

上面这张图就是在官网首页的架构图,我们从上到下分析一下。

  • 绿色的部分Core核心模块,包括零拷贝、API库、可扩展的事件模型。
  • 橙色部分Protocol Support协议支持,包括Http协议、webSocket、SSL(安全套接字协议)、谷歌Protobuf协议、zlib/gzip压缩与解压缩、Large File Transfer大文件传输等等。
  • 红色的部分Transport Services传输服务,包括Socket、Datagram、Http Tunnel等等。

以上可看出Netty的功能、协议、传输方式都比较全,比较强大。

三、搭建 Hello World

首先搭建一个HelloWord工程,先熟悉一下API,还有为后面的学习做铺垫。
以下面这张模型图为依据:

模型说明:

  1. Netty抽象出2组线程池:
    • BossGroup:专门接受客户端的连接
    • WorkerGroup:专门负责网络的读写
  2. BossGroup和WorkerGroup类型都是NioEventLoopGroup
  3. NioEventLoopGroup相当于一个事件循环组,这个组中含有多个事件循环,每一个事件循环是NioEventLoop
  4. NioEventLoop表示一个不断循环的执行处理任务的线程,每个NioEventLoop都有一个selector,用于监听绑定在其上的socket的网络通讯
  5. NioEventLoop可以有多个线程,即可以含有多个NioEventLoop
  6. 每个Boss NioEventLoop循环执行的步骤有3步:
    • 轮询accept事件
    • 处理accept事件,与client建立连接,生成NioSocketChannel,并将其注册到某个worker
    • 处理任务队列的任务,即runAllTasks
  7. 每个Worker NIOEventLoop循环执行的步骤:
    • 轮询read,write事件
    • 处理I/O事件,即read,write事件,在对应NioSocketChannel处理
    • 处理任务队列的任务,即runAllTasks
  8. 每个Worker NIOEventLoop处理业务时,会使用pipeline(管道),pipeline中包含了channel,即通过pipeline可以获取到对应channel,管道中维护了很多的处理器

0. 项目结构概览

1. 引入Maven依赖

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

2. 创建服务端启动类


package org.example.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.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;

public class NettyServer {

    public static void main(String[] args) {
        NettyServer server = new NettyServer();
        server.start();
    }

    public  void start() {
        //创建两个线程组 boosGroup、workerGroup
        EventLoopGroup bossGroup = new NioEventLoopGroup();
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        try {
            //创建服务端的启动对象,设置参数
            ServerBootstrap bootstrap = new ServerBootstrap();
            //设置两个线程组boosGroup和workerGroup
            bootstrap.group(bossGroup, workerGroup)
                    //设置服务端通道实现类型
                    .channel(NioServerSocketChannel.class)
                    //设置线程队列得到连接个数
                    .option(ChannelOption.SO_BACKLOG, 128)
                    //设置保持活动连接状态
                    .childOption(ChannelOption.SO_KEEPALIVE, true)
                    //使用匿名内部类的形式初始化通道对象
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel socketChannel) throws Exception {
                            //给pipeline管道设置处理器
                            socketChannel.pipeline().addLast(new MyNettyServerHandler());
                        }
                    });//给workerGroup的EventLoop对应的管道设置处理器
            System.out.println("java技术爱好者的服务端已经准备就绪...");
            //绑定端口号,启动服务端
            ChannelFuture channelFuture = bootstrap.bind(6666).sync();
            //对关闭通道进行监听
            channelFuture.channel().closeFuture().sync();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }

}


3. 创建服务端处理类

package org.example.server;

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

import java.util.concurrent.TimeUnit;

/**
 * 自定义的Handler需要继承Netty规定好的HandlerAdapter
 * 才能被Netty框架所关联,有点类似SpringMVC的适配器模式
 **/
public class MyNettyServerHandler extends ChannelInboundHandlerAdapter {

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        //获取客户端发送过来的消息
        ByteBuf byteBuf = (ByteBuf) msg;
        String strMsg = byteBuf.toString(CharsetUtil.UTF_8);
        System.out.println("收到客户端" + ctx.channel().remoteAddress() + "发送的消息:" + strMsg);

        /*
         * 如果Handler处理器有一些长时间的业务处理,可以交给taskQueue异步处理。
         */
        // 长时间操作,不至于长时间的业务操作导致Handler阻塞。
        ctx.channel().eventLoop().execute(new LongTimeMethod(strMsg));

        // 延时任务队列和上面介绍的任务队列非常相似,只是多了一个可延迟一定时间再执行的设置
        ctx.channel().eventLoop().schedule(new LongTimeMethod(strMsg), 5, TimeUnit.SECONDS);
    }

    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
        //发送消息给客户端
        ctx.writeAndFlush(Unpooled.copiedBuffer("服务端已收到消息,并给你发送一个信号。", CharsetUtil.UTF_8));
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        //发生异常,关闭通道
        ctx.close();
    }

}

class LongTimeMethod implements Runnable{
    private  String message;

    public  LongTimeMethod (String message) {
        this.message = message;
    }

    @Override
    public void run() {
        try {
            Thread.sleep(5000);
            System.out.println("长时间的业务处理: " + message);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }
}

4. 创建客户端启动类

package org.example.client;

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

public class NettyClient {

    public static void main(String[] args) throws Exception {
        NettyClient client = new NettyClient();
        client.start();

    }

    public void start() {
        NioEventLoopGroup eventExecutors = new NioEventLoopGroup();
        try {
            //创建bootstrap对象,配置参数
            Bootstrap bootstrap = new Bootstrap();
            //设置线程组
            bootstrap.group(eventExecutors)
                    //设置客户端的通道实现类型
                    .channel(NioSocketChannel.class)
                    //使用匿名内部类初始化通道
                    .handler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel channel) throws Exception {
                            //添加客户端通道的处理器
                            channel.pipeline().addLast(new MyNettyClientHandler());
                        }
                    });
            System.out.println("客户端准备就绪,随时可以起飞~");
            //连接服务端
            ChannelFuture channelFuture = bootstrap.connect("127.0.0.1", 6666).sync();
            this.addFutureListener(channelFuture);
            //对通道关闭进行监听
            channelFuture.channel().closeFuture().sync();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            //关闭线程组
            eventExecutors.shutdownGracefully();
        }
    }

    private void  addFutureListener (ChannelFuture future) {
        //添加监听器
        future.addListener(new ChannelFutureListener() {
            //使用匿名内部类,ChannelFutureListener接口
            //重写operationComplete方法
            @Override
            public void operationComplete(ChannelFuture future) throws Exception {
                //判断是否操作成功
                if (future.isSuccess()) {
                    System.out.println("连接成功");
                } else {
                    System.out.println("连接失败");
                }
            }
        });
    }


}




5. 创建客户端处理类

package org.example.client;


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

public class MyNettyClientHandler extends ChannelInboundHandlerAdapter {

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        //发送消息到服务端
        String msg = "歪比巴卜~茉莉~Are you good~马来西亚~";
        System.out.println("给服务端发送信息:" + msg);

        ctx.writeAndFlush(Unpooled.copiedBuffer(msg, CharsetUtil.UTF_8));
    }

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        //接收服务端发送过来的消息
        ByteBuf byteBuf = (ByteBuf) msg;
        String strMsg = byteBuf.toString(CharsetUtil.UTF_8);

        System.out.println("收到服务端" + ctx.channel().remoteAddress() + "的消息:" + strMsg);
    }
}


6. 测试

先启动服务端,再启动客户端,就可以看到结果:

服务端结果:

# java技术爱好者的服务端已经准备就绪...
# 收到客户端/127.0.0.1:11972发送的消息:歪比巴卜~茉莉~Are you good~马来西亚~
# 长时间的业务处理: 歪比巴卜~茉莉~Are you good~马来西亚~
# 长时间的业务处理: 歪比巴卜~茉莉~Are you good~马来西亚~

客户端结果:

# 客户端准备就绪,随时可以起飞~
# 给服务端发送信息:歪比巴卜~茉莉~Are you good~马来西亚~
# 连接成功
# 收到服务端/127.0.0.1:6666的消息:服务端已收到消息,并给你发送一个信号。

此 Netty 笔记为学习尚硅谷韩老师讲的 Netty 整理完成,原视频讲解十分详细,建议对 Netty 框架感兴趣的同学们可以看一遍原视频:
https://www.bilibili.com/video/BV1DJ411m7NR






Ref

标签:Netty,netty,开始,服务端,io,import,快速,public,channel
From: https://www.cnblogs.com/eddyz/p/17836987.html

相关文章

  • 手写快速排序
    什么是快速排序快速排序的基本思想:通过一趟排序将待排记录分隔成独立的两部分,其中一部分记录的关键字均比另一部分的关键字小,则可分别对这两部分记录继续进行排序,以达到整个序列有序。算法描述和实现快速排序使用分治法来把一个串(list)分为两个子串(sub-lists)。具体算法描述如下......
  • 从头开始一个vtk.js项目
    从头开始一个vtk.js项目初始化项目mkdirmy-vtkjs-appcdmy-vtkjs-appnpminit安装@kitware/vtk.js依赖npminstall@kitware/vtk.js对于这个例子,我们将使用webpack来构建我们的应用程序。npminstall-Dwebpack-cliwebpackwebpack-dev-server项目脚手架我们......
  • JDK系列---【linux系统脚本快速安装JDK】
    1.install.shtar-zxvfjdk8.tar.gzmvjdk8u201jdk8#查看是否系统自带openjdkrpm-qa|grepjava#卸载自带openjdkrpm-etadata-java-2022a-1.el8.noarchjavapackages-filesystem-5.3.0-1.module+el8+2447+6f56d9a6.noarchjava-1.8.0-openjdk-headless-1.8.0.322.b06-11.......
  • 开源一套快速部署程序的工具(CI/CD)
    随着微服务越写越多,程序发布就成了一个麻烦事,所以写了一个部署工具Vela,只要填写一个git地址、编译命令等简单信息,就能自动完成程序的部署。Vela特性:代码可在任意一台电脑自动完成编译,再自动上传至服务器可指定程序以Docker容器方式运行编译命令采用终端运行,理论支持......
  • 快速入门:构建您的第一个 .NET Aspire 应用程序
    前言云原生应用程序通常需要连接到各种服务,例如数据库、存储和缓存解决方案、消息传递提供商或其他Web服务。.NETAspire旨在简化这些类型服务之间的连接和配置。在本快速入门中,您将了解如何创建.NETAspireStarter应用程序模板解决方案。准备条件要使用.NETAspire,您需......
  • setTimeout可以将字符串当成代码执行,类比eval函数。当遇到setTimeout或者SetInterval,
    请问以下JS代码的输出顺序是?letdate=newDate()setTimeout(()=>{console.log('1')},2000)setTimeout('console.log(2)',1000);setTimeout(function(){console.log('3')},1500);while((newDate()-date)<3000){}A报错B......
  • 想快速进入人工智能领域的Java程序员?你准备好了吗?
    引言今天我们来探讨一下作为Java程序员,如何迅速融入人工智能的领域。,当前有一些流行的LLMs选择,例如ChatGPT、科大讯飞的星火、通义千问和文心一言等。如果你还没有尝试过这些工具,那么现在也不失为一个很好的机会,赶快体验一下吧。这些工具不仅能够为你的Java编程工作提供更多的可......
  • Docker部署fastposter | 一款快速开发海报的工具
    一、介绍fastposter海报生成器是一款快速开发海报的工具。只需上传一张背景图,在对应的位置放上组件(文字、图片、二维码、头像)即可生成海报。点击代码直接生成各种语言SDK的调用代码,方便快速开发。二、搭建fastposter绿联DX4600为例,首先我们打开Docker管理器,进入镜像管理,然......
  • 如何快速将钉钉员工信息同步到飞书
    当企业内部在使用钉钉跟飞书时,那么当钉钉员工信息发生更改时,我们应该如何将信息快速同步到飞书上呢,接下来我们借助RestCloud AppLink平台进行演示。第一步:获得钉钉以及飞书认证授权钉钉接入采用自建应用的方式,首先进入钉钉开发平台:++https://open-dev.dingtalk.com/++登录后,在应用......
  • 如何快速将钉钉员工信息同步到飞书
    当企业内部在使用钉钉跟飞书时,那么当钉钉员工信息发生更改时,我们应该如何将信息快速同步到飞书上呢,接下来我们借助RestCloudAppLink平台进行演示。第一步:获得钉钉以及飞书认证授权钉钉授权钉钉接入采用自建应用的方式,首先进入钉钉开发平台:https://open-dev.dingtalk.com/登......