高性能网络编程 - The C10K problem 以及 网络编程技术角度的解决思路

文章目录

  • C10K
  • C10K的由来
  • C10K问题在技术层面的典型体现
  • C10K问题的本质
  • C10K解决思路
    • 思路一:每个进程/线程处理一个连接
    • 思路二:每个进程/线程同时处理多个连接(IO多路复用)
      • ● 实现方式1:直接循环处理多个连接
      • ● 实现方式2: select
      • ● 实现方式3: poll
      • ● 实现方式4: epoll
      • ● 实现方式5: libevent

在这里插入图片描述


C10K

在这里插入图片描述

英文地址: http://www.kegel.com/c10k.html

中文地址: https://www.oschina.net/translate/c10k

C10K是指单机1万网络并发连接和数据处理能力。
C10M是指单机1000万网络并发连接和数据处理能力


C10K的由来

众所周知,互联网的基础是网络通信,早期的互联网规模有限,只涉及小规模用户群体。在这个阶段,互联网的用户数量相对较少,一台服务器同时在线100个用户已经算是大规模应用了,因此并没有面临所谓的C10K难题。

互联网的爆发期可以追溯到出现www网站、浏览器和雅虎等平台之后。早期互联网被称为Web1.0时代,主要用于下载HTML页面,用户通过浏览器查看网页上的信息,而不需要进行复杂的实时交互。在这个时期,并不需要解决C10K问题。

然而,随着Web2.0时代的到来,情况发生了变化。一方面,互联网的普及率大幅提高,用户数量呈几何级增长。另一方面,互联网不再局限于简单的网页浏览,而是逐渐变为互动的平台,应用程序的逻辑也变得更加复杂,从简单的表单提交发展到了实时通信和在线实时互动。正是在这个背景下,C10K问题开始显现出来。因为每个用户都需要与服务器保持TCP连接以进行实时数据交互。

早期的腾讯QQ也遇到了C10K问题,不过他们采用了UDP这种原始的包交换协议,绕过了这一难题,尽管过程相当具有挑战性。如果当时已经有epoll技术,他们很可能会选择使用TCP。众所周知,后来的手机QQ和微信都采用了TCP协议。

实际上,当时也有一些异步模型,例如select和poll模型,但它们都存在一些限制。比如,select模型的最大连接数不能超过1024,而poll没有这个限制,但每次需要遍历每个连接以检查哪个连接有数据请求。

这时候出现了一个关键问题:最初的服务器基于进程/线程模型,每当有新的TCP连接时,就需要分配一个新的进程或线程。然而,进程是操作系统中最昂贵的资源之一,因此一台机器无法同时创建大量进程。如果要应对C10K问题,可能需要创建上万个进程,这对单台机器来说是难以承受的,效率低下甚至可能导致系统崩溃。如果采用分布式系统,要维护上亿用户的在线连接可能需要上万台服务器,成本巨大 。

鉴于上述情况,如何突破单机性能的限制成为高性能网络编程不得不面对的问题。这些限制和问题最早由Dan Kegel总结和归纳,他首次系统地分析并提出了解决方案。后来,这些普遍存在的网络现象和技术限制都被广泛称为C10K问题。
在这里插入图片描述


C10K问题在技术层面的典型体现

C10K问题的主要特点在于,当程序设计不够精良时,其性能与连接数量和机器性能之间的关系通常表现为非线性

举例来说,如果没有考虑C10K问题,一个经典基于select的程序在旧服务器上可以良好地处理1000个并发连接,但在性能翻倍的新服务器上却常常无法处理2000个并发连接。这是因为在策略不当的情况下,大量操作的开销与当前连接数n成线性相关,导致单个任务的资源消耗与当前连接数之间呈线性关系,即O(n)。对于服务程序来说,需要同时处理数以万计的套接字I/O操作,累积下来的资源开销相当可观,这显然会导致系统吞吐量无法与机器性能相匹配。

这便是C10K问题在技术层面的典型体现。这也解释了为什么大多数开发人员可以相对容易地从功能上实现相同的功能,但一旦应用到高并发场景中,初级开发人员和高级开发人员的技术实现会产生截然不同的实际应用效果。

