惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
D
Darknet – Hacking Tools, Hacker News & Cyber Security
F
Fortinet All Blogs
小众软件
小众软件
博客园_首页
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
Microsoft Azure Blog
Microsoft Azure Blog
MongoDB | Blog
MongoDB | Blog
罗磊的独立博客
大猫的无限游戏
大猫的无限游戏
量子位
N
Netflix TechBlog - Medium
B
Blog
P
Proofpoint News Feed
月光博客
月光博客
Apple Machine Learning Research
Apple Machine Learning Research
人人都是产品经理
人人都是产品经理
云风的 BLOG
云风的 BLOG
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
腾讯CDC
Engineering at Meta
Engineering at Meta
Y
Y Combinator Blog
AI
AI
Stack Overflow Blog
Stack Overflow Blog
U
Unit 42
M
MIT News - Artificial intelligence
Vercel News
Vercel News
D
DataBreaches.Net
P
Palo Alto Networks Blog
宝玉的分享
宝玉的分享
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
Cisco Talos Blog
Cisco Talos Blog
T
Threatpost
The Hacker News
The Hacker News
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
Security Latest
Security Latest
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
有赞技术团队
有赞技术团队
Attack and Defense Labs
Attack and Defense Labs
Recent Announcements
Recent Announcements
Hugging Face - Blog
Hugging Face - Blog
Webroot Blog
Webroot Blog
Cyberwarzone
Cyberwarzone
美团技术团队
博客园 - 司徒正美
Cloudbric
Cloudbric
J
Java Code Geeks
T
Tailwind CSS Blog
The Last Watchdog
The Last Watchdog
A
About on SuperTechFans

博客园 - Nucky_yang

windows系统下安装openclaw,无权限打开软件,无权限打开浏览器,无权限读写文件的问题。 just a demo presto集成 hive(转载) java可重入锁与不可重入锁 的设计 动态规划 背包问题 java ThreadPoolExecutor线程池在美团的最佳实践 回溯 八皇后问题 与 0-1背包 技术学习 线程间通信 计算机网络基础知识总结(各种协议) 大数据Phoenix专题 ClickHouse深度揭秘 hbase 查询组件phoenix redis的设计与实现 第二版 位图 Java并发编程:volatile关键字解析 mongDB需要注意的事项和 监控命令mongostat mongotop mongoDB的事务 mongoDB学习 mongo的聚合框架、join
socket 多路复用原理和代码 select poll epoll
Nucky_yang · 2020-08-30 · via 博客园 - Nucky_yang

B站有学习视频 

https://www.bilibili.com/video/BV1n5411b76b?p=1 可以直接从该视频第一小节6:00 开始看。

老师从BIO 开始讲BIO的缺陷,改进方案:多线程BIO ,在一步步进化到NIO,最后进化到调用linux内核的多路复用。

多路复用简化图流程如下:

首先需要思考,最原始的socket流有何缺陷,"痛点"在哪里,根据痛点又是如何改造的。比如:

1、流是单向的,通道是双向的,可读可写。
2、流读写是阻塞的,通道可以异步读写,效率的提升很明显。

下面是我整理出老师讲的进化过程:

单线程原生socket

首先回顾一下socket clinet和socket server是怎么调用的。

//服务端 
ServerSocket serverSocket = new ServerSocket(); serverSocket.setReuseAddress(true);//这个设置要放在绑定端口前 serverSocket.bind(new InetSocketAddress(8090)); while(true){ Socket socket = serverSocket.accept();//阻塞 socket.getInputStream().read();//阻塞 } //客户端 Socket clientSocket = new Socket("127.0.0.1", 8090); clientSocket.getOutputStream().write("aaa".getBytes()); clientSocket.close();

以上服务端代码在遇到高并发的客户端访问时,会不停的创建对象,有性能问题
为了解决性能问题,需要引入多线程,进化版如下:

 多线程socket

  //服务器
ServerSocket serverSocket = new ServerSocket();
serverSocket.setReuseAddress(true);//这个设置要放在绑定端口前
serverSocket.bind(new InetSocketAddress(8090));
ExecutorService pool = Executors.newFixedThreadPool(10000);//线程池
while(true){
    Socket socket = serverSocket.accept();//阻塞
    //socket.getInputStream().read();//这段阻塞的代码放入子线程HandleSocketSer中
    pool.execute(new HandleSocketSer(socket));
}
//客户端和之前一样

