Showing posts with label Internet. Show all posts
Showing posts with label Internet. Show all posts

Sunday, January 25, 2015

C10K 问题引发的技术变革

C10K 问题

服务器同时支持并发 10K 量级的连接,这些连接可能是保持存活状态的。
解决这一问题,思路主要有两个方面,一个是对于每个连接处理分配一个独立的进程/线程;另一个思路是用同一进程/线程来同时处理若干连接。

每个进程/线程处理一个连接

这一思路最为直接。但是由于申请进程/线程会占用相当可观的系统资源,同时对于多进程/线程的管理会对系统造成压力,因此这种方案不具备良好的可扩展性。
因此,这一思路在服务器资源还没有富裕到足够程度的时候,是不可行的;即便资源足够富裕,效率也不够高。
问题:资源占用过多,可扩展性差。

每个进程/线程同时处理多个连接

传统思路

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

select

要解决上面阻塞的问题,思路很简单,如果我在读取文件句柄之前,先查下它的状态,ready 了就进行处理,不 ready 就不进行处理,这不就解决了这个问题了嘛?
于是有了 select 方案。用一个 fd_set 结构体来告诉内核同时监控多个文件句柄,当其中有文件句柄的状态发生指定变化(例如某句柄由不可用变为可用)或超时,则调用返回。之后应用可以使用 FD_ISSET 来逐个查看是哪个文件句柄的状态发生了变化。
这样做,小规模的连接问题不大,但当连接数很多(文件句柄个数很多)的时候,逐个检查状态就很慢了。因此,select 往往存在管理的句柄上限(FD_SETSIZE)。同时,在使用上,因为只有一个字段记录关注和发生事件,每次调用之前要重新初始化 fd_set 结构体。
int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
  • 思路:有连接请求抵达了再检查处理。
  • 问题:句柄上限+重复初始化+逐个排查所有文件句柄状态效率不高。

poll

poll 主要解决 select 的前两个问题:通过一个 pollfd 数组向内核传递需要关注的事件消除文件句柄上限,同时使用不同字段分别标注关注事件和发生事件,来避免重复初始化。
int poll(struct pollfd *fds, nfds_t nfds, int timeout);
  • 思路:设计新的数据结构提供使用效率。
  • 问题:逐个排查所有文件句柄状态效率不高。

epoll

既然逐个排查所有文件句柄状态效率不高,很自然的,如果调用返回的时候只给应用提供发生了状态变化(很可能是数据 ready)的文件句柄,进行排查的效率不就高多了么。
epoll 采用了这种设计,适用于大规模的应用场景。
实验表明,当文件句柄数目超过 10 之后,epoll 性能将优于 select 和 poll;当文件句柄数目达到 10K 的时候,epoll 已经超过 select 和 poll 两个数量级。
int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);
  • 思路:只返回状态变化的文件句柄。
  • 问题:依赖特定平台(Linux)。

libevent

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

C10K 到 C10M

随着技术的演进,epoll 已经可以较好的处理 C10K 问题,但是如果要进一步的扩展,例如支持 10M 规模的并发连接,原有的技术就无能为力了。
那么,新的瓶颈在哪里呢?
从前面的演化过程中,我们可以看到,根本的思路是要高效的去阻塞,让 CPU 可以干核心的任务。
当连接很多时,首先需要大量的进程/线程来做事。同时系统中的应用进程/线程们可能大量的都处于 ready 状态,需要系统去不断的进行快速切换,而我们知道系统上下文的切换是有代价的。虽然现在 Linux 系统的调度算法已经设计的很高效了,但对于 10M 这样大规模的场景仍然力有不足。
所以我们面临的瓶颈有两个,一个是进程/线程作为处理单元还是太厚重了;另一个是系统调度的代价太高了。
很自然地,我们会想到,如果有一种更轻量级的进程/线程作为处理单元,而且它们的调度可以做到很快(最好不需要锁),那就完美了。
这样的技术现在在某些语言中已经有了一些实现,它们就是 coroutine(协程),或协作式例程。具体的,Python、Lua 语言中的 coroutine(协程)模型,Go 语言中的 goroutine(Go 程)模型,都是类似的一个概念。实际上,多种语言(甚至 C 语言)都可以实现类似的模型。
它们在实现上都是试图用一组少量的线程来实现多个任务,一旦某个任务阻塞,则可能用同一线程继续运行其他任务,避免大量上下文的切换。每个协程所独占的系统资源往往只有栈部分。而且,各个协程之间的切换,往往是用户通过代码来显式指定的(跟各种 callback 类似),不需要内核参与,可以很方便的实现异步。

参考文献

  • http://www.ulduzsoft.com/2014/01/select-poll-epoll-practical-difference-for-system-architects/

Friday, October 11, 2013

RFC中的奇葩(2) - The Twelve Networking Truths

http://tools.ietf.org/html/rfc1925

12个网络相关的事实,发表在1996年的愚人节。

虽说是在愚人节,讲的东西却颇有道理。以幽默的方式阐述了设计网络这个前无古人的大工程中要注意的事实。

1)必须要能工作。。
2)不能增加光的速度。。
3)足够的推力,猪也可以飞,但显然没必要这么做。一个是很难控制它的着陆,另一个是当它们飞在空中经过的时候,坐在下面是一件很危险的事情。。
4)有些东西,不实际体验下,永远难以很好的认识和理解。
5)把多个问题糅合到一起,提出一个复杂的解决方案,不是一个好主意。
6)把问题转移到其他地方,比解决它要容易。
7)永远是某个存在。(怀疑有遗漏,推论是凡事总有tradeoff)
8)比你想象的更为复杂。
9)对所有的资源,你总是需求更多。
10)一个尺寸不能适应所有的事情。
11)所有的老主意总会被再次提出,换个名字,换个表达方式,不管是否工作的好。
12)在设计协议的时候,完美的目标,不是说无法再添加什么上去了,而是说无法再拿掉什么了。