因此,一些缺乏大规模并发实践经验的技术从业者,例如那些开发即时通讯应用等网络应用的人,往往在没有经受检验和考验的情况下声称其应用能够在单台服务器上支持上万、上十万甚至上百万的用户,这种说法常常难以经受实际验证。

在这里插入图片描述


C10K问题的本质

C10K问题本质上与操作系统的设计和性能密切相关。在Web1.0和Web2.0时代,传统的同步阻塞I/O模型在操作系统中普遍存在,处理方式都以"requests per second"为标准,而区分处理10K并发和100并发的关键在于CPU性能。

当创建大量进程和线程时,会导致数据频繁拷贝(包括缓存I/O、内核将数据从内核空间拷贝到用户进程空间等),并且进程/线程上下文切换的成本较高。这导致操作系统面临巨大的挑战,可能因此崩溃,这正是C10K问题的本质所在。

因此,解决C10K问题的关键在于尽可能减少对CPU等核心计算资源的消耗,从而充分发挥单台服务器的性能,以突破C10K问题所描述的性能瓶颈。这可以通过采用异步I/O模型、事件驱动编程、使用高效的多路复用技术如epoll等,以及优化操作系统内核等方式来实现。通过这些方法,可以显著提高服务器的吞吐量,减少资源消耗,从而解决C10K问题。

在这里插入图片描述


C10K解决思路

要解决这一问题,从纯网络编程技术角度看,主要思路有两个

  • 对于每个连接处理分配一个独立的进程/线程
  • 用同一进程/线程来同时处理若干连接

思路一:每个进程/线程处理一个连接

这一思路最为直接。但是由于申请进程/线程会占用相当可观的系统资源,同时对于多进程/线程的管理会对系统造成压力,因此这种方案不具备良好的可扩展性。

因此,这一思路在服务器资源还没有富裕到足够程度的时候,是不可行的。即便资源足够富裕,效率也不够高。

总之,此思路技术实现会使得资源占用过多,可扩展性差。


思路二:每个进程/线程同时处理多个连接(IO多路复用)

IO多路复用从技术实现上又分很多种,我们逐一来看看下述各种实现方式的优劣。

● 实现方式1:直接循环处理多个连接

传统思路最简单的方法是循环挨个处理各个连接,每个连接对应一个 socket,当所有 socket 都有数据的时候,这种方法是可行的。但是当应用读取某个 socket 的文件数据不 ready 的时候,整个应用会阻塞在这里等待该文件句柄,即使别的文件句柄 ready,也无法往下处理。

实现小结:直接循环处理多个连接。
问题归纳:任一文件句柄的不成功会阻塞住整个应用。


● 实现方式2: select

select要解决上面阻塞的问题,思路很简单,如果我在读取文件句柄之前,先查下它的状态,ready 了就进行处理,不 ready 就不进行处理,这不就解决了这个问题了嘛?

于是有了 select 方案。用一个 fd_set 结构体来告诉内核同时监控多个文件句柄,当其中有文件句柄的状态发生指定变化(例如某句柄由不可用变为可用)或超时,则调用返回。之后应用可以使用 FD_ISSET 来逐个查看是哪个文件句柄的状态发生了变化。这样做,小规模的连接问题不大,但当连接数很多(文件句柄个数很多)的时候,逐个检查状态就很慢了。

因此,select 往往存在管理的句柄上限(FD_SETSIZE)。同时,在使用上,因为只有一个字段记录关注和发生事件,每次调用之前要重新初始化 fd_set 结构体。

intselect(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);

实现小结:有连接请求抵达了再检查处理。
问题归纳:句柄上限+重复初始化+逐个排查所有文件句柄状态效率不高。


● 实现方式3: poll

poll 主要解决 select 的前两个问题:通过一个 pollfd 数组向内核传递需要关注的事件消除文件句柄上限,同时使用不同字段分别标注关注事件和发生事件,来避免重复初始化。

实现小结:设计新的数据结构提供使用效率。
问题归纳:逐个排查所有文件句柄状态效率不高。


● 实现方式4: epoll

poll既然逐个排查所有文件句柄状态效率不高,很自然的,如果调用返回的时候只给应用提供发生了状态变化(很可能是数据 ready)的文件句柄,进行排查的效率不就高多了么。

epoll 采用了这种设计,适用于大规模的应用场景。实验表明,当文件句柄数目超过 10 之后,epoll 性能将优于 select 和 poll;当文件句柄数目达到 10K 的时候,epoll 已经超过 select 和 poll 两个数量级。

