当前位置:  开发笔记 > 编程语言 > 正文

关于Web开发的一些基本但重要的问题?

如何解决《关于Web开发的一些基本但重要的问题?》经验,为你挑选了2个好方法。

到目前为止,我已经使用PHP,Python和Java开发了一些基于Web的应用程序.但是一些基本但非常重要的问题仍然是我所不知道的,所以我发这篇文章是为了得到你们的帮助和澄清.

假设我使用一些编程语言作为我的后端语言(PHP/Python/.Net/Java等),并使用Web服务器(apache/lighttpd/nginx/IIS等)部署我的应用程序.并且假设在时间T,我的一个页面得到来自不同用户的100个同时请求.所以我的问题是:

    我的Web服务器如何处理这100个同时发出的请求?Web服务器是否会为每个请求生成一个进程/线程?(如果是,进程或线程?)

    后端语言的解释器如何做?它将如何处理请求并生成正确的html?解释器是否会为每个请求生成一个进程/线程?(如果是,进程或线程?)

    如果解释器将为每个请求生成一个进程/线程,那么这些进程(线程)如何?他们会分享一些代码空间吗?他们会互相沟通吗?如何处理后端代码中的全局变量?或者它们是独立的进程(线程)?进程/线程的持续时间有多长?它们会在处理请求并返回响应时被销毁吗?

    假设Web服务器只能支持100个并发请求,但现在它同时有1000个请求.它如何处理这种情况?它会像队列一样处理它们并在服务器可用时处理请求吗?还是其他方法?

    这几天我读了一些关于Comet的文章.而且我发现长连接可能是处理实时多用户用例的好方法.那么长连接怎么样?它是某些特定Web服务器的功能还是可用于每个Web服务器?长连接需要一个长期存在的解释程序吗?


编辑:最近我读了一些关于CGI和fastcgi的文章,这让我知道fastcgi的方法应该是hanlde请求的典型方法.

该协议在多个独立的FastCGI请求之间复用单个传输连接.这支持能够使用事件驱动或多线程编程技术处理并发请求的应用程序.

引自fastcgi规范,其中提到了可以处理多个请求的连接,并且可以在多线程技术中实现.我想知道这个连接可以被视为进程,它可以为每个请求生成多个线程.如果这是真的,我对如何处理每个线程中的共享资源变得更加困惑?

PS感谢托马斯关于将帖子分成几个帖子的建议,但我认为问题是相关的,最好将它们组合在一起.

感谢S.Lott给出了很好的答案,但每个问题的一些答案都过于简短或根本没有涉及.

感谢大家的回答,这让我更接近事实.



1> Crast..:

更新,2018年春季:

我在2010年写了这篇回复,从那时起,Web后端开发人员的世界发生了很多变化.也就是说,"云"转向服务(例如一键式负载平衡器和自动扩展到商品)的出现使得扩展应用程序的实际机制更容易上手.

也就是说,我在2010年的这篇文章中写的内容今天仍然大部分都是正确的,并且了解您的Web服务器和语言托管环境实际工作方式以及如何调整它的机制可以为您节省大量的托管成本.出于这个原因,我已经把这篇文章留给了下面最初写的文章给那些开始肘部深入调整堆栈的人.


1.取决于网络服务器(有时还配置).各种型号的说明:

Apache与mpm_prefork(默认在unix上):按请求处理.为了最大限度地缩短启动时间,Apache会保留一组空闲进程,等待处理新请求(您可以配置其大小).当有新请求进入时,主进程会将其委派给可用的worker,否则会生成一个新的请求.如果有100个请求进入,除非你有100个闲置工人,否则需要进行一些分支以处理负载.如果空闲进程的数量超过MaxSpare值,则在完成请求之后将获得一些空闲进程,直到只有这么多空闲进程.

Apache与mpm_event,mpm_worker,mpm_winnt:每个请求的线程.同样,apache在大多数情况下都会保留一个空闲线程池,也是可配置的.(一个小细节,但功能相同:mpm_worker运行多个进程,每个进程都是多线程的).

Nginx/Lighttpd:这些是基于事件的轻量级服务器,它使用select()/ epoll()/ poll()来复用多个套接字而无需多个线程或进程.通过非常仔细地编码和使用非阻塞API,它们可以扩展到商用硬件上的数千个同时请求,提供可用带宽和正确配置的文件描述符限制.需要注意的是,在服务器环境中实现传统的嵌入式脚本语言几乎是不可能的,这会抵消大多数好处.两者都支持FastCGI,但是对于外部脚本语言.

