首页 > 其他分享 >NIO

NIO

时间:2022-10-01 21:12:27浏览次数:65  
标签:java NIO buffer selector byteBuffer new channel

网络编程

Socket

.getByte() 字符串转换为字节

socket是传输层和应用层之间的连接

Socket套接字是通信的基石,是支持TCP/IP协议的网络通信的基本操作单元

服务端监听端口,客户端发送消息,收到客户端的消息并输出

服务端serversocket

文件传输:

用输入流将文件读入内存

用输出流将文件输出到socket

用输出流先为文件预留位置

用输入流将socket从网卡输入到内存

用输出流将内存中的文件输出到硬盘

udp:

DatagramPacket

IO

File 类

https://blog.csdn.net/ai_bao_zi/article/details/81015141

其实现了Serializable, Comparable两大接口以便于其对象可序列化和比较

可获得上级目录,文件名等

输入:文件输入内存(java程序) 输出:内存输出到文件

io流不关的话会一直占用内存空间,直到jvm结束

这里写图片描述

文件流

File类给我们提供了抽象的表示File.separator,屏蔽了系统层的差异

InputStream

InputStream:字节输入流基类 抽象类

InputStream extends Object implements Closeable

file:

read()读取一个字节的数据 返回-1表示读完了

将流放入i 判断

while ((i=inputStream.read())!=-1){
    System.out.println((char) i);
}

outputStream

OutputStream是输出流的抽象,它是将程序内存中的数据写入到目的地(也就是接收数据的一端)

System.out是一个PrintStream流

file:

FileOutputStream outputStream = new FileOutputStream("C:a.txt");

向文件中写入字符

outputStream.write('a');

写入字符串

outputStream.write("dwxcc".getBytes(StandardCharsets.UTF_8));

限长写入,从第二个字符g 写两位 gv

outputStream.write("tgvbdwsc".getBytes(StandardCharsets.UTF_8),1,2);

文件复制 io版

FileInputStream inputStream = new FileInputStream("C:a.txt");
FileOutputStream outputStream = new FileOutputStream("C:b.txt");
byte[] bytes = new byte[1024];
int a = 0;
while ((a=inputStream.read(bytes))!=-1){
    outputStream.write(bytes,0,a);
}
inputStream.close();outputStream.close();

处理流

BufferedReader

按行读取 效率高 读取文件内容,输出到控制台

BufferedReader reader = new BufferedReader(new FileReader("C:\\Users\\admin\\Desktop\\Projects\\cloud-ali\\demo\\src\\main\\java\\com\\example\\poo\\file1\\a.txt"));
String el;
while ((el = reader.readLine()) != null){
    System.out.println(el);
}
reader.close();

BufferedWriter

0 代表从第0个开始,3代表向后数3个字符串

writer.write("dcc",0,3);
 writer.newLine(); 换行
while ((el = reader.readLine()) != null){
    writer.write(el);
    writer.newLine();
}

对象处理流 序列化

ObjectInputStream ObjectoutputStream

ObjectOutputStream os = new ObjectOutputStream(new FileOutputStream("C:\\Users\\admin\\Desktop\\Projects\\cloud-ali\\demo\\src\\main\\java\\com\\example\\poo\\file2\\n.txt"));
os.write

什么都能输入

序列化:

ObjectOutputStream os = new ObjectOutputStream(new FileOutputStream("C:\\Users\\admin\\Desktop\\Projects\\cloud-ali\\demo\\src\\main\\java\\com\\example\\poo\\file2\\n.txt"));
os.writeObject(UDPsocket.class);

指定格式保存 为自定义后缀

反序列化

Object o = inputStream.readObject();
System.out.println(o.getClass());

字符流处理纯文本数据

NIO

https://blog.csdn.net/zcw4237256/article/details/78662762

概念

BIO 阻塞io

thread-》write,read-》socket

如果有100个线程,而有100个用户在下载,第101个用户的请求会无法处理

客户端有连接请求时服务器需要启动一个线程进行处理,如果线程没有炒作时会造成不必要的开销

一个线程一个读写io,一个socket

非阻塞io

thread-》selector-》channel(write,read)-》buffer-》socket

一个线程通过一个选择器操作多个io,每个io一个socket

io事件本身不阻塞,获取io事件的方法需要阻塞

非阻塞是面向缓冲区编程,使用selector实现nio

