赞
踩
【Linux多线程服务端编程】| 【01】线程安全的对象生命期管理笔记
【Linux多线程服务端编程】| 【02】线程同步精要
【Linux多线程服务端编程】| 【03】多线程服务器的适用场合和常用编程模型
【Linux多线程服务端编程】| 【04】C++多线程系统编程精要
Linux系统编程 | 【03】进程、环境变量、IPC
Linux | Linux中的线程、互斥量、信号量的基本使用
面谈
(同一个服务器)通信,可时刻
知道对方是否已死,电话谈
(不同一个服务器通信)通过心跳包
周期来判断对方是否存活;
【设计分布式需要考虑】:
容错
:突然有服务器宕机;扩容
:增加新发服务器;负载均衡
:将一个服务器的工作移到另外一台;退休
:一个服务器要修复bug,等它处理完任务就重启;【Reactor模型】 在高性能网络程序中,最广泛non-blocking IO + IO multiplexing
,适用该模型的有;
【Reactor模型】 另外一种模型,常见的有:
【基本结构】:事件循环、事件驱动、事件回调(函数必须为非阻塞)方式来实现;
【优点】:效率高,易编写
,可用于读写socket、连接建立、DNS解析等都可用非阻塞
来提高并发,对于IO密集
不错;
【常见】:
请求
创建一个线程,适用阻塞IO
,伸缩性不佳;线程池
,适用阻塞IO
操作,比第一种性能略提升;non-blocking IO+IO multiplexing
,默认使用这种;默认选择第三种,即non-blocking IO + one loop per thread
来编写;
该模型下,每个IO线程
都有一个event loop
即Reactor
,用于处理读写
和定时
事件;
【优点】:
线程数目自设置基本固定
, 不会频繁创建与销毁
;
可方便再线程间调配负载
;
IO事件发生的线程是固定的,同一个TCP连接不必考虑事件并发;
Eventloop为线程的主循环
,若要指定线程干活,即将timer
或IOchannel
注册到哪个线程的loop;
对实时性
有要求、数据量大
的connection可单用一个线程;
将数据处理任务分摊
到另外几个线程中;
其他次要的辅助性connections可共享
一个线程;
对于non-trivial服务端
程序,一般会采用non-blockingIO + IOmultiplexing
,每个connect/accept
都会注册到某个event loop
上,每个线程至多只有一个event loop;
需要线程安全
,允许一个线程给别的线程里塞东西;
适用阻塞队列
针对于没有IO只有计算任
务的线程;
【简单实现】:
可参考muduo中的无界BlockingQueue和有界的BoundedBlockingQueue及Intel Threading Building Blocks的concurrent_queue<T>;
【推荐使用】:one(event) loop per thread + thread pool
;
event loop
即IO loop用作IO multiplexing,配合non-blocking IO
和定时器
;thread pool
用来计算
,处理任务队列或生产者消费者队列;特殊任务的线程
,如loggin;首选Socket
,主要是TCP;
【好处】:可以跨主机
,具有伸缩性;
【pipe应用场景】:写Reactor/event loop
时用来异步唤醒epoll_wait
调用;
TCPport由一个进程独占,且操作系统会自动回收监听端口
和socket文件描述符
,进程结束时会关闭所有文件描述符;
独占
可防止程序重复启动,抢不到port;【与IPC相比】:TCP可记录、可重现、可跨语言、可再生;
tcpdump
和Wireshark
解决进程间协议和状态争端,可分析性能;tcpcopy
来压测;消息格式
,如Google Portocol Buffers;通常一个分布式系统运行再多台机器的多进程组成,进程间采用TCP长连接通信
;
【长连接好处】:
netstat -tpna | grep :port来查看服务客户端地址
,再客户端上用netstat
或lsof
可找出哪个进程发起的连接;这样可在迁移服务的时候能防止出现outage
;(TCP短链接及UDP没有该特性)接收
和发送队列
的长度易定位网络或程序故障;
netstat -tn
打印的Recv-Q
和Send-Q
应该在0左右
;Revc-Q
不变或持续增加,则服务进程处理速度可能变慢,死锁或阻塞
;Send-Q
不变或持续增加,则可能服务器忙,来不及处理,或网络中丢包、掉线
;【处理并发连接】
高于
CPU数目的线程,此时一线程只处理一个TCP连接(半个),一般使用阻塞IO
;相当于
CPU数目,此时一个线程要处理多个TCP连接上的IO
,通常使用非阻塞IO和IO多路复用
;【在处理并发连接时,要充分发挥硬件资源,当一台多核机器提供一种服务或执行一个任务,可用模式】:
单
线程的进程;多
线程的进程;多个
单线程的进程:
多个多
线程的进程;【使用速率为50MB/s的数 据压缩库、在进程创建销毁的开销是800μs、线程创建销毁的开销是 50μs的前提下,考虑如何执行压缩任务:】:
偶尔压缩1GB
的文件,时间是20s,则一个进程
去做是合理的,因为进程启动和销毁的开销远远小于实际任务的耗时
;经常压缩500kB
的文本,预计运行时间是10ms,那么每次都起进程似乎有点浪费了,可以每次单独起一个线程
去做;频繁压缩10kB
的文本数据,预计运行时间是200μs,那么每次起线程似乎也很浪费,可直接在当前线程搞定
;也可以用一个线程池
,处理任务,避免阻塞当前线程
;【必须使用的场合】:
fork
;exec
,变为另一个程序或子程序运行fastcgi程序,或看门狗进程
(该程序必须为单线程);当前程序
;CPU占用率
;
【单线程的缺点】
非抢占式
的,若a优先级高于b,a需要1ms来处理,b需要10ms;若b早于a发送,则当a事件唤醒时,程序已执行poll,并处理b,那么a只有等10ms后才有机会被处理,该情况为优先级反转
,可多线程来解决
;【多线程下性能不一定能提升】
在很少的IO流量
下,让CPU跑满,用多线程无法提高性能
;
磁盘IO
和网络IO
;一个线程即可撑满IO,即在硬件容量上已达饱和,无法提高;3a模式
;主要提高响应速度
、让IO
和计算重叠
、降低潜在因素
,可提高平均响应性能;
【满足条件】:
多个CPU
可用,单核机器上多线程没有性能优势;共享数据
,即内存中的全局状态,若没有共享数据,用模式3b;可修改
,不是静态的常量表,若数据不能修改,则可在进程间用共享内存,适用模式3;优先级
差异,可用专门的线程来处理优先级高的事件,防止优先级反转;相对的计算量
,不是逻辑简单IO bound或CPU bound;异步
操作,如日志,往文件或服务器都不应该阻塞;可预测
的性能;责任与功能
;每个线程逻辑任务单一,不能都塞到一个event loop;【机群管理软件】:Linux服务器机群,8个计算节点,1个控制节点,需要3个程序:
master
,监视
控制机群状态
;salve
,负责启动和终止job
,并监控本机的资源;【其中】:
看门狗进程
,会启动别的job进程,需要用单线程
,不需要占太多资源;模式2
的多线程程序:
模式1
,则浪费87.5%的资源;共享且可变的若用模式3
,则进程间同步会是大问题,若用共享内存,则成了多进程的多线程,一个进程阻塞或crash,则其他进程死锁
;延迟
,尽快响应事件
,不会出现IO/CPU跑满;优先级
区别,一个程序正常运行结束核异常处理的优先级不同,计算节点的磁盘满
和机箱温度
优先级也不同,若单线程,则会出现优先级反转
;TCP连接
,则master采用2或4个IO线程来处理8个TCP
连接可能有效降低延迟;异步
往本地硬盘写log,需要要求log lib有自己的IO线程;读写
数据库,则数据库连接第三方lib可能要有自己的线程
,并回调master代码;用多线程降低客户响应事件
,即可再用2个IO线程专门处理和clents的通信;广播推送机群的状态
,即用户不需主动轮询;可用单独线程实现
;【TCP聊天服务器】
聊天(人与人、机器与机器)
,服务器特点是并发连接之间数据交换,一个连接收到的data转发给其他多个连接,故只能用模式1、2
;
数据交换
,则模式1,CPU快
,单线程应付几百个连接也可以;关键字过滤,黑名单,防灌水
等每项功能都会占CPU,则需要考虑模式2
;若顺序处理可能导致消息发送延迟
,则将这些任务分散到多个线程中;【服务器分类】:
IO线程
,主循环在IO multiplexing,阻塞等待epoll_wait上,也处理定时
事件,也可处理消息编码解码
等;计算线程
,主循环在阻塞
队列,阻塞等在条件变量
上,一般为线程池
中的线程,不涉及IO,避免阻塞
;第三方库
所用的线程,如loggin,database;Linux能同时启动多少个线程
多线程能提高并发度吗
并发
连接,则不能;thread per connection
不适合高并发场合,其可扩展性不佳,one loop per thread
的并发度足够大,且与CPU数目成正比
;多线程提高吞吐量吗
计算密集型服务不能
;线程池
,大小需要满足阻抗匹配
原则;
1/5
,则用线程池是合理的;等待IO
,则为了进一步提高吞吐量,则需要使用其他模型,如Proactor;多线程能降低响应时间?
充分利用
多核资源;【例1 计算密集型】:多线程处理输入以memcached服务端
为例,memcached一次请求响应为以下步骤:
串行执行
;输入线程(4个)
,并在建立连接时按循环法把新连接
分派给其中一个输入线程
,即one loop per thread
模型;即第一步可多线程并
,提高速度,第二步需要锁
,还是单线程,可改进;【例2 计算密集型】:多线程分担负载,若我们要左一个求解Sudoku的服务,这个服务程序在9981端口接受请求,输入一行81个数字,输出为填好后的81个数字(1~9)若无解,输出NO\r\n;
线程池
来计算,能达到的吞吐量上限为800qps;多线程程序如何让IO和计算互相重叠,降低延迟
【基本思路】:把IO操作通过阻塞队列
交给别的线程去做;
【例1】:日志,在多线程服务器程序中,日志很重要,仅考虑log file;
在一次请求响应中,可能要写多条日志信息,而若用同步
方式写文件,多半会降低性能
;
慢
,线程阻塞在IO
上,让CPU闲置,增加相应时间;加锁
,让线程等待
,降低并发度;单独
的loggin线程,负责写磁盘文件
,通过一个或多个阻塞队列对外提供接口;当别的线程要写日志时,先把消息准备好,往队列中加,即不用等待;【例2】:memcached客户端,若我们用memcached来保存用户最后的发帖时间,则每次响应用户发帖的请求时,程序要去设置一下memcached里的值,若这里用同步IO,则会增加延迟;
write-only idempotent
操作,不需要等待memcached返回操作结果,不需要在乎set操作是否失败,可借助多线程来降低响应延迟;
异步
操作,但也有缺点:每次write都要再线程间传递数据,若TCP缓冲区为空,则我们可以再本线程写完,不要交给专门的IO线程(Netty使用该做法);什么是线程池大小的阻抗匹配原则
T = C/P;T值上下浮动50%
;P<0.2
则T可以取固定值,则5xC
;C为分配给这个任务的CPU数目;还有其他的non-trivial多线程编程模型?
【Proactor】:若一次请求响应中要和别的进程多打几次交道
,则用该模型可提高并发度
,但代码繁杂,不能降低延迟
;
【例1】:一次HTTP proxy的请求若没有命中本地cache,则:
3次往返
,每次可能都要花几百毫秒:
线程池
,则线程数将会很多,不利于管理;event
,继续按Reactor
编程,即每次客户请求就不能用一个函数从头到尾执行完成,而要分成多个阶段
,并管理好请求的状态;模式2和模式3a该如何取舍
内存大小
来取舍;多线程
,避免CPU cache换入换出
,影响性能;Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。