ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

Netty

2022-04-23 13:31:09  阅读:180  来源: 互联网

标签:Netty NIO BIO Telnet 线程 客户端


Netty

Netty介绍和应用场景

Netty的介绍

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

Netty的应用场景

  • 互联网行业

    1. 互联网行业:在分布式系统中,各个 节点之间需要远程服务调用,高性能 的 RPC 框架必不可少,Netty 作为异步 高性能的通信框架,往往作为基础通 信组件被这些 RPC 框架使用
    2. 典型的应用有:阿里分布式服务框架 Dubbo 的 RPC 框架使用 Dubbo 协议进 行节点间通信,Dubbo 协议默认使用 Netty 作为基础通信组件,用于实现各 进程节点之间的内部通信
  • 游戏行业

    1. 无论是手游服务端还是大型的网络游戏, Java 语言得到了越来越广泛的应用
    2. Netty 作为高性能的基础通信组件,提 供了 TCP/UDP 和 HTTP 协议栈,方便定制和开发私有协议栈,账号登录服务器
    3. 地图服务器之间可以方便的通过 Netty 进行高性能的通信
  • 大数据领域

    1. 经典的 Hadoop 的高性能通信和 序列化组件 Avro 的 RPC 框架, 默认采用 Netty 进行跨界点通信
    2. 它的 Netty Service 基于 Netty 框 架二次封装实现。
  • 其它开源项目使用到Netty

    1. 网址: https://netty.io/wiki/related-projects.html

Netty的学习参考资料

  • Netty In Action
  • Netty 权威指南

Java BIO编程

I/O模型

I/O 模型基本说明

  1. I/O 模型简单的理解:就是用什么样的通道进行数据的发送和接收,很大程度上决定了程序通信的性能
  2. Java共支持3种网络编程模型/IO模式:BIO、NIO、AIO
  3. Java BIO : 同步并阻塞(传统阻塞型),服务器实现模式为一个连接一个线程,即客户端 有连接请求时服务器端就需要启动一个线程进行处理,如果这个连接不做任何事情会造成 不必要的线程开销
  4. Java NIO : 同步非阻塞,服务器实现模式为一个线程处理多个请求(连接),即客户端发送的连接请求都会注册到多路复用器上,多路复用器轮询到连接有I/O请求就进行处理
  5. Java AIO(NIO.2) : 异步非阻塞,AIO 引入异步通道的概念,采用了 Proactor 模式,简化了程序编写,有效的请求才启动线程,它的特点是先由操作系统完成后才通知服务端程

BIO、NIO、AIO适用场景分析

  1. BIO方式适用于连接数目比较小且固定的架构,这种方式对服务器资源要求比较高, 并发局限于应用中,JDK1.4以前的唯一选择,但程序简单易理解。
  2. NIO方式适用于连接数目多且连接比较短(轻操作)的架构,比如聊天服务器,弹幕 系统,服务器间通讯等。编程比较复杂,JDK1.4开始支持。
  3. AIO方式使用于连接数目多且连接比较长(重操作)的架构,比如相册服务器,充分 调用OS参与并发操作,编程比较复杂,JDK7开始支持。

Java BIO 基本介绍

  1. Java BIO 就是传统的java io 编程,其相关的类和接口在 java.io
  2. BIO(blocking I/O) : 同步阻塞,服务器实现模式为一个连接一个线程,即客户端有连 接请求时服务器端就需要启动一个线程进行处理,如果这个连接不做任何事情会造 成不必要的线程开销,可以通过线程池机制改善(实现多个客户连接服务器)。
  3. BIO方式适用于连接数目比较小且固定的架构,这种方式对服务器资源要求比较高, 并发局限于应用中,JDK1.4以前的唯一选择,程序简单易理解

Java BIO 工作机制

  • 工作原理图

    graph TD; Scoket_1 --- read/write_1 --- Thread_1 Scoket_2 --- read/write_2 --- Thread_2 Scoket_3 --- read/write_3 --- Thread_3
  • BIO编程简单流程

    1. 服务器端启动一个ServerSocket
    2. 客户端启动Socket对服务器进行通信,默认情况下服务器端需要对每个客户 建立一个线程与之通讯
    3. 客户端发出请求后, 先咨询服务器 是否有线程响应,如果没有则会等待,或者被拒绝
    4. 如果有响应,客户端线程会等待请求结束后,在继续执行