当用户线程发起一个read操作后,并不需要等待,而是马上就得到了一个结果。如果结果是一个error时,它就知道数据还没有准备好,于是它可以再次发送read操作。一旦内核中的数据准备好了,并且又再次收到了用户线程的请求,那么它马上就将数据拷贝到了用户线程,然后返回。

所以事实上,在非阻塞IO模型中,用户线程需要不断地询问内核数据是否就绪,也就说非阻塞IO不会交出CPU,而会一直占用CPU

io核心:

Channels

Buffers

Selectors

系统流:

byte[] data = new byte[10];
System.in.read(data);
System.out.println();
for (int i = 0; i < data.length; i++) {
    System.out.println((char)[i]);
}

各种io理解

同步阻塞IO, 其实就是服务端创建一个ServerSocket, 然后就是客户端用一个Socket去连接服务端的那个ServerSocket, ServerSocket接收到了一个的连接请求就创建一个Socket和一个线程去跟那个Socket进行通讯。接着客户端和服务端就进行阻塞式的通信,客户端发送一个请求,服务端Socket进行处理后返回响应,在响应返回前,客户端那边就阻塞等待,上门事情也做不了。 这种方式的缺点, 每次一个客户端接入,都需要在服务端创建一个线程来服务这个客户端,这样大量客户端来的时候,就会造成服务端的线程数量可能达到了几千甚至几万,这样就可能会造成服务端过载过高,最后崩溃死掉

核心组件关系

BIO基于字节流和字符流进行操作,nio基于channel和buffer操作 buffer通过file可实现双向操作

thread-》selector-》channel(write,read)-》buffer-》socket

Channel

channel 是通道 读取与写入 双工

  • FileChannel 从文件读写数据
  • SocketChannel 通过TCP读写网络数据
  • ServerSocketChannel 可以监听新进来的TCP连接,并对每个链接创建对应的SocketChannel
  • DatagramChannel 通过UDP读写网络中的数据

FileChannel

读操作:将通道数据读到缓冲区

写操作:将缓冲区的数据写入通道

buf.flip 读写转换

/**
 * 创建文件流 channel通道
 * 创建字符缓冲区
 * 将数据读取到channel中 当其不等于负一时代表没读完
 * flip读写转换 写完了后开始读取
 * byteBuffer.hasRemaining()表示里面是否有剩余内容
 * 通过get取出字符 char转换
 */
public static void main(String[] args) throws IOException {
    RandomAccessFile file = new RandomAccessFile("a.txt","rw");
    FileChannel channel = file.getChannel();
    ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
    int bytesRead = channel.read(byteBuffer);
    while (bytesRead != -1){
        System.out.println( bytesRead);
        byteBuffer.flip();
        while (byteBuffer.hasRemaining()){
            System.out.println((char) byteBuffer.get());
        }
        byteBuffer.clear();
        bytesRead = channel.read(byteBuffer);
    }
    file.close();
}

向txt写入数据 替换式写入

不能之间向channel写数据,要写入buffer再写入channel

RandomAccessFile file = new RandomAccessFile("C:\\Users\\admin\\Desktop\\Projects\\cloud-ali\\demo\\src\\main\\java\\com\\example\\poo\\file1\\a.txt", "rw");
FileChannel channel = file.getChannel();
ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
String a = "data";
byteBuffer.clear();
byteBuffer.put(a.getBytes(StandardCharsets.UTF_8));
byteBuffer.flip();
while(byteBuffer.hasRemaining()){
    channel.write(byteBuffer);
}
channel.close();

position: 调用position方法在文件特定位置进行读写

image-20220402160125043

ASCII码编码方案中,一个英文字符占用一个字节,一个汉字字符占用两个字节的空间;二:在Unicode编码方案中,一个英文字符或一个汉字字符都占用两个字节的空间;三:在UTF-8编码方案中,一个英文字符占用一个字节,一个汉字字符占用三个字节的空间

image-20220402161054653

image-20220402161117263

RandomAccessFile file = new RandomAccessFile("C:\\Users\\admin\\Desktop\\Projects\\cloud-ali\\demo\\src\\main\\java\\com\\example\\poo\\file1\\a.txt", "rw");
RandomAccessFile file1 = new RandomAccessFile("C:\\Users\\admin\\Desktop\\Projects\\cloud-ali\\demo\\src\\main\\java\\com\\example\\poo\\file1\\b.txt","rw");
FileChannel channel = file.getChannel();
FileChannel channel1 = file1.getChannel();
channel1.transferFrom(channel,0,channel.size());
channel1.close();
channel.close();
channel.transferTo(0,channel.size(),channel1);
channel1.transferFrom(channel,0,channel.size());
大小为写入者的大小

