简单的搭建一个高并发低时延系统

标签: 建一 并发 时延 | 发表时间:2012-06-02 07:01 | 作者:常高伟
出处:http://www.cnblogs.com/

首先声明一点:这里的“高并发”是相对的,相对于硬件而言,而不是绝对的高并发。后者需要分布式来实现,这里不做讨论。本文关注的是单机的高并发。

最近在做一个语音通信系统,要求在线用户2W,并发1K路通话。硬件是两台服务器,酷睿多核,4G内存,千兆网卡(我用过的最好的硬件,负担这些应该问题不大)。

系统的另一个指标是呼叫时延和语音时延。这是这个系统的关键。最终我们的系统拿到用户现场测试的时候,效果可能有点太好,对方测试不大相信。其实降低时延只要几个地方把握好了,应该问题不大的。这里总结一下。

 

1、 整体结构:

整体上采用控制与承载相分离的结构。控制部分负责流程的控制部分,包括流程的建立,处理,语音资源的管理等,是系统核心部分。承载部分主要负责语音处理,包括语音编解码,加解密,转发录音等。这样的好处是:1)降低系统的整体复杂度。2)提高系统的可扩展性。特别是如果用户数上去,这种结构更好扩展。

这在通信中其实就是一个典型的软交换结构。两台服务器,一台负责控制,一台负责承载。控制和承载之间通过网络通信。

控制程序是一个进程,可以管理多个承载程序。

2、 流程:

要降低时延,关键的一点是功能实现流程的设计。要减少不必要的环节和网元间的交互。数据能够一次通知就不要两次交互。必要的时候,为了时延,可以牺牲一点协议的标准性,使用私有协议完成(至少从目前看没有问题,这个系统是一个端对端封闭的系统。)。

3、开发语言:

控制层面使用的python来实现的。控制部分流程逻辑复杂,而python很擅长描述逻辑。本来有点担心python的运行效率,其实没有必要:整个系统的压力在承载,而不在控制部分,控制部分不会有太多的压力;另外,cpu够强悍,时延的瓶颈在I/O。况且,python也重用了我们之前用C实现的协议编解码库。

承载部分用C来实现。

4、 利用多核

利用多进程来利用多核。在承载服务器上,并行跑了两个进程,每个分别处理500路通话。也许线程切换成本更低,但是编程复杂度高。对于线程,我只用最简单的模型。

控制部分没有多进程,似乎利用不能这个服务器的多核,不过目前来看,还不需要,因为现在就能很好的满足需求。

5、 网络通信

承载服务器的压力中很大一部分来自于网络通信。按照我们的功能,1000路语音并发,意味着没20毫秒至少要处理1000个语音包(最恶劣的情况是2000个语音包,包括收发)。

Libevent开源,号称轻量级高性能,而且应用也广泛,也许是个不错的选择。不过在我看来还是有些庞大,很多特性(跨平台,多种通信模型)我都用不上。

更为关键的一点事,linux的epool接口足够简单,而且非常好用。接口中提供一个参数可以设置用户数据,这样我可以把一些数据包括函数指针放进去,从而很方便的构造一个事件驱动的网络模块。它能够保证代码足够简单。

6、 文件读写

整个系统涉及到文件读写的主要有两块:录音及日志。我们常用的文件操作接口都是阻塞式,进程(线程)会被挂起,等待读写完毕,然后在继续执行。我们都知道,对磁盘的操作要慢很多,所以这个地方是请求时延的一个瓶颈。

异步IO可以解决这个问题。参考资料: http://www.ibm.com/developerworks/cn/linux/l-async/。不过网上看到有人说AIO接口有bug。时间不多,没有时间深入研究,还是保守的放弃了这个思路。新技术有风险,使用需谨慎。

Libeio也应该是一个选择。参考资料: http://rdc.taobao.com/blog/cs/?p=1524,它是用线程池来模拟异步IO。问题是,我们的程序主要是写文件,而且一般不需要知道结果,在这种情况下使用libeio的必要性有多大?