2.取决于您使用的部署模型的语言或某些语言.某些服务器配置仅允许某些部署模型.

Apache mod_php,mod_perl,mod_python:这些模块为每个apache worker运行一个单独的解释器.其中大部分都不能很好地与mpm_worker一起工作(由于客户端代码中的线程安全性的各种问题),因此它们大多局限于分叉模型.这意味着对于每个apache进程,你都有一个php/perl/python解释器在里面运行.这会严重增加内存占用:如果给定的apache worker通常会占用系统内存大约4MB的内存,一个使用PHP的内存可能需要15mb,而一个使用Python可能需要20-40MB的内存.其中一些将是进程之间的共享内存,但一般来说,这些模型很难扩展到非常大的范围.

Apache(支持的配置),Lighttpd,CGI:这主要是一种奄奄一息的托管方式.CGI的问题在于,您不仅要为处理请求分配新流程,而且还要为每个请求执行此操作,而不仅仅是在需要增加负载时.由于当今的动态语言具有相当大的启动时间,因此不仅会为您的Web服务器创建大量工作,还会显着增加页面加载时间.一个小的perl脚本可能可以作为CGI运行,但是大型python,ruby或java应用程序相当笨拙.在Java的情况下,您可能正在等待一秒或更多仅用于应用程序启动,只需要在下一个请求时再次执行.

所有Web服务器,FastCGI/SCGI/AJP:这是运行动态语言的"外部"托管模型.有一个有趣的变体列表,但要点是你的应用程序监听某种套接字,Web服务器处理HTTP请求,然后通过另一个协议将其发送到套接字,仅用于动态页面(静态页面是通常由网络服务器直接处理).

这赋予了很多好处,因为你需要更少的比你需要处理连接的能力动态的工人.如果对于每100个请求,一半用于静态文件,例如图像,CSS等,而且如果大多数动态请求很短,则可能需要20个动态工作者处理100个并发客户端.也就是说,由于给定的Web服务器保持活动连接的正常使用是80%空闲,因此您的动态解释器可以处理来自其他客户端的请求.这比mod_php/python/perl方法要好得多,当你的用户加载一个CSS文件或根本没有加载任何东西时,你的解释器就坐在那里使用内存而不做任何工作.

Apache mod_wsgi:这特别适用于托管python,但它具有webserver托管应用程序(简易配置)和外部托管(进程多路复用)的一些优点.当您在守护进程模式下运行时,mod_wsgi仅在需要时将请求委托给您的守护程序工作程序,因此4个守护程序可能能够同时处理100个用户(取决于您的站点及其工作负载)

Phusion Passenger:Passenger是一个apache托管系统,主要用于托管ruby应用程序,并且像mod_wsgi一样提供外部和webserver托管托管的优势.

3.同样,我将基于托管模型拆分问题,以适用于何处适用.

mod_php,mod_python,mod_perl:只有应用程序的C库通常会在apache worker之间共享.这是因为apache首先分叉,然后加载你的动态代码(由于细微之处,大多数不能使用共享页面).口译员在此模型中不会相互通信.通常不共享全局变量.对于mod_python,您可以让globals保持在进程内的请求之间,但不能跨进程.这可能导致一些非常奇怪的行为(浏览器很少永远保持相同的连接,并且大多数打开几个给定的网站)所以要非常小心你如何使用全局变量.使用类似memcached或数据库或文件的东西来处理会话存储和需要共享的其他缓存位.

FastCGI/SCGI/AJP/Proxied HTTP:因为您的应用程序本身就是一个服务器,这取决于服务器编写的语言(通常与您的代码语言相同,但并非总是如此)和各种因素.例如,大多数Java部署使用每个请求的线程.Python及其"flup"FastCGI库可以在prefork或线程模式下运行,但由于Python及其GIL是限制性的,因此您可能会从prefork获得最佳性能.

mod_wsgi/passenger:服务器模式下的mod_wsgi可以配置它如何处理事情,但我建议你给它一个固定数量的进程.你想把你的python代码留在内存中,旋转并准备好了.这是保持延迟可预测和低的最佳方法.

