swoft 与 laravel-swoole 框架选型对比,包括ab压测工具的使用、等待协程完成

本文详细记录了在PHP框架中,针对Swoft和Laravel-Swoole进行后端技术选型的测试过程。通过在不同并发数下执行查询用户记录的基准测试,比较了两者的性能表现。测试结果显示,Swoft在并发处理、网络消耗时间和响应时间等方面表现出色,但随着并发数增加,最大处理时间相对较差。此外,文章还探讨了Swoft框架中协程创建函数srun和sgo的区别,并提供了关于如何同步协程输出的解决方案。
摘要由CSDN通过智能技术生成

因项目需要,需要做php框架的后端技术选型,于是开始着手测试基于swoole的框架swoft与laravel的扩展包laravel-swoole进行评估。 刚开始打算是在cygwin中使用laravel-s这个laravel扩展包,然而报出了一个cli_set_process_title() failed异常。 找了半天原因,从swoole的官方文档中看到,在macOS与低版本的linux系统中,是无法使用cli_set_process_title这个函数的。搜索了半天,也没有找到有效的解决方案,于是最后选择了替代方案:laravel-swoole

测试环境: 阿里云服务器4C8G数据库与服务器使用内网通信,排除网络io的干扰。

测试环境为线下的测试服务器与测试数据库,测试条件是查询根据传过去的用户uid查出一条用户记录,并返回查询结果,没有使用redismemcache等缓存。

测试工具:

  • ab

查询sql:

select * from  where id = xxxx 

测试过程中会出现以下问题:

[2020-12-15 10:43:50 *3602.1]    NOTICE    finish (ERRNO 1004): send 5 bytes failed, because session#2 is closed

这个问题是AB工具本身的问题,具体的原因可以参考:https://wiki.swoole.com/wiki/page/1527.html

benchmark:

  • 测试在持续60秒内在不同的并发数下的效果,具体执行条件为:ab -t 60 -c 2000 http://127.0.0.1:1215/api/user-info/2052,其中c为变量,意思为并发数。

测试效果对比:

  • 并发数为10:

 

  • 并发数为100:

 

  • 并发数200:

  • 并发数500:

  • 并发数1000:

 

  • 并发数1500:

 

  • 并发数2000:

 

  • 并发数3000:

  • 并发数5000:

 

运行时数据库状况:

关键指标:

  • Complete requests:请求完成数
  • Failed requests:请求失败数
  • Connection Times:网络消耗时间。
  • Time per request(mean): 服务器收到请求后,响应页面的平均时间
  • Time per request(mean, across all concurrent requests): 并发的每个请求平均消耗时间
  • Percentage of the requests served within a certain time (ms): 一定的时间内,完成的请求数所花的时间比。

总结:

  • 从并发的对比图中,从请求成功数请求失败数来看,swoftlaravel-swoole相比,成功率较高;从网络消耗时间对比,由于有swoft有连接池的存在,明显可以看出,网络IO的时间要优于laravel-swoole;从响应页面的平均时间并发的每个请求平均消耗时间看,swoft性能还是强于laravel-swoole;从一定的时间内,完成的请求数所花的时间比swoft大部分的情况下,处理完成的平均处理时间是优于laravel-swoole。但是随着并发数的上升,请求的最大处理时间与laravel-swoole对比,即最完成全部请求来需要花费的时间,性能相对来说差,综合性能上来看,swoft有一定的优势。

没覆盖测试到的:laravel-swoole加上数据库连接池中间件之后的效果。

从初步使用体验看,swoft要求更高,约束更强,特别是引入了注解概念,所谓I注解即路由,增强了代码的简洁性,同时牺牲了代码的可读性。 语法上,使用PHP7的强类型语法约束与模型数据字段的映射,好处是增强了代码的稳健性,但是缺点也很明显:降低了php的开发效率swoft文档比较简单,没有过多的停留在概念性解释上面,结合在搭建测试环境中遇到的问题,坑还是有不少,相关的搜索结果与laravel相比会少很多,有些问题可能会需要从框架源码着手解决,因此对使用者会有一定的要求。