实现小结:只返回状态变化的文件句柄。
问题归纳:依赖特定平台(Linux)。

因为Linux是互联网企业中使用率最高的操作系统,Epoll就成为C10K killer、高并发、高性能、异步非阻塞这些技术的代名词了。FreeBSD推出了kqueue,Linux推出了epoll,Windows推出了IOCP,Solaris推出了/dev/poll。这些操作系统提供的功能就是为了解决C10K问题。epoll技术的编程模型就是异步非阻塞回调,也可以叫做Reactor,事件驱动,事件轮循(EventLoop)。Nginx,libevent,node.js这些就是Epoll时代的产物。


● 实现方式5: libevent

由于epoll, kqueue, IOCP每个接口都有自己的特点,程序移植非常困难,于是需要对这些接口进行封装,以让它们易于使用和移植,其中libevent库就是其中之一。

跨平台,封装底层平台的调用,提供统一的 API,但底层在不同平台上自动选择合适的调用。

按照libevent的官方网站,libevent库提供了以下功能:当一个文件描述符的特定事件(如可读,可写或出错)发生了,或一个定时事件发生了,libevent就会自动执行用户指定的回调函数,来处理事件。

目前,libevent已支持以下接口/dev/poll, kqueue, event ports, select, poll 和 epoll。Libevent的内部事件机制完全是基于所使用的接口的。

因此libevent非常容易移植,也使它的扩展性非常容易。目前,libevent已在以下操作系统中编译通过:Linux,BSD,Mac OS X,Solaris和Windows。使用libevent库进行开发非常简单,也很容易在各种unix平台上移植。

一个简单的使用libevent库的程序如下:
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/123996.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

线上 kafka rebalance 解决

上周末我们服务上线完毕之后发生了一个kafka相关的异常,线上的kafka频繁的rebalance,详细的报错我已经贴到下面,根据字面意思:消费者异常 org.apache.kafka.clients.consumer.CommitFailedException: 无法完成提交,因为…

设计模式-状态模式 golang实现

一 什么是有限状态机 有限状态机,英⽂翻译是 Finite State Machine,缩写为 FSM,简称为状态机。 状态机不是指一台实际机器,而是指一个数学模型。说白了,一般就是指一张状态转换图。 已订单交易为例: 1.…

unity打AB包,AssetBundle预制体与图集(三)

警告: spriteatlasmanager.atlasrequested wasn’t listened to while 条件一:图片打图集里面去了 条件二:然后图集打成AB包了 条件三:UI预制体也打到AB包里面去了 步骤一:先加载了图集 步骤二:再加载UI预…

Spring Cloud LoadBalancer基础知识

LoadBalancer 概念常见的负载均衡策略使用随机选择的负载均衡策略创建随机选择负载均衡器配置 Nacos 权重负载均衡器创建 Nacos 负载均衡器配置 自定义负载均衡器(根据IP哈希策略选择)创建自定义负载均衡器封装自定义负载均衡器配置 缓存 概念 LoadBalancer(负载均衡器)是一种…

jenkins部署job

apt install fontconfig openjdk-11-jre wget https://mirrors.tuna.tsinghua.edu.cn/jenkins/war/2.429/jenkins.wardeb包安装 wget https://mirrors.tuna.tsinghua.edu.cn/jenkins/debian-stable/jenkins_2.414.3_all.debdpkg -i jenkins_2.414.3_all.deb 访问 http://…

垂直领域大模型落地思考

相比能做很多事,但每件事都马马虎虎的通用大模型;只能做一两件事,但这一两件事都能做好,可被信赖的垂直大模型会更有价值。这样的垂直大模型能帮助我们真正解决问题,提高生产效率。 本文将系统介绍如何做一个垂直领域…

JavaScript脚本操作CSS

脚本化CSS就是使用JavaScript脚本操作CSS,配合HTML5、Ajax、jQuery等技术,可以设计出细腻、逼真的页面特效和交互行为,提升用户体验,如网页对象的显示/隐藏、定位、变形、运动等动态样式。 1、CSS脚本化基础 CSS样式有两种形式&…

学习笔记:CANOE模拟LIN主节点和实际从节点进行通信测试

