一个典型的服务器结构


主要由三部分组成



网络I/O+服务器高性能编程技术+数据库




一:网络I/O

网络I/O方面,linux下面使用

epoll,windows上面有IOCP,其他平台还有kqueue,dev/poll等机制。




二:服务器及数据库的负载均衡


1.数据库

数据库可能会有以下几点需要解决:


1.超出数据库连接数

    假设数据库并发连接数10个,应用服务器这边有1000个并发访问请求,将会有990个失败。


    


解决方法:

队列 + 连接池


    即加一个中间层DAL(数据库访问层),DAL和应用服务器即可以部署在同一台服务器,


也可以部署在不同台服务器,它们使用TCP通信。最好使用DAL队列服务+连接池的方式。



2.超出时限

数据库并发连接数10个,数据库1秒之内最多能处理1000个请求,应用服务器这边有10000个并发请求,会出现0~10秒的等待




解决方法:

使用缓存


    数据库上的业务逻辑处理应该简单,主要业务逻辑的处理放在应用服务器上。不过这也很有限的降低


数据库的压力。更好的方法是缓存。但是一旦应用了缓存,就会出现缓存的更新,缓存的同步问题。


1.缓存具有一定的时效timeout,如果缓存失效,有两种方法:


(1)对于查询上的缓存,就需要重新去数据库里面查询,查询到数据后更新缓存,进行缓存同步。


将一些热点数据存至缓存,这种方法实时性比较差。


(2)一旦数据库中数据更新,立即通知前端的缓存更新,实时性比较高,但实现有一定难度。




2.缓存换页


内存不够,将不活跃的数据换出缓存。可以使用FIFO,LRU(least recently used)即最近最少使用、


LFU(least frequently used)最近最不频繁使用。


这些技术nosql中都有这种功能。比如redis,memcached都可以作为分布式缓存。


同样,由于

可伸缩性,缓存也可以不跟服务器放在同一台机器上。如果放在同一台机器上,它就只是


一个局部的缓存,只能被该机器访问。如果是部署在独立的机器上,并且使用了分布式缓存机制,它就是全局缓存,


可以被各个服务器访问。




那么如果前端还有大并发请求呢?


我们应该做数据库

读写分离。


对于大部分数据库而言,数据库的读操作多于写操作。我们可以对数据库进行负载均衡。


现在主流的数据库都有replication机制。


查询到读库,写到写库,对于写数据需要更新到读库,这就是主从分布replication机制。



那么数据库负载均衡了,现在压力又到应用服务器上了。



二.应用服务器

应用服务器的负载均衡


方案一:

应用服务器被动接受任务方案


增加一个任务服务器来实现,任务服务器可以监视应用服务器的负载,到底是CPU高还是I/O高,还是并发


高,或者是内存换页高。


应用服务器可以暴露一个http接口给任务服务器,任务服务器选取负载最低的服务器分配任务。




方案二:

应用服务器主动请求任务方案


当应用服务器任务执行完毕后,主动去任务服务器请求任务。




那么任务服务器如果故障了怎么办呢?


任务服务器需要有两台,甚至多台!任务服务器应该具有

fail over(故障转移)机制。


当其中一台任务服务器故障或者失效了,另一台应该可以立刻投入使用,它们两者之间通过心跳来实现,


这就是

服务器的高可用(HA机制high abalitity)。




但是对于大型的应用来说,数据库的量是非常庞大的。


这时候应该对数据库进行数据分区(分库,分表)


分库,即垂直分区,是指数据库可以按照一定的逻辑,把表分散到不同的数据库。比如一个数据库有用户


相关的表,有业务相关的表,有基础信息相关的表,就可以分成三个库。




但更加常用的是水平分区,将一个数据库水平切割为若干个数据库,每个数据库都有这些表,用户表,业务表,信息表。


这种方式需要改写DAL的代码。



三:高性能编程技术

服务器性能四大杀手:


数据拷贝:应该尽可能减少数据拷贝,内存拷贝,同样得利用缓存机制解决。


环境切换:(理性创建线程)线程切换,该不该用多线程,单线程好还是多线程好?


答:单核服务器(状态机编程效率是最高的),单核服务器是不能够并行处理任务的。如果使用多线程,


   会增加线程间的切换开销。如果服务器是多核的,多线程能够充分发挥多核并行的优势,但是多核


   服务器使用多线程,也应该尽可能避免线程之间的环境切换。


内存分配:使用内存池技术解决


锁竞争:锁竞争会降低程序性能,应该尽可能通过逻辑来避免锁的使用。