数据交换上,swoft提供httprpcwebsocket等支持,不再需要再引入第三方依赖,而laravel-swoole作为laravel的扩展包,主要是支持http;在事件的支持上,swoftlaravel都支持同步与异步的事件驱动,在异步处理方面,swoft是基于swoft的协程,而laravel是基于队列。

数据库驱动上,目前swoft官方的文档上只有mysqlredis的驱动,如果项目中有用到mongoDBPostgreSQLSSDB等其他数据库则需要使用第三方的轮子或自己造。

附测试使用swoft遇到的一个有意思的问题:

  • 开启协程有srunsgo,两者有何不同? sgo:开启新协程。 srun:启动协程并等待执行结束。 文档在这一点没说清楚,对两者的说明,网上搜索也没几个相关内容。 在swoft的命令行测试对比的结果:
echo 'begin'.PHP_EOL;
sgo(function(){
    Co::sleep(2);
    echo "middle".PHP_EOL;
});
echo "end".PHP_EOL;

此时输出:

begin
end
middle

如果换成:

echo 'begin'.PHP_EOL;
srun(function(){
    Co::sleep(2);
    echo "middle".PHP_EOL;
    return true;
});
echo "end".PHP_EOL

那么此时输出:

begin
[WARNING] Swoft\Co:run(83) Already is in coroutine, not need to use `run`!
middle
end

从上面对比看出,顺序执行了(即已经做了同步),但是会抛出一个警告,已经是协程环境不要使用run方法,这可能就是框架作者反复强调再次强调,框架中只能使用 sgo 函数创建协程。的原因之一。因此,我们只能用sgo方法在框架内开协程,srun方法的应用场景更多的应该是在自定义进程等非框架内使用的。

可是如果我既想做顺序输出又不想抛出这个警告呢?

显然我们会注意到sgo方法会有第二个入参wait。然而,在框架文档里没有解释的,sgo方法wait到底是嘛玩意?既然默认是false,什么情况应该用true呢?既然文档没有,那么只能看源代码了。跟到代码里面去会发现这么一段:

return Coroutine::create(function () use ($callable, $tid, $wait) {
            // Current cid
            $id = Coroutine::getCid();
            try {
                // Storage fd
                self::$mapping[$id] = $tid;
                if ($wait) {
                    Context::getWaitGroup()->add();
                }

                PhpHelper::call($callable);
            } catch (Throwable $e) {
                Error::log(
                    "Coroutine internal error: %s\nAt File %s line %d\nTrace:\n%s",
                    $e->getMessage(),
                    $e->getFile(),
                    $e->getLine(),
                    $e->getTraceAsString()
                );

                // Trigger co error event
                Swoft::trigger(SwoftEvent::COROUTINE_EXCEPTION, $e);
            }

            if ($wait) {
                // Trigger defer
                Swoft::trigger(SwoftEvent::COROUTINE_DEFER);

                Context::getWaitGroup()->done();
            }

看到了熟悉的waitgroupwaitgroupswoole的文档中就提到了它的作用,用来了做同步的, 一般操作有3个方法adddone,以及用来同步等待的wait。那么同理,它在这里,肯定也是用来做同步的。那么让我们试试,看不能能输出预期的:

begin
middle
end

代码改成如下:

echo 'begin'.PHP_EOL;
sgo(function(){
    Co::sleep(2);
    echo "middle".PHP_EOL;
},true);
echo "end".PHP_EOL;

然而,输出结果并没有如预期,实际输出:

begin
end
middle

等等,回过头去看sgo方法的实现,好像它这里少了点什么? 是的,没有看到哪里调用wait方法做同步。 于是,我们跟到 Context::getWaitGroup()里面去看代码:确实里面有个wait方法。我们把这个函数加进去看看效果:

echo 'begin'.PHP_EOL;
sgo(function(){
    Co::sleep(2);
    echo "middle".PHP_EOL;
},true);
Context::getWaitGroup()->wait();
echo "end".PHP_EOL;

实际输出:

begin
middle
end

符合我们的预期,也就是意味着,如果我们在sgo方法有同步需求的时候,需要自己手动在业务代码处调用Context::getWaitGroup()->wait();这一个方法使数据同步。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值