我们最终的方案是参考libeio,直接为承载进程申请了一个线程来负责写文件。主线程负责语音编解码及转发,完全非阻塞,以保证低延迟。涉及的文件写操作,通过接口发送给另外一个线程调用阻塞IO接口来实现。线程间接口很简单,一块要写的内容加一个路径名。

7、 数据库操作

我们的数据库使用的是mysql。和文件读写一样,数据库操作也是请求时延的一个瓶颈。在整个流程中,我们会多次的读写数据。我们的做法是:系统启动后,将运行时用到的数据全部读到内存,后面直接查看内存。好在数据不大,这个工作也简单。如果涉及到数据的新增修改删除。则另外一个线程完成相关操作,再通知主线程更新内存。

最终的结果是,主线程是完全非阻塞的,涉及到阻塞的操作,全部移到另外一个线程中。两个线程不共享任何全局数据,只通过FIFO交互。

这个地方redis也许是可以考虑的一种选择,它的数据保存在内存,读写效率也非常好。不过相对来说还是有点复杂,而且还是nosql,我们的开发人员并不熟悉。“最小惊讶原则”不但适用于程序接口,也适用于系统。

 

经过所有的这些考虑和优化,可以基本达到目标,而且足够简单。

 

如何榨干服务器:

经过上面的一些优化,基本上可以满足用户的需求了。但我知道,还没有完全的利用服务器的能力(包括CPU,IO)。要进一步榨干服务器的能力,可以在承载服务器上将每个进程的处理能力扩大一倍,每个进程处理1000路。也可以考虑再多跑几个进程。

控制服务器没有充分的利用多核,可以考虑在控制服务器也运行两个承载程序。

这样下来,初步估计硬件不提高的情况下,注册用户数至少能够提高到6W,并发呼叫数目至少能够提高到3K。

提高绝对容量和并发:

业务特点不同,通信行业高并发的解决方案和互联网行业可能会有一些区别。可以想一下我们使用的电话系统,就是一个实例。它通过分布在全国各地的一个个用户归属的局端,再配合强大的路由能力,以及端到端之间非常标准的协议来解决。

采用类似的方案也可以提高本系统的容量和并发,不过,目前系统的容量已经可以满足我们公司市场几年内的需求,没有进一步提升的必要,还是保持简单的好。

 

总结:

很多时候,使用开源软件都是一个非常不错的主意,可以避免“重复发明轮子”。而且,它还有一定的诱惑:它可以为你的履历加分。

但是有的时候,你需要的可能并不是“轮子”,想想为一个滑板安装一个汽车轮是什么效果。

 

什么样的方案才是好的方案?

1、 满足现在的需求及未来50%的需求。当一个可预见的需求发生概率超过一半时,为它考虑可扩展时必要的。否则会过度设计而冲击简单性。

2、 保持简单。

 

最后,再次提一下 KISS原则,keep it simple and stupid !

本文链接

相关 [建一 并发 时延] 推荐:

简单的搭建一个高并发低时延系统

- - 博客园_首页
首先声明一点:这里的“高并发”是相对的,相对于硬件而言,而不是绝对的高并发. 后者需要分布式来实现,这里不做讨论. 最近在做一个语音通信系统,要求在线用户2W,并发1K路通话. 硬件是两台服务器,酷睿多核,4G内存,千兆网卡(我用过的最好的硬件,负担这些应该问题不大). 系统的另一个指标是呼叫时延和语音时延.

高并发

- - 开源软件 - ITeye博客
垂直扩展是一种用于增加单个ActiveMQ代理连接数(因而也增加了负载能力)的技术.默认情况下,. ActiveMQ的被设计成尽可高效的传输消息以确保低延迟和良好的性能. 默认情况下,ActiveMQ使用阻塞IO来处理传输连接,这种方式为每一个连接分配一个线程. 你可以为ActiveMQ代理使用非阻塞IO(同时客户端可以使用默认的传输)以减少线程的使用.

