ubuntu下安装swoole以及swoole各种服务和异步讲解

现在我们讲一下ubuntu下如何安装swoole扩展
a.
首先我们需要有phpize用来生成configure文件:
phpize for PHP7由Ubuntu 16.04中的php7.0-dev软件包提供。要安装它,请打开终端并输入
sudo apt install php7.0-dev
phpize7.0将安装到/usr/bin/phpize7.0

b.
github上clong下来swoole
cd /home/huxiaobai/work/softpakage 么有此目录就去创建目录
不要去git clone https://gitee.com/swoole/swoole.git 因为下载下来的是4.3.1版本 很多东西不能使用比如异步mysql redis等都不能用
所以去http://pecl.php.net/package/swoole 下载2.1.0版本吧 上传到服务器解压即可!

c.进入到解压后的swoole目录里面去 然后执行
/usr/bin/phpize7.0 执行这么一句就有了configure等文件

d.
./configure --with-php-config=/usr/bin/php-config
等待执行完成
然后make
然后make install
生成的swoole.so就存储在这个目录下边了哦!

e:
去修改php.ini配置文件 增加扩展:
extennsion=swoole.so

f:
进入到swoole目录下的example下的server 找到echo.php
执行 php echo.php
然后就完事了
打开另外的终端 执行命令
netstat -anp | grep 9501 //检测端口号
说明swoole扩展安装成功哦!!!

小总结
php -i | grep php.ini 寻找真实的php.ini配置文件的路径
whereis php 查看php安装到了哪里
要知道phpize是干啥的
php-config的路径要知道 以及是干啥的

现在讲解一下如何配置tcp服务器
直接上代码:(放在了我的服务器/home/huxiaobai/work/softpackage/example/server/ceshi.php)既然安装了php并且配置了环境变量 那么直接在该目录下执行php ceshi.php即可运行
代码如下:

//创建Server对象,监听 127.0.0.1:9501端口
//有四个参数 第三个是模式不用设置 第四个如果不设置默认就是tcp
$serv = new swoole_server("127.0.0.1", 9501);

$serv->set([
'worker_num' => 8,   //worker进程数  cpu内核数量的1-4倍
'max_request' => 1000,  //去看手册吧https://wiki.swoole.com/wiki/page/13.html
])

//监听连接进入事件
/*
 * $fd  客户端的唯一标示
 * $reactor_id 线程id
 * */
$serv->on('connect', function ($serv, $fd, $reactor_id) {
    echo "Client: 线程id->{$reactor_id} -- 客户标示:{$fd} Connect.\n";
});

//监听数据接收事件
/*
 * $from_id 其实和上边的$reactor_id是一样的 线程id 我们也可以改成$reactor_id
 * $fd 客户端唯一标示  也代表着将信息发送给谁  $data 客户端发送过来的消息
 * */
$serv->on('receive', function ($serv, $fd, $from_id, $data) {
    //send()向客户单发送消息
    $serv->send($fd, "Server: 客户标示->{$fd} -- 线程id-> {$from_id}".$data);
});

//监听连接关闭事件
$serv->on('close', function ($serv, $fd) {
    echo "Client: Close.\n";
});

//启动服务器
$serv->start();

此刻tcp服务已经在运行了
打开其他几个终端 多开几个 然后
telnet 127.0.0.1 9501

表示链接成功
然后tcp服务端就会出现:

说明你已经链接进来了
然后你再在你的客户单输入内容 就会有响应给你返回会去
简单的通讯实现了!

现在讲解一下如何利用php脚本链接tcp服务器并发送消息:
直接上代码:(放在了我的服务器/home/huxiaobai/work/softpackage/example/server/client.php)既然安装了php并且配置了环境变量 那么直接在该目录下执行php client.php即可运行
代码如下:

//链接swool tcp 服务
//具体可去查看手册 https://wiki.swoole.com/wiki/page/29.html
$client = new swoole_client(SWOOLE_TCP);
//端口号必须保持和tcp服务器一致9501
if(!$client->connect('127.0.0.1',9501)){
    echo "链接失败!";
    exit();
}

//获取cli模式下用户输入的数据
fwrite(STDOUT,'请输入消息:');
$msg = trim(fgets(STDIN));

