NIO基本介绍

  • Java NIO(New IO) 也有人称之为Java non-blocking IO 是从Java1.4版本开始引入的一个新的IO API,可以代替标准的IO API。NIO与原来的IO有同样的作用和目的,但是使用的方式完全不同,NIO支持面向缓冲区的,基于通道的IO操作。NIO将以更加高效的方式进行文件的读写操作。NIO可以理解为非阻塞IO,传统的IO的read和write只能阻塞执行,线程在读写IO期间不能干其他事情,比如调用socket.read()时,如果服务器一直没有数据传输过来,线程就一直阻塞,而NIO中可以配置socket为非阻塞模式
  • NIO有三大核心部分:Channel(通道),Buffer(缓冲区),Selector(选择器)

NIO和BIO的比较

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

NIO三大核心原理

Buffer缓冲区

  • 缓冲区本质上是一块可以写入数据,然后可以从中读取数据的内存,这块内存被包装成NIO Buffer对象,并提供了一组方法,用来方便的访问该块内存,相比较直接对数组的操作,Buffer API更加容易操作和管理

Channel(通道)

  • Java NIO的通道类似流,但又有些不同 : 既可以从通道中读取数据,又可以写数据到通道。但流的(input或output)读写通常是单向的。通道可以非阻塞读取和写入通道,通道可以支持读取或写入缓冲区,也支持异步地读写

Selector选择器

  • Selector是一个Java NIO组件,可以能够检查一个或多个NIO通道,并确定哪些通道已经准备好进行读取或写入。这压根,一个单独的线程可以管理多个channel,从而管理多个网络连接

  • 每个Channel都会对应一个Buffer
  • 一个线程对应Selector,一个Selector对应多个Channel(连接)
  • 程序切换到那个Channel是由事件决定的
  • Selector会根据不同的事件,在各个通道上切换
  • Buffer就是一个内存块,底层是一个数组
  • 数据的读取写入是通过Buffer完成的,BIO中要么是输入流,或者是输出流,不能双向,但是NIO的Buffer时可以读也可以写。
  • Channel负责传输,Buffer负责存取数据

缓冲区Buffer

  • 一个用于特定基本数据类型的容器。由 Java。nio包定义的,所有缓冲区都是Buffer抽象类的子类。Java NIO中的Buffer主要用于与NIO通道进行交互,数据是从通道读入缓冲区,从缓冲区写入通道中的

Buffer类及其子类

Buffer就像一个数组,可以保存多个相同类型的数据。根据数据类型不同,有以下Buffer常用子类:

  • ByteBuffer
  • CharBuffer
  • ShortBuffer
  • xxxBuffer(xxx代表八种基本数据类型)

上述Buffer类 他们都采用相似的方法进行管理数据,只是各自管理的数据类型不同而已。都是通过如下方法获取一个Buffer对象:

//创建一个容量为capacity的xxxBuffer对象
static xxxBuffer allocate(int capacity);
|
|
|
IntBuffer buffer = IntBuffer.allocate(10);

Buffer中的重要概念

  • 容量(capacity):创建后不能更改,且容量不能为负

  • 限制(limit):表示缓冲区中可以操作数据的大小.缓冲区的限制不能为负,并且不能大于其容量.

    写入模式,限制等于buffer的容量.读取模式下,limit等于写入的数据量

  • 位置(position):下一个要读取或写入的数据的索引.缓冲区的位置不能为负,并且不能大于其限制

  • 标记(mark)与重置(reset):标记是一个索引,通过Buffer中的mark()方法 指定 Buffer中一个特定的position,之后可以通过调用reset()方法恢复到这个position.

常用API测试

