最近用swoole实现了一个功能:异步非阻塞获取酒店OTA最低价。
场景是这样的:
一个酒店对应多个OTA(我们的供应商),我需要获取酒店每一个OTA在某一个入住/离店日期的最低报价。
当数据量很大时,比如目前1天需要获取20多万次酒店报价(这个数字还在增长),每一个酒店又有1-5个OTA(以后可能更多),这样每天需要调用上百万次获取最低价的接口,接口在没有缓存的情况下,使用http协议获取价格可能需要1-2秒,如果使用同步阻塞方式获取,所用时间会很长,甚至无法完成任务。
所以我选择用swoole来实现
程序流程大概是这样的:
客户端:
1.循环一组要获取价格的酒店ID
2.每次循环创建swoole客户端,向服务器send数据,维护一个clients数组,存放当前活动的客户端
3.使用while循环+swoole_client_select()方法监控从服务器端返回的数据,获取到一个客户端的数据就关闭相应客户端并从clients中删除此客户端
4.直到clients中元素个数为0,退出while循环
服务器端:
1.服务器端开启了8个worker进程和50个worker_task进程
2.一个客户端连接会占用一个worker进程,当onReceive事件被触发时,需要创建task进程,有几个OTA就创建几个task进程
3.onTask事件回调方法用来调用接口,获取酒店某个OTA的最低价
4.对于同一个客户端连接,服务器端必须要等待所有相关task进程都跑完以后,再统一返回数据给客户端,比如一个酒店有3个OTA,这3个OTA必须都获取到价格以后,才能返回给客户端,所以我用一个$data变量来存放同一个客户端不同task返回的数据
5.onTaskFinish事件回调中,把当前OTA最低价写入$data[$fd][$task_id]中
4.onTaskFinish事件回调中,判断同一个客户端的所有task都执行完成以后(count($data[$fd])===$ota_count),把数据send到客户端。
实际执行的效果还是不错的,已经稳定运行24小时,目前大概1秒钟可以获取20-30个酒店所有OTA的最低价,这个速度还可以提高,只要服务器内存够用,开启更多进程即可。
用swoole写代码和之前在PHP-fpm环境有一点最大的不同就是必须重视程序的容错性和健壮性,因为进程是常驻内存的,任何一个微小的bug都可能导致服务终止,还必须避免长期运行产生的内存泄露。
客户端伪代码
服务器端伪代码