大型网站是如何解决多用户高并发访问的?

分布式是以缩短单个任务的执行时间来提升效率的,而集群则是通过提高单位时间内执行的任务数量来提升效率。

集群主要分为:高可用集群,负载均衡集群,科学计算集群。
分布式是指将不同的业务分布在不同的地方;而集群是将几台服务器集中在一起,实现同一业务。分布式中的每一个节点都能当做集群,而集群不一定是分布式的。

为了解决大型网站的访问量大、并发量高、海量数据的问题,我们一般会考虑业务拆分和分布式部署。我们可以把那些关联不太大的业务独立起来,部署到不同机器上,从而实现大规模的分布式系统。但这之中也存在一个问题,那就是用户如何选择相应的机器的问题,这也被称为访问统一入口问题,而解决的方法是我们可以再集群机器的前面加负载均衡设备,实现流量分发。

负载均衡就是将负载(工作任务、访问请求等)进行平衡、分摊到多个操作单元(服务器、组件等)上进行执行,是解决高性能,单点故障(高可用,如果你是单机版网络,一旦服务器挂了,那么用户就无法请求了,但对于集群来说,一台服务器挂了,负载均衡器会把用户的请求发给其他的服务器进行处理),扩展性(这里主要是指水平伸缩)的终极解决方案。

Nginx使用的注意事项:
(1)nginx的负载均衡配置中默认是采用轮询的方式,这种方式汇总,每个请求按照时间顺序逐一分配到不同的后端服务器,如果后端down掉,能自动剔除,但存在各个服务器的session共享问题。
(2)另一种方式是ip_hash:每个请求按照访问的ip的hash结果分配,如果访问的IP是固定的,那么在正常情况下,该用户的请求都会分配到后台同一台服务器去处理,但是如果用户每次请求的IP不同,也会存在session在各个服务器上的共享问题。
(3)如果集群中的服务器的性能不一致,可以通过配置各个服务器的权值来实现资源利用率的最大化,即性能好的优先选择。

解决服务器共享session问题:使用redis来共享各个服务器的session,并同时通过redis来缓存一些常用的资源,加快用户获得请求资源的速度。

Linux进程间的通信方式

(1)信号:
信号又叫软终端,通知程序发生异步事件,程序执行中随时被各种信号中断,进程可以忽略该信号,也可以中断当前程序而去处理信号。
(2)管道
管道的优点是不需要加锁,缺点是默认缓冲区太小,只有4k,同时只适合父子间的进程通信,而且一个管道只适合单向通信,如果要双向通信需要建立两个管道。而且不适合多个子进程,因为消息会乱,它的发送接收机制是read/write这种适用流的,缺点是数据本身没有边界,需要应用程序自己解释,而一般消息大多是一个固定长的消息头,和一个变长的消息体,一个子进程从管道read到消息头后,消息体可能被别的子进程收到。

特点:管道满时,写阻塞,空时,读阻塞。

分类:普通管道(仅仅用于父子间进程通信)位于内存;命名管道位于文件系统,没有亲子关系管道只要知道管道名称就可以通信。

管道是由内核管理的一个缓冲区(buffer),相当于我们放入内存中的一个纸条。管道的一段连接一个进程的输出。这个进程会向管道中放入信息,另一端连接一个进程的输入,这个进程取出管道中的信息。当管道中没有信息时,读进程会等待,当管道中放满信息时,尝试放入信息的进程会等待,知道另外一端的进程取出信息。

(3)消息队列
消息队列也不用加锁,它并不局限与父子进程间的通信,只有一个相同的key,就可以让不同的进程定位到同一个消息队列上,也可以用来双向通信。
(4)共享内存
共享内存不局限于父子进程间通信,最大的问题就是应用程序自己做互斥。
(5)信号量是一种用于提供不同进程或者一个进程间不同线程同步的原语。

为什么要用线程池

在java中,如果当一个请求到达就创立一个新线程,那么开销是非常大的。在实际使用中,每个请求创建新线程的服务器在创建和销毁线程上花费的时间和消耗的系统资源,甚至可能比花在实际处理的用户请求的时间和系统资源要多得多。除了创建和销毁线程的开销之外,活动的线程也需要消耗系统资源。如果在一个JVM里创建太多的线程,可能会导致系统由于过度消耗内存或者“切换过度”而导致系统资源不足。为了防止资源不足,服务器应用程序需要一些办法来限制给定时刻处理的请求数目,尽可能地减少创建和销毁线程的次数,尽量利用已有对象来进行服务。

线程池主要用来解决线程生命周期开销问题和资源不足问题。通过对多个任务重用线程,线程创建的开销就被分摊到多个任务上了,而且由于在请求到达是线程已经存在,就消除了线程创建锁带来的延迟。这样,就可以立即为请求服务,使得应用程序更快。另外,适当的调整线程池中线程的数量可以防止出现资源不足的问题。

创建一个线程池

一个比较简单的线程池至少应该包含线程池管理器、工作线程、任务队列、任务接口等部分。其中线程池管理器的作用是创建、销毁并且管理线程池,将工作线程放入线程池中;工作线程是一个可以循环执行任务的线程,在没有子进程时等待;任务队列的作用是一种缓冲机制,将没有处理的任务放在任务队列当中;任务接口是每个任务必须实现的接口,主要用来规定任务的入口、任务执行完后的收尾工作、任务的执行状态的等。工作线程通过该接口调度任务的执行。

线程池的基础概念

执行任务时:
(1)如果线程池中线程数量<core,新建一个线程执行任务;
(2)如果线程池中线程数量>=core,则将任务放入工作队列
(3)如果线程池中线程数量>=core且<maxPoolSize,则会创建新的线程
(4)如果线程池中线程数量>core,当线程空闲时间超过了keepalive时,则会销毁线程;由此可见线程池的队列如果是无界队列,那么设置线程池最大数量是无效的。