业务逻辑:用户进行投票,投票之后写入记录;投票成功后更改用户状态,不得再投票。
直接通过postman测试接口是没问题的,数据都正常。但是只要通过多进程脚本运行测试的话,写入记录会增多。
目前想到的解决方案:
投票代码:
$uid = Token::getCurrentUid();
$codeNum = Token::getCurrentTokenVar('codeNum');
//并发处理
$redis = Redis::getRedisConn();
$key = RedisKeyNameLibrary::USER_VOTE.$codeNum;
$score = array_sum(explode(' ', microtime()));
$value = build_rand_str(32).':'.$uid;
$redis->zRemRangeByScore($key, 0, time() - 1);//清除1秒前的集合
$redis->zAdd($key, $score, $value);
$zRangeArr = $redis->zRange($key, 0, -1);
if ($zRangeArr[0] <> $value) return returnError('提交失敗,請重新提交!', 30002);
$tran = $this->db();
$tran->startTrans();
try{
//根据ID获取对应模型
$model = self::get($uid);
$data = array_merge($model->toArray(), $data);
//验证
$validate = new CodeValidate();
$result = $validate->check($data, [], 'vote');
if(!$result) return returnError($validate->getError(), 30001);
$errorMsg = returnError('提交失敗,請重新提交!', 30002);
//更改为已投票
$status = $model->data($data)->allowField(true)->save(['status' => self::STATUS_1]);
//保存成功后追加投票记录
if($status !== false) {
$saveData = [];
$models = User::all(array_map('intval', $data['user_ids']))->all();
foreach($models as $k => $v) array_push($saveData, [
'code_num' => $data['code_num'], 'uid' => $v->data['id'],
'name' => $v->data['name'], 'group_id' => $v->data['group_id']
]);
$model->logs()->saveAll($saveData);
$tran->commit();
return returnSuccess();
}
return $errorMsg;
}catch (Exception $e){
$tran->rollback();
return $errorMsg;
}
以下是多进程测试投票接口的脚本:
for ($i = 0; $i < 6; $i ++) {
$pid = pcntl_fork();
if ($pid == - 1) {
die("could not fork");
} elseif ($pid) {
echo "I'm the Parent $i\n";
} else {
$token = '123123';
$url = 'http://api.com/user';
$query = 'user_ids[]=1&user_ids[]=39&user_ids[]=19&user_ids[]=30';
$command = 'curl -H "token:' . $token . '" -X POST -d "' . $query . '" ' . $url;
// 子进程处理
$res = system($command);
file_put_contents($i . '_work.log', var_export($res, true));
exit(); // 一定要注意退出子进程,否则pcntl_fork() 会被子进程再fork,带来处理上的影响。
}
}
// 等待子进程执行结束
while (pcntl_waitpid(0, $status) != - 1) {
$status = pcntl_wexitstatus($status);
echo "Child $status completed\n";
}
想问问,除了以上解决方案之外,还有没有别的解决方案?(最好不需要启动别的服务之类的)
拉莫斯之舞
ITMISS
叮当猫咪