//发送消息给tcp server服务器
$client->send($msg);
//接受来自server服务器的数据
$result = $client->recv();
echo $result;

通过如下命令可以查看我们设置了多少个线程数:
ps aft | grep ceshi.php

UDP服务没讲
tcp和udp的区别https://baijiahao.baidu.com/s?id=1572911681189299&wfr=spider&for=pc

现在讲一下如何启动http服务:
直接上代码:(放在了我的服务器/home/huxiaobai/work/softpackage/example/server/http_server.php)既然安装了php并且配置了环境变量 那么直接在该目录下执行php http_server.php即可运行
代码如下:

//详细参数介绍参考手册:https://wiki.swoole.com/wiki/page/478.html
$http =  new swoole_http_server("0.0.0.0",9502);
$http->on('request',function($request,$response){
    //所有的http请求都会经过这里 $request表示请求  $response表示发送
    $res = $request->get;
     //end()里面只能使字符串 所以数组最好转变成json格式哦!
    $response->end("<h1>Hello Swoole. #".json_encode($res)."</h1>");
});
$http->start();

如何测试呢swoole_http服务呢?
上边我们启动了http_server.php那么就表示http服务启动了 然后再打开一个终端
利用curl命令: a.curl 127.0.0.1:9502 即可执行 但是没法传递参数 想要传递参数只能配置虚拟域名跑一跑了哦!
b.当然我们也可以跟上边一样 netstat -anp | grep 9502开http服务是否起来了!
c.我们之前不是配置过192.168.1.174:8005吗?现在我们为了测试swoole的http服务,我们首先运行起来上边讲到的http_server.php 让swoole的http服务在服务器首先运行起来 然后在浏览器当中访问:
192.168.1.174:9502?name=hushaoliang 那么页面展示的结果为在这里插入图片描述
说明我们的http服务是完好无损的哦!

现在我们讲一下swoole下http服务当中请求静态资源:
我们只需要将代码改造成如下:

$http =  new swoole_http_server("0.0.0.0",9502);
$http->set([
    'enable_static_handler'=>true,
    //静态资源存放的位置
    'document_root'=>"/home/huxiaobai/work/softpackage/swoole/data"
]);
$http->on('request',function($request,$response){
    //所有的http请求都会经过这里 $request表示请求  $response表示发送
    $res = $request->get();
     //end()里面只能使字符串 所以数组最好转变成json格式哦!
    $response->end("<h1>Hello Swoole. #".json_encode($res)."</h1>");
});
$http->start();

改造成这个样子以后那么当你访问192.168.1.174:9502/index,html的时候就会自动去
/home/huxiaobai/work/softpackage/swoole/data下边去寻找index.html静态文件!从而不再走接下来的获取请求和发送请求的代码了哦!

现在我们来学习一下WeSocket服务

什么是websocket? websocket协议是基于TCP的一种新的网络协议,它实现了浏览器与服务器全双工通信 —
允许服务器主动发送信息给客户端。可以做类似于服务器推送消息给web的客户端 也可以做web的聊天室等!

为什么需要websocket? http虽然也是基于tcp 但是htp的通讯只能由客户端发起
做不到服务端主动推送消息给客户端的!除非轮训太耗费资源!因为你需要不断的建立连接 所以就有了websocket!

websocket的特点? 建立在tcp基础之上 性能开销小通信高效 客户端可以与任意服务器通信 协议标识符ws wss
持久化网络通信协议

现在我们来讲解一下如何配置websocket服务:
直接上代码:(放在了我的服务器/home/huxiaobai/work/softpackage/example/server/ws_server.php)既然安装了php并且配置了环境变量 那么直接在该目录下执行php ws_server.php即可运行

//创建websocket服务器对象,监听0.0.0.0:9502端口
$ws = new swoole_websocket_server("0.0.0.0", 9503);
//监听WebSocket连接打开事件
$ws->on('open', function ($ws, $request) {
            echo "客户唯一标识:".$request->fd;
  });

 //监听WebSocket消息事件   接受客户端发送过来的数据  并可以通过push推送消息到前端;
 $ws->on('message', function ($ws, $frame) {
            echo "客户发送的消息为: {$frame->data}\n";
            $ws->push($frame->fd, "服务端推送的消息是:很高兴认识你哦!");
   });

  //监听WebSocket连接关闭事件
   $ws->on('close', function ($ws, $fd) {
            echo "client-{$fd} is closed\n";
     });

    $ws->start();

