GigaHttpd 设计思想 0.1 版,请大家帮忙看看,是否可行,谢谢!
lu_yi_ming
|
1#
lu_yi_ming 发表于 2008-04-08 21:22
GigaHttpd 设计思想 0.1 版,请大家帮忙看看,是否可行,谢谢!
项目网址: http://gigahttpd.sourceforge.net/
版本: 0.1 提交时间:2008-04-08 本版作者:鲁义明 (Yiming Lu) lu.yiming.lu@gmail.com 所属: GigaHttpd 开发文档 * 先说实话 写这一版设计思想的时候,我知道自己还很无知,很多想法可能都是错的,甚至整个设计都是彻底不可行。不过没关系,让我们来持续改进。 * HTTP Server 功能的简单描述 接收一个 HTTP 请求,返回请求的数据。 * 功能细化一下 (1) 建立一个 TCP 连接。 (2) 接收 HTTP 请求,GET 或 POST,验证身份 Session,解析 URL。 (3) 从所有 Object 中找到对应 URL 的 Object。 (4) 如果这个 Object 需要计算(比如 Post Body 中解析出来数据进行运算 ),则启动计算过程,保存计算结果。 (5) 将这个 Object 的数据返回给用户。 * 我们面对的挑战 每秒处理并响应 10 亿个 HTTP 请求,每个请求处理过程都要经过以上的步骤。 * 我们可用的资源 (1) 1000 台 PC。 (2) 每台 PC 上有 8 到 16 个 64 位的 CPU Core。 (3) 每台 PC 上有 8 到 16 GB 内存。 (4) 每台 PC 上有 8 到 16 块 1G/10G 的以太网卡。 (5) 每台 PC 上有两块 1TB 的硬盘,通过硬件 RAID 卡模拟成一块。 (6) 足够多的 10G 以太网交换机。 (7) 开源的 Linux 操作系统。 * 系统运行的环境 以下条件假设已经或将会存在,即我们不在我们考虑之列。 (1) 设备间空间够大,电力充足 (2) 支持 1G 用户同时访问的足够的外网带宽,可能是很多路宽带接入,包括 DNS 负载均衡。 (3) 全部 IPv6。如果有 IPv4 接入,前端接 IPv6/IPv4 NAT 转换器吧。 (4) 有足够好足够多的的 TCP 连接负载均衡器,能把 1G 个 TCP 连接分摊到上万块网卡上。 * 一个应用! 在开始设计之前,请先明白一个核心问题,我们的系统只处理一个应用!这意味着这是一个精简到最小的不能再分解的应用。 举例说明一:假设应用是一个大论坛,由很多子论坛,每个子论坛相对独立。这个应用可以不用我们的系统,因为每个自论坛可以单独的使用简单的 HTTP Server。当然,如果因为某种原因,比如统一验证身份 Session,或复杂的内部结算交易,使用我们的系统是合适的。 举例说明二:假设应用是一个多人实时绘画系统,支持 10 亿人同时在一张纸上画画。这个应用就很难再分解,我们的系统适合于这种应用。 哦,不要笑话某些想法的疯狂,我们就是要支持更加疯狂的公益创意或商业创意,能够付诸实现 * TCP 连接 在网卡的驱动程序里处理。即不进入内核的 TCP/IP 协议栈,也没有某个进程在阻塞在 socket 的 listen() 中。 所以每太 PC 中需要有“正常”的网卡,也有专用的“数据网卡”。我们为“数据网卡”设计单独的驱动程序。 “数据网卡”驱动程序从以太数据包中直接判断,如果是 TCP 连接请求,直接返回 accept。 “正常”网卡的数据处理过程不做任何修改。我们可以通过“正常”网卡登录到 PC 的 Linux 上,进行管理。 * 身份认证 用户身份认证包括首次认证,即用户名/密码检验;以及 HTTP Session 验证。 我们有 1G 用户,假设每个用户占用 4K 字节(一页),包括用户名、密码、该用户当前所有的 Session/TCP 连接信息、TCP 输入数据缓冲区、TCP 输出信息,以及一些附加信息,等等。总共需要 4TB 内存来存放所有的用户信息。 为了实现高性能,系统运行中所有数据均存放在内存,为了便于统一存取,我们把 10TB 的内存设计在一个连续的内存地址空间内,即内部数据引用都用 64 位的地址指针来表示。 每台 PC 大约有 10G 内存,所以 4TB 的用户数据存放在 400 台 PC 上。1G 用户用 32 位二进制数就可以定位,所以 1G 用户信息连续的存放在 4TB 空间内,用 44 位内存地址来定位。 用户名/密码检验时,把用户的用户名用 Hash 算法翻译成一个地址指针,然后去内存中连续的小范围内查找用户,找到后,检查用户名/密码。检查通过后,把该用户的 32 位地址,再加上一个大随机数,合并作为 Session ID。后续的 Session 检验就简单了。为了便于提高性能,可以把 SessionID 放在 URL 中,关于这个后续版本再讨论。 所以,当一个 PC 的“数据网卡”收到一个用户登录请求后,相应的 CPU Core 首先计算用户的 32 位地址,如果这个地址不在本 PC,那就把这个请求发送给那个地址所在的 PC。 出于性能的考虑,在设计网页时,一次提交 Form 的数据的不要超过一个以太网数据包的容量(约 1.4KB),这也是向客户端发送的 TCP 窗口尺寸。接收 Upload 文件的情况留待后续设计考虑。 所以,在每个 PC 中的 CPU Core 分成两种,一个是“正常”的 CPU Core,其他的是“数据 CPU Core”,“数据 CPU Core” 只处理“数据网卡”的收发数据任务。“数据 CPU Core” 工作在内核级,主要是为了高速度读写网卡,如果进出内核的开销能够忍受的话,也可以工作在用户级。工作在内核级的“数据 CPU Core”,为了不影响正常的操作系统内核数据,可以设定单独的内存映射,内存页目录页表,即不能访问正常的操作系统数据。当然“数据 CPU Core” 不参与正常的操作系统任务调度。“数据 CPU Core” 有自己的调度方式。在 “正常 CPU Core” 空闲的时候,也可以把它暂时设定成“数据 CPU Core”。 * 解析 URL,找到 Object 出于性能的考虑,系统中的所有 Object 都保存成一块静态内存数据区,通过地址指针来访问。 系统中根据用户提交的数据计算而成的结果,也保存成静态内存数据区。比如论坛里提交的帖子;博客里提交的文章;博客里的评论;实时绘图绘制出来的图像等。实时图像的数据区过期后可以重复使用。 静态的文件,从硬盘上预读入内存。 如果应用是大数据文件,比如电影,则适当增加全系统的内存,或者按照一定的算法做硬盘缓冲,或者限制用户的数据流量(只要保证电影连续播放),来减少带宽和内存占用。关于这个问题留待以后设计讨论。 所以系统中的 Object,可以嵌套包含 Object。 每个 Object 对应的 URL,在明确可预期是静态数据的情况下,可以用地址指针来做 URL,即使系统重新启动,重新加载所有数据也保持地址不变。如果 Object 是动态的,可以用字符串或者字符窜加 ID 来表示。 所以整个系统的所有 Object,也是统一的放在一个巨大的地址空间中。另一方面,Object 的总量是有限的,1G 的用户存取的很多内容是重复的,往往很多人同时访问的数据量并不会很大,比如商品股票交易数据、热门的影视作品。而像商品股票交易数据重点需要做好存储,不一定是大量历史数据的实时访问。如果很多人同时访问大量的数据,比如全球精细地图系统、人类拍摄过的所有电影高清晰版全库,则数据流量不会很大,而且是静态数据,每个用户的带宽可以限制在一定范围内,所以可以采用硬盘数据缓冲机制。 所以整个系统中,可以用 5TB 来保存所有 Object,每个 Object 通过指针地址来访问。所有的 Object 分布在超过 500 台 PC 上。考虑到访问量大的 Object,应该在多个 PC 上保存副本,以实现高性能。此处负载均衡的问题留待以后设计讨论。 关于访问权限,某个用户是否有访问某个 Object 权限的问题,可以设计一定格式一定数量的“授权码”,每个需要被保护的 Object 都给定一个或几个“授权码”。这些授权码还可以组成角色,或多级(多层)角色。用户可以被设定成多种角色,以此来实现大范围的授权。至于 Object 对单个用户授权,则可以在 Object 中记录用户的 ID(地址指针),比如博客内容只能由博客作者自己来修改。 权限的内存占用设计在 1MB 到 100MB 之间。关于这个后续设计可以继续讨论。 * 计算 Object 在此特制计算量大的 Object,比如实时绘图,或者商品交易撮合。 计算过程如果可以并行分解后合成,比如实时绘图,那就分解到若干个 CPU Core 上进行计算,计算后发送到一个 CPU Core 上合成。如果计算过程不能并行分解,比如某单一产品交易撮合,那就在一个 CPU Core 上计算。 计算的结果保存成静态数据。如果需要复制到多个 PC 上,则通过以太网广播。如果广播太多影响性能,可以再划分子网。 * 将 Object 数据发给用户 出于性能的考虑,不使用输出缓冲,即不在全局内存间复制数据作为输出缓冲。 为每个 Object 设定一个输出 TCP 连接池,池中的每个连接数据是 TCP 连接的发送状态。完整的 HTTP 响应数据就通过这里发送回去,即发送静态 Object 的一部分数据,大小是以太网数据包尺寸与用户 TCP 接受窗口尺寸的相比取小,以太数据包里面打成IP包,包括服务器端的公共 IP 地址,准备好后扔给路由器,发送给用户。 这部分 TCP 连接池的内存是动态分配的,大小接近 1TB,分摊到每个 Object 所在的 PC。内存分配管理方法可以类似 Linux 内核的 Slab,整页分配。因为 TCP 的发送时间可以设定超时,所以可以保证这些内存会在一定时间内重新投入使用。 * 一个典型的 HTTP 请求/响应过程 (1) 用户正在使用浏览器/聊天工具之类的客户端,发 TCP 请求(一个 IP 包)到系统,首先到达负载均衡器。 (2) 负载均衡器将 TCP 请求发给一台 PC 的“数据网卡”,“数据网卡”的驱动程序直接回复,建立 TCP 连接。 (3) 用户端发过来一个 HTTP POST 请求(假设不超过一个以太数据包),负载均衡器将此请求发给用户管理 PC 的“数据网卡”。 (4) 用户管理 PC 内的一个“数据 CPU Core”解析 Session ID,发现不是自己的用户,于是将此请求转发给第二个用户管理 PC。 (5) 第二个用户管理 PC 检验用户的 Session ID 正确后,解析 URL 得到 Object ID,然后按负载均衡算法将此请求转发给某个 Object 管理 PC 的“数据网卡”。 (6) Object 管理 PC 的“数据 CPU Core” 解析用户的 POST Body,计算并更新 Object 的静态数据。 (7) Object 管理 PC 把更新过的静态数据广播发送其他的 Object 管理 PC。 (8) Object 管理 PC 在此 Object 的 TCP 连接池中增加一个连接记录。 (9) Object 管理 PC 通知负载均衡器,此 TCP 连接的后续 IP 包都发到本机,最好发到本网卡。 (10) Object 管理 PC 从静态数据中取出一部分,打包成包含 IP 包的以太网包,IP 包的目的地址是用户的 IP 地址,发给路由器。 (11) 用户收到 IP 包后,发回接收确认 ACK 包,负载均衡器将此包发给 Object 管理 PC,Object 管理 PC 继续打包并发送下一个数据包。 (12) Object 管理 PC 发送全部数据,或者连接超时,从连接池中删除此连接记录。如果一个页面中的所有连接记录都被删除,则回收此连接池。 (13) 一个大小为 10KB 的 Object 请求/响应能在 1 秒钟内走完上述过程,在同时有 1G 个请求的情况下。 * 遗留问题 本版设计先不考虑下列问题,后续版本会加入。 (1) SSL/TLS (2) 热插拔/容错 (3) 数据在硬盘上如何存放,以及是否使用数据库。 (4) 内部负载均衡 (5) 内部网络数据转发丢失问题 (6) CPU Cache 优化 |