应用程序比如浏览器、电子邮件、文件传输服务器等产生的数据,会通过传输层协议进行传输。而应用程序是不会和传输层直接建立联系的,而是有一个能够连接应用层和传输层之间的套件,这个套件就是 Socket

Java BIO 应用实例

  • 实例说明
  1. 使用BIO模型编写一个服务器端,监听6666端口,当有客户端连接时,就启动一个线程与之通讯
  2. 要求使用线程池机制改善,可以连接多个客户端
  3. 服务器端可以接收客户端发送的数据(telnet 方式即)
1.什么是Telnet?
  对于Telnet的认识,不同的人持有不同的观点,可以把Telnet当成一种通信协议,但是对于入侵者而言,Telnet只是一种远程登录的工具。一旦入侵者与远程主机建立了Telnet连接,入侵者便可以使用目标主机上的软、硬件资源,而入侵者的本地机只相当于一个只有键盘和显示器的终端而已。
2.为什么需要telnet?
  telnet就是查看某个端口是否可访问。我们在搞开发的时候,经常要用的端口就是 8080。那么你可以启动服务器,用telnet 去查看这个端口是否可用。
  Telnet协议是TCP/IP协议家族中的一员,是Internet远程登陆服务的标准协议和主要方式。它为用户提供了在本地计算机上完成远程主机工作的能力。在终端使用者的电脑上使用telnet程序,用它连接到服务器。终端使用者可以在telnet程序中输入命令,这些命令会在服务器上运行,就像直接在服务器的控制台上输入一样。可以在本地就能控制服务器。要开始一个telnet会话,必须输入用户名和密码来登录服务器。Telnet是常用的远程控制Web服务器的方法。
3.Windows XP怎么执行telnet 命令?
	1)、点击开始 → 运行 → 输入CMD,回车。
	2)、在出来的DOS界面里,输入telnet测试端口命令: telnet IP 端口 或者 telnet 域名 端口,回车。
  如果端口关闭或者无法连接,则显示不能打开到主机的链接,链接失败;端口打开的情况下,链接成功,则进入telnet页面(全黑的),证明端口可用。
  Telnet 客户端命常用命令:
  open : 使用 openhostname 可以建立到主机的 Telnet 连接。
  close : 使用命令 close 命令可以关闭现有的 Telnet 连接。
  display : 使用 display 命令可以查看 Telnet 客户端的当前设置。
  send : 使用 send 命令可以向 Telnet 服务器发送命令。支持以下命令:
  ao : 放弃输出命令。
  ayt : “Are you there”命令。
  esc : 发送当前的转义字符。
  ip : 中断进程命令。
  synch : 执行 Telnet 同步操作。
  brk : 发送信号。
  上表所列命令以外的其他命令都将以字符串的形式发送至 Telnet 服务器。例如,sendabcd 将发送字符串 abcd 至 Telnet 服务器,这样,Telnet 会话窗口中将出现该字符串。
  quit :使用 quit 命令可以退出 Telnet 客户端。
  
windows10 中telnet默认是关闭的,需要打开才能使用
控制面板 -> 程序 -> 启动或关闭Windows功能 -> 勾选 Telnet客户端 后点击确定
如果 Telnet客户端 依旧无法使用或无法建立多个连接,则需重启计算机

使用步骤
1. window+r —> cmd —> telnet回车 —> 输入quit退出telnet模式
2. telnet IP PORT -> 连接成功后会跳到Tenlet控制页面(全黑,啥也看不到),输入crlt+] 进入命令模式 -> send hello world -> 退出Telnet控制台,quit指令
/**
 * 线程工厂
 *
 * @author 
 * @since 
 */
public class ThreadFactoryBuild {

    private final String NUM = "%d";

    private final String STR = "%s";

    /**
     * %d 数字
     * %s 字符串
     */
    private String nameType = STR;

    private String name;

