提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
多线程与高并发
一、BIO(block input output)阻塞式的输入和输出
- 按照此代码可以写一个简单的服务端接收代码
- cmd连接服务端
- 发送消息到服务端
- 服务端接收
- 问题
- c10k问题(10w个线程会大大的占用服务器内存),解决通过NIO模式来解决。
- 需要等待一个线程完成后再执行另一个客户端,当连接数过多最后一个连接等待时间将会特别长。
提示:以下是本篇文章正文内容,下面案例可供参考
二、NIO(non-blocking input output)非阻塞式输入输出
- 编写测试代码
NIO主要是因为配置了非阻塞:
实现原理:创建一个list,通过while轮询,检查是否有连接到服务端,入股有就存放到list中,然后再统一便利list处理所有客户端数据。
2.1 NIO问题
如果用一个list存放,当有10w个连接时将每次遍历10万次list
2.2 多路复用器
改造原来的代码,插入以下代码:
底层原理:
epoll:linux内核创建了一个epoll实例集合对象。
package com.example.demo;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;
@SpringBootTest
public class NIOSelectorApplicationTests {
@Test
public void nioTest() throws IOException {
ServerSocketChannel serverSocket = ServerSocketChannel.open();
serverSocket.socket().bind(new InetSocketAddress(9002));
// 设置位非阻塞主要的代码
serverSocket.configureBlocking(false);
// 引入多路复用器
Selector selector = Selector.open();
// 注册多路复用器
SelectionKey selection = serverSocket.register(selector, SelectionKey.OP_ACCEPT);
System.out.println("服务启动成功。。。。");
while(true){
// 阻塞等待需要处理的事件发生
selector.select();
// 获取Selector中注册的全部事件。
Set<SelectionKey> selectionKeys = selector.selectedKeys();
Iterator<SelectionKey> iterator = selectionKeys.iterator();
// 便利SelectionKey对事件进行处理
while (iterator.hasNext()){
SelectionKey key = iterator.next();
// 如果时OP_READ事件,则进行连接获取和事件注册。
if(key.isAcceptable()) {
ServerSocketChannel server = (ServerSocketChannel) key.channel();
SocketChannel socketChannel = server.accept();
socketChannel.configureBlocking(false);
// 此处只注册了读操作,如果要给客户端发数据可以注册写事件
SelectionKey register = socketChannel.register(selector, SelectionKey.OP_READ);
System.out.println("连接成功。。。。");
}else if(key.isReadable()){ // 这里只写了读操作
SocketChannel socketChannel = (SocketChannel) key.channel();
ByteBuffer byteBuffer = ByteBuffer.allocate(128);
// 非阻塞模式下read方法不会阻塞否则会阻塞
int read = socketChannel.read(byteBuffer);
// 如果有数据把数据打印出来
if(read > 0){
String readStr = new String(byteBuffer.array());
System.out.println("接收到的数据:"+readStr);
}else { // 如果客户端断开,就把socket从列表中移除
System.out.println("客户端断开连接。。。");
}
}
// 从事件删除本次处理的连接,防止重复处理
iterator.remove();
}
}
}
}
epoll最关键的三大函数
提示:redis底层也是使用了epoll
三、使用Netty
编写一个简单的netty
其他知识
linux查看函数或者命令的帮助文档,可输入:man
提示:这里可以添加本文要记录的大概内容: