IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> java必备Netty技术(1) -> 正文阅读

[Java知识库]java必备Netty技术(1)

  • 涉及的一些基础
    涉及的设计模式:观察者模式、命令模式、责任链模式
    涉及的数据结构:链表(管道的底层使用了链表)

一.netty介绍

是一个java开源项目
是一个异步的基于事件驱动的网络应用框架,用以开发高性能、高可用的网络io程序

异步是相对于同步而言的
同步:在同步中,当浏览器向服务器发送了一个请求1,需要等待服务器向浏览器返回了响应2后,浏览器才能进行操作3。
在

异步:在异步中,浏览器向服务器发起一个请求,但是它并不会因为响应没有到达,而发生阻塞,而是可以进行操作3,不需要等待响应2,提供了性能
在这里插入图片描述

高性能、高可用其实就是对java中的io进行了重写
主要针对于在TCP协议下,面向Clients的高并发应用,或者是peer-to-peer > 场景下的大量数据持续传输的应用
在这里插入图片描述

Netty本质是一个NIO框架,适用于服务通讯的多种应用场景,所以学好NIO。

二.Netty应用的场景

  1. 作为基础的通信组件被RPC框架使用,例如dubbo
  2. 游戏行业
  3. 大数据领域:AVRO实现数据文件的共享

三.IO模型

三种IO模型:用什么样的通道进行数据的发送和接受,很大程度上决定了程序通道的性能

BIO:同步并阻塞(传统阻塞),服务器实现模式为一个链接一个线程,即客户端有链接请求时服务器端就需要启动一个线程进行处理,如果这个链接不做任何事情就会造成不必要的线程开销
在这里插入图片描述
当一个服务器被多个客户端请求时,就会对服务器造成压力,求每个线程都会有一定的开销。

BIO:同步非阻塞,服务器实现模式为一个线程处理多个请求(链接),即客户端发送的链接请求都会注册到多路复用器上,多路复用器轮询到连接有IO请求就进行处理。
在这里插入图片描述
一个线程维护一个选择器,选择器可以对客户端的通道进行轮询操作,实现了可以处理更多的并发

AIO:异步非阻塞,AIO引入异步通道的概念,采用Procactor模式,简化了程序编写,有效的请求才启动线程,它的特点时由操作系统完成后才通知服务端程序开启线程去处理,一般适用于连接数较多且连接时间较长的应用(目前还不常用)

IO适用场景:

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

四.BIO讲解

同步阻塞,线程的开销大,可以通过线程池改善开销
编程流程:

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

BIO相关案例:使用BIO模拟客户端向服务端发送数据

import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class BIOServer {
    public static void main(String[] args) throws IOException {

        //创建一个线程池
        ExecutorService newCachedThreadPool= Executors.newCachedThreadPool();

//        创建一个ServerScocket
        ServerSocket serverSocket=new ServerSocket(6666);

        System.out.println("服务启动了");
        while(true){
            //主线程
            System.out.println("线程信息 id:"+Thread.currentThread().getId()+" 名字为:"+Thread.currentThread().getName());
            final Socket socket=serverSocket.accept(); //可能会进行阻塞
            System.out.println("有客户端进行连接了");

            newCachedThreadPool.execute(new Runnable() {
                @Override
                public void run() {

                    //与客户端进行通讯
                    handler(socket);
                }
            });

        }
    }

    //编写一个Handler方法,与客户端进行通讯
    public static void handler(Socket socket){
        try{
            byte[] bytes=new byte[1024];
            InputStream inputStream = socket.getInputStream(); //可能会进行阻塞
            //循环读取客户端发送的数据
            while(true){
                System.out.println("等待连接");
                System.out.println("线程信息 id:"+Thread.currentThread().getId()+" 名字为:"+Thread.currentThread().getName());
                System.out.println("read-------");
                int read = inputStream.read(bytes); //返回读了多少个数据
                if(read!=-1){
                    System.out.println(new String(bytes,0,read));//输出客户端发送的数据
                }else {
                    break;
                }
            }

        }catch (Exception e){
            e.printStackTrace();
        }finally {
            System.out.println("关闭连接");
            try {
                socket.close();
            } catch (IOException e) {

                e.printStackTrace();
            }
        }
    }
}

