60.Linux/Unix 系统编程手册(下) -- SOCKET: 服务器设计

1.迭代型和并发型服务器
	迭代型:服务器每次只处理一个客户端,只有当完全处理完一个客户端的请求之后才去处理下一个客户端
	并发型:这种类型的服务器被设计为能够同时处理多个客户端的请求

	并发型服务器的其他设计方案:
		1.在服务器上预先创建进程或线程,其核心理念如下:
			1.服务器程序在启动阶段就立即预先创建好一定数量的子进程(或线程),而不是针对每个客户端来创建一个新的子进程(或线程)。
			  这些子进程构成了一个服务池
			2.服务池中的每个子进程一次只处理一个客户端。在处理完客户端请求后,子进程并不会终止,而是获取下一个待处理的客户端继续处理。
		采用上述技术需要在服务器应用中仔细的管理子进程。服务池应该足够大,以确保能充分响应客户端的请求。这意味着服务器父进程必须对未占用
	  的子进程加以监视,并且在服务器处于负载高峰时增加服务池的大小,这样就总会有足够的子进程存在。如果负载下降了,那么应该响应的减少服务池
	  的大小,因为过多的空余进程会降低系统的整体性能。
	    此外,服务池中的子进程必须遵循某些协议,使得它们能够以独占的方式选择一个客户端连接。在大多数 Unix 实现中(包括Linux),让服务池中的
	  每个子进程在监听描述符的 accept() 调用上阻塞就足够了。换句话说,服务器父进程在创建任何子进程之前先创建监听套接字,然后每个子进程在 fork()
	  调用中继承该套接字的文件描述符。当一个新的客户端连接到来时,只有其中一个子进程能够完成 accept() 调用。但是,由于 accpet() 在一些老式的实现
	  中不是一个原子化的系统调用,因此可能需要通过一些互斥技术,如文件锁等来支持,以确保每次只有一个子进程可以执行 accept() 调用。
	    还有其他方法可以让服务池中所有的子进程都执行 accept() 调用。如果服务池由分离的进程组成,服务器父进程可以执行 accept() 调用,然后将代表新
	  连接的文件描述符传递给空闲的进程之一。如果服务池由线程组成,主线程可以执行 accept() 调用,然后通知服务池上的空闲线程,有新的已连接上的客户端
	  正在等待处理。

	    2.在单个进程中处理多个客户端
	    	在某些情况下,我们可以设计让单个服务器进程来处理多个客户端。为了实现这一点,我们必须采用一种能够允许单个进程同时监视多个文件描述符上IO
	      事件的IO模型(IO多路复用,信号驱动IO 或者 epoll())
	        在设计单进程服务器时,服务器进程必须做一些通常由内核来处理的调度任务。在每个客户端一个服务器进程的解决方案中,我们可以依靠内核来确保每个
	      服务器进程能够公平的访问到服务器主机的资源。但当我们用单个服务器进程来处理多个客户端时,服务器进程必须自行确保一个或多个客户端不会霸占服务器,
	      从而使其他客户端处于饥饿状态。

	    3.采用服务器集群
	    	其他用来处理高客户端负载的方法还包括使用多个服务器系统---服务器集群(server farm)
	    	构建服务器集群最简单的方法就是 --- DNS 轮转负载均衡,一个地区的域名权威服务器将同一个域名映射到多个 IP 地址上。DNS 轮询的优势是,成本低,
	      而且容易实施。问题是,远端 DNS 服务器上所执行的缓存操作,这意味着今后位于某个特定主机上的客户端发出的请求会绕过循环轮转的 DNS 服务器,并总是
	      由一个服务器来负责处理。此外,DNS 轮询并没有任何内建的用来确保到达良好负载均衡或者是高可用性的机制。另外一个需要我们考虑的是服务器的亲和性。这
	      就是说,确保一个客户端的请求序列能够全部定向到同一台服务器,这样由服务器维护的任何有关客户端状态的信息都能够保持准确。
	        一个更灵活的解决方案是服务器负载均衡。在这种场景下,由一台负载均衡服务器将客户端请求路由到服务器集群中的一个成员上。(为了确保高可用,可能还会
	      有一台备用服务器。一旦负载均衡主服务器崩溃,备用服务器就立刻接管主服务器的任务)。这消除了远端 DNS 缓存引起的问题,因为服务器集群只对外提供了一
	      个单独的IP地址(也就是负载均衡服务器的IP地址)。负载均衡服务器结合一些算法来衡量或计算服务器的负载,并智能的将负载分发到服务器集群的各个成员上。
	      负载均衡器也会自动检测集群中失效的成员。最后,负载均衡服务器还可能提供对服务器的亲和力。

2.inetd 守护进程
	inetd 被设计用来消除大量非常用服务器进程的需要,inetd 提供了2个好处:
		1.与其为每个服务运行一个单独的守护进程,现在只需要一个进程--- inetd 守护进程,就可以监听一组指定的套接字接口,并按照需要启动其他的服务。因此,可以
	  降低系统上运行的进程数量。
	    2.inetd 简化了启动其他服务的编程工作。

	inetd 守护进程所做的操作:通常在系统启动时运行
		1.对于配置在 /etc/inetd.conf 中指定的每项服务,inetd 都会创建恰当类型的套接字,然后绑定到指定的端口上。此外,每个 tcp 套接字都会通过 listen() 
		  调用允许客户端发来连接。
		2.通过 select() 调用, inetd 对前一步中创建的所有套接字进行监视,看是否有数据报或者连接请求发送过来。
		3.select() 进入阻塞状态,直到一个 udp 数据报可读或者 tcp 套接字上接收到了连接请求。在 tcp 连接中,inetd 在进入下一个步骤之前会先为连接执行 accept()
		  调用。
		4.要启动这个套接字上指定的服务,inetd 调用 fork() 创建一个新的进程,然后通过 exec() 启动服务器程序。在执行 exec() 之前,子进程执行如下的步骤:
			1.除了用于 udp 数据报和接受 tcp 连接的文件描述符之外,将其他所有从父进程继承而来的文件描述符都关闭
			2.在文件描述符 0,1,2上复制套接字文件描述符,并关闭套接字文件描述符本身。完成这一步之后,启动的服务器进程就能够通过这3个标准的文件描述符同套接字通信了
			3.这一步是可选的,为启动的服务器进程设定用户和组 ID, 设定的值可在 /etc/inetd.conf 中设置。
		5.在第3步中,如果在 tcp 套接字上接受了一个连接,inetd 就关闭这个连接套接字
		6.inetd 服务跳回第2步继续执行。

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值