很多时候,我们以为采用了一种新技术(尤其是成熟的技术),过程应该是一马平川的。然而实际上,采用新技术的过程却是掉入了各种坑里。究其原因,或者是使用方式有问题,或者是效率的白白浪费。这一章,我想讲讲,我在使用Boost Asio进程网络编程时,所遇到的各种坑。
其一、CPU占用100%问题。
在没有采用异步编程之前,程序占用100%基本是不敢想象的事情,因为一旦程序占用100%的CPU,那必然是代码中出现了死循环的BUG。但是采用了Boost Asio后,我发现新系统很容易就跑满了CPU。Why?
1)服务端接收任务过快。快一直是我们系统所追求的,为此我们特别进行了优化,支持一次接收一组任务(100-300之间)。然而我们处理任务时,client一次最多只能够处理10条任务,这就是典型的生产者-消费者问题。生产者能力大于消费者,而且消费者在消化时(HTTP发送任务时),对方反馈是同步反馈的,这就造成了CPU的持续增加。再则,任务完毕被发送到report_runner时,反馈任务是一条一条反馈,这又是生产能力大于了消费能力。
针对此问题,我们做了一些限制和优化:stream_server增加限速功能,report_client可以合并反馈。
2)bind意味着拷贝,尽量在bind时传递指针而不是实例。
其二、shared_ptr和多态。
如果我有一个基类的shared_ptr,我可以用它来指向具体的子类吗?答案是需要将子类的shared_ptr赋值于这个指针,否则无法实现多态。
其三、子进程问题。
由于我们的客户端只运行于linux,所以这里只描述在基于boost asio的多线程程序中如何再做到多进程。注意:这里有一个前提,子进程中只调用shell或可执行,并不用到父进程中的asio实例。
为什么要单独讲子进程问题呢?因为Boost的asio并不是fork安全的,而且其Doc中特别指出了在子进程中需要调用notify_event来通知asio以使fork安全。这至少造成了我的疑惑:如果我不需要在子进程中使用Asio,我还需要notify吗?答案是不需要。正如我们系统所遇到的,只需要调用linux的exec*即可。
进一步,由于我们并不需要拷贝父进程,所以用vfork取代了fork。