Thursday, August 08, 2013

RFC中的奇葩(1) - Discard Protocol

RFC见证了互联网的发展历史。
一群技术人在一起,总会产生很多让人忍俊不禁的故事,包括几个著名的愚人节发布的RFC。
本系列将关注RFC中一些“卓尔不群”的经典提案。

本文提到的RFC其实还真是一个挺有用的RFC,发表于1983年,全文就1页,在http://tools.ietf.org/html/rfc863。主要用处就是定义了Discard Protocol,即丢弃服务。

简单的说,该RFC干的事情就是定义了啥都不干。

该RFC煞有其事地分了TCP和UDP两种情况进行讨论,定义了端口号9。

让人对当年神往不已。

Thursday, March 17, 2011

Internet: Clean Slate vs Evolution

互联网的clean slate (清白历史?) 和 evolution (演进)之争由来已久。
当今的互联网在设计之初并未考虑到后世会发展到如此大规模,有如此多的应用,占据人类社会如此重要的地位。即使是在现在和可以预见的未来,都很难再找出第二个事物来与之相提并论。随着技术的发展和规模的增加,互联网逐渐浮现出一些难以解决的问题,包括管理、分发、计费、扩展等等。
于是学术界提出了clean slate的想法,基本出发点是放弃原有的设计,去掉一切限制,重新设计架构。这些年也着实作出了不少的成果。另一方面的观点认为,我们应该采取逐步演化的方式,在原有架构上缝缝补补,慢慢改进。可以看出前者带有明显的理想主义色彩,属于学院派风格。后者则更为实用,属于工业界的观点。
其实,这两派观点的出现并非偶然。对于学术界来说,标准的思路是研究人员制定好标准,工程师来将它实现。而互联网从一开始就缺乏一个统一主导的管理者角色,是通过各种技术的竞争演变而来。因此,技术的实用性、代价和反应速度等都很关键。工业界并不在乎某个设计是否ugly,至少它能很快的工作起来,那么就有很大的几率被部署和推广。对于Clean Slate来说,需要花费太大的代价进行迁移。牵扯利益太多,几乎是不可能的,所以只能是作为学术界研讨的课题。
因此,从根本上说,现代互联网,是无统一管理(NGO)的产物,正因为它的发展太过迅速,来不及让人来思考或等待,还没有想好谁来管,它就已经开始运行了。但正如我此前的观点,产业的成熟必然催化垄断。在互联网产业,也已经隐隐有此兆头。学术界,甚至在工业界,技术、标准、监管都已经开始统一化、垄断化。未来的互联网很可能具有极强的管理性,到那时,Clean Slate或许将成为可行。

Thursday, March 10, 2011

互联网的社会史

今天跟朋友聊天,无意中说到互联网产业的发展历史,感觉跟人类社会的历史发展有颇多共同之处。
马克思所说的“生产力决定生产关系”,看起来很简单,却一语道破人类社会发展的根本规律。对于自然科学来说,能够看穿复杂的事物表面,准确把握内在规律,这绝对是大师级人物才能做到;而对更为复杂的社会科学做到这一点,古往今来,不过寥寥数人。马克思能被评为“千年思想家”这不是巧合,绝对是名副其实的。
借用这一观点,互联网产业的发展,根本上是由各个阶段的生产力水平——也即IT技术水平来决定的。
在最初的阶段(实验到DARPA时代,60年代及以前),网络规模小、链路带宽低、用途目的简单,正像是原始社会的部落制度。各个小网之间甚至网络内部联系都十分松散,应用是傻大粗的奔放模式,更多的是作为试验品,缺乏高级的应用。
第二个阶段(70年代~80年代)是TCP/IP的发展和成熟以及一些应用开始出现,包括telnet、UseNet、email等等。这些应用就好比是种植业的出现,使得互联网的实用性和效率都有了显著的提高。同时,互联网仍然是集中在少数国家,集中性相当明显。类似于奴隶制下的集中低效生产模式。
第三个阶段(80~2000年代)则是互联网的进一步普及,美洲、欧洲、亚洲不少大学和科研机构纷纷接入网络,网络规模进一步扩大,DNS出现,NSFnet建成,并由实验网转入商业运营。www协议的出现更是极大的促进了网络的普及。相关机构成立,互联网的管理由几大组织(IETF等)和几大运营商来负责。类似于封建制下严格的等级管理模式。
第四个阶段(2000年至今),互联网相关技术进一步成熟并趋向稳定,创新的大浪潮已经过去,难以出现革命性的变化,新出现的应用多是改进和扩展。云计算相关技术的发展让资源进一步集中,利益划分更为明确。物理链路牢牢掌握在各个政权手中,而垄断的运营商们之间相互竞争和合作,各国对网络上运行的数据信息监控力度加大,现有模式下有大的变化已经毫无可能,正是当今世界凑综复杂制衡格局的极好展现。
总体来说,互联网的出现,极大打破了传统行业中的层级和垄断模式,让无数“平民”等级可以凭借智慧和努力在产业链上分得一勺羹。但随着技术的提高以及网络应用的成熟,互联网的重要性对各个国家都不言而喻,相关的监控或管理力度必然会进一步加大。在互联网产业越来越成熟的今天,想要挤占一方市场将会越来越困难。