ServerSocketChannel

ServerSocketChannel 是一个可以监听新进来的TCP连接的通道

可阻塞式监听和非阻塞式监听

开启监听
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
设置监听端口
serverSocketChannel.socket().bind(new InetSocketAddress(9999));

阻塞式接收

while(true){
    SocketChannel socketChannel =
            serverSocketChannel.accept();
    System.out.println(socketChannel.socket().getRemoteSocketAddress());
}

非阻塞: 非阻塞会使cpu占用过高

while(true){
    SocketChannel socketChannel =
            serverSocketChannel.accept();
             if(socketChannel != null){
       System.out.println(socketChannel.socket().getRemoteSocketAddress());
    }
   
}

SocketChannel

SocketChannel是一个连接到TCP网络套接字的通道,它是Socket类的对等类,通常SocketChannel用在客户端以向服务器发起
连接请求。每个SocketChannel对象创建时都关联一个对等的Socket对象

image-20220403160447270

image-20220403160506850

SocketChannel socketChannel = SocketChannel.open(new InetSocketAddress("127.0.0.1",9999));
 ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
 byteBuffer.put(new String("dwx").getBytes(StandardCharsets.UTF_8));
 byteBuffer.flip();
 socketChannel.configureBlocking(true);
 while(byteBuffer.hasRemaining()){
     socketChannel.write(byteBuffer);
 }

DatagramChannel

DatagramChannel是一个能收发UDP包的通道,其底层实现为DatagramSocket+Selector。DatagramChannel可以调用socket()方法获取对等DatagramSocket对象

一个未绑定具体的端口DatagramChannel仍可以接受数据包,当一个底层的socket被创建时,一个随机的端口号被分配给他

upd 数据传输:

public static void sendData() throws IOException {
    DatagramChannel channel = DatagramChannel.open();
    ByteBuffer buffer = ByteBuffer.wrap("dw对我说".getBytes(StandardCharsets.UTF_8));
    InetSocketAddress address = new InetSocketAddress("127.0.0.1",7000);
    channel.send(buffer,address);
}

接 收

public static void receiveData() throws IOException {
    DatagramChannel channel = DatagramChannel.open();
    channel.socket().bind(new InetSocketAddress(7000));
    ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
    while (true){
        byteBuffer.clear();
        SocketAddress address = channel.receive(byteBuffer);
        byteBuffer.flip();
        System.out.println(address.toString());
        System.out.println(StandardCharsets.UTF_8.decode(byteBuffer));
    }

}

分散聚集

image-20220404134150789

java channel 与 netty channel:

netty是更高级的实现

Buffer 缓冲区

https://blog.csdn.net/czx2018/article/details/89502699

image-20220404134850265

传统阻塞型I/O的问题

传统的网络I/O编程是ServerSocket的accept方法一直等待着TCP请求的接入,每当收到一个TCP请求后,ServerSocket就会创建出一组I/O流,把它们交给一个线程去处理

1.假设访问的高峰期并发量较大,我们必须为程序配置一个较大的线程池,可是当过了高峰期,并发量减少了,那些空闲的线程岂不是一种资源的浪费?
2.当I/O流打开以后如果数据传输并不频繁,那么这个线程很大一部分时间都是在等待中度过的,这又是一种资源的利用率低下

nio让更少的线程能够处理更多的事情

在这里插入图片描述

Buffer(缓冲区)的底层其实就是一个数组,它提供了一些方法来向数组中写入和读出数据

Buffer的几个常用方法:

allocate() - 初始化一块缓冲区
put() - 向缓冲区写入数据
get() - 向缓冲区读数据
filp() - 将缓冲区的读写模式转换
clear() - 这个并不是把缓冲区里的数据清除,而是利用后来写入的数据来覆盖原来写入的数据,以达到类似清除了老的数据的效果
compact() - 从读数据切换到写模式,数据不会被清空,会将所有未读的数据copy到缓冲区头部,后续写数据不会覆盖,而是在这些数据之后写数据
mark() - 对position做出标记,配合reset使用
reset() - 将position置为标记值

Buffer的几个核心属性:

capacity - 缓冲区大小,缓冲区一旦创建出来以后,这个属性就不会再变化了
position - 读写数据的定位指针,用来标识当前读取到了哪一个位置。
limit - 读写的边界,用于限制指针的最大指向位置。当指针走到边界上的时候就要停住,否则就会抛出BufferUnderflowException

使用buffer

创建缓冲区 ,写数据到buffer , 读写转换,读数据,调用clear以便再次接收

读写文件

RandomAccessFile file = new RandomAccessFile
FileChannel channel = file.getChannel();
ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
channel.read(byteBuffer);
byteBuffer.flip();
while (byteBuffer.hasRemaining()){
    System.out.println((char) byteBuffer.get());
}
file.close();

file方法 clear方法

直接打印byytebuffer

java.nio.HeapByteBuffer[pos=0 lim=5 cap=1024]
d
java.nio.HeapByteBuffer[pos=1 lim=5 cap=1024]
w
java.nio.HeapByteBuffer[pos=2 lim=5 cap=1024]
o
java.nio.HeapByteBuffer[pos=3 lim=5 cap=1024]
k
java.nio.HeapByteBuffer[pos=4 lim=5 cap=1024]
c

pos代表的就是position属性,lim代表limit属性,cap代表capacity属性

在缓冲区刚初始化出来的时候,position指向的是数组的第一个位置,limit和数组的容量一样。

用图片来表示大概就如下图

在这里插入图片描述

向缓冲区中每写入一个数据,指针就会后移一位

在这里插入图片描述

当调用了flip()方法后,position又会重新指向数组的第一个位置,而limit会指向原来的position的位置

intBuffer.hasRemaining()方法就是判断position < limit

读写数据

放入什么类型就要用什么类型取出

channel.read(buffer)

buffer.put()

读到channel

channel.write()

MappedByteBuffer

可以让文件在内存(堆外的内存)修改

操作系统不需要拷贝

RandomAccessFile file
FileChannel channel

表示读写模式,从索引0开始,修改5个字节

MappedByteBuffer byteBuffer = channel.map(FileChannel.MapMode.READ_WRITE,0,5);

分散聚集

Selector

Selector 一般称 为多路复用器 。它是Java NIO核心组件中的一个,用于检查一个或多个NIO Channel(通道)的状态是否处于可读、可写

使用 Selector, 我们首先需要将 Channel 注册到 Selector 中, 随后调用 Selector 的 select()方法, 这个方法会阻塞, 直到注册在 Selector 中的 Channel 发送可读写事件(或其它注册事件). 当这个方法返回后, 当前的这个线程就可以处理 Channel 的事件了

select监控注册的通道,当有io时将对应的key加入到集合中并返回,可设置超时时间

channel注册成功后返回key

filechannel无法注册到selector

步骤:

创建channel selector 将channel设置非阻塞,

使用 Channel.register()方法 注册

Selector selector = Selector.open();
ServerSocketChannel socketChannel = ServerSocketChannel.open();
socketChannel.configureBlocking(false);
socketChannel.bind(new InetSocketAddress(9999));
socketChannel.register(selector, SelectionKey.OP_ACCEPT);

Connect, 连接事件(TCP 连接), 对应于

SelectionKey.OP_CONNECT 连接就绪事件,表示客户与服务器的连接已经建立成功
SelectionKey.OP_ACCEPT 接收连接进行事件,表示服务器监听到了客户连接,那么服务器可以接收这个连接了
SelectionKey.OP_READ, 读就绪事件,表示通道中已经有了可读的数据,可以执行读操作了(通道目前有数据,可以进行读操作了)
SelectionKey.OP_WRITE,写就绪事件,表示已经可以向通道写数据了(通道目前可以用于写操作)
用或运算可以组合事件

通过 SelectionKey 获取相对应的 Channel 和 Selector


Select会等待通道注册 selector.select()判断通道事件发生

调用select方法,返回有事件发生个数 SelectionKey返回集合

通过key反向获取channel

next.isAcceptable() 和 accept

一个不知道会有连接来,需要等待,一个知道连接来了,不用等待

channel发生连接,可读事件

// 取消注册
key.cancel();

可以通过key得到channel

聊天室

服务端:

定义属性:selector serversocketchannel

package com.example.poo.nio;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.Iterator;

public class GroupChatServer {

    // 定义需要用到属性
    private Selector selector;
    private ServerSocketChannel listenChannel;
    private static final int PORT = 6666;