/**
* @PROJECT_NAME: JAVA_Test
* @DESCRIPTION:
* @USER: 罗龙达
* @DATE: 2021/2/10 17:34
*/
public class apiTest { public void print(Buffer buffer){
System.out.println("pos = " + buffer.position());
System.out.println("lim = " + buffer.limit());
System.out.println("cap = " + buffer.capacity());
} @Test
public void test001(){
//1. 分配一个缓冲区,容量设置为10
ByteBuffer buffer = ByteBuffer.allocate(10);
print(buffer);
//2. put往缓冲区中添加数据
System.out.println("--------缓冲区添加数据--------");
buffer.put("LongDa66".getBytes());
print(buffer);
//3. Buffer flip() --> 将缓冲区的界限设置为当前位置,并将当前位置设置为0 可读模式
System.out.println("--------调用flip()方法--------");
buffer.flip();
print(buffer);
//4. get数据的读取
System.out.println("--------缓冲区中读取数据--------");
byte b = buffer.get();
System.out.println("从缓冲区中读取 " + (char)b);
print(buffer);
} @Test
public void test002(){
//1. 分配一个缓冲区,容量设置为10
ByteBuffer buffer = ByteBuffer.allocate(10);
print(buffer);
//2. put往缓冲区中添加数据
System.out.println("--------缓冲区添加数据--------");
buffer.put("LongDa66".getBytes());
print(buffer);
//2. 清除缓冲区中的数据,调用clear方法后只是将pos移到了0.
System.out.println("-------调用clear()方法后--------");
buffer.clear();
byte b = buffer.get();
System.out.println("从缓冲区中读取 " + (char)b);
print(buffer);
} @Test
public void test003(){
//1. 分配一个缓冲区,容量设置为10
ByteBuffer buffer = ByteBuffer.allocate(10);
print(buffer);
//2. put往缓冲区中添加数据
System.out.println("--------缓冲区添加数据--------");
System.out.println("向缓冲区添加 : LongDa66");
buffer.put("LongDa66".getBytes());
print(buffer); buffer.flip();
//3. 从缓冲区中读取前4位
System.out.println("--------缓冲区读取前4位数据--------");
byte[] bytes = new byte[4];
buffer.get(bytes);
String s = new String(bytes);
System.out.println(s);
print(buffer);
System.out.println("-----接着用mark()标记后,读取的数据-----");
buffer.mark();
byte[] bytes2 = new byte[4];
buffer.get(bytes2);
String s2 = new String(bytes2);
System.out.println(s2);
print(buffer);
System.out.println("-----调用reset()回到标记位置-----");
buffer.reset();
print(buffer);
System.out.println("-----调用remaining,看看position和limit之间剩余元素个数-----");
print(buffer);
System.out.println("缓冲区剩余元素个数" + buffer.remaining()); }
}

直接缓冲区与非直接缓冲区

  • 非直接缓冲区 : 通过allocate() 方法分配缓冲区,将缓冲区建立在JVM的内存中。

  • 直接缓冲区 : 通过 allocateDirect() 方法分配直接缓冲区,将缓冲区建立在物理内存中,可以提高效率。

通道Channel

  • 通道(Channel):表示IO源与目标打开的连接。Channel类似于传统的“流”.只不过Channel本身不能直接访问数据,Channel只能与Buffer进行交互

Channel与流的区别

  • Channel可以同时进行读写,而流只能读或者写
  • Channel可以实现异步读写数据
  • Channel可以从缓冲读数据,也可以写数据到缓冲

Channel在NIO中是一个接口

Channel常用实现类

  • FileChannel : 用于读取/写入/映射和操作文件的通道
  • DatagramChannel : 通过UDP读写网络中的数据通道
  • SocketChannel : 通过TCP读写网络中的数据
  • ServerSocketChannel : 可以监听新进来的TCP连接,对每一个新进来的连接都会创建一个SocketChannel

FileChannel类

常用方法测试

  • 写入文件
@Test
public void test004() {
try {
//1. 字节输出流通向目标文件
FileOutputStream fos = new FileOutputStream("D:\\data2.txt");
//2. 得到字节输出流对应的通道
FileChannel channel = fos.getChannel();
//3. 分配缓冲区
ByteBuffer bufer = ByteBuffer.allocate(1024);
bufer.put("hello,world".getBytes());
//4. 切换成写模式
bufer.flip();
channel.write(bufer);
//5. 关闭通道
channel.close();
} catch (Exception e) {
e.printStackTrace();
}
}
  • 读取文件
@Test
public void test005(){
try {
//定义一个文件字节输入流与源文件连通
FileInputStream fis = new FileInputStream("D:\\data.txt");
//得到文件字节输入流的文件通道
FileChannel channel = fis.getChannel();
//定义一个缓冲区
ByteBuffer buffer = ByteBuffer.allocate(1024);
//读取数据到缓冲区
channel.read(buffer);
buffer.flip();
//读取缓冲区中的数据
String s = new String(buffer.array());
System.out.println(s);
} catch (Exception e) {
e.printStackTrace();
}
}
  • 文件的复制测试