现在服务写好了 那么如何测试是否成功使用呢?
两种方式

A:通过http服务来做测试,上边我们讲到可以通过swoole的http服务请求静态资源我们写一个静态文件通过js来请求websocket服务:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <script>
            var wsServer = 'ws://192.168.1.174:9503';
            var websocket = new WebSocket(wsServer);
            //实例化对象的onopen属性
            websocket.onopen = function (evt) {
                //向服务端发送数据;
                websocket.send('hello,websocket!');
                console.log("链接上了websocket服务器");
            };

            //实例化 onmessage接受服务端返回结果数据
            websocket.onmessage = function (evt) {
                console.log('服务端返回的数据是:' + evt.data);
            };

            websocket.onclose = function (evt) {
                console.log("close");
            };

            //返回错误信息0 
            websocket.onerror = function (evt, e) {
                console.log('Error occured: ' + evt.data);
            }; 
    </script>
</head>
<body>
    <a>测试websocket通讯</a>
</body>
</html>

将上边的代码放到ws_client.html当中,文件存放在/home/huxiaobai/work/softpackage/data目录下边
因为我们的http服务配置的静态文件路径就在这个data目录下边; 然后一个窗口开启websocket服务 另外一个窗口开启http服务
然后我们在浏览器访问http服务的静态资源:192.168.1.174:9502/ws_client.html即可! 缓存比较大
注意清理浏览器缓存哦!

B: 第二种测试的形式其实和swoole当中的http服务一样 直接访问静态文件

//创建websocket服务器对象,监听0.0.0.0:9502端口
$ws = new swoole_websocket_server("0.0.0.0", 9503);
//增加了静态文件路径
$ws->set([
    'enable_static_handler'=>true,
    'document_root'=>"/home/huxiaobai/work/softpackage/swoole/data"
]);

//监听WebSocket连接打开事件
$ws->on('open', function ($ws, $request) {
//          var_dump($request->fd, $request->get, $request->server);
    echo "客户唯一标识:".$request->fd;
//            $ws->push($request->fd, "hello, welcome\n");
});

//监听WebSocket消息事件
$ws->on('message', function ($ws, $frame) {
    echo "客户发送的消息为: {$frame->data}\n";
    $ws->push($frame->fd, "服务端推送的消息是:很高兴认识你哦!");
});

//监听WebSocket连接关闭事件
$ws->on('close', function ($ws, $fd) {
    echo "client-{$fd} is closed\n";
});

$ws->start();

然后启动websocket服务 就没哟必要启动http服务了 用不到它了哦!
然后直接访问192.168.1.174:9503/ws_client.html即可!

现在我们来讲一下swoole里面的task任务的使用:

为什么要使用到task? 假设网站当中很多人通知提交了申请 我们需要给1000个人同时发送邮件
那么如果按照顺序执行的话一定会造成服务器卡慢等问题。如果通过swoole的task任务来执行的话呢,将任务放到线程池当中去慢慢地去执行
那么就大大降低了服务器的压力!不会影响到用户的体验度哦! tcp udp http websocket都可以使用task任务

现在我们拿websoket服务来做测试来上代码(测试的时候还是按照websocket当中第二种B的形式来测试即可!):