    private final ThreadFactory nameThreadFactory;

    public ThreadFactoryBuild() {
        nameThreadFactory = r -> {
            if (STR.equals(nameType)) {
                name = UUID.randomUUID().toString().replace("-", "");
            } else {
                name = String.valueOf(System.currentTimeMillis());
            }
            return new Thread(r, name);
        };
    }

    public ThreadFactoryBuild setName(String nameType) {
        this.nameType = nameType;
        return this;
    }

    public ThreadFactory build() {
        return this.nameThreadFactory;
    }
}

/**
 * BIO 服务器
 *
 * @author 
 * @since 
 */
public class BioService {
    /**
     * 自定义线程名称,方便出错的时候朔源
     */
    private static final ThreadFactory NAME_THREAD_FACTORY = new ThreadFactoryBuild().setName("%s").build();

    /**
     * 编码集
     */
    private static final Charset GBK = Charset.forName("GBK");

    /**
     * 引导程序
     *
     * @param port 端口号
     */
    public static void bootstrap(int port) throws IOException {
        // 思路
        // 1. 创建一个线程池
        // 2. 如果有客户端链接就创建一个线程与之通讯(单独写一个方法)
        // 获取CPU核数
        int processors = Runtime.getRuntime().availableProcessors();
        System.out.println("CPU cores: " + processors);
        // 线程池机制
        ThreadPoolExecutor pool = new ThreadPoolExecutor(processors, processors, 0L, TimeUnit.MICROSECONDS, new LinkedBlockingQueue<>(), NAME_THREAD_FACTORY);
        // 创建ServerSocket
        ServerSocket serverSocket = new ServerSocket(port);
        System.out.println("服务器启动了");
        while (true) {
            System.out.println("线程信息 id = " + Thread.currentThread().getId() + " 名称 = " + Thread.currentThread().getName());
            // 监听客户端链接
            System.out.println("等待连接...");
            final Socket socket = serverSocket.accept();
            System.out.println("连接到一个客户端");
            // 创建一个线程与之通讯(单独写一个方法)
            pool.execute(() -> {
                handler(socket);
            });
        }
    }