@Test
public void test006(){
File file = new File("D:\\data.txt");
try {
//得到字节输入/输出流
FileInputStream fis = new FileInputStream(file);
FileOutputStream fos = new FileOutputStream("D:\\data3.txt"); //得到输入输出流的通道
FileChannel fisChannel = fis.getChannel();
FileChannel fosChannel = fos.getChannel(); //分配缓冲区
ByteBuffer buffer = ByteBuffer.allocate(1024);
while(true){
//清空缓冲区再写入数据
buffer.clear();
//判断文件是否结束
int flag = fisChannel.read(buffer);
if(flag == -1){
break;
}
//切换写模式,写入数据
buffer.flip();
fosChannel.write(buffer); fisChannel.close();
fosChannel.close();
}
} catch (Exception e) {
e.printStackTrace();
}
}
  • 分散读取和聚集操作数据
@Test
public void test007(){
File file = new File("D:\\data.txt");
File file2 = new File("D:\\data3.txt"); try {
//字节输入输出流
FileInputStream fis = new FileInputStream(file);
FileOutputStream fos = new FileOutputStream(file2);
//定义多个缓冲区 --> 数据分散
ByteBuffer buffer1 = ByteBuffer.allocate(4);
ByteBuffer buffer2 = ByteBuffer.allocate(400);
ByteBuffer[] buffers = {buffer1,buffer2};
//从通道中读取数据分散到各个缓冲区
FileChannel fisChannel = fis.getChannel();
FileChannel fosChannel = fos.getChannel();
//从通道中读取数据分散到各个缓冲区
fisChannel.read(buffers);
//从每个缓冲区中查询是否有数据读取到了
for (ByteBuffer buffer : buffers) {
buffer.flip();
System.out.println(new String(buffer.array(),0,buffer.remaining()));
}
//聚集操作缓冲区
fosChannel.write(buffers);
fisChannel.close();
fosChannel.close();
} catch (Exception e) {
e.printStackTrace();
}
}
  • TransferFrom() & TransferTo()方法
    @Test
public void test008(){
File file = new File("D:\\data.txt");
File file2 = new File("D:\\data3.txt"); try {
//字节输入输出流
FileInputStream fis = new FileInputStream(file);
FileOutputStream fos = new FileOutputStream(file2);
//从通道中读取数据分散到各个缓冲区
FileChannel fisChannel = fis.getChannel();
FileChannel fosChannel = fos.getChannel();
//复制数据
//从目标通道中复制原通道数据
// fosChannel.transferFrom(fisChannel,fisChannel.position(),fisChannel.size());
//把原通道数据复制到目标通道数据
fisChannel.transferTo(fisChannel.position(),fisChannel.position(),fosChannel); fisChannel.close();
fosChannel.close();
} catch (Exception e) {
e.printStackTrace();
}
}

NIO非阻塞式网络通信原理分析

Selector可以实现 : 一个I/O线程可以并发处理N个客户端连接和读写操作,这从根本上解决了传统同步阻塞I/O已连接一线程模型,架构的性能,弹性伸缩能力和可靠性都得到了极大的提升.

入门案例

  • 服务器端
/**
* @PROJECT_NAME: JAVA_Test
* @DESCRIPTION: 目标 : NIO非阻塞通信下的入门案例 : 服务器端
* @USER: 罗龙达
* @DATE: 2021/2/11 0:33
*/
public class Server {
public static void main(String[] args) throws IOException {
System.out.println("---------服务端启动-----------");
//获取通道 --> 接收客户端的连接请求
ServerSocketChannel ssChannel = ServerSocketChannel.open();
//切换为非阻塞模式
ssChannel.configureBlocking(false);
//绑定连接的端口
ssChannel.bind(new InetSocketAddress(9999));
//获取选择器 Selector
Selector selector = Selector.open();
//将通道都注册到选择器上,并且开始指定监听接收事件
ssChannel.register(selector, SelectionKey.OP_ACCEPT);
//使用Selector轮询已经准备就绪的事件
while (selector.select() > 0){
//获取选择器中的所有注册的通道中已经准备就绪的事件
Iterator<SelectionKey> it = selector.selectedKeys().iterator();
//遍历已经准备好的时间
while (it.hasNext()){
System.out.println("开始一轮事件处理");
//提取当前事件
SelectionKey sk = it.next();
//判断这个事件具体是什么
if(sk.isAcceptable()){
//接收事件准备就绪,直接获取当前接入的客户端通道
SocketChannel sChannel = ssChannel.accept();
//切换成非阻塞模式
sChannel.configureBlocking(false);
//将本客户端通道注册到选择器里 服务器端监听读事件
sChannel.register(selector,SelectionKey.OP_READ);
}
//读事件
else if(sk.isReadable()){
//获取当前选择器上的读就绪事件
SocketChannel sChannel = (SocketChannel) sk.channel();
//读取数据
ByteBuffer buffer = ByteBuffer.allocate(1024);
int len = 0;
while ((len = sChannel.read(buffer)) > 0){
buffer.flip();
System.out.println(new String(buffer.array(),0,len));
//清除之前的数据
buffer.clear();
}
} //处理完毕移除当前事件 防止重复监听
it.remove();
}
} }
}
  • 客户端