在上面提到的几乎所有模型中,进程/线程的生命周期比单个请求长.大多数设置遵循apache模型的一些变化:保留一些备用工作人员,在需要时产生更多,根据一些可配置的限制在太多时收获.大多数这些设置 - 不会 - 在请求之后销毁进程,尽管有些可能会清除应用程序代码(例如在PHP fastcgi的情况下).

4.如果您说"Web服务器只能处理100个请求",则取决于您是指实际的Web服务器本身还是Web服务器的动态部分.实际和功能限制之间也存在差异.

例如,在Apache的情况下,您将配置最大数量的工作者(连接).如果此连接数为100并且已达到,则在有人断开连接之前,apache将不再接受任何连接.启用保持活动后,这100个连接可能会长时间保持打开状态,比单个请求长得多,其他900个等待请求的人可能会超时.

如果您的限制足够高,则可以接受所有这些用户.即使使用最轻量级的apache,每个工作程序的成本约为2-3mb,因此仅使用apache就可以说3gb +内存只是为了处理连接,更不用说其他可能有限的操作系统资源,如进程ID,文件描述符,和缓冲区,这是在考虑您的应用程序代码之前.

对于lighttpd/Nginx,它们可以在很小的内存空间中处理大量连接(数千个),通常只有几千兆的连接(取决于缓冲区和设置异步IO apis的因素).如果我们假设大多数连接保持活动且80%(或更多)空闲,这非常好,因为您不会浪费动态处理时间或大量内存.

在任何外部托管模型(mod_wsgi/fastcgi/ajp/proxied http)中,假设您只有10名工作人员和1000名用户发出请求,您的网络服务器会将请求排队到您的动态工作人员.这是理想的:如果您的请求快速返回,您可以继续处理更大的用户负载而无需更多工作人员.通常,溢价是内存或数据库连接,通过排队,您可以使用相同的资源为更多用户提供服务,而不是拒绝某些用户.

要小心:假设你有一个页面可以构建一个报告或进行搜索并需要几秒钟,而且很多用户会将工作人员与此相关联:想要加载首页的人可能会排队等候几秒钟而所有这些长时间运行的请求完成.替代方案是使用单独的工作池来处理报告应用程序部分的URL,或单独执行报告(如在后台作业中),然后再轮询其完成.那里有很多选项,但要求你在你的应用程序中加入一些思考.

5.由于内存占用率高的原因,大多数使用apache需要处理大量并发用户的人会关闭保持活动状态.或者启用了keep-alive的Apache,具有短暂的保持活动时间限制,比如10秒(这样您就可以在单页加载中获得首页和图像/ CSS).如果您确实需要扩展到1000个或更多连接并希望保持活动状态,那么您将需要查看Nginx/lighttpd和其他基于事件的轻量级服务器.

可能会注意到,如果你确实需要apache(为了易于使用,或者需要托管某些设置),你可以使用HTTP代理将Nginx放在apache之前.这将允许Nginx处理保持活动连接(最好是静态文件)和apache来处理只有繁琐的工作.有趣的是,在编写日志文件时,Nginx恰好比apache更好.对于生产部署,我们对apache前面的nginx非常满意(在这个实例中使用mod_wsgi).apache不进行任何访问日志记录,也不处理静态文件,允许我们在apache中禁用大量模块以保持较小的占用空间.

我已经回答了这个问题,但是没有,如果你有一个很长的连接,它不必对解释器运行多长时间有任何影响(只要你使用外部托管应用程序,现在应该清楚的是非常优越).因此,如果你想使用彗星,并且长时间保持活着(这通常是一件好事,如果你能处理的话)考虑nginx.

Bonus FastCGI问题 您提到fastcgi可以在单个连接中进行多路复用.这确实得到了协议的支持(我相信这个概念被称为"通道"),因此理论上单个套接字可以处理大量连接.但是,它不是fastcgi实现者的必需功能,实际上我不相信有一个服务器使用它.大多数fastcgi响应者也不使用此功能,因为实现这一点非常困难.大多数Web服务器一次只能在给定的fastcgi套接字上发出一个请求,然后跨越该套接字创建下一个请求.因此,每个进程/线程通常只有一个fastcgi套接字.

您的fastcgi应用程序是使用处理还是线程(以及您是通过"主"流程实现它来接受连接和委托,还是只是执行各自的事务的大量流程)取决于您; 并且根据您的编程语言和操作系统的功能而有所不同.在大多数情况下,无论库使用什么默认值都应该没问题,但是要做好一些基准测试和参数调整的准备.