在cmd中使用

telnet 127.0.0.1 6666    

ctrl+] 进入命令行界面
后使用send命令进行发送

send helloWorld

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

五.Java NIO基本介绍

  1. 全程java non-blocking IO ,是一系列改进的输入/输出的新特性,被统称为NIO,是同步非阻塞的

  2. NIO相关类都被放在java.nio 包和其子包下,并且对原java.io包中很多类进行了改写

  3. 三大核心部分:Channel(管道) ,Buffer(缓冲区),Selector(选择器)

  4. NIO是面向缓冲区,或者面向块编程的,数据读取到一个它稍后处理的缓冲区,需要时可在缓冲区中进行前后移动,这就增加了处理过程中的灵活性,使用它可以提供非阻塞式的高伸缩性网络。
    在这里插入图片描述

  5. java NIO 的非阻塞模式,是一个线程从某通道发送的请求或者读取数据,但是它仅能得到且目前可用的数据,如果目前没有数据可用时,就什么都不会获得,而不是保持线程阻塞,所以直至数据变的可以读取之前,该线程可以继续做其他的事情,而非阻塞也是如此,一个线程请求写入一些数据到某通道,但是不需要等待它完全写入,这个线程同时可以去做别的事情。

  6. 通俗理解:NIO是可以做到用一个线程来处理多个操作的,假设有10000个请求过来,根据实际情况,可以分配50个或100个,不会像阻塞IO那样,非得分配10000个

  7. HTTP 2.0 使用了多路复用的技术,做到同一个连接并发处理多个请求,而且并发请求的数量比HTTP 1.1大了好几个数量级

NIO的Buffer小案例:

文件1

import java.nio.IntBuffer;

public class BasicBuffer {
    public static void main(String[] args) {

//        创建一个buffer,大小为5,可以存放5个int
        IntBuffer intBuffer=IntBuffer.allocate(5);

//        向Bufer中存放数据
        intBuffer.put(10);
        intBuffer.put(11);
        intBuffer.put(12);
        intBuffer.put(13);
        intBuffer.put(14);

//        如何向Buffer中读取数据
//        将bufer读写切换
        intBuffer.flip();

        while(intBuffer.hasRemaining()){
            System.out.println(intBuffer.get());//取得后,索引后移
        }
    }
}

NIO与BIO的比较

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

NIO三大核心

在这里插入图片描述

  1. 每个channel都对应一个Buffer
  2. Selector对应一个线程
  3. 一个线程可以对应多个channel,一个channel相当于一个连接
  4. 该图反应了有3个channel注册到了Selector
  5. 程序切换到哪个channel是由事件决定的,Event是一个很重要的事件
  6. Seletor会根据不同的事件在各个通道进行切换
  7. Buffer相当于就是一个内存块,底层是一个数组
  8. 数据的读取是通过Buffer,这个和BIO是完全不同的,BIO要么是输入流或者是输出流,不是双向的;但是NIO的Buffer是可以读也可以写,但是需要有flip进行切换
  9. channel是双向的,可以返回底层操作系统的情况,如linux底层的操作系统通道就是双向的

缓冲区Buffer

缓冲区本质上是一个可以读写数据的内存块,可以理解是一个容器对象(含数组),该对象提供了一组方法,可以更加轻松地使用内存块,缓冲区对像内置了一些机制,能够跟踪和记录缓冲区地状态变化情况,channel提供文件、网络读取数据地渠道,但是读取或写入地数据必须是经过Buffer,如图:
在这里插入图片描述
Buffer即其子类:
在这里插入图片描述
先看看Buffer源码地几个重要地成员属性:
在这里插入图片描述