/**
* @PROJECT_NAME: JAVA_Test
* @DESCRIPTION: 目标: 客户端案例实现 - 基于NIO非阻塞通信
* @USER: 罗龙达
* @DATE: 2021/2/11 0:56
*/
public class Client {
public static void main(String[] args) throws IOException {
//获取通道
SocketChannel socketChannel = SocketChannel.open(new InetSocketAddress("127.0.0.1",9999));
//切换成非阻塞模式
socketChannel.configureBlocking(false);
//指定缓冲区大小
ByteBuffer buffer = ByteBuffer.allocate(1024);
//发送数据给服务端
Scanner scanner = new Scanner(System.in);
while(true){
System.out.println("请说:");
String s = scanner.nextLine();
LocalDateTime timeNow = LocalDateTime.now();
buffer.put((timeNow + " 波妞 : " + s).getBytes());
buffer.flip();
socketChannel.write(buffer);
buffer.clear();
}
}
}

群聊案例

  • 服务器端
/**
* @PROJECT_NAME: JAVA_Test
* @DESCRIPTION:
* @USER: 罗龙达
* @DATE: 2021/2/11 1:47
*/
public class Server {
//定义选择器,服务端通道,端口
private Selector selector;
private ServerSocketChannel ssChannel;
private static final int PORT = 9999; //初始化
public Server(){
try {
//创建选择器
selector = Selector.open();
//获取通道
ssChannel = ServerSocketChannel.open();
//绑定客户端连接的端口
ssChannel.bind(new InetSocketAddress(PORT));
//设置非阻塞通信模式
ssChannel.configureBlocking(false);
//八通道注册到选择器上,并且开始指定接收事件
ssChannel.register(selector, SelectionKey.OP_ACCEPT);
} catch (IOException e) {
e.printStackTrace();
}
} /**
* 监听事件
*/
private void listen(){ try {
while(selector.select() > 0){
//获取选择器中所有注册通道的就绪事件
Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
//开始遍历
while (iterator.hasNext()){
SelectionKey sk = iterator.next();
//判断事件的类型
if(sk.isAcceptable()){
//客户端接入请求
//获取当前客户端通道
SocketChannel socketChannel = ssChannel.accept();
socketChannel.configureBlocking(false);
socketChannel.register(selector,SelectionKey.OP_READ);
}
else if(sk.isReadable()){
//处理这个客户端的消息,接收它然后实现转发逻辑
readClientData(sk);
}
iterator.remove();//处理完毕,移除当前事件
}
}
} catch (IOException e) {
e.printStackTrace();
} } /**
* 接受当前客户端通道的信息,转发给其他全部客户端通道
* @param sk
*/
private void readClientData(SelectionKey sk) {
SocketChannel socketChannel = null;
try{
//获取当前客户端通道
socketChannel = (SocketChannel) sk.channel();
//创建缓冲区对象开始接受客户端通道的数据
ByteBuffer buffer = ByteBuffer.allocate(1024);
int count = socketChannel.read(buffer);
if (count > 0){
buffer.flip();
//提取读取到的信息
String msg = new String(buffer.array(), 0, buffer.remaining());
System.out.println("接收到客户端消息 : " + msg);
sendMsgToAllClient(msg,socketChannel); } }catch (Exception e){
try {
System.out.println("有人离线了 : " + socketChannel.getRemoteAddress());
//当前客户端离线
sk.cancel();
socketChannel.close();
} catch (IOException ioException) { }
}
} /**
* 把当前客户端的消息数据都推送给当前全部在线注册的channel
* @param msg
* @param socketChannel
*/
private void sendMsgToAllClient(String msg, SocketChannel socketChannel) {
System.out.println("服务端开始转发消息, 当前处理的线程 : " + Thread.currentThread().getName());
for (SelectionKey key : selector.keys()) {
Channel channel =key.channel();
if(channel instanceof SocketChannel && socketChannel != channel){
ByteBuffer buffer = ByteBuffer.wrap(msg.getBytes());
try {
((SocketChannel)channel).write(buffer);
} catch (IOException e) {
e.printStackTrace();
}
}
}
} public static void main(String[] args) {
//创建服务端对象
Server server = new Server();
//开始监听客户端的各种消息事件
server.listen();
}
}
  • 客户端
