1)如何设计如何扩展
2)什么是高并发
(1)任务:完成某个功能的一个一个目标任务,服务器程序也是不例外的。
(2)CPU核心:完成具体任务的,是CPU核心 + 周围的外设(读写磁盘IO、网络IO) + 内存
(3)每个CPU核心可以同时调度不同的任务,如:I7双核:同时调度2个可以调度的任务;
提升单个核心主频(摩尔定律) + CPU的数目(不同核心处理不同的任务),从而处理性能;
服务器专门用的机器:32多个核心,同时调度32个不同的任务 “同时执行”;
OS-->分配核心--》调度可以被调度的任务(线程),一个线程--》任务--》OS会分配核心去调度执行它。
我们有几个核心,你就可以调度多个线程
(4)高并发:
处理一个任务,一个核心,我们需要0.3s。 假如,双核,1s可以处理多少个业务呢?
并发数量:1个CPU核心处理一个任务需要0.25s,那么一个核心可以处理4个任务;4个任务*2 = 8个任务, 1s可以并发8个任务。
例子1:但是有时候,我们无法接近个任务,什么情况下我们无法做到8个任务呢:
如果我们把这些任务放到了一个OS可以调度的线程里面:
main(){
while(网络事件){
...// 1s只能处理4个任务,这个代码永远只有1个核心来处理这个任务。--》1s最多是处理4个任务。
}
}
同样的硬件,不同的代码,写出来的服务器的处理能力就不一样。由于我们的设计没有做到--》50%的CPU在休息。
结论: 我们要使用多线程 or 多进程,增加我们的os可调度的单元,能发挥我们的多核的优势。
例子2:处理一个IO的任务,CPU把内存的数据--》磁盘驱动--》磁盘IC数据持久化到我们的磁盘--》数据就存到这个磁盘了;
WriteFile("内存数据", 文件)--》CPU--》内存数据--》磁盘的驱动(比较快0.001s完成)--》等待磁盘IC控制磁头,将内存的
数据持久化到硬盘上。
假设WriteFile需要0.25s,CPU是0.001s干完,其余0.249s就无事可干。
假设我们的CPU是一个核心,任务WriteFile 0.25 = 0.001 + 0.249(等待磁盘处理的时间),CPU的实际工作只有0.001s,
CPU 0.249s可以做其它事情,我们的代码等在了IO上面。
我们并发的业务: 1/0.25s = 4次,CPU都是等待,我们本来可以让CPU做其它事情,但是你让它等待了。
不让它等待:
(1)加多线程、多进程,OS调度CPU核心做其它的任务--》提供了业务并发。
(2)采用异步:不傻等WriteFile完成,而是提交完0.001s,马上返回去做别的。磁盘你去存就可以了。
异步也是增加了线程。 和多线程本质并没有其它的区别。
结论:当我们等待外部设备处理完成后,才能继续下一个任务的时候,我们要提供更多的调度单元,让OS去调度CPU去完成其它任务。
就跟快递员一样,不用傻等,而是去给其它人去派送。
我们就可以多个线程,不要让CPU没有任务可做。
代码问题、、、等待外设。。。这2个例子。
(5)一定不要傻等在一个任务上,你要做一个可以调度的单元,让OS去调度其它任务--》并发:
10个快递--》0.25 0.001--》0.249
0.001-->A
0.001-->B
0.001-->C
0.001-->D
0.001-->E
0.001-->F
...
0.001-->10人-->0.001s
那么:0.01 + 0.249--》0.259s就可以完成调度。 而1个一个的派送,则需要2.5s
(6)安排足够的任务(线程)--》给足够的核心去调度。
32核心: 就安排了6个核心去调度,那么大部分核心都在睡觉。
(7)增加调度单元:
多线程调度:根据核心数目,来运行我们的代码,16核心和32核心,是一样的。
多进程来部署:让不同的东西部署到不同的进程上。
3)服务器多线程架构
多线程去处理网络请求, 成千上万个客户端。
哪个线程去处理哪个事件。
别再派送一个快递的时候,别的快递都傻等到这里了。有核心,没有任务。
4)多台服务器,如何部署
(1)内部快展
地图A
地图B
...
地图副本服务器
假设只能买到4核心,
代码不用变。按照逻辑功能,划分到不同的分区。
机器1:通用服务
机器2:地图1服务
机器3:地图2服务
返回对应的连接。 让不同的机器跑不同的任务。
分区调度器--》单服。 足够多的任务可以被OS调度到。 客户端连接的不同的地址的服务器而已。