• 关于服务器编程的笔记


    1. 把进程比作是人,每个人有自己的记忆(memory),人与人通过谈话(消息传递)来交流,谈话既可以是面谈(同一台机器),也可以在电话里谈(不同的服务器,有网络通信)。而面谈和电话谈的区别在于,面谈可以立即知道对方是否死了(crash,SIGCHILD),而电话谈只能通过周期性的心跳来判断对方是否活着。

    2.设计分布式系统时可以采取”角色扮演“,团队里的几个人各自扮演一个进程,人的角色由进程代码决定(管登陆的,管消息分发的,管买卖的等等),每个人有自己的记忆,但不知道别人的记忆,不知道别人的想法,只能通过交谈。然后需要思考:容错(万一有人突然死了);扩容(新人中途进来);负载均衡(把甲的活给乙做);退休(甲要修复bug,先别派任务)

    3. 在高性能的网络程序中,使用最广泛的恐怕要属”non-blocking IO + IO multiplexing"这种模型,即Reactor模型。程序的基本结构是一个事件循环(event loop),以事件驱动(event-drivern)和事件回调的方式实现业务逻辑。

    4. Reactor模型优点是编程简单,效率不错,非阻塞可以提高并发度和吞吐量,对于IO密集的应用是个不错的选择,lighttpd是个例子。缺点是因为回调函数时非阻塞的,对于涉及网络IO的请求响应式协议,容易割裂业务逻辑,使其散布于多个回调函数中,相对不容易理解和维护。

    5. 多线程服务器的编程模型:1. 每个请求一个线程,使用阻塞式IO,以前的Java网络编程方式 2. 在1的基础上使用线程池,3. 使用非阻塞IO+one loop per thread (IO 复用),即现在的java NIO方式,4. Leader/Follower,半同步半异步(工作中用到的)等高级方式

    6. one loop per thread的用法是,程序中有多个event loop,但是每个线程至多有一个event loop。线程数目固定,可以在程序启动时设置,event loop代表线程的主循环,需要在那个线程下面干活,就把timer或者IO channel(TCP连接)注册到哪个线程的loop中。对实时性有要求的可以单独用一个线程,数据量大的可以独占一个线程,并把数据处理任务分摊到另几个计算线程中(线程池)

    7. 多线程程序对event loop提出了更高的要求,那就是线程安全,要允许一个线程往另一个线程的event loop里塞东西,这个loop必须是线程安全的。

    8. 对于没有IO而光有计算任务的线程,可以用任务队列或生产者消费者队列(本质都有一样,看传递的是task还是object),BlockingQueue<T>是多线程编程的利器。

    9. IPC通信方式中,TCP是双向的,Linux的pipe是单向的,而且进程要有父子关系才能用pipe,这些都限制了pipe的使用。在收发字节流这一通信模型下,没有比Socket/TCP更自然的IPC了。当然,pipe也有一个经典应用场景,那就是写Reactor/event loop时用来异步唤醒select(或等价的poll/epoll_wait)调用(在Linux下,可以用eventfd代替,效率更高)

    10. TCP port有一个进程独占,且操作系统会自动回收(在进程结束时关闭),这说明,即使程序意外退出,也不会给系统留下垃圾,程序重启之后能比较容易恢复,而不需要重启操作系统(用跨进程的mutex有这个风险),还有一个好处,既然port是独占的,可以防止程序重复启动,后面那个进程抢不到port,自己没法初始化了。两个进程通过TCP通信,如果一个崩溃了,操作系统会关闭连接,另一个几乎立刻就能感知,可以快速failover,当然应用层的心跳也是必不可少的。另外,TCP协议的一个天生的好处是“可记录,可重现”。TCP还能跨语言,服务端和客户端不必使用同一种语言。

    11. 服务器开发用一句话形容就是:跑在多核机器上的Linux用户态的没有用户界面的长期运行的网络应用程序。我提倡用多线程,并不是说把整个系统放到一个进程里实现,而是指功能划分之后,在实现每一类服务进程时,在必要时可以借助多线程来提高性能。

    12. 服务器程序的一个基本任务是处理并发连接。现在主要有两种方式:当“线程”廉价时,一台机器上可以创建远高于CPU数目的“线程”,这时一个线程只处理一个TCP连接,通常使用阻塞IO,如Python gevent,Go goroutine, Erlang actor,这里的线程有语言的runtime自行调度。当线程很宝贵时,一台机器上只能创建与CPU数目相当的线程,这时要处理多个TCP连接上的IO,通常使用非阻塞IO和IO复用,这是原生线程。

    13. event loop有一个明显的缺点,它是非抢占的,假设事件a的优先级高于b,处理a需要1ms,处理事件b需要10ms。如果事件b早于a发生,那么当事件a到来的时候,程序已经离开poll调用,并开始处理事件b,事件a要等上10ms才有机会被处理,总的响应时间是11ms。这等于发生了优先级反转。这个缺点可以用多线程克服,这是多线程的主要优势

    14. 多线程并不是具有绝对意义上的性能优势,如果用很少的CPU负载就能把IO跑满,或者用很少的IO流量就能让CPU跑满,那么多线程也没有特别优势:对于静态web服务器,或者FTP服务器,CPU的负载较轻,主要瓶颈在磁盘IO和网络IO方面,用多线程并不能提高吞吐量,因为IO已经饱和了

    15. 我认为多线程的使用场景是:提高响应速度,让IO和“计算”相互重叠。让多线程充分利用多个CPU的优势;线程间共享数据,即内存中的全局状态,如果使用多进程,那么进程间的状态同步会成大问题。为了尽快地响应各种事件,比如说它有多个TCP连接,在CPU不会跑满的情况下,那么采用2个或4个IO线程来处理8个TCP连接能有效较低延迟,特别是不同的TCP连接如果有数据转发更适合用多线程。监控的事件可以有优先级区别,放在不同的线程中处理。如果要异步地写log,需要logging有独立的IO线程。虽然线程数目略多于CPU的数目,但是这些线程很多时候是空闲的,可以依赖进程调度来保证可控的延迟。

    16. 多线程如何降低响应时间?1.多个IO线程:比如两个用户同时发出请求,并且他们的输入都比较大,那么这两个用户刚好分配在两个IO线程,就可以并行执行 2. 计算密集型:如果每个TCP连接都需要比较长的计算时间,那么使用一个IO线程,多个计算线程(线程池)可以充分利用多CPU并发计算。

    17. 单个event loop处理1万个并发长连接并不罕见,一个multi-loop的多线程程序应该能轻松支持5万并发连接,thread per connection并不适合高并发场合,其scalability不佳,one loop per thread的并发度足够大,且与CPU数目成正比。

    18. 多线程程序如何让IO和计算相互重叠,降低latency?如果服务线程等在IO上,让CPU闲置,会增加相应时间,比如说logging要使用一个logging线程,负责写磁盘,通过一个或多个blockingqueue对外提供接口。别的线程要写日志时,先把消息准备好,往queue里一塞,基本不用等待。这样服务线程和logging线程的磁盘IO相互重叠,降低了服务线程的响应时间。

    19. 为什么第三方库往往要用一个单独的线程?如果在我们的Reactor event handler里调用第三方库的阻塞函数,那么latency就堪忧了。我们可以为第三方库阻塞函数做一次线程封装,比如说查询数据库,然后异步等待结果。

    20. 选一个多线程的进程,还是多个单线程的进程?在其他体检相同的情况下,可以根据工作集的大小来取舍,工作集是指服务程序响应一次请求所访问的内存大小。如果工作集较大,那么就用多线程,避免cache换入换出,影响性能;否则就用单线程多进程,享受单线程编程的便利。

    21. 事件的分发:一个对象,由于它的原理具有和外部的交互能力,能否产生事件比如可读可写的基本事件,通过一个分发器分发给不同的回调去处理,这是异步编程的思想。比如没有TCP连接,有可读可写的事件。分发器是Eventloop,而channel这个连接在分发器上的锚点,通知callback工作,这叫基于TCP连接的事件分发。

    22. 确认公开接口和内部实现,在公开接口中,哪些是值语义,哪些是对象语义。在公开接口中,由用户和库共同控制的对象中(比如由库返回的指针和由用户给库传递的指针),是否要用shared_ptr来管理。哪些是生命期只有用户控制的,这些都要弄清楚。

    23. 基于事件的非阻塞网络编程时编写高性能并发网络服务程序的主流模式,需要转换思维模式,变主动为被动,处理函数要避免阻塞。

    24. TCP网络编程的三个半事件:建立,断开,消息到达和消息发送(但不考虑送达)。难点有:1. 关闭连接前能否保证消息已发送 2. 建立能否retry 3. 以什么方式关注事件(电平还是边缘) 4. 发送缓冲设计,防止消息堆积 5. 接收缓冲设计,包划分

    25. 不同的网络库的应用场景可以分为两种:1. 高吞吐量 2. 高并发性。分别对应TCP的长连接和短连接。高吞吐量是为了传输的效率,即reader最快能读多快,高并发性是为了IO事件处理的效率,在很多的连接,同时很多活动连接情况下,处理IO事件的有多快。高吞吐量可以通过每秒收到的数据量作为衡量指标,比如说pingpong可以轻易制造数据把带宽跑满,高并发性是在巨量连接情况下处理同样数目的事件需要的时间作为衡量指标,比如说击鼓传花可以轻易制造IO事件。

    26. [Me] 一个模型的瓶颈主要是在Event loop的时间监听和分发的速度,这会影响高并发下的表现情况。另一个瓶颈是底层缓冲的设计,这会影响高吞吐量下的表现情况

    27. 要做一个并发服务器,可以同时服务多个客户连接,并且要是多线程的,这样才能发挥多核硬件的能力,并且要做好负载均衡。注意并发和并行的区别,并发是指多个连接都能响应,但是在IO复用的时候有延迟,并行是指同时进行,一般就是指不同线程跑在不同的CPU核上。

    28. 并发模型要考虑这些因素:1. 多进程 2. 多线程 3. 阻塞IO 4. IO 复用 5. 长连接 6 并发性7. 多核? 8. 开销 9 互通10. 顺序性 11. 线程数。 一个网络库要干三件事 1. 接收新连接, 2. 网络IO 3. 计算任务, 接收新连接肯定使用一个IO线程(连接线程),至于网络IO (网络线程)和计算任务(计算线程)则可以使用单线程或多线程来做,甚至直接在接受连接的IO线程做

    29. 并发模型少不了reactor模式,它通过IO复用并且通过回调函数来处理IO事件。在reactor基础上可以扩展 1. 一个线程reactor IO,然后所有事件处理通过threadpool,这样在thread poll里可以做一些高CPU,或者阻塞的工作, 要注意的是TCPconnection有跨线程问题,比如多个IO事件来了,最后要处理,处理结果返回的顺序性不一样 2. 多个线程做reactor IO,当前先用一个主线程做TCP accept reactor IO,其他只做read/write reactor IO, muduo使用这个方案,可以指定参数设定多少个后台线程做read/write reactor IO,前台的线程只做TCP accept reactor IO. 3. 同时使用1和2,即多个reactor IO线程和多个计算线程

    30. 以上并发模型可以类比银行柜台办理业务。连接线程相当于处理开户,和网络线程相当于处理业务,可以多次请求,计算线程相当于银行柜台(处理其中一次业务)。1. 最普通的reactor,只有一个门和柜台,开户(一次,户就是连接)和每次办理业务(可能多次,业务就是数据)都进同一个门,里面有人办理业务或者开户的,后来的人要在门外等(poll)2. reactor+计算线程s,一个门+多个柜台,开户和每次办理业务还是都进同一个门,不同的是有多个柜台,放进去快,但是要等空闲的柜台呗(poll容易,通过计算队列)3. 连接reactor + 网络reactor,现在最外面的门只办理开户,然后给你安排固定每次进出办理业务的门,每个门后只有一个柜台,以后就从办理业务的门进出,可以为VIP提供专享服务 4.连接reactor + 网络reactor + 计算线程,这个最复杂,开户一个门,办理业务指定门,下次办理业务,一次业务还有多个柜台可以选,VIP的话等的时间就更短了。

    31. 在非阻塞网络编程中,发送的代码比接收的代码难写。不论发送和接收,应用层(相对于操作系统)的buffer都是必须的。对发送来说,操作系统可能接收不了那么多数据,数据应暂时缓存到应用层,而网络库负责操作缓存,对应用程序来说是透明的,应用程序此时可能已经发起一个关闭连接的请求。对于接收来说,因为操作系统返回的数据可能是不完整的,数据也应缓存起来,直到一条完整的消息收到后再给应用程序。

    32. non-blocking(非阻塞)和IO multiplexing往往就是一起使用,单用任何一个都不现实。

    33. Level trigger的编程方式更简单,更不容易漏掉事件

    34. codec是encoder和decoder的缩写,它的基本功能是TCP的分包,确定每条消息的长度,为消息划边界。

    设计?

    1. 如何设计应用层缓冲区?

    一方面希望减少系统调用,一次读的数据越多越划算,那么应该设计一个大的缓冲区。另一方面希望减少内存占用,预分配的内存小一些比较好。可以利用readv来解决这个问题。本质上是用了一块公共的空间,所有的线程都能访问,因为访问它的几率不大,争用也不多。

  • 相关阅读:
    OCP-1Z0-053-V12.02-668题
    Oracle DB 监控和优化RMAN
    OCP-1Z0-052-V8.02-123题
    OCP-1Z0-053-V12.02-151题
    OCP-1Z0-053-V12.02-66题
    OCP-1Z0-053-V12.02-163题
    OCP-1Z0-052-V8.02-51题
    OCP-1Z0-052-V8.02-53题
    ostringstream、istringstream、stringstream
    OCP-1Z0-053-V12.02-205题
  • 原文地址:https://www.cnblogs.com/qiangxia/p/4482197.html
Copyright © 2020-2023  润新知