现在代码接收连接的是主线程,已经让子线程来处理每个连接了。

但是还有性能问题,有1万个连接,1万个连接中只有200个连接有数据发送过来,但是却起了1万个线程,会有资源浪费的情况,需要进一步优化

单线程多路复用

 

 思路就是添加一个列表,写个循环,一直监控socket连接中有没有数据过来,这样接收新的连接不会阻塞,每次有数据发送过来,都会先添加到列表中,在遍历一次列表获取数据,然后阻塞到serverSocket.accept()继续等待。

这里的时候已经使用ServerSocketChannel通道了,N个请求过来,都是复用这一个通道来处理(读/写)的。

单线程 (使用linux内核做)多路复用

为了进一步优化代码性能,将轮训监控列表的部分,放到linux内核执行(通过jvm调用linux内核),可以提高性能,进化版如下:

如上图所以,如果现在有三个连接,1和2发送数据,而3没有数据只做了连接,是不会做读/写操作的。

SelectorServerDemo

public class SelectorTest {
    public static void main(String[] args) throws IOException {
        ServerSocketChannel ssc= ServerSocketChannel.open();
        ssc.configureBlocking(false);//配置为非阻塞模式
        ssc.socket().bind(new InetSocketAddress(7707));

        Selector selector =Selector.open();
        ssc.register(selector, SelectionKey.OP_ACCEPT);
        ByteBuffer buff=ByteBuffer.allocate(48);

        while (true){
            int n=selector.select();
            if(n==0) continue;
            Iterator<SelectionKey> it=selector.selectedKeys().iterator();
        while(it.hasNext()){
            SelectionKey sk=it.next();
            if(sk.isAcceptable()){
                System.out.println("accpet----------");
//这里类型转为ServerSocketChannel 主要用来处理请求,“实际干活的”是下面SocketChannel
//在netty中,进化为BossGroup和workGroup SocketChannel ssc_a
=((ServerSocketChannel) sk.channel()).accept(); ssc_a.configureBlocking(false); ssc_a.register(selector,SelectionKey.OP_READ); }else if(sk.isConnectable()){ System.out.println("Connect----------"); //DOOTHER }else if(sk.isReadable()){ System.out.println("Read----------"); SocketChannel ssc_r=(SocketChannel) sk.channel(); //清理缓存并接收数据 buff.clear();
try {
int count=ssc_r.read(buff); if (count > 0) { System.out.println(new String(buff.array(),0,count)); ssc_r.register(selector, SelectionKey.OP_WRITE); }
} catch (IOException e) {
sk.cancel();//关闭需要2步
ssc_r.close();
}
}else if(sk.isWritable()){
                System.out.println("Write----------");
                buff.clear();
                // 返回为之创建此键的通道。
                SocketChannel ssc_w = (SocketChannel) sk.channel();
                String  sendText="response message ------";
                //向缓冲区中输入数据
                buff.put(sendText.getBytes());
                //将缓冲区各标志复位,因为向里面put了数据标志被改变要想从中读取数据发向服务器,就要复位
                buff.flip();
                //输出到通道
                ssc_w.write(buff);
                ssc_w.register(selector, SelectionKey.OP_READ);
            }
            it.remove();
        }

        }
    }
}

ClinetDemo

public class SelectorClient {
    public static void main(String[] args) throws IOException {
        SocketChannel sc=SocketChannel.open();
        sc.connect(new InetSocketAddress("127.0.0.1",7707));
        ByteBuffer bf= ByteBuffer.allocate(48);

        bf.putChar('N');
        bf.putChar('B');
        bf.putChar('A');
        bf.flip();   //flip 将写模式切换为读取模式(原理是通过改变游标和位置)
        sc.write(bf); //模拟发送
        sc.close();  
        System.out.println("client end====");
    }
}

 后续的一个演化版本就是netty了,一个高性能、异步事件驱动的NIO框架。

参考 

https://www.bilibili.com/video/BV1n5411b76b?p=1   (享学课堂视频)

https://www.jianshu.com/p/0d497fe5484a   (简书狼哥博客)