//创建websocket服务器对象,监听0.0.0.0:9502端口
        $ws = new swoole_websocket_server("0.0.0.0", 9503);

        $ws->set([
            'enable_static_handler'=>true,
            'document_root'=>"/home/huxiaobai/work/softpackage/swoole/data",
//这个是必须要写的 不写这个是不会吊起task任务来的哦! 具体干啥的看手册! https://wiki.swoole.com/wiki/page/276.html
            'task_worker_num' => 4
        ]);

        //监听WebSocket连接打开事件
        $ws->on('open', function ($ws, $request) {
//          var_dump($request->fd, $request->get, $request->server);
            echo "客户唯一标识:".$request->fd;
//            $ws->push($request->fd, "hello, welcome\n");
        });

        //监听WebSocket消息事件
        $ws->on('message', function ($ws, $frame) {
//要向task任务当中发送的数据
            $data = [
                'task' => 1,
                'fd' => $frame->fd
            ];
//我们这里执行了task那么就会去回调OnTask()函数 我们在task任务当中睡眠了5秒钟 但是后边的echo 以及push不会等5秒输出 这里是异步的  task任务另起了线程去处理业务逻辑 !
            $ws->task($data);//调用task会去执行回调函数OnTask()
            echo "客户发送的消息为: {$frame->data}\n";
            $ws->push($frame->fd, "服务端推送的消息是:很高兴认识你哦!");
        });
//$serv 我们这里是websocket那么就表示对象websoket 如果是http那么就表示swoole_http_server https://wiki.swoole.com/wiki/page/134.html
        $ws->on('Task',function($serv,$taskId,$workerId,$data){
            print_r($data);//在终端打印了传递进来的参数
            sleep(5);//模拟真实环境  睡会
            return "这个task任务执行完毕了哦!";//返回任务处理结果
        });
//任务处理完成一定是会回调OnFinish()函数的 也就是走这里!  $data是task任务的返回结果 不要弄错哦!
        $ws->on('Finish',function($serv,$taskId,$data){
            echo "taskId:".$taskId.'\n';
            echo "task任务处理结果返回:".$data;
        });

        //监听WebSocket连接关闭事件
        $ws->on('close', function ($ws, $fd) {
            echo "client-{$fd} is closed\n";
        });

        $ws->start();

接下来讲的是swoole当中的异步IO场景下的操作

先来讲解一下swoole里面的定时器:(测试的时候还是按照websocket当中第二种B的形式来测试即可!)

//创建websocket服务器对象,监听0.0.0.0:9502端口
     $ws = new swoole_websocket_server("0.0.0.0", 9503);

     $ws->set([
         'enable_static_handler'=>true,
         'document_root'=>"/home/huxiaobai/work/softpackage/swoole/data"
     ]);

     //监听WebSocket连接打开事件
     $ws->on('open', function ($ws, $request) {
         //表示每隔3秒执行一次输出  会在终端看到打印结果
         swoole_timer_tick(3000, function($timer_id){
             echo "2s定时器 timerId:{$timer_id}\n";
         });
         echo "客户唯一标识:".$request->fd;
     });

     //监听WebSocket消息事件
     $ws->on('message', function ($ws, $frame) {
         echo "客户发送的消息为: {$frame->data}\n";
//表示5s之后才执行的操作  这是异步的哦  其实程序会先执行下边的:$ws->push($frame->fd, "服务端推送的消息是:很高兴认识你哦!");  5秒之后再执行echo  和 push操作!
         swoole_timer_after(5000,function() use($ws,$frame){
             echo "5S-after\n";
             $ws->push($frame->fd, "测试5s只有执行哦!");
         });
         $ws->push($frame->fd, "服 务端推送的消息是:很高兴认识你哦!");

     });


     //监听WebSocket连接关闭事件
     $ws->on('close', function ($ws, $fd) {
         echo "client-{$fd} is closed\n";
     });

     $ws->start();

现在我们拉讲解一下异步IO场景下的异步mysql操作:
首先也是在服务器上的某个文件夹当中创建mysql.php文件
代码如下所示:

<?php
class AysMysql
{
    public $dbSource = '';
    public $dbConfig = [];
    public function __construct(){
        $this->dbSource = new Swoole\Mysql;
        $this->dbConfig = [
            'host' => '192.168.1.174',
            'port' => 3306,
            'user' => 'root',
            'password' =>'root',
            'database' => 'tprbac',
            'charset' => 'utf8',
        ];
    }