/**
* @PROJECT_NAME: JAVA_Test
* @DESCRIPTION: 客户端代码逻辑实现
* @USER: 罗龙达
* @DATE: 2021/2/11 17:33
*/
public class Client { private Selector selector;
private static int PORT = 9999;
private static SocketChannel socketChannel; public Client(){
try {
//创建选择器
selector = Selector.open();
//连接服务端
socketChannel = SocketChannel.open(new InetSocketAddress("127.0.0.1",PORT));
//设置非阻塞通信模式
socketChannel.configureBlocking(false);
//八通道注册到选择器上,并且开始指定接收事件
socketChannel.register(selector, SelectionKey.OP_READ);
System.out.println("当前客户端准备完成");
} catch (IOException e) {
e.printStackTrace();
}
} public static void main(String[] args) {
Client client = new Client();
//定义一个线程专门负责监听服务端发送过来的读消息事件
new Thread(client::readInfo).start(); Scanner sc = new Scanner(System.in);
while (sc.hasNextLine()){
String s = sc.nextLine();
Client.sendMsg(s);
}
} private static void sendMsg(String s) {
try {
socketChannel.write(ByteBuffer.wrap(("波仔说:" + s).getBytes()));
} catch (IOException e) {
e.printStackTrace();
}
} private void readInfo() {
try{
while(selector.select() > 0){
Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
while(iterator.hasNext()){
SelectionKey key = iterator.next();
if (key.isReadable()){
SocketChannel sc = (SocketChannel) key.channel();
ByteBuffer buffer = ByteBuffer.allocate(1024);
sc.read(buffer);
System.out.println(new String(buffer.array()).trim());
System.out.println("--------分割线-----------");
}
iterator.remove();
} }
}catch (Exception e){
e.printStackTrace();
} }
}

AIO异步非阻塞IO

  • Java AIO : 异步非阻塞,服务器实现模式为一个有效请求一个线程,客户端的I/O请求都是由OS先完成了再通知服务器应用去启动线程进行处理

与NIO不同,当进行读写操作时,只须直接调用API的read或write方法即可,这两种方法均为异步的,对于读操作而言,当有流可读取时,操作系统会将可读的流传入read方法的缓冲区,对于写操作而言,当操作系统将write方法传递的流写入完毕时,操作系统会主动通知应用程序

可以理解为,read/write方法都是异步的,完成后会主动调用回调函数,在JDK1.7中,这部分内容被称作NIO 2

BIO,NIO,AIO三者比较

  • Java BIO :

    同步并阻塞,服务器实现模式为一个链接一个线程,即客户端有连接请求时服务器端就需要启动一个线程进行处理,如果这个链接不做任何事情会造成不必要的线程开销,可以通过线程池机制改善.

  • Java NIO :

    同步非阻塞,服务器实现模式为一个请求一个线程,即客户端发送的连接请求都会注册到多路复用器上,多路复用轮询到连接有I/O请求时才启动一个线程进行处理

  • Java AIO :

    异步非阻塞,服务器实现模式为一个有效请求一个线程,客户端的I/O请求都是由OS先完成了再通知服务器应用去启动线程进行处理

使用场景分析

  • BIO适用于连接数目较小且固定的架构,这种方式对服务器资源要求比较高,并发局限于应用中,但程序直观简单易理解
  • NIO方式适用于连接数目多且连接比较短的架构,比如聊天服务器,并发局限于应用中,编程比较复杂
  • AIO方式适用于连接数目多且连接比较长的架构,比如相册服务器,充分调用OS参与并发操作,编程比较复杂