并发导论

- - 并发编程网 - ifeve.com
由于之前工作中的疏忽,在使用Java多线程并发的时候出了问题,遂决心全面学习并发相关知识. 写作本文的意图只是希望在写作过程中把想不清楚或是一时无法掌握的地方反复揣摩记录下来. 写作本文参考的各种资料较多,抱歉的是文末的参考文献中对一些叫不上名字或没有出处的资料文献并未列举出来. 由于本人是初入职场的菜鸟,更是并发的门外汉,文中关于并发以及其他软硬件、程序设计语言的论据也许不够客观甚至不够正确.

Redis时延问题分析及应对

- - 博客 - 伯乐在线
Redis的事件循环在一个线程中处理,作为一个单线程程序,重要的是要保证事件处理的时延短,这样,事件循环中的后续任务才不会阻塞;. 当redis的数据量达到一定级别后(比如20G),阻塞操作对性能的影响尤为严重;. 下面我们总结下在redis中有哪些耗时的场景及应对方法;. keys、sort等命令.

J.U.C并发框架

- - 并发编程网 - ifeve.com
作者:Doug Lea. 在J2SE1.5中,java.util.concurrent包下的大部分同步工具(锁、屏障等)以AbstractQueuedSynchronizer类为基础来构建. 这个框架提供了一些常用机制用于自动管理并发状态、阻塞及非阻塞线程,以及队列. 本论文描述了该框架的根源、设计、实现、用法及性能.

高并发---限流

- - Java - 编程语言 - ITeye博客
在开发高并发系统时有三把利器用来保护系统:缓存、降级和限流. 缓存的目的是提升系统访问速度和增大系统能处理的容量,可谓是抗高并发流量的银弹;而降级是当服务出问题或者影响到核心流程的性能则需要暂时屏蔽掉,待高峰或者问题解决后再打开;而有些场景并不能用缓存和降级来解决,比如稀缺资源(秒杀、抢购)、写服务(如评论、下单)、频繁的复杂查询(评论的最后几页),因此需有一种手段来限制这些场景的并发/请求量,即限流.

tomcat 高并发优化

- - 企业架构 - ITeye博客
maxThreads:tomcat起动的最大线程数,即同时处理的任务个数,默认值为200. minSpareThreads 表示空闲的线程数,据我的理解,类似于连接池. acceptCount:当tomcat起动的线程数达到最大时,接受排队的请求个数,默认值为100.    另外,有时候,还需要调整jvm的启动参数.

应对高并发攻击

- - 开源软件 - ITeye博客
近半个月过得很痛苦,主要是产品上线后,引来无数机器用户恶意攻击,不停的刷新产品各个服务入口,制造垃圾数据,消耗资源. 他们的最好成绩,1秒钟 可以并发6次,赶在Database入库前,Cache进行Missing Loading前,强占这其中十几毫秒的时间,进行恶意攻击. 为了应对上述情况,做了如下调整:.

高并发库存控制

- - 企业架构 - ITeye博客
1、在秒杀的情况下,肯定不能如此高频率的去读写数据库,会严重造成性能问题的. 必须使用缓存,将需要秒杀的商品放入缓存中,并使用锁来处理其并发情况. 当接到用户秒杀提交订单的情况下,先将商品数量递减(加锁/解锁)后再进行其他方面的处理,处理失败在将数据递增1(加锁/解锁),否则表示交易成功. 当商品数量递减到0时,表示商品秒杀完毕,拒绝其他用户的请求.

Java并发编程基础

- - 并发编程网 - ifeve.com
并发是一种能并行运行多个程序或并行运行一个程序中多个部分的能力. 如果程序中一个耗时的任务能以异步或并行的方式运行,那么整个程序的吞吐量和可交互性将大大改善. 现代的PC都有多个CPU或一个CPU中有多个核. 是否能合理运用多核的能力将成为一个大规模应用程序的关键. 进程是以独立于其他进程的方式运行的,进程间是互相隔离的.