属性概述
Capacity容量,即可以容纳地最大数据量;在缓冲区创建时被设定并且不能改变
Limit表示缓冲区地当前终点,不能对缓冲区超过极限地位置进行读写操作。且极限是可以修改的
Position位置,下一个要被读或写的元素的索引,每次读写缓冲区数据时都会改变值,为下一次读写操作准备
Mark标记

可以对 文件1 进行代码debug调式:
在这里插入图片描述
当position到达5时,就无法进行写的操作了
接下来我们使用filp进行反转,我们先看看这个方法中的源代码:
在这里插入图片描述

将限制limit定位到当前position,表示读操作时必须在limit范围内,接着将position置为0,进行新的读操作。
Buffer中常用的方法:
在这里插入图片描述

ByteBuffer是Buffer的子类,也是我们进行网络传输最常用的一个类

在这里插入图片描述

通道Channel

基本介绍:

1.BIO中的Stream是单向的,例如FileInputStream对象只能进行读取文件数据的操作,而NIO中的通道channel是双向的,可以读操作,也可以写操作。
2.Channel在NIO中是一个接口,常用的Channel类有:FileChannel、DatagramChannel、ServerSocketChannel、SocketChannel
3.FileChannel用于文件的数据读写,DatagramChannel用于UDP的数据的读写,ServerSocketChannel和SocketChannel用于TCP的数据读写

FileChannel是其中一个重要的类,常见的方法有:
int read(ByteBuffer dst) :从通道读取数据并放到缓冲区中
int write(ByteBuffer src) :把缓冲区的数据写到通道中
long transferFrom(ReadableByteChannel src,long position,long count): 从目标通道中复制数据到当前通道
long transferTo(long position,long count,WritiableByteChannel target) : 把数据从当前通道复制给目标通道
在这里插入图片描述

channel、buffer小案例

建议以下案例自行进行debug,观察buffer中那4个关键字段的变化

1.使用前面学习的ByteBuffer和FileChannel,将一段文字写入到file01.txt中

public class ChannelTest1 {

    public static void main(String[] args) throws Exception{
        String s="hello world";
        //创建一个文件输出流

        FileOutputStream fileOutputStream=new FileOutputStream("d://myNIO//file01.txt");
//        注意:虽然在NIO中我们没有怎么提到输出流,但是Channel实际是输出流的一个包装,所以我们可以通过输出流获得一个Channel
//        fileChannel实际是fileChannelmpl
        FileChannel channel = fileOutputStream.getChannel();

        //创建一个缓冲区,用于内存数据到channel的一个缓冲区
        //这个buffer是被写的
        ByteBuffer buffer = ByteBuffer.allocate(1024);
        //将s放入到buffer中
        buffer.put(s.getBytes());

//        现在我们需要将buffer中的数据读到channel中,需要对buffer进行反转
        buffer.flip();

        //将bytebuffer写入到channel
        channel.write(buffer);
        //关闭FileoutputStream即可关闭所有流



    }
}

案例2:

使用channel和buffer读取文件file01.txt

/**
 * 使用channel和buffer读取文件file01.txt
 */
public class ChannelTest2 {
    public static void main(String[] args) throws Exception {
        File file = new File("d://myNIO//file01.txt");
        //创建文件输入流
        FileInputStream fileInputStream = new FileInputStream("d://myNIO//file01.txt");
        //获得通道
        FileChannel channel = fileInputStream.getChannel();
//        创建缓冲区,因为我们文件已知长度,所以这里直接将缓冲区大小设置为和文件大小一样
        ByteBuffer buffer = ByteBuffer.allocate((int)file.length());
        //将通道的数据写入到缓冲区
        int read = channel.read(buffer);
        //从buffer中获取数据并将字节转化为字符串
        //这里直接获取buffer底层的整个数组,与position无关,所以可以不用filp反转了
        System.out.println(new String(buffer.array()));
        fileInputStream.close();



    }
}

  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章           查看所有文章
加:2022-03-03 15:57:54  更:2022-03-03 16:03:05 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 11:56:50-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码