Java中NIO的简单介绍的更多相关文章

  1. java中数据流的简单介绍

    java中的I/O操作主要是基于数据流进行操作的,数据流表示了字符或者字节的流动序列. java.io是数据流操作的主要软件包 java.nio是对块传输进行的支持 数据流基本概念 “流是磁盘或其它外 ...

  2. java 中的多线程简单介绍

    package com.zxf.demo; /* * 多线程的实现方式两种? * 一..实现 runnable 接口 * 2.重写run方法 Run():当一个线程启动后,就会自动执行该方法 * 3. ...

  3. 多线程(三) java中线程的简单使用

    java中,启动线程通常是通过Thread或其子类通过调用start()方法启动. 常见使用线程有两种:实现Runnable接口和继承Thread.而继承Thread亦或使用TimerTask其底层依 ...

  4. Java基础-JAVA中常见的数据结构介绍

    Java基础-JAVA中常见的数据结构介绍 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.什么是数据结构 答:数据结构是指数据存储的组织方式.大致上分为线性表.栈(Stack) ...

  5. java反射机制的简单介绍

    参考博客: https://blog.csdn.net/mlc1218559742/article/details/52754310 先给出反射机制中常用的几个方法: Class.forName (& ...

  6. java基础---->java中nio的使用(一)

    JDK 1.4 中引入的新输入输出 (NIO) 库在标准 Java 代码中提供了高速的.面向块的 I/O.今天我们就简单的学习一下nio的知识.我笑,便面如春花,定是能感动人的,任他是谁. nio的简 ...

  7. Java中NIO、BIO、AIO相关概念及应用场景

    1.同步阻塞IO(JAVA BIO):同步并阻塞,服务器实现模式为一个连接一个线程,即客户端有连接请求时,服务器端就需要启动一个线程进行处理,如果这个连接不做任何事情会造成不必要的线程开销,当然可以通 ...

  8. Java泛型使用的简单介绍

    目录 一. 泛型是什么 二. 使用泛型有什么好处 三. 泛型类 四. 泛型接口 五. 泛型方法 六. 限定类型变量 七. 泛型通配符 7.1 上界通配符 7.2 下界通配符 7.3 无限定通配符 八. ...

  9. 史上最全 Java 中各种锁的介绍

    更多精彩原创内容请关注:JavaInterview,欢迎 star,支持鼓励以下作者,万分感谢. 锁的分类介绍 乐观锁与悲观锁 锁的一种宏观分类是乐观锁与悲观锁.乐观锁与悲观锁并不是特定的指哪个锁(J ...

随机推荐

  1. ACNet:用于图像超分的非对称卷积网络

    编辑:Happy 首发:AIWalker Paper:https://arxiv.org/abs/2103.13634 Code:https://github.com/hellloxiaotian/A ...

  2. 北航OO第四单元作业总结(4.1~4.3)及课程总结

    前言 在学习过JML规格描述语言之后,本单元进行了UML(Unified Modeling Language)的学习.和JML单纯用语言描述的形式不同,UML通过可视化的图形形式,对一系列有关类的元素 ...

  3. C语言-字符串函数的实现(一)之strlen

    C语言中的字符串函数有如下这些 获取字符串长度 strlen 长度不受限制的字符串函数 strcpy strcat strcmp 长度受限制的字符串函数 strncpy strncat strncmp ...

  4. SpringBoot 使用逆向工程 构建Mapper.xml Dao层(持久层) 实体类

    逆向工程 注: 有数据库表即可 第一步为创建数据库表 (可选)使用PowerDesigner设计数据库表,物理模型构建 添加pom.xml 逆向工程生成代码插件 <!--plugin 逆向工程生 ...

  5. SpringBoot - yml写法

    1 #区分大小写 2 server: 3 port: 8081 4 path: hello 5 6 #字面量:普通的值(数字,字符串,布尔): 7 #字符串:双引号 - 不转义 单引号 - 转义 8 ...

  6. 前端DDD总结与思考

    软件开发架构演化与DDD起源 单体服务架构:大概10年前,我在武汉工作的时候,甲方客户购买我们的产品,一般都是连着设备一起购买,一套软件系统,一台惠普或者戴尔的企业级服务器,再加一个黑色的铁盒,销售部 ...

  7. HTTP 基础(特性、请求方法、状态码、字段)

    1. HTTP 简介(含义.特性.缺点) 2. HTTP 报文 3. GET 和 POST 4. 状态码 5. HTTP 头字段 1. HTTP 简介 HTTP 的含义 HTTP (HyperText ...

  8. k8s 证书更新操作

    kubernetes证书更新 版本:1.14.2,以下操作在3台master节点上操作 1.各个证书过期时间 /etc/kubernetes/pki/apiserver.crt #1年有效期 /etc ...

  9. 13- jmeter性能测试案例

    配置原件 HTTP请求默认值 前置处理程序 定时器 取样器 后置处理器:正则表达式提取器 断言 监听器 性能测试流程 1.评估获取性能测试需求(访问量大,操作频繁) 2.确定性能测试目标 : 并发用户 ...

  10. 小图标文字对齐的终极解决方案demo

    CSS代码: .icon { display: inline-block; width:20px; height:20px; background: url(delete.png) no-repeat ...