先写点感想,在LIN开发阶段,我一般用图莫斯USB工具来进行模拟主机节点发送数据。后来公司买了CANOE工具就边学习边搭建了LIN的测试工程,网上的资料真的很少,主要是靠自己一点点摸索前进,总算入门。几个月后的今天&#…

基于swing的人事管理系统

概述 个人项目人事管理系统,针对部门和人员之间的管理。 详细 一、项目UI 二、项目结构 三、项目使用方法 Eclipse导入现有现有项目到工作空间即可,会自动加载包内相关jar包,使用的java源文件 四、部分代码 MainFrm.java package view…

Docker指定容器使用内存

Docker指定容器使用内存 作者:铁乐与猫 如果是还没有生成的容器,你可以从指定镜像生成容器时特意加上 run -m 256m 或 --memory-swap512m来限制。 -m操作指定的是物理内存,还有虚拟交换分区默认也会生成同样的大小,而–memory-…

ubuntu系统黑屏,且光标不闪烁

选择第二个,进入恢复模式 选择第二个,进入恢复模式 选择root 输入: startx然后就可以进入文本界面或者图形化界面了,如果不行,报错,可能需要需要下载这个包,把这个错误到网上搜索一下就可以找…

批量迁移redis实例的key

我们知道migrate 命令可以迁移redis的多个key,但是如果redis的key有非常多,那用起来就很不方便了。 所以下面分享一个脚本来实现批量key的迁移,主要使用的命令为dump和restore 脚本如下: #!/bin/bash redis-cli -h host1 -p 63…

Redis被攻击纪实

一、前言 声明:本文仅供技术交流使用,严禁采用本文的方法进行任何非法活动。 上周新来的同事分享Redis的原理和机制,想起2017年的时候测试环境Redis被攻击,最后只能重新安装服务器,今天试验一把利用Redis漏洞进行攻击…

成集云 | 英克对接零售O2O+线上商城 | 解决方案

方案介绍 零售O2O线上商城是一种新型的商业模式,它通过线上和线下的融合,提供更加便捷的购物体验。其中,O2O指的是线上与线下的结合,通过互联网平台与实体店面的结合,实现线上线下的互动和协同。线上商城则是指通过互…

AI优秀企业案例——机器人流程自动化:达观数据RPA

通过学习业内领先公司的最佳实践,我们可以更好地将它们应用到我们自己的公司和业务中。特别是第三部分,提供了大量应用案例,让我们一起期待看到这些案例的结尾。 1.简介 达观数据是一家专注于智能文本机器人的国家高新技术企业,…

Leetcode-面试题 02.02 返回倒数第 k 个节点

快慢指针:让快指针先移动n个节点,之后快慢指针一起依次向后移动一个结点,等到快指针移动到链表尾时,慢指针则移动到倒数第n个结点位置。 /*** Definition for singly-linked list.* public class ListNode {* int val;* …

矩阵等价和向量组等价的一些问题

什么是向量组?答:向量组是由若干同维数的列向量(或同维数的行向量)组成的集合。什么是向量组等价?答:两个向量组,各自拼成矩阵A和B,向量组等价就是三秩相等,即r&#xff…

点信息标注_BillboardTextActor3D

开发环境: Windows 11 家庭中文版Microsoft Visual Studio Community 2019VTK-9.3.0.rc0vtk-example参考代码 demo解决问题:点附近创建左边或其他信息,且信息面板显示状态不受相机缩放、旋转影响 prj name: BillboardTextActor3D #include…

STM32笔记—定时器

目录 一、TIM简介 二、基本定时器(TIM6和TIM7) 1. TIM6和TIM7简介 2. TIM6和TIM7的主要特性 3. TIM6和TIM7的功能 3.1 时基单元 3.2 计数模式 3.3 时钟源 三、通用定时器 1. TIMx(2、3、4、5)简介 2. TIMx主要功能 3. 时钟选择 4. 影子寄存…

计算机视觉驾驶行为识别应用简述

一、什么是计算机视觉识别? 计算机视觉识别是一种基于图像处理和机器学习的人工智能应用技术,可以用于多个场景。常见应用场景包括人脸识别、场景识别、OCR识别以及商品识别等。今天以咱们国产系统豌豆云为例,为大家梳理一下在车辆驾驶行为中…