服务器设计目的
高性能:能够处理大量请求同时到来,及时的给予响应;
高可用:服务器7×24小时不间断运行,即使主机故障,也有备用主机可以使用;
伸缩性:服务器可以进行扩展,良好的分层设计,业务分离,并且可以灵活部署;
C/S结构
任何服务器模型都可以抽象C/S结构,一个充当客户段,一个充当服务端,客户端请求服务器资源;
而服务器和服务器之间通信可以看成C/S结构,因为一个请求的服务器,可以抽象为是客户端,而响应的服务器可以认为是服务端;
并且我们常见的B/S模型,也就是浏览器和服务器模型,本质也是C/S结构,只不过是浏览器充当客户端,服务器充当服务端,浏览器一般都是HTTP请求,而服务器就是HTTP服务器;
一个典型的服务器架构
在Linux中,使用高性能的epoll来充当网络I/0模型;
通常来说,服务器性能瓶颈都会发生在数据库中;
当大量请求服务器时候,假设这些请求都要访问数据库,而数据库的并发量又不高,如何能够及时的响应数据给客户端呢?
解决办法:1.搞一个队列+连接池
解决办法:搞一个队列,接受未处理的请求到来,使其排队,并且为了减少链接数据库的时间,可以提前创建好一批链接数据库的链接,也就是所谓的连接池,让请求一但到来就可以直接访问数据库,而不用再链接数据库了;
根据服务器的设计原则:伸缩性可知,我们的DAL(数据库访问层)可以部署在任何地方,可以和我们的服务器部署在同一台机器,也可以作为单独机器使用;
数据库还有一个请求超出响应事件的问题:最简单的是假如我的服务器处理请求响应时间为5秒,处理1000个请求1秒,而你请求连接到来有10000个在请求队列排队,也就是说,按理论处理也要花10秒,而我们系统时间只有5秒,并发能力也就是5000,并没有达到10000个连接到来都能够及时处理;
如何减少我们数据库的压力呢?
解决办法 1:将请求的数据主业务逻辑在服务器中进行处理;而我们数据库做的只是简单的辅助业务逻辑处理,因为服务器的计算能力是比数据库计算能力好的,所以这种方式是可行;但是这也仅仅很有限的出来罢了;
解决办法 2:增加一个新的缓存层,把从用户读取的数据,作为响应返回给客户端同时也缓存一部分到我们的缓存层中;那么下次客户端请求时候,不用去访问数据库,而直接访问缓存层的数据即可;但是也会导致一个问题,缓存同步和更新的问题,到底在什么时候?
一种办法就是,给缓存设置一个超时时间,但是这种方式实时性不高;
另一种办法就是,一但我们改写数据,直接访问数据库的数据,对其进行更新,然后在缓存更新后的数据到数据库,这种方式实时性比较高;
**假如我们的缓存内存不足如何解决?**使用缓存换页技术,将不活跃的数据换出到磁盘即可;如常见的LRU算法;
一般来说,缓存层,不需要自己实现,使用我们的NoSQL,非关系数据库,如Redis来帮助我们完成;当然服务器的伸缩性告诉我们,该缓冲层是可以灵活部署在任何主机上的;
即使上面使用那么多方式来对我们的数据库进行优化,但是还存在不足;
假如有海量的请求连接都来访问数据库,那么此时数据库依旧会出现性能瓶颈;
对于写操作来说,每次写的请求都要进行上锁,而导致后续读的请求无法被处理,只能阻塞;
所以基于以上原因:我们提供的方式就是读写分离数据库;分析我们的数据库访问请求是读的多还是写的多;设置只用于读的数据库,和只用于写的数据库;当我们有访问数据库的请求连接到来,给我们的连接池进行负载均衡式的选择哪台数据库进行操作;
一但有写的请求发生,在写的数据库进行修改后也要同步的更新到读的数据库操作;
***
自此,数据库的并发能力大大的提升并且解决;
现在我们要解决的问题是:如何增加服务器的并发能力;
- 很明显我们的服务器很灵活,可以部署多台服务器,以增加及其来增加并发能力;
- 于此同时,每台服务仅仅处理某一个或者几个APP的业务逻辑,这样也可能增加并发能力;
- 再着我们可以搞一个任务服务器,可以负载均衡式选择我们的业务逻辑服务器进行处理,并检测我们业务逻辑的负载情况,他们直接通信可以采用简单的HTTP进行通信即可;
- 与此同时,业务逻辑的服务器,也可以主动的向任务服务器发起请求获取任务,当业务逻辑服务器空闲就主动发起请求获取,但是这也有确定,增加了任务服务器的实现难度;
- 任务服务器也需要有故障转移的,也就是说即使奔溃了,也可以转移到其他任务服务器继续提供服务;
服务器四大性能杀手
- 数据拷贝–内核拷贝数据到用户,用户拷贝到内核–>解决办法,增加缓存机制;
- 环境切换–上下文切换,导致开销,是否使用多线程还是单线程做服务器处理请求;单核CPU–>不应该使用多线程,最好使用状态机编程,一个线程执行一定的时间,即使没处理完,就标记为另一个状态,使得其他进程可以执行;类似内核的进程切换,每个进程都有一定的时间片,这个方式处理的思想;对于多核心CPU,使用多线程可以最大程度的发挥性能,提升效率,但是线程切换也是问题,所以创建线程数量应该根据场景设定;
- 内存分配—>减少平凡的像OS申请内存,可以搞内存池的方式;
- 锁竞争–>减少锁的使用;