记录一次测试正常,但是上线后出现问题的事件

需求描述

用户可以领取多张优惠券,每个优惠券附带用户身份信息,比如在某段时间内获取了某些属性课程的用户才可以使用该张优惠券。 当用户下单时,需要根据当前登录的用户身份过滤出当前商品可用的优惠券。

难点

因为一个用户可以有多个优惠券,如果每个优惠券逐个检查,当用户持有大量优惠券时,可能会接口超时。

解决方案

基于swoole协程,在php-fpm中调用shell_exec执行多协程并发,每个协程检查一个优惠券。 代码如下:

bash 复制代码
\Swoole\Runtime::enableCoroutine();

$result = [];
$userIdentity = new UserIdentity();
$userCouponId2BatchInfo = $params['userCouponId2BatchInfo'];
$orderCouponMap = $params['orderCouponMap'];
$appId = $params['appId'];
$userId = $params['userId'];
$productInfoList = $params['productInfoList'];

foreach($orderCouponMap as $userCouponId => $userCouponProdIds) {
            go(function() use ($userCouponId2BatchInfo, $userCouponId, $userCouponProdIds, $userIdentity, &$result, $appId, 
                $userId, $productInfoList) {
             

                if(!isset($userCouponId2BatchInfo[$userCouponId])) {
                    return;
                }
                $batchInfo = $userCouponId2BatchInfo[$userCouponId];

                if($batchInfo['user_limit_identity'] == 0) {
                    $result[$userCouponId] = $userCouponProdIds;
                } else if($batchInfo['user_limit_identity'] == 1) {
                    $userLimitInfo = $batchInfo['user_limit_identity_info'];
                    if(is_string($userLimitInfo)) {
                        $userLimitInfo = json_decode($userLimitInfo, true);
                    }
                    if($userIdentity->match($appId, $userId, $userLimitInfo, $productInfoList)) {
                        $result[$userCouponId] = $userCouponProdIds;
                    }
                } else if($batchInfo['user_limit_identity'] == 2) {
                    $userLimitInfo = $batchInfo['user_limit_identity_info'];
                    if(is_string($userLimitInfo)) {
                        $userLimitInfo = json_decode($userLimitInfo, true);
                    }
                    if(!$userIdentity->match($appId, $userId, $userLimitInfo, $productInfoList)) {
                        $result[$userCouponId] = $userCouponProdIds;
                    }
                }
            });
        }
        
swoole_event_wait();
        
echo json_encode($result);

在业务请求的处理器Controller中,如下调用上面的命令:

ini 复制代码
$cmd = sprintf("/usr/local/php7/bin/php /app/Exec/Command/UserCoupon/UserIdentityMatch.php '%s'", json_encode($params));
        
$result = shell_exec($cmd);

在测试环境和灰度环境验收一切正常,但是当上线以后,部分帐号展示不了预期能够使用的优惠券。

问题排查

通过查看php_error.log看到如下报错:

css 复制代码
[10-Apr-2024 09:40:49 Asia/Shanghai] PHP Warning:  shell_exec(): Unable to execute '/usr/local/php7/bin/php 

将代码中的$cmd命令输出到文件中,手动执行,报错:

bash 复制代码
# sh /tmp/test.sh 
/tmp/test.sh: line 1: /usr/local/php7/bin/php: Argument list too long

查阅资料以后发现,linux系统有个ARG_MAX作为命令行和命令行参数的总大小限制,

arduino 复制代码
# getconf ARG_MAX
131072

再查看脚本命令行大小

bash 复制代码
# ll /tmp/test.sh 
-rw-r--r--    1 root     root        163067 Apr 10 09:49 /tmp/test.sh

原来是命令行参数太大导致!所以有些帐号会出现问题。而测试帐号在灰度和测试环境因为没有超过命令行参数限制。

bash 复制代码
# ll /tmp/test.sh 
-rw-r--r--    1 root     root         58658 Apr 10 09:54 /tmp/test.sh

解决方案

  1. 启动一个本地swoole服务,通过socket通信调用swoole。
  2. 使用golang重构。
相关推荐
Lsetea9 分钟前
OpenSSL报certificate chain too long:error 22与verify_depth深度排查
linux·https·ssl证书·openssl·证书链
爱上网的小舟1 小时前
星际战士2启动错误怎么办?5种游戏报错解决方法
开发语言·php
不像程序员的程序媛1 小时前
linux常见文本操作命令
linux·运维·服务器
wangbing11252 小时前
开发指南149-IotDB-常用命令
linux·运维·iotdb
看着博客敲代码2 小时前
WorkBuddy 接上 Kali MCP 做自动化渗透:信息收集到初步验证的落地记录
linux·人工智能·安全·网络安全·自动化
唐青枫3 小时前
Yii2 Applications 详解:应用生命周期、组件配置与多入口实战
php
我是唐青枫3 小时前
Yii2 Controller 详解:路由、Action、过滤器与 CRUD 实战
php
小HANN3 小时前
华为云企业网站上云实战|从零搭建高可用WordPress全站架构(ECS+RDS+ELB+弹性伸缩+云监控闭环)
linux·运维·服务器·经验分享
星恒随风4 小时前
Linux库的底层原理:ELF文件格式、静态链接、程序加载、虚拟地址空间与GOT/PLT详解
linux·运维·笔记·学习·状态模式
GeW4 小时前
2026新质生产力路线图:Linux、数据库与智能算力融合报告
linux