    public function execute($id,$username){
        //链接mysql  swoole当中异步mysql的链接必须使用connetc()方法
        //connet()两个参数 第一个是配置array 第二个参数是回调方法  function()第一个参数是相当于$this->dbSource 第二个参数是返回值状态 true或者false链接成功或者失败
        $this->dbSource->connect($this->dbConfig,function($db,$result) use($id,$username){
            if($result == false){
                var_dump($db->connect_error);
            }

            $sql = "select * from ocenter_api_token where id = 3";
            //query方法执行sql   增删改查
            $db->query($sql,function($db,$result){ 
                //select => result返回的是查询的结果集
                //add  update delete返回的是布尔型
                if(is_array($result)){
                    print_r($result);
                }elseif($result == false){
                    echo "失败!";
                }else{
                    echo "成功!";
                }
                $db->close();
            });
        });
        return true;
    }
}
$obj = new AysMysql();
$st = $obj->execute(1,'huxiaobai');
echo ""wahaha;
?>

然后通过 php mysql.php来执行 最终得到的结果是 true wahaha 结果集数组

因为我们这里是swoole当中的异步IO 异步mysql操作 也就是说mysql的操作都是异步的
是另外起来线程进行操作的;所以程序会先返回true 然后是我们打印的wahaha 最后才是异步mysql返回的结果数组!

异步mysql的使用场景描述: 比如阅览一片文章 当读取这篇文章的时候我们需要对阅读量增加1
这个增加1的操作就可以异步mysql来执行;这只是举个例子如果是其他费时的mysql操作 作用会体现的更加明显;就解释到这里吧!

现在我们来讲解一下如何异步redis操作

a. 首先我们要安装redis 这个我们在之前的总结当中写过ubuntu下如何安装redis 去参考即可!

b. 我们需要安装hiredis库 https://github.com/redis/hiredis/releases
去下载vo.13.3版本 然后解压到softpageck目录里面去 当然别的地方也行 执行如下命令 unzip
hiredis-0.13.3.zip cd hiredis-0.13.3 make -j make install ldconfig
这样hiredis库安装完毕!

c. 现在我们需要重新编译swoole 因为之前我们已经安装好了phpize7.0
并且在之前的swoole解压后的目录生成了configure所以这里不再多讲! ./configure
–with-php-config=/usr/bin/php-config --enable-async-redis 然后 make clean //这个是清楚上次编译余留的垃圾文件 make make install 完成编译! 查看是否安装成功: php -m
是否存在swoole扩展 php --ri swoole命令查看下边如图:
在这里插入图片描述
说明php支持了hiredis库!

d.
我们来上代码:

<?php
$client = new swoole_redis();
//有密码那么就得要这么写哦!
$client->__construct($option=['password'=>'huxiaobai']);
$client->connect('0.0.0.0',6379,function(swoole_redis $client,$result){
    if($result === false){
        echo "链接失败!";
    }
    $client->set('key','swoole',function(swoole_redis $client,$result){
        var_dump($result);
        $client->close();
    });
    $client->get('key',function(swoole_redis $client,$result){
        //这里的$result就是获取到的key的值
        var_dump($result);
        $client->close();
    });
});

//因为swoole里面的redis是异步的 所以会先走到这里打印start 然后进入到redis里面set 以及 get echo
“start”;

?>

进程详解:
主进程当中创建单个子进程

<?php
//swoole当中创建子进程  swoole_process()一共有四个参数  详情参考手册:https://wiki.swoole.com/wiki/page/214.html
$process = new swoole_process(function(swoole_process $pro){
    //我们第二个参数设置为了true那么打印将直接计入到swoole当中进程管道里面不会在屏幕上打印 所以为了测试我们这里启动http_server服务
    $pro->exec('/usr/bin/php','/root/work/softpackage/swoole/examples/server/httpserver.php');
},false);
//要点去start开启子进程
$pid = $process->start();
echo $pid.PHP_EOL;
//等待子进程结束
swoole_process::wait();
?>

现在我们来讲解一下主进程当中创建多个子进程:
假设有六个url我们需要获取里面的内容 我们可以开启六个进程分别取读取其中一个url的内容放入到进程管道当中 然后再从进程管道里面读取出来 打印到控制台
上代码:

<?php
echo "程序开始时间:".date('Y-m-d H:i:s').PHP_EOL;
$urls = [
    'http://www.baidu.com',
    'http://www.qq.com',
    'http://www.sina.com',
    'http://www.mico.com',
    'http://www.wahaha.com',
    'http://www.shuangwaiwai.com'
];
$workers = [];
//此处for循环 用来创建6个子进程
for($i=0;$i<6;$i++){
    //new swoole_process()创建子进程  里面有三个参数 去看手册construct()吧!   看到下班的use($i,$urls) 了吗 必须use 不然在闭包函数当中没法使用外边的哦!
    $process = new swoole_process(function(swoole_process $worker) use($i,$urls){
        $content = curlData($urls[$i]);
        //echo $content.PHP_EOL;//因为第二个参数是true 所以这里的echo  也就直接进入到了进程的管道当中去了
        $worker->write($content.PHP_EOL);//此处这么写和上边echo都是一样的 都是写入到管道当中去!
    },true);
    $pid = $process->start();//启动创建子进程
    $workers[$pid] = $process;//我们将创建好的子进程放入到数组当中 方便我们后边调用读取管道里面的内容
}

function curlData($url){
    sleep(1);//模拟一下吧
    return "输出结果为:".$url;
}

foreach($workers as $process){
    echo $process->read();
}

echo "程序结束时间:".date('Y-m-d H:i:s').PHP_EOL;
?>

现在我们来讲解一下swoole当中内存

swoole当中的内存操作 有7种类型 我们只讲解table类型 其他的用到的时候再说 我们执行php脚本
在执行过程当中我们创建了table表格并且写入了数据 也许子进程当中也共享使用了table表里面的数据
但是当php脚本执行完毕之后table表里面的数据就被释放了哦!

<?php
//创建内存表
$table = new swoole_table(1024);
//内存表当中增加一列
$table->column('id',$table::TYPE_INT,4);
$table->column('name',$table::TYPE_STRING,64);
$table->column('age',$table::TYPE_INT,4);
$table->create();

//这个就跟redis里面的队列一样或者说hash值一样 需要有个key可以理解成mysql当中的主键
$table->set('singwa_imooc',['id'=>1,'name'=>'singwa','age'=>5]);
//对singwa_imooc这个key下的age字段的值加2
$table->incr('singwa_imooc','age',2);
//获取的时候要读取主键
print_r($table->get('singwa_imooc'));

//另外一种设置的方法如下
// $table['singwa_imooc2'] = [
//     'id' =>2,
//     'name' =>'huxiaobai',
//     'age'=>6
// ];
// print_r($table['singwa_imooc2']);
?>

//关于内存当中table表格的使用还有很多方法 比如删除 自减 判断key是否存在等 自己去看手册吧!

现在我们来讲解一下swoole当中的协程

swoole2以后内置了协程的能力 现在我们单纯的来讲解一下swoole当中的redis协程的使用

直接上代码:

<?php
$http = new swoole_http_server('0.0.0.0',9503);
$http->on('request',function($request,$response){
    $redis = new Swoole\Coroutine\Redis();
    $redis->connect('192.168.1.174',6379);
    $value = $redis->get($request->get['a']);
    $response->header('Content-type','text/plain');
    $response->end($value);
});
$http->start();
?>

之前我们在学习swoole当中的redis异步的时候 redis都是异步执行的
这句话好像是废话一般,都是在下边的程序执行完成之后又去执行的redis操作
这样说实话不怎么好!并且swoole当中的更高版本建议我们废弃异步 直接使用协程来处理 协程 比如这里的redis协程
和其他代码都是同步的 但是协程其实又是在进程当中起来一个线程去执行的
效率很高,协程是线程的一种;我们感觉像是同步的其实它是另起了一个线程去执行的 效率大大滴!

如果我们在redis当中需要用到mysql当中的数据 那么我们使用swoole当中的协程redis以及协程mysql
两者运行的效率远远比平时我们等待mysql查询完在去执行redis这种顺序执行要快很多,所以swoole当中mysql和redis协程的运行时间是两者的较大值
而不是两者运行时间的和 这个一定要记住哦!

我们要分清什么时候使用异步redis什么手使用同步redis什么时候使用征程的redis类
当我们从redis当中往外读取数据的时候就不要使用异步和协程redis了!而是使用redis.so扩展即可!

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值