    /**
     * 构造器,进行初始化
     */
    public GroupChatServer() {
        try {
            // 得到选择器
            selector = Selector.open();
            // 得到ServerSocketChannel
            listenChannel = ServerSocketChannel.open();
            // 绑定端口
            listenChannel.socket().bind(new InetSocketAddress(PORT));
            // 设置非阻塞模式
            listenChannel.configureBlocking(false);
            // 将listenChannel注册到selector上
            listenChannel.register(selector, SelectionKey.OP_ACCEPT);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 监听
     */
    public void listen() {
        try {
            while (true) {
                int count = selector.select();
                // count > 0 就表示有事件要处理
                if (count > 0) {
                    // 拿到selectionKey的集合
                    Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
                    while (iterator.hasNext()) {
                        // 取出selectionKey
                        SelectionKey key = iterator.next();
                        // 监听到不同的事件进行不同的处理
                        listenHandler(key);
                        // 删除当前key,防止重复处理
                        iterator.remove();
                    }
                } else {
                    System.out.println("当前没事件要处理,等待中……");
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }


    /**
     * 针对监听到的不同事件做不同的处理
     * @param key
     * @throws Exception
     */
    public void listenHandler(SelectionKey key) {
        SocketChannel sc = null;
        try {
            // 如果是连接事件
            if (key != null && key.isAcceptable()) {
                // 拿到socketChannel并注册到selector上
                sc = listenChannel.accept();
                sc.configureBlocking(false);
                sc.register(selector, SelectionKey.OP_READ);
                // 提示:xxx上线了
                System.out.println(sc.getRemoteAddress().toString().substring(1) + "上线了");
            }
            // 如果是读取事件
            if (key != null && key.isReadable()) {
                // 拿到socketChannel
                sc = (SocketChannel) key.channel();
                sc.configureBlocking(false);
                // 创建buffer
                ByteBuffer buffer = ByteBuffer.allocate(1024);
                // 将客户端消息读到buffer中去
                int count = sc.read(buffer);
                if (count > 0) {
                    // 如果读取到了数据,就把缓冲区数据转成字符串输出
                    String msg = new String(buffer.array());
                    System.out.println("from client " + msg);
                    // 将该消息转发给其他客户端,
                    sendMsgToClient(msg, sc);
                }
            }
        } catch (IOException e) {
            try {
                System.out.println(sc.getRemoteAddress().toString().substring(1) + "离线了");
                // 取消注册
                key.cancel();
                // 关闭通道
                sc.close();
            } catch (IOException e1) {
                e1.printStackTrace();
            }
        }
    }


    /**
     * 发送消息到客户端,要排除当前客户端。
     * 假如A、B、C三个客户端,当前消息是A发来的,那么只转发给B和C就可以。
     * @param msg
     * @param self
     * @throws IOException
     */
    public void sendMsgToClient(String msg, SocketChannel self) throws IOException {
        System.out.println("服务器转发消息中");
        // 遍历所有注册到selector上的SocketChannel,并排除自己
        for (SelectionKey key : selector.keys()) {
            Channel channel = key.channel();
            if (channel instanceof SocketChannel && channel != self) {
                SocketChannel targetChannel = (SocketChannel) channel;
                // 将msg写到buffer中
                ByteBuffer buffer = ByteBuffer.wrap(msg.getBytes());
                // 将buffer数据写入通道
                targetChannel.write(buffer);
            }
        }
    }


    public static void main(String[] args) {
        GroupChatServer server = new GroupChatServer();
        server.listen();
    }
}
package com.example.poo.nio;

import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Scanner;
import java.util.concurrent.TimeUnit;

public class GroupChatClient {

    // 定义相关属性
    private final String HOST = "127.0.0.1";
    private final int PORT = 6666;
    private Selector selector;
    private SocketChannel socketChannel;
    private String username;

    /**
     * 构造器,进行初始化
     */
    public GroupChatClient() {
        try {
            selector = Selector.open();
            // 连接服务器
            socketChannel = SocketChannel.open(new InetSocketAddress(HOST, PORT));
            // 设置非阻塞
            socketChannel.configureBlocking(false);
            // 将socketChannel注册到selector上
            socketChannel.register(selector, SelectionKey.OP_READ);
            // 拿到username
            username = socketChannel.getLocalAddress().toString().substring(1);
            System.out.println(username + " is ok");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 向服务端发送消息
     *
     * @param msg
     */
    public void sendMsgToServer(String msg) {
        msg = username + ":" + msg;
        try {
            // 发送消息
            socketChannel.write(ByteBuffer.wrap(msg.getBytes()));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 接收服务端发来的消息
     */
    public void acceptMsgFromServer() {
        try {
            int readChannel = selector.select();
            if (readChannel > 0) { // 有可用的通道
                Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
                while (iterator.hasNext()) {
                    SelectionKey key = iterator.next();
                    if (key.isReadable()) {
                        // 获取到通道
                        SocketChannel sc = (SocketChannel) key.channel();
                        // 创建buffer
                        ByteBuffer buffer = ByteBuffer.allocate(1024);
                        // 将通道中的数据读到buffer中
                        sc.read(buffer);
                        // 将buffer中的数据转成字符串
                        String msg = new String(buffer.array());
                        System.out.println(msg.trim());
                        // 移除当前的key,防止重复操作
                        iterator.remove();
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        // 1. 启动客户端
        GroupChatClient client = new GroupChatClient();
        // 2. 启动线程,每隔3秒就读取一次服务端的消息
        new Thread(() -> {
            while (true) {
                client.acceptMsgFromServer();
                try {
                    TimeUnit.SECONDS.sleep(1); } catch (Exception e) { e.printStackTrace();}
            }
        }).start();
        // 3. 发送数据到服务端
        @SuppressWarnings("resource")
        Scanner scanner = new Scanner(System.in);
        while (scanner.hasNextLine()) {
            String msg = scanner.nextLine();
            client.sendMsgToServer(msg);
        }
    }
}

标签:java,NIO,buffer,selector,byteBuffer,new,channel
From: https://www.cnblogs.com/saylk/p/16747775.html

相关文章

  • Netty 学习(六):创建 NioEventLoopGroup 的核心源码说明
    Netty学习(六):创建NioEventLoopGroup的核心源码说明作者:Grey原文地址:博客园:Netty学习(六):创建NioEventLoopGroup的核心源码说明CSDN:Netty学习(六):创建NioEventLoopG......
  • win10安装minio
    window10安装minion1、首先第一步我们先下载minio我这里是下载的是3.0版本根据需求可以从官网上下不同的版本2、启动minio切记不要双击minio,1、把下载好的minio.exe放......
  • [Typescript] 41. Medium - IsUnion
    Implementatype IsUnion,whichtakesaninputtype T andreturnswhether T resolvestoauniontype.Forexample:typecase1=IsUnion<string>//false......
  • java bio nio aoi
    一个简单的服务器分别使用BIONIOAOI实现BIO阻塞IO每个链接单独起来你一个线程点击查看代码packageorg.example.bio;importjava.io.BufferedReader;importja......
  • minio通过docker方式部署
    MinIO是在GNUAffero通用公共许可证v3.0下发布的高性能对象存储。它是与AmazonS3云存储服务兼容的API官方文档http://docs.minio.org.cn/docs/master/minio-adm......
  • [Typescript] Tips: Use 'in' operator to transform a union to another union(watch
    WewanttoconvertexporttypeEntity=|{type:"user"}|{type:"post"}|{type:"comment"}to typeEntityWithId=|{type:"user",userId:string......
  • docker集成分词器时报Caused by: java.nio.file.FileSystemException: /usr/share/ela
    官网https://github.com/medcl/elasticsearch-analysis-ik/releases/tag/v6.8.6下载好的zip包cp到/usr/share/elasticsearch/plugins/下后解压它会带有一个config包,或者......
  • [Typescript] Tips: Derive a union type from an object
    constfruitCounts={apple:12,banana:23}typePropUnion<TextendsRecord<PropertyKey,any>>={[KinkeyofT]:{[K2inK]:T[K2]}}[keyof......
  • 【笔记】Oracle union all&for update锁
    【笔记】Oracleunionall&forupdateunionall在Oracle中有三种类型的集合操作UNION:求并,重复记录只显示一次UNIONALL:求并集,显示所有记录信息INTERSECT:求交集MINUS......
  • union和union all的区别
    从结果上来看1. UNION和UNIONALL都是将两个结果集合并为一个,但是UNION在进行表链接后会筛选掉重复的记录,UnionAll不会去除重复记录。2.Union将会按照字段的顺序进行......