Linux高性能服务器技术总结(用自己电脑搭建linux服务器)
三石哥 2022-07-12 15:56:32 306
1 服务器简介
服务器是提供计算服务的设备, 由于服务器需要响应用户请求,因此在处理能力、稳定性、安全性、可扩展性、可管理性等方面提出了较高要求。随着虚拟化技术的进步, 云服务器(ECS) 已经快速的在国内普及开来, 其管理方式比物理服务器更简单高效。用户可迅速创建或释放任意多台云服务器, 帮助企业降低开发运维的难度和整体 IT 成本, 使整个研发周期更专注于核心业务的创新。在网络环境下,根据服务器提供的服务类型不同,分为文件服务器、 数据库服务器、应用程序服务器、 WEB 服务器等。
此次学习总结的主要内容:
- 如何处理多个客户端连接。
- 探讨面对百万千万级客户端连接时的性能优化。
- 服务器如何高效处理并发的数据。
- 深度分析大数据通信时, Linux 内核瓶颈。
- 如何攻克瓶颈
2 I/O复用技术
2.1 循环方式
当服务器有多个网络连接需要看管,那么循环遍历打开的网络连接的列表,来判断是否有要读取的数据。
缺点:
- 速度缓慢(必须遍历所有的网络连接)
-
效率低(处理一个连接时可能发生阻塞,妨碍其他网络连接的检查和处理)
示例:
typedefstructClientInfo{intclient_fd; stringclient_ip; }ClientInfo;//客户端结构体std::deque<ClientInfo> m_client1;//客户端队列 1std::deque<ClientInfo> m_client2;//客户端队列 2voidMServer::ClientHandel(std::deque<ClientInfo> *client){ chardata[1024] = {0}; intlen = 0; for(inti = 0; i < client->size(); ++i){ //当没有数据可读时, 发生阻塞len = read(client->at(i).client_fd, data, sizeofdata); //处理数据bzero(data,sizeofdata);//清空缓存} }
2.2 select 方式
select 首先将第二三四个参数指向的 fd_set 拷贝到内核,对每个被 SET 的描述符进行poll,记录在临时结果中(fdset),如果有事件发生, select 会将临时结果写到用户空间并返回。
缺点:
select 返回后,需要逐一检查描述符是否被 SET(事件是否发生)。(select 支持的
文件描述符数量太小了,默认是 1024)。
示例:
voidMServer::ClientHandel(std::deque<ClientInfo> *client){ chardata[1024] = {0}; fd_set input;// fdset 记录 poll 结果intlen = 0; intretval = 0; FD_ZERO(&input);//清空记录for(inti = 0; i < client->size(); ++i){ FD_SET(client->at(i).client_fd, &input); retval = select(client->at(i).client_fd + 1, &input, NULL, NULL, NULL); //检测事件是否发生if(retval > 0&& FD_ISSET(client->at(i).client_fd, &input)){ //读取数据len = read(client->at(i).client_fd, data, sizeofdata); //处理数据bzero(data,sizeofdata); } //处理其他事情} }
2.3 poll方式
poll 与 select 不同,通过一个 pollfd 数组向内核传递需要关注的事件,故没有描述符个数的限制, pollfd 中的 events 字段和 revents 分别用于标示关注的事件和发生的事件,故 pollfd 数组只需要被初始化一次。 poll 的实现机制与 select 类似,其对应内核中的 sys_poll,只不过poll 向内核传递 pollfd 数组,然后对 pollfd 中的每个描述符进行 poll,相比处理 fdset 来说, poll 效率更高。
缺点:
poll 需要对 pollfd 中的每个元素检查其 revents 值,来得知事件是否发生。
示例:
std::vector<struct pollfd> pollfds; voidMServer::ClientHandel(std::deque<ClientInfo> *client){ intnready = 0; intlen = 0; chardata[1024] = {0}; //初始化 pollfds 容器for(inti = 0; i < client->size(); ++i){ structpollfdpfd;pfd.fd = client->at(i).client_fd;//设置 pollfdpfd.events = POLLIN;//设置 pollin 事件pfd.revents = 0;//设置没有任何事件返回,置为零pollfds.push_back(pfd);} while(1){ nready = poll(&*pollfds.begin(), pollfds.size(), -1);//负数表示无限等待,直到发生事件才返回 for(PollFdList::iterator it = pollfds.begin(); it != pollfds.end() && nready > 0; ++it){ //遍历查看 fd 产生的事件if(it->revents & POLLIN){ len = read(it->fd, buf, sizeofdata); //处理数据bzero(data,sizeofdata); } } //处理其他事情} }
需要C/C++ Linux服务器架构师学习资料加qun812855908获取(资料包括C/C++,Linux,golang技术,Nginx,ZeroMQ,MySQL,Redis,fastdfs,MongoDB,ZK,流媒体,CDN,P2P,K8S,Docker,TCP/IP,协程,DPDK,ffmpeg等),免费分享
2.4 epoll 方式
epoll 与 select、 poll 不同,其不用每次调用都向内核拷贝事件描述信息,在第一次调用后,事件信息就会与对应的 epoll 描述符关联起来。其次, epoll 不是通过轮询,而是通过在等待的描述符上注册回调函数,当事件发生时,回调函数负责把发生的事件存储在就绪事件链表中,最后写到用户空间。
epoll 返回后,该参数指向的缓冲区中即为发生的事件,对缓冲区中每个元素进行处理即可,而不需要像 poll、 select 那样进行轮询检查。
示例:
voidMServer::ClientHandel(std::deque<ClientInfo> *client){ intwait_fds;//事件产生的数量inti = 0; intlen = 0; chardata[1024] = {0}; intepoll_fd = epoll_create(1024);//创建 epollfor(i = 0; i < client->size(); ++i){ structepoll_eventev;ev.events = EPOLLIN | EPOLLET;//设置触发事件的类型ev.data.fd = client->at(i).client_fd; //向 epoll 中增加 client_fdif( epoll_ctl( epoll_fd, EPOLL_CTL_ADD, client->at(i).client_fd, &ev ) < 0){ printf("Epoll Error : %d\n", errno); exit( EXIT_FAILURE ); } }structepoll_eventevs[1024];//epoll 事件缓存区while(1){ if( ( wait_fds = epoll_wait( epoll_fd, evs, 0, -1) ) == -1){ break; } for( i = 0; i < wait_fds; ++i){ len = read( evs[i].data.fd, data, sizeofdata); //处理数据bzero(data,sizeofdata); } } }
3 多线程方式
多线程技术也可以处理高并发的客户端连接,因为在服务器中可以创建大量的线程来监视连接。
缺点:
多线程技术则不太适合处理长连接,因为建立一个线程 linux 中会消耗栈空间, 当产生大量的连接后, 会导致系统内存消耗殆尽。
示例:
typedefstructClientInfo{intclient_fd; pthread_tpid; boolpthread_enlable; }ClientInfo; std::deque<ClientInfo> client;//客户端队列voidMServer::ClientHandel(){ inti = 0; //创建多线程处理连接for(i = 0; i < client.size(); ++i){ if(pthread_create(&client[i].pid, NULL, ClientPthread, &client[i]) != 0){ client[i].pthread_enlable = true; } } //等待线程结束for(i = 0; i < client.size(); ++i){ if(client[i].pthread_enlable) pthread_join(client[i].pid, NULL); } }//end func ClientHandelvoid*MServer::ClientPthread(void*arg){ chardata[1024] = {0}; intlen = 0; while(1){ len = read(((MServer*)arg)->client_fd, data, sizeofdata); //处理数据bzero(data,sizeofdata);//清空缓存} pthread_exit(NULL); }
多线程 + I/O 复用技术,使用一个线程负责监听一个端口和描述符是否有读写事件产生,
再将事件分发给其他的工作线程处理数据。
模型架构:
这种架构主要是基于单线程 I/O 多路复用(select/poll/epoll),达到高并发效果,同时避免了多线程 I/O 来回切换的各种开销,而基于线程池的多工作者线程,进一步提高业务处理能力和避免产生过多线程。
4 CPU多核并行计算
程序的线程是指能同时并发执行的逻辑单元的个数,是通过时间片分配算法实现的;
CPU 的线程是指将 CPU 的指令执行过程(取指、译指、执行、 访存、写数)做出流水线从而提高并发度的方法。
并行计算和多线程的区别:
- 并行计算比多线程具有更高的 CPU 利用率,因此效率相对更高。
- 并行计算是利用 CPU 的多核进行计算,而多线程是利用 CPU 一个核在不同时间段内进行计算。
- 并行计算是多个线程运行在多核 CPU 上,多线程是多线程运行在单核 CPU 上。
综合上述得出多线程并不能真正提高数据处理能力, 其局限于单核 CPU 的性能, 当服务器需要进行大量的数据运算(如图形处理、 复杂的算法) 时考虑多核并行计算。
5 深度分析内核性能
5.1 中断处理
当网络中大量数据包到来时,会产生频繁的硬件中断请求,这些硬件中断可以打断之前较低优先级的软中断或者系统调用的执行过程,如果这种打断频繁的话,将会产生较高的性能开销。
5.2 内存拷贝
正常情况下,一个网络数据包从网卡到应用程序需要经过如下的过程:数据从网卡通过 DMA (直接存储器访问) 等方式传到内核开辟的缓冲区,然后从内核空间拷贝到用户态空间,在 Linux 内核协议栈中,这个耗时操作甚至占到了数据包整个处理流程的 57.1%。
5.3 上下文切换
频繁到达的硬件中断和软中断都可能随时抢占系统调用的运行,这会产生大量的上下文切换开销。另外,在基于多线程的服务器设计框架中,线程间的调度也会产生频繁的上下文切换开销,同样,锁竞争的耗能也是一个非常严重的问题。
5.4 局部性失效
如今主流的处理器都是多个核心的,这意味着一个数据包的处理可能跨多个 CPU 核心,比如一个数据包可能中断在 cpu0,内核态处理在 cpu1,用户态处理在 cpu2,这样跨多个核心,容易造成 CPU 缓存失效,造成局部性失效。
5.5 内存管理
传统服务器内存页为 4K,为了提高内存的访问速度,避免 cache miss,可以增加 cache 中映射表的条目,但这又会影响 CPU 的检索效率。综合以上问题,可以看出内核本身就是一个非常大的瓶颈所在, 解决方案就是想办法绕过内核。
6 高性能网络框架DPDK
DPDK 为 Intel 处理器架构下用户空间高效的数据包处理提供了库函数和驱动的支持,它不同于 Linux 系统以通用性设计为目的,而是专注于网络应用中数据包的高性能处理。
DPDK 官网: https://www.dpdk.org/
DPDK 架构图:
Linux 内核网络数据流程:
- 硬件中断--->取包分发至内核线程--->软件中断--->内核线程在协议栈中处理包--->处理
- 完毕通知用户层
- 用户层收包-->网络层--->逻辑层--->业务层
DPDK 网络数据流程:
- 硬件中断--->放弃中断流程
- 用户层通过设备映射取包--->进入用户层协议栈--->逻辑层--->业务层
下面就具体看看 dpdk 做了哪些突破?
UIO (用户空间的 I/O 技术)的加持, dpdk 能够绕过内核协议栈,本质上是得益于 UIO技术,通过 UIO 能够拦截中断,并重设中断回调行为,从而绕过内核协议栈后续的处理流程。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 3561739510@qq.com 举报,一经查实,本站将立刻删除。
转载请注明来自专注SEO技术,教程,推广 - 8848SEO,本文标题:《Linux高性能服务器技术总结(用自己电脑搭建linux服务器)》
- 搜索
- 最新文章
- 热门文章
-
- 网站排名不稳定有哪些因素?影响排名的因素有哪些?
- 抖音直播间如何开通闪购权限?开通闪购权限有哪些步骤?
- 快手刷礼物主播能得多少?主播如何提高收益?
- 百度搜索引擎的特点及其应用(探究百度搜索引擎的技术特点及应用价值)
- 网站更新频率多少对SEO优化好?更新频率对SEO有何影响?
- SEO优化中如何高效地进行外链建设(掌握外链建设技巧)
- 抖音短视频带货标题技巧有哪些?带货标题写作方法是什么?
- 抖音文案怎么写才能吸引人?抖音文案写作技巧详解
- 抖音商品品质分有几个等级?抖音商品品质分评定标准是什么?
- 如何优化SEO文章标题的写作(掌握这些技巧)
- 抖音DOU+功能在哪里?DOU+入口在哪里可以找到?
- 抖音实名16岁会限制使用时间吗?实名认证有哪些限制?
- SEO搜索引擎优化是什么意思?SEO优化的基本概念是什么?
- 如何利用标签优化SEO(掌握和描述标签的使用技巧)
- 网站重复内容对SEO优化有何影响?重复内容如何处理?
- 抖音合集功能如何开通?开通合集功能有哪些条件?
- 抖音精选联盟是什么?加入精选联盟有哪些好处?
- 如何提高SEO网站排名(优化技巧分享)
- 抖音在线状态在哪里设置?在线状态如何更改?
- 小红书养号成功的标准是什么?如何判断养号是否成功?
- 热门tag