一 服务器分类从软件性能角度,高性能服务器分:cpu密集型服务器/IO密集型服务器
(1)CPU密集型:该类服务器没有对io的访问/没有同步点,性能瓶颈在于对cpu的充分利用。典型的如转发服务器/代理服务器/协议转换类服务器/分布式总线服务器等。(2)IO密集型:该类服务器存在对cache/db/硬盘等的同步访问,或者对fcgi/其他服务器等的同步访问。简单说有同步访问点的均归属此类服务器。当前硬件基础下,有同步操作的服务器,性能瓶颈均在同步点的返回快慢上,而非cpu。二 网络层机制对 上述两类服务器,均需要同样高效的网络层机制。当前高效的网络层也就是大家熟知的iocp/epoll/kqueue/port/dev.poll等,在 各个os下使用宿主os推荐的高效网络层机制,任何通过其他机制绕过这些机制的做法都不可能达到最好性能。这里推荐下boost.asio,文档齐全,示 例丰富,学习曲线平缓。三 CPU密集型服务器设计(1)单进程单线程是改类服务器的本质特征。整个进程只存在一个线程,所有代码均运行在同一个线程中,均顺序执行,任何地方不需要加锁。由于网络线程的存在,实际上该类程序的唯一线程就是网络线程,以linux为例,就是epoll线程。在多核情况下,fork和cpu个数相同的进程数并且如果可能使用sched_setaffinity类函数将进程和cpu绑定。以充分利用多核性能。该类服务器的代表:tuxedo/nginx.(2)单进程多线程,但多线程均完成同样的功能,彼此之间互不依赖/互不影响 ,这是该类服务器的变体。单 进程单线程无疑是该类服务器最理想最完美的实现。但有时候为了简化部署,简化业务上报,业务自检,统一日志,尤其是统计类日志/配置动态生效等附加功能考 虑,不得已牺牲少许性能而将上述“单进程单线程,fork多个充分利用多核”方案改造为“独立多线程充分利用多核”方案。该方案中,多线程中的各个线程仍然是顺序执行,任何地方不需要加锁,均为独立的网络线程。相对方案(1), 该方案编程更复杂,而linux下线程调度又不如进程高效,整体看为方便性牺牲了少许性能。该类服务器的代表:我们的协议转换网关/分布式总线服务器等。(3)高效算法优化耗时较多算法/挑选合适容器,完成固定任务,尽量减少cpu的运算量。(4)错误设计:区分网络线程/业务线程,将业务线程根据业务特点划分各个线程阶段。对 cpu密集型的服务器来说,关键在于充分利用cpu,尽量减少无用代码的执行。引如中间处理线程,意味着引入锁切换/内存复制/更多无效代码,不可否认, 在已有协议栈情况下,根据业务特点化分线程可以简化编程。单纯的单一线程意味着更复杂的编码,尤其是涉及到更多中间状态时。在该场景下,有位牛人,对线程的点评:“线程是给那些不能将程序执行序转换成状态机的笨人用的” 这句话真是再合适不过了。四 IO密集型服务器设计(1)网络层多线程,中间线程按照业务特点设定,同步点操作使用多线程同 步点使用多线程是该类服务器的本质特征。在同步操作的返回时间不能由本服务器控制的前提下,本服务器所能做的也就只能是加多线程数,提供同步并发数。线程 数的最优配置取决于网络层入口并发数以及同步操作返回的时间。简单划分可以网络线程数=cpu个数/2.同步点线程数还取决于同步操作的代价,若为廉价的 cache操作,则可适当增多,若为昂贵的db操作,则要根据可以分配的连接数决定。(2)减少人为产生的同步点尽量减少访问其他系统使用同步接口。(3)优化同步点根据同步操作的特点优化: 异步/增大缓存/批量等。五 内存操作/锁机制/内核态用户态切换/日志操作(1)内存操作内存申请:减少内存动态分配,推荐tcmalloc内存复制:CPU密集型,必须的内存复制:(a)网络读:处从内核态复制到用户态,仅1次 (b)网络写:异步内存复制/用户态到内核态 ,仅2次 IO密集型,内存复制非关键点。(2)锁机制 CPU密集型:尽量无锁. IO密集型: 非关键点(3)内核态用户态切换两类服务器均相同,尽量减少内核态/用户态互相切换:每次调用系统调用尽可能读取更多字符/仅可能减少不必要的系统调用(去除不必要的调用/通过缓存机制减少调用次数)。(4)日志操作 略六 进程vs线程vs协程进程和线程(略)
协程:和进程/线程这种cpu调度单元不同,它更多是线程内对象之间一种调度理念的优化。协程对象有自己的堆栈,可以通过直接跳转直接转换执行点,减少了内存寻址操作。它特别适合用来优化线程内的某些基础组件,包括:状态机/调停者模式(或者线程内队列)。在CPU密集型服务器的设计中,说道“线程是给那些不能将程序执行序转换成状态机的笨人用的”,而有了协程,我们有了一种新的简化编程的方法。将协程用于网络层,可以手动实现类似select的功能,用于多对象参与的复杂中间状态,可以简化编程。
但从整体性能角度看,协程则是鸡肋的存在,从几年前出现boost.Coroutine,到现在该项目停止开发,boost引入更多其他方案asio/mpl/statechart,协程一路蹒跚。七 总结在当前硬件体系架构下,服务器性能的关键仍然是传统的cpu/io/memory.cpu密集型的服务器,需要最大限度充分利用所有cpu,以及尽量少的进行内存申请/内存复制。IO密集型服务器,需要最大限度提高io能力,为达到该目的,可以在非同步线程牺牲对cpu的利用率/牺牲对memory的高效使用,一切为提高io并发能力服务。八 后记特别感谢张杰同学代替我编译探测程序代码,让我有时间码点文字。