至于共享状态,我建议您假装任何传统的进程内共享状态使用都不存在:即使它们现在可以正常工作,您也可能必须在以后将您的动态工作线分开.对于像购物车等的状态; 数据库可能是最好的选择,会话登录信息可以保存在securecookies中,对于临时状态,类似于memcached的东西非常简洁.您依赖共享数据的功能("无共享"方法)越少,您可以在未来扩展的规模越大.

后记:我在上面的整个设置范围内编写和部署了大量动态应用程序:上面列出的所有Web服务器,以及PHP/Python/Ruby/Java范围内的所有内容.我已经对这些方法进行了广泛的测试(使用基准测试和实际观察),结果有时令人惊讶:通常更少.一旦你离开了在网络服务器进程中托管你的代码,你经常可以使用极少数的FastCGI/Mongrel/mod_wsgi/etc工作人员.这取决于您的应用程序在数据库中停留的时间,但通常情况下,比2*数量的CPU更多的进程实际上不会为您带来任何好处.



2> S.Lott..:

我的Web服务器如何处理这100个同时发出的请求?Web服务器是否为每个请求生成一个进程/线程?(如果是,进程或线程?)

它有所不同.Apache具有处理请求的线程和进程.Apache启动了几个并发进程,每个进程可以运行任意数量的并发线程.您必须配置Apache以控制它实际为每个请求播放的方式.

后端语言的解释器如何做?它将如何处理请求并生成正确的html?解释器是否会为每个请求生成一个进程/线程?(如果是,进程或线程?)

这取决于您的Apache配置和语言.对于Python,一种典型的方法是让守护进程在后台运行.每个Apache进程都拥有一个守护进程.这是通过mod_wsgi模块完成的.它可以配置为以多种不同的方式工作.

如果解释器将为每个请求生成一个进程/线程,那么这些进程(线程)如何?他们会分享一些代码空间吗?他们会互相沟通吗?如何处理后端代码中的全局变量?或者它们是独立的进程(线程)?进程/线程的持续时间有多长?它们会在处理请求并返回响应时被销毁吗?

线程共享相同的代码.根据定义.

进程将共享相同的代码,因为这是Apache的工作方式.

他们没有 - 故意 - 相互沟通.您的代码无法轻松确定其他内容.这是设计的.您无法分辨您正在运行的进程,也无法分辨此进程空间中正在运行的其他线程.

这些过程是长期的.它们不会(也不应该)动态创建.您可以将Apache配置为在启动时分叉自身的多个并发副本,以避免创建进程的开销.

线程创建的开销要小得多.Apaches如何在内部处理线程并不重要.但是,您可以将Apache视为每个请求启动一个线程.

假设Web服务器只能支持100个并发请求,但现在它同时有1000个请求.它如何处理这种情况?它会像队列一样处理它们并在服务器可用时处理请求吗?还是其他方法?

这是"可扩展性"问题.简而言之 - 随着负载的增加,性能将如何降低.一般的答案是服务器变慢.对于某些负载级别(假设100个并发请求),有足够的进程可用,它们都可以快速运行.在某个负载级别(比如101个并发请求),它开始变慢.在其他一些负载级别(谁知道有多少请求)它变得如此之慢,你对速度感到不满意.

有一个内部队列(作为TCP/IP工作方式的一部分,通常),但是没有调控器将工作负载限制为100个并发请求.如果您获得更多请求,则会创建更多线程(而不是更多进程),并且运行速度会更慢.


关于mod_wsgi你的语句'对于Python,一种典型的方法是让守护进程在后台运行.每个Apache进程都拥有一个守护进程.这是通过mod_wsgi模块完成的.它可以配置为以不同的方式工作.是错的.在mod_wsgi的情况下,每个Apache进程都不拥有守护进程.Apache父进程是所有mod_wsgi守护程序进程的所有者/管理者.任何Apache服务器子进程都可以代理任何mod_wsgi守护程序进程.他们代理的将依赖于WSGIProcessGroup的配置.
有关mod_wsgi进程/线程模型的更多信息,请参阅"http://code.google.com/p/modwsgi/wiki/ProcessesAndThreading".
推荐阅读
周扒pi
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有