    /**
     * 与客户端通讯处理
     *
     * @param socket 套接字
     */
    private static void handler(Socket socket) {
        long id = Thread.currentThread().getId();
        try {
            System.out.println("线程信息 id = " + id + " 名称 = " + Thread.currentThread().getName());
            byte[] buff = new byte[1024];
            // 通过socket获取输入流
            InputStream inputStream = socket.getInputStream();
            // 循环读取客户端发送数据
            while (true) {
                System.out.println("id = " + id + "=>read...");
                int read = inputStream.read(buff);
                if (read != -1) {
                    // 输出客户端发送数据
                    System.out.println("id = " + id + "=>" + new String(buff, 0, read, GBK));
                } else {
                    break;
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            System.out.println("id = " + id + "=>关闭与client的连接");
            try {
                socket.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

/**
 * BIO 启动类
 *
 * @author 
 * @since 
 */
public class BioStudy {
    public static void main(String[] args) throws IOException {
        BioService.bootstrap(6666);
    }
}

Java BIO 问题分析

  1. 每个请求都需要创建独立的线程,与对应的客户端进行数据 Read,业务处理,数据 Write 。
  2. 当并发数较大时,需要创建大量线程来处理连接,系统资源占 用较大。
  3. 连接建立后,如果当前线程暂时没有数据可读,则线程就阻塞 在 Read 操作上,造成线程资源浪费

Java NIO编程

Java NIO 基本介绍

  1. Java NIO 全称 java non-blocking IO,是指 JDK 提供的新 API。从 JDK1.4 开始,Java 提供了一系列改进的输入/输出 的新特性,被统称为 NIO(即 New IO),是同步非阻塞
  2. NIO 相关类都被放在 java.nio 包及子包下,并且对原 java.io 包中的很多类进行改写。
  3. NIO 有三大核心部分:Channel(通道),Buffer(缓冲区), Selector(选择器)
  4. NIO是 面向缓冲区 ,或者面向 块 编程的。数据读取到一个它稍后处理的缓冲区,需要时可在缓冲区中前后移动,这就 增加了处理过程中的灵活性,使用它可以提供非阻塞式的高伸缩性网络
  5. Java NIO的非阻塞模式,使一个线程从某通道发送请求或者读取数据,但是它仅能得 到目前可用的数据,如果目前没有数据可用时,就什么都不会获取,而不是保持线 程阻塞,所以直至数据变的可以读取之前,该线程可以继续做其他的事情。 非阻塞 写也是如此,一个线程请求写入一些数据到某通道,但不需要等待它完全写入,这个线程同时可以去做别的事情
  6. 通俗理解:NIO是可以做到用一个线程来处理多个操作的。假设有10000个请求过来, 根据实际情况,可以分配50或者100个线程来处理。不像之前的阻塞IO那样,非得分配10000个
  7. HTTP2.0使用了多路复用的技术,做到同一个连接并发处理多个请求,而且并发请求的数量比HTTP1.1大了好几个数量级

案例说明NIO Buff

public class BasicBuffer {
    public static void main(String[] args) {
        // 举例说明Buffer的使用(简单说明)
        // 创建一个Buffer, 大小为5,即可以存放5个int
        IntBuffer buffer = IntBuffer.allocate(5);
        // 向buffer存放数据 <code>buffer.put(5) | buffer.put(2) | buffer.put(0)</code>
        for (int i = 0; i < buffer.capacity(); i++) {
            buffer.put(i * 2);
        }
        // 如何从buffer读取数据
        // 将 buffer 转换, 读写切换(!!!)
        buffer.flip();
        while (buffer.hasRemaining()) {
            System.out.println(buffer.get());
        }
    }
}

NIO 和 BIO 的比较

  1. BIO 以流的方式处理数据,而 NIO 以块的方式处理数据,块 I/O 的效率比流 I/O 高很多
  2. BIO 是阻塞的,NIO 则是非阻塞的
  3. BIO基于字节流和字符流进行操作,而 NIO 基于 Channel(通道)和 Buffer(缓冲区)进 行操作,数据总是从通道读取到缓冲区中,或者从缓冲区写入到通道中。 Selector(选择器)用于监听多个通道的事件(比如:连接请求,数据到达等),因此使用单个线程就可以监听多个客户端通道

NIO 三大核心原理示意

  • Selector 、Channel 和 Buffer 的关系图(简单版)
graph TD; Thread --- Selector Selector --> Channel_1 --> Buffer_1 --> 程序_1 Selector --> Channel_2 --> Buffer_2 --> 程序_2 Selector --> Channel_3 --> Buffer_3 --> 程序_3
  • 关系图的说明
    1. 每个channel 都会对应一个Buffer
    2. Selector 对应一个线程, 一个线程对应多个channel(连接)
    3. 该图反应了有三个channel 注册到 该selector
    4. 程序切换到哪个channel 是有事件决定的, Event 就是一个重要的概念
    5. Selector 会根据不同的事件,在各个通道上切换
    6. Buffer 就是一个内存块 , 底层是有一个数组
    7. 数据的读取写入是通过Buffer, 这个和BIO不同 , BIO 中要么是输入流,或者是 输出流, 不能双向,但是NIO的Buffer 是可以读也可以写, 需要 flip 方法切换
    8. channel 是双向的, 可以返回底层操作系统的情况, 比如Linux ,底层的操作系统 通道就是双向的

缓冲区(Buffer)

基本介绍

缓冲区(Buffer):缓冲区本质上是一个可以读写数据的内存块,可以理解成是一个 容器对象(含数组),该对象提供了一组方法,可以更轻松地使用内存块,,缓冲区对 象内置了一些机制,能够跟踪和记录缓冲区的状态变化情况。Channel 提供从文件、 网络读取数据的渠道,但是读取或写入的数据都必须经由 Buffer

flowchart LR; NIO程序 <-->|data| 缓冲区 <-->|Channel| 文件[/文件/]

标签:Netty,NIO,BIO,Telnet,线程,客户端
来源: https://www.cnblogs.com/Zzzyyw/p/16182198.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有