• Netty学习(二)-- 概述和初体验


    1、概述

    1.1、什么是 Netty?

    Netty 是由 Trustin Lee 提供的一个 Java 开源框架,现为 GitHub 上的独立项目。Netty 是一个基于 NIO 的客户、服务器端的编程框架。

    Netty 是一个异步的、基于事件驱动的网络应用框架,用于快速开发可维护、高性能的网络服务器和客户端。

    1.3、Netty 的地位

    Netty 在 Java 网络应用框架中的地位就好比:Spring 框架在 JavaEE 开发中的地位。

    以下框架都是用了 Netty ,它们都有网络通信需求!

    • Cassandra - NoSQL 数据库
    • Spark - 大数据分布式计算框架
    • Hadoop - 大数据分布式存储框架
    • RockMQ - 阿里开源的消息队列
    • ElasticSearch - 搜索引擎
    • gRPC - rpc 框架
    • Dubbo - rpc 框架
    • Spring 5.x - Fulx api 完全抛弃了 Tomcat,使用 Netty 作为服务端
    • Zookeeper - 分布式协调框架
    1.4、Netty 的优势
    • Netty 对比 NIO(工作量大,BUG多)
      • 需要自己构建协议
      • 解决 TCP 传输问题,如黏包、半包
      • epoll 空轮询导致 CPU 100%
      • 对 API 进行增强,使之更易使用。如 FastThreadLocal => ThreadLocal、ByteBuf=>ByteBuffer。
    • Netty 对比 其他网络应用框架
      • Mina 由 Apache 维护。Netty API 更简洁,文档更优秀。

    2、Hello World

    2.1、初体验

    开发一个简单的服务器和客户端

    • 客户端向服务器发送 hello,world
    • 服务器仅接收,不返回
    2.2、导入依赖

    1、netty 依赖

    <dependency>
      <groupId>io.nettygroupId>
      <artifactId>netty-allartifactId>
      <version>4.0.42.Finalversion>
    dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5

    2、日志依赖:

     
    <dependency>
        <groupId>org.projectlombokgroupId>
        <artifactId>lombokartifactId>
        <version>1.18.12version>
    dependency>
    
    <dependency>
        <groupId>org.slf4jgroupId>
        <artifactId>slf4j-apiartifactId>
        <version>1.7.7version>
    dependency>
    
    <dependency>
        <groupId>org.apache.logging.log4jgroupId>
        <artifactId>log4j-slf4j-implartifactId>
        <version>2.9.1version>
    dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    3、log4j2.xml 文件配置

    
    
    
    
    <configuration status="INFO" monitorInterval="30">
        
        <appenders>
            
            <console name="Console" target="SYSTEM_OUT">
                    
                	
                    <PatternLayout pattern="%date{HH:mm:ss} [%-5level] [%thread] %logger{17} - %m%n"/>
            console>
    
            
    
            <RollingFile name="logfile" fileName="G:/Typora/Netty/Netty/logs/system.log" filePattern="G:/Typora/Netty/Netty/logs/$${date:yyyy-MM}/system-%d{yyyy-MM-dd}-%i.log">
                <ThresholdFilter level="INFO" onMatch="ACCEPT" onMismatch="DENY"/>
                <PatternLayout pattern="%date{HH:mm:ss} [%-5level] [%thread] %logger{17} - %m%n"/>
                <Policies>
                    <TimeBasedTriggeringPolicy/>
                    <SizeBasedTriggeringPolicy size="50 MB"/>
                Policies>
                
                <DefaultRolloverStrategy compressionLevel="0" max="10"/>
            RollingFile>
        appenders>
    
        
    	
        <loggers>
            
            <logger name="com.example" level="DEBUG" additivity="false">
                <appender-ref ref="Console" />
            logger>
            <root level="ERROR">
                <appender-ref ref="Console" />
            root>
        loggers>
    configuration>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    2.3、服务器端代码
    public class HelloServer {
        public static void main(String[] args) {
            // 1、服务端启动器,负责组装 netty 组件,启动服务器
            new ServerBootstrap()
                    // 2、BossEventLoop WorkerEventLoop(包含selector, Thread), group 组
                    .group(new NioEventLoopGroup())
                    // 3、选择 服务器的 ServerSocketChannel 实现 (EpollServerSocketChannel、OioServerSocketChannel)
                    .channel(NioServerSocketChannel.class)
                    // 4、boss负责处理连接 worker(child)负责处理读写。决定 worker(child) 能执行哪些操作(handler)
                    .childHandler(
                            // 5、Channel 代表和客户端进行数据读写的通道;Initializer 初始化,负责添加别的 handler
                        new ChannelInitializer<NioSocketChannel>() {
                        @Override
                        protected void initChannel(NioSocketChannel nioSocketChannel) throws Exception {
                            // 6、添加具体的 handler
                            // 将 ByteBuf 转换为字符串
                            nioSocketChannel.pipeline().addLast(new StringDecoder());
                            // 自定义 handler
                            nioSocketChannel.pipeline().addLast(new ChannelInboundHandlerAdapter() {
                                @Override   // 读事件
                                public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
                                    // 打印上一步转换好的字符串
                                    System.out.println(msg);
                                }
                            });
                        }
                    })
                    // 7、绑定监听端口
                    .bind(8888);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    2.4、客服端代码
    public class HelloClient {
        public static void main(String[] args) throws InterruptedException {
            // 1、客户端启动器
            new Bootstrap()
                    // 2、添加 EventLoop
                    .group(new NioEventLoopGroup())
                    // 3、选择客户端 Channel 事件
                    .channel(NioSocketChannel.class)
                    // 4、添加处理器,
                    .handler(new ChannelInitializer<NioSocketChannel>() {
                        @Override   // 连接建立后被调用
                        protected void initChannel(NioSocketChannel ch) throws Exception {
                            ch.pipeline().addLast(new StringEncoder());
                        }
                    })
                    // 5、连接到服务器
                    .connect(new InetSocketAddress("localhost", 8888))
                    .sync()
                    .channel()
                    // 6、向服务器发送数据
                    .writeAndFlush("hello, world");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    2.5、执行流程图

    在这里插入图片描述

    2.6、一些理解
    • 把 Channel 理解为数据的通道。
    • 把 msg 理解为流动的数据,最开始输入 ByteBuf,但经过 pipeline(流水线) 的加工,会变成其他类型对象,最后输出又变成 ByteBuf。
    • 把 handler 理解为数据处理的处理工序
      • 工序有多道,合在一起就是 pipeline(流水线),pipeline 负责发布事件(读、读取完成…)传播给每个 handler,handler 对自己感兴趣的事件进行处理(重写了相应事件处理方法)
      • handler 分 Inbound(入站) 和 Outbound(出站) 两类
    • 把 EventLoop 理解为处理数据的工人
      • 工人可以管理多个 Channel 的 IO 操作,并且一旦工人负责了某个 Channel,就要负责到底(绑定)。
      • 工人既可以执行 IO 操作,也可以进行任务处理,每位工人有任务队列,队列里可以堆放多个 Channel 的待处理任务,任务分为普通任务、定时任务。
      • 工人按照 pipeline 顺序,依次按照 handler 的规则(代码)处理数据。可以为每道工序指定不同的工人(非 IO 操作)。
  • 相关阅读:
    Nautilus Chain上线主网,为DeFi和流支付的未来构建基础
    【安全测试学习】自动化注入攻击之 FuzzDB和Burp 组合拳
    CMU 15-445 Project #3 - Query Execution(Task #1、Task #2)
    React基础之Context
    JAVA【常见基础知识】
    基于微信小程序的汽车租赁系统源码
    uniapp 处理表格数据,合并行数据处理
    磁盘管理:磁盘调度算法
    Kafka与ELK实现一个日志系统
    C# XML Helpers实现XML文件操作
  • 原文地址:https://blog.csdn.net/weixin_43989102/article/details/126735668