Notice: Undefined index: key in /home/worker/data/www/liuzimu-blogs/usr/plugins/MysqlRun/Plugin.php on line 138 记一次swoole下协程文件死锁 - liuzimu ‘ blog
  • Home
  • Notice: Undefined index: key in /home/worker/data/www/liuzimu-blogs/usr/plugins/MysqlRun/Plugin.php on line 138
  • archives
  • Github

  • Notice: Undefined index: key in /home/worker/data/www/liuzimu-blogs/usr/plugins/MysqlRun/Plugin.php on line 138
  • Notice: Undefined index: key in /home/worker/data/www/liuzimu-blogs/usr/plugins/MysqlRun/Plugin.php on line 138

记一次swoole下协程文件死锁

bug原因:

# 协程中获取拼音首字母,要获取字典文件内容,fseek 方法对同一个文件指针中定位
fseek($fp, $offset * 8, SEEK_SET);

解决方式

 /**
     * 获取$string的拼音
     * @param string $string 词条字符串
     * @param bool $first 是否只取首字母
     * @return string
     */
    public static function getPY($string, $first = false, $phonetic = false)
    {
        $count = 10;
        $key = __CLASS__ . __FUNCTION__;
        // 协程使用该方法会死锁,这里加一个1s的redis锁
        start:
        $redis = RedisUtil::redis();
        if ($redis->incr($key) !== 1) {
            $redis->expire($key, 1);
            var_dump(\Hyperf\Utils\Coroutine::id() . ":getPY 等待锁~");
            $count--;
            if ($count < 0) {
                return '';
            }
            // 等待 20ms - 40 ms
            usleep(rand(20000, 40000));
            goto start;
        } else {
//            var_dump("getPY 运行成功");
            $redis->expire($key, 1);
            $pdat = BASE_PATH . '/storage/py.dat'; // 多音字dat数据
            $fp = fopen($pdat, 'rb');
            if (!$fp) {
                return '*';
            }
            $in_code = strtoupper('utf-8');
            $out_code = 'GBK';
            $strlen = mb_strlen($string, $in_code);
            $ret = '';
            for ($i = 0; $i < $strlen; $i++) {
                $py = '';
                $izh = mb_substr($string, $i, 1, $in_code);
                if (preg_match('/^[a-zA-Z0-9]$/', $izh)) {
                    if ($first && $i == 0) {
                        $ret = $izh;
                        break;
                    }
                    $ret .= $izh;
                } elseif (preg_match('/^[\x{4e00}-\x{9fa5}]+$/u', $izh)) { // 只取纯汉字,其他非汉字符号一概忽略。
                    $char = mb_convert_encoding($izh, $out_code, $in_code);
                    $high = ord($char[0]) - 0x81;
                    $low = ord($char[1]) - 0x40;
                    $offset = ($high << 8) + $low - ($high * 0x40);
                    if ($offset >= 0) {
                        fseek($fp, $offset * 8, SEEK_SET);
                        $p_arr = unpack('A8py', fread($fp, 8));
                        $py = isset($p_arr['py']) ? ($phonetic ? $p_arr['py'] : substr($p_arr['py'], 0, -1)) : '';
                        if ($first && $i == 0) {
                            $ret = empty($py[0]) ? '' : $py[0];
                            break;
                        }
                        $ret .= $py;
                    }
                } elseif ($first) {
                    $ret = $izh;
                    break;
                }
            }
            fclose($fp);
            // 解锁
            $redis->del($key);
            return $ret;
        }

    }

做一个加锁和等待锁

多个文件进行优化

由于测试过,一次读取单个拼音要花费14mn,在密集需要导入用户的场景下,速度非常慢。
这里复制了10个文件,采用10个协程跑同一个逻辑,这样就能提高导入速度。

/**
     * 获取$string的拼音
     * @param string $string 词条字符串
     * @param bool $first 是否只取首字母
     * @return string
     */
    public static function getPY($string, $first = false, $phonetic = false)
    {
        $count = 10;
        // 协程使用该方法会死锁,这里加一个1s的redis锁
        start:
        static $fileNumList = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
//        static $fileNumList = [0, 1];
        if (!empty($fileNumList)) {
            $fileNum = array_pop($fileNumList);
        } else {
//            var_dump(\Hyperf\Utils\Coroutine::id() . ":getPY- 全再用");
            $fileNum = rand(0, 10);
        }
        $key = 'card-service:lock-get-py:' . $fileNum;
        $redis = RedisUtil::redis();
        if ($redis->incr($key) !== 1) {
            $redis->expire($key, 1);
            var_dump(\Hyperf\Utils\Coroutine::id() . ":getPY-{$fileNum} 等待锁~");
            $count--;
            if ($count < 0) {
                return '';
            }
            // 等待 20ms - 40 ms
            array_push($fileNumList, $fileNum);
            usleep(rand(10000, 20000));
            goto start;
        } else {
//            var_dump("getPY 运行成功");
            $redis->expire($key, 1);
            // 文件有并发问题,所以多搞几个文件
            $pdat = BASE_PATH . '/storage/py' . $fileNum . '.dat'; // 多音字dat数据
            $fp = fopen($pdat, 'rb');
            if (!$fp) {
                array_push($fileNumList, $fileNum);
                return '*';
            }
            $in_code = 'UTF-8';
            $out_code = 'GBK';
            $strlen = mb_strlen($string, $in_code);
            $ret = '';
            for ($i = 0; $i < $strlen; $i++) {
                $py = '';
                $izh = mb_substr($string, $i, 1, $in_code);
                if (preg_match('/^[a-zA-Z0-9]$/', $izh)) {
                    if ($first && $i == 0) {
                        $ret = $izh;
                        break;
                    }
                    $ret .= $izh;
                } elseif (preg_match('/^[\x{4e00}-\x{9fa5}]+$/u', $izh)) { // 只取纯汉字,其他非汉字符号一概忽略。
                    $char = mb_convert_encoding($izh, $out_code, $in_code);
                    $high = ord($char[0]) - 0x81;
                    $low = ord($char[1]) - 0x40;
                    $offset = ($high << 8) + $low - ($high * 0x40);
                    if ($offset >= 0) {
                        fseek($fp, $offset * 8, SEEK_SET);
                        $p_arr = unpack('A8py', fread($fp, 8));
                        $py = isset($p_arr['py']) ? ($phonetic ? $p_arr['py'] : substr($p_arr['py'], 0, -1)) : '';
                        if ($first && $i == 0) {
                            $ret = empty($py[0]) ? '' : $py[0];
                            break;
                        }
                        $ret .= $py;
                    }
                } elseif ($first) {
                    $ret = $izh;
                    break;
                }
            }
            fclose($fp);
            // 解锁
            $redis->del($key);
            array_push($fileNumList, $fileNum);
            return $ret;
        }

    }

本文链接:

http://zimu.devorz.com/archives/23/
1 + 5 =
快来做第一个评论的人吧~