ps做电商网站尺寸是多少,荆州网络推广公司,四川重庆旅游必去十大景点推荐,南通优化网站收费标准0、前言
本文所有代码可见 【gitee code demo】 本文涉及的主题#xff1a;
1、BIO、NIO的业务实践和缺陷
2、Redis IO多路复用#xff1a;redis快的主要原因
3、epoll 架构
部分图片 via 【epoll 原理分析】
1、BIO单线程版
1.1 业务代码
client client代码相同…0、前言
本文所有代码可见 【gitee code demo】 本文涉及的主题
1、BIO、NIO的业务实践和缺陷
2、Redis IO多路复用redis快的主要原因
3、epoll 架构
部分图片 via 【epoll 原理分析】
1、BIO单线程版
1.1 业务代码
client client代码相同 启动多个即可
public class RedisClient1 {public static void main(String[] args) throws IOException {SimpleDateFormat sdf new SimpleDateFormat(HH:mm:ss);Socket socket new Socket(127.0.0.1, 6300);log({} {} 尝试连接服务 {}, sdf.format(new Date()) ,socket.getLocalPort(), socket.getPort());OutputStream outputStream socket.getOutputStream();while (true) {Scanner scanner new Scanner(System.in);log({} {} , sdf.format(new Date()) ,socket.getLocalPort());String string scanner.nextLine();if (string.equalsIgnoreCase(quit)) {break;}socket.getOutputStream().write(string.getBytes());log({} {} 发送数据{}, sdf.format(new Date()) ,socket.getLocalPort(), string);}outputStream.close();socket.close();}}server
public class RedisServerBIO {public static void main(String[] args) throws IOException {SimpleDateFormat sdf new SimpleDateFormat(HH:mm:ss);ServerSocket serverSocket new ServerSocket(6300);while (true) {log({} {} , sdf.format(new Date()), serverSocket.getLocalPort());Socket socket serverSocket.accept();//阻塞1 ,等待客户端连接log({} {} {} 连接到服务, sdf.format(new Date()), socket.getLocalPort(), socket.getPort());InputStream inputStream socket.getInputStream();int length -1;byte[] bytes new byte[1024];log({} {} , sdf.format(new Date()), serverSocket.getLocalPort());while ((length inputStream.read(bytes)) ! -1)//阻塞2 ,等待客户端发送数据{log({} {} 收到 {} 的消息{}, sdf.format(new Date()), serverSocket.getLocalPort(), socket.getPort(), new String(bytes, 0, length));}inputStream.close();socket.close();}}
}1.2 结果演示
现象
1、client1 连接到serverclient2尝试连接被阻塞
2、client2 先发送的消息未被server接受client1后发送的repeat消息被server接受
结论
BIO会一直阻塞单线程下只能处理一个socket连接
存在的问题
多 client 访问时效率低 2、BIO多线程版
2.1 业务代码
public class RedisServerBIOMultiThread {public static void main(String[] args) throws IOException {ServerSocket serverSocket new ServerSocket(6300);SimpleDateFormat sdf new SimpleDateFormat(HH:mm:ss);log({} {} , sdf.format(new Date()), serverSocket.getLocalPort());while (true) {Socket socket serverSocket.accept();//阻塞1 ,等待客户端连接log({} {} {} 连接到服务, sdf.format(new Date()), socket.getLocalPort(), socket.getPort());new Thread(() - {try {InputStream inputStream socket.getInputStream();int length -1;byte[] bytes new byte[1024];while ((length inputStream.read(bytes)) ! -1)//阻塞2 ,等待客户端发送数据{log({} {} 收到 {} 的消息{}, sdf.format(new Date()), serverSocket.getLocalPort(), socket.getPort(), new String(bytes, 0, length));}inputStream.close();socket.close();} catch (IOException e) {e.printStackTrace();}}, Thread.currentThread().getName()).start();System.out.println(Thread.currentThread().getName());}}
}2.1 结果演示
现象
client1 、client2 都能正常连接到 server且正常发送、接受消息
结论
BIO多线程提高处理能力可以同时处理多个socket连接
存在的问题
每个线程只能处理一个socket当client数量大时需要消耗大量线程资源 3、NIO
3.1 业务代码
当一个客户端与服务端进行连接这个socket就会加入到一个容器中隔一段时间遍历一次看这个socket的read()方法能否读到数据这样一个线程就能处理多个客户端的连接和读取了
public class RedisServerNIO {static ArrayListSocketChannel socketList new ArrayList();static ByteBuffer byteBuffer ByteBuffer.allocate(1024);public static void main(String[] args) throws IOException {ServerSocketChannel serverSocket ServerSocketChannel.open();SimpleDateFormat sdf new SimpleDateFormat(HH:mm:ss);serverSocket.bind(new InetSocketAddress(127.0.0.1, 6300));serverSocket.configureBlocking(false);//设置为非阻塞模式while (true) {for (SocketChannel element : socketList) {int read element.read(byteBuffer);if (read 0) {byteBuffer.flip();byte[] bytes new byte[read];byteBuffer.get(bytes);//System.out.println(JSONUtil.toJsonStr(element));log({} {} 收到 {} 的消息{}, sdf.format(new Date()), element.socket().getLocalPort(), element.socket().getPort(), new String(bytes, 0, read));byteBuffer.clear(); }}SocketChannel socketChannel serverSocket.accept();if (socketChannel ! null) {log({} {} {} 连接到服务, sdf.format(new Date()), socketChannel.socket().getLocalPort(), socketChannel.socket().getPort());socketChannel.configureBlocking(false);//设置为非阻塞模式socketList.add(socketChannel);log({} {} socket 数量 {} , sdf.format(new Date()), socketChannel.socket().getLocalPort(), socketList.size());}}}
}3.2 结果演示
现象
1、client1 、client2 都能正常连接到 server且正常发送、接受消息
2、server 没有创建额外线程
结论
NIO 可以实现一个线程处理多个 socket 连接
存在的问题
1、每次遍历所有socket有很多无用功
2、遍历过程在用户态还需要将数据从内核态读取到用户态 4、IO多路复用
1、使用 epoll() 实现多个网络连接 socket 复用同一个线程
2、基于事件驱动机制socket 中有数据会主动通知内核并加入到就绪链表中不需要遍历所有 socket
3、减少了内核态和用户态的切换
Redis IO多路复用实现 4.1 epoll_create()
创建内核中的fd容器 4.2 epoll_ctl()
epoll_ctl函数用于增加删除修改epoll事件epoll事件会存储于内核epoll结构体红黑树中 4.3 epoll_wait
用于监听套接字事件可以通过设置超时时间timeout来控制监听的行为为阻塞模式还是超时模式 4.4 epoll 软件架构