muduo_inspect库源码分析(41)

muduo的inspect库以及TCP的Keep-Alive时间分析 一:源码分析     今天剖析muduo inspect库。muduo_inspect库通过HTTP的方式为服务器提供监控接口。比如提供以下功能: 接收了多少个TCP连接当前有多少个活动连接一共响应了多少次请求每次请求的平均响应时间为多少ms...     inspect库主要有Inspector类组成,它包含了一个HttpServer对象,并同过ProcessInspector类返 阅读详情

muduo_inspect库通过HTTP方式为服务器提供监控接口
接受了多少个TCP连接
当前有多少个活动连接
一共响应了多少次请求
每次请求的平均响应时间多少毫秒
。。。

Inspector     // 包含了一个HttpServer对象
ProcessInspector // 通过ProcessInfo返回进程信息

ProcessInfo // 获取进程相关信息





ProcessInspectort头文件

ProcessInspector.h

// Copyright 2010, Shuo Chen.  All rights reserved.
// http://code.google.com/p/muduo/
//
// Use of this source code is governed by a BSD-style license
// that can be found in the License file.

// Author: Shuo Chen (chenshuo at chenshuo dot com)
//
// This is an internal header file, you should not include this.

#ifndef MUDUO_NET_INSPECT_PROCESSINSPECTOR_H
#define MUDUO_NET_INSPECT_PROCESSINSPECTOR_H

#include <muduo/net/inspect/Inspector.h>
#include <boost/noncopyable.hpp>

namespace muduo
{
namespace net
{

class ProcessInspector : boost::noncopyable
{
 public:
  void registerCommands(Inspector* ins);    // 注册命令接口

 private:
  static string pid(HttpRequest::Method, const Inspector::ArgList&);
  static string procStatus(HttpRequest::Method, const Inspector::ArgList&);
  static string openedFiles(HttpRequest::Method, const Inspector::ArgList&);
  static string threads(HttpRequest::Method, const Inspector::ArgList&);

};

}
}

#endif  // MUDUO_NET_INSPECT_PROCESSINSPECTOR_H




ProcessInspectort源文件

ProcessInspector.cc


// Copyright 2010, Shuo Chen.  All rights reserved.
// http://code.google.com/p/muduo/
//
// Use of this source code is governed by a BSD-style license
// that can be found in the License file.

// Author: Shuo Chen (chenshuo at chenshuo dot com)
//

#include <muduo/net/inspect/ProcessInspector.h>
#include <muduo/base/ProcessInfo.h>
#include <stdio.h>

using namespace muduo;
using namespace muduo::net;

/*命令注册*/
void ProcessInspector::registerCommands(Inspector* ins)
{
  /*muduo             :   proc
    command           :   pid
    commandCallback   :   pid
    commnadHelp       :   print id
  */
  ins->add("proc", "pid", ProcessInspector::pid, "print pid");
  ins->add("proc", "status", ProcessInspector::procStatus, "print /proc/self/status");
  ins->add("proc", "opened_files", ProcessInspector::openedFiles, "count /proc/self/fd");
  ins->add("proc", "threads", ProcessInspector::threads, "list /proc/self/task");
}

string ProcessInspector::pid(HttpRequest::Method, const Inspector::ArgList&)
{
  char buf[32];
  snprintf(buf, sizeof buf, "%d", ProcessInfo::pid());
  return buf;
}

string ProcessInspector::procStatus(HttpRequest::Method, const Inspector::ArgList&)
{
  return ProcessInfo::procStatus();
}

string ProcessInspector::openedFiles(HttpRequest::Method, const Inspector::ArgList&)
{
  char buf[32];
  snprintf(buf, sizeof buf, "%d", ProcessInfo::openedFiles());
  return buf;
}

string ProcessInspector::threads(HttpRequest::Method, const Inspector::ArgList&)
{
  std::vector<pid_t> threads = ProcessInfo::threads();
  string result;
  for (size_t i = 0; i < threads.size(); ++i)
  {
    char buf[32];
    snprintf(buf, sizeof buf, "%d\n", threads[i]);
    result += buf;
  }
  return result;
}





Inspector头文件

Inspector.h

// Copyright 2010, Shuo Chen.  All rights reserved.
// http://code.google.com/p/muduo/
//
// Use of this source code is governed by a BSD-style license
// that can be found in the License file.

// Author: Shuo Chen (chenshuo at chenshuo dot com)
//
// This is a public header file, it must only include public header files.

#ifndef MUDUO_NET_INSPECT_INSPECTOR_H
#define MUDUO_NET_INSPECT_INSPECTOR_H

#include <muduo/base/Mutex.h>
#include <muduo/net/http/HttpRequest.h>
#include <muduo/net/http/HttpServer.h>

#include <map>
#include <boost/function.hpp>
#include <boost/noncopyable.hpp>
#include <boost/scoped_ptr.hpp>

namespace muduo
{
namespace net
{

class ProcessInspector;

// A internal inspector of the running process, usually a singleton.
class Inspector : boost::noncopyable
{
 public:
  typedef std::vector<string> ArgList;
  typedef boost::function<string (HttpRequest::Method, const ArgList& args)> Callback;
  Inspector(EventLoop* loop,
            const InetAddress& httpAddr,
            const string& name);
  ~Inspector();

  // 如add("proc", "pid", ProcessInspector::pid, "print pid");
  // http://192.168.159.188:12345/proc/pid这个http请求就会相应的调用ProcessInspector::pid来处理
  void add(const string& module,
           const string& command,
           const Callback& cb,
           const string& help);

 private:
  typedef std::map<string, Callback> CommandList;
  typedef std::map<string, string> HelpList;

  void start();
  void onRequest(const HttpRequest& req, HttpResponse* resp);

  HttpServer server_;
  boost::scoped_ptr<ProcessInspector> processInspector_;
  MutexLock mutex_;
  std::map<string, CommandList> commands_;
  std::map<string, HelpList> helps_;
  /*
commands_ --- > <muduo commandlist>
helps_    ----> <muduo helplist>

commandlist---> <command callback>
HelpList  ----> <command helplist>

commands_[][]=xx
helps_[][]=xxx 

  */
};

}
}

#endif  // MUDUO_NET_INSPECT_INSPECTOR_H




Inspector源文件

Inspector.cc

// Copyright 2010, Shuo Chen.  All rights reserved.
// http://code.google.com/p/muduo/
//
// Use of this source code is governed by a BSD-style license
// that can be found in the License file.

// Author: Shuo Chen (chenshuo at chenshuo dot com)
//

#include <muduo/net/inspect/Inspector.h>

#include <muduo/base/Thread.h>
#include <muduo/net/EventLoop.h>
#include <muduo/net/http/HttpRequest.h>
#include <muduo/net/http/HttpResponse.h>
#include <muduo/net/inspect/ProcessInspector.h>

//#include <iostream>
//#include <iterator>
//#include <sstream>
#include <boost/bind.hpp>
#include <boost/algorithm/string/classification.hpp>
#include <boost/algorithm/string/split.hpp>

using namespace muduo;
using namespace muduo::net;

namespace
{
Inspector* g_globalInspector = 0;

// Looks buggy
std::vector<string> split(const string& str)
{
  std::vector<string> result;
  size_t start = 0;
  size_t pos = str.find('/');
  while (pos != string::npos)
  {
    if (pos > start)
    {
      result.push_back(str.substr(start, pos-start));
    }
    start = pos+1;
    pos = str.find('/', start);
  }

  if (start < str.length())      // 说明最后一个字符不是'/'
  {
    result.push_back(str.substr(start));
  }

  return result;
}

}

Inspector::Inspector(EventLoop* loop,
                     const InetAddress& httpAddr,
                     const string& name)
    : server_(loop, httpAddr, "Inspector:"+name),
      processInspector_(new ProcessInspector)
{
  //断言在主线程当中
  assert(CurrentThread::isMainThread());
  assert(g_globalInspector == 0);
  g_globalInspector = this;
  /*设置请求的回调函数,这里的请求是完成了协议的解析之后*/
  server_.setHttpCallback(boost::bind(&Inspector::onRequest, this, _1, _2));
  /*注册命令*/
  processInspector_->registerCommands(this);
  // 这样子做法是为了防止竞态问题
  // 如果直接调用start,(当前线程不是loop所属的IO线程,是主线程)那么有可能,当前构造函数还没返回,
  // HttpServer所在的IO线程可能已经收到了http客户端的请求了(因为这时候HttpServer已启动),那么就会回调
  // Inspector::onRequest,而这时候构造函数还没返回,也就是说对象还没完全构造好。那么就会出现问题了
  loop->runAfter(0, boost::bind(&Inspector::start, this)); // little race condition
}

Inspector::~Inspector()
{
  assert(CurrentThread::isMainThread());
  g_globalInspector = NULL;
}

void Inspector::add(const string& module,
                    const string& command,
                    const Callback& cb,
                    const string& help)
{
  /*这里要不要加锁??
因为在构造函数时 程序是注册完 processInspector_->registerCommands(this); 才执行
loop->runAfter(0, boost::bind(&Inspector::start, this)); // little race condition。

如果程序进行扩充时,就要了。
class TcpInspector : boost::noncopyable
{
 public:
  void registerCommands(Inspector* ins);  // 注册命令接口

 private:
  static string pid(HttpRequest::Method, const Inspector::ArgList&);
  static string procStatus(HttpRequest::Method, const Inspector::ArgList&);
  static string openedFiles(HttpRequest::Method, const Inspector::ArgList&);
  static string threads(HttpRequest::Method, const Inspector::ArgList&);

};

MyInspector :public ProcessInspector
{
   TcpInspector
} ;
那么
MyInspector{

  HttpServer server_;
  boost::scoped_ptr<ProcessInspector> processInspector;
  boost::scoped_ptr<ProcessInspector> TcpInspector;
  MutexLock mutex_;
  std::map<string, CommandList> commands_;
  std::map<string, HelpList> helps_;
}
那么MyInspector 在初始化时,会初始化ProcessInspector 和TcpInspector,
如果ProcessInspector初始化完毕后,启动了OnreRequest(),而TcpInspector还没注册完,
也就是说TcpInspector还在add()函数中,那么不加锁的话,就会出现问题了。
  */
  MutexLockGuard lock(mutex_);
  commands_[module][command] = cb;
  helps_[module][command] = help;
}

void Inspector::start()
{
  server_.start();
}

void Inspector::onRequest(const HttpRequest& req, HttpResponse* resp)
{
  if (req.path() == "/")
  {
    string result;
    MutexLockGuard lock(mutex_);
    // 遍历helps 
    for (std::map<string, HelpList>::const_iterator helpListI = helps_.begin();
         helpListI != helps_.end();
         ++helpListI)
    {
      const HelpList& list = helpListI->second;
      for (HelpList::const_iterator it = list.begin();
           it != list.end();
           ++it)
      {
        result += "/";
        result += helpListI->first;      // module
        result += "/";
        result += it->first;         // command
        result += "\t";
        result += it->second;            // help
        result += "\n";
      }
    }
    resp->setStatusCode(HttpResponse::k200Ok);
    resp->setStatusMessage("OK");
    resp->setContentType("text/plain");
    resp->setBody(result);
  }
  else
  {
    // 以"/"进行分割,将得到的字符串保存在result中
    std::vector<string> result = split(req.path());
    // boost::split(result, req.path(), boost::is_any_of("/"));
    //std::copy(result.begin(), result.end(), std::ostream_iterator<string>(std::cout, ", "));
    //std::cout << "\n";
    bool ok = false;
    if (result.size() == 0)
    {
      // 这种情况是错误的,因此ok仍为false
    }
    else if (result.size() == 1)
    {
      // 只有module,没有command也是错的,因此ok仍为false
      string module = result[0];
    }
    else
    {
      string module = result[0];
      // 查找module所对应的命令列表
      std::map<string, CommandList>::const_iterator commListI = commands_.find(module);
      if (commListI != commands_.end())
      {
        string command = result[1];
        const CommandList& commList = commListI->second;
        // 查找command对应的命令
        CommandList::const_iterator it = commList.find(command);
        if (it != commList.end())
        {
          ArgList args(result.begin()+2, result.end());     // 传递给回调函数的参数表
          if (it->second)
          {
            resp->setStatusCode(HttpResponse::k200Ok);
            resp->setStatusMessage("OK");
            resp->setContentType("text/plain");
            const Callback& cb = it->second;
            resp->setBody(cb(req.method(), args));       // 调用cb将返回的字符串传给setBody
            ok = true;
          }
        }
      }

    }

    if (!ok)
    {
      resp->setStatusCode(HttpResponse::k404NotFound);
      resp->setStatusMessage("Not Found");
    }
    //resp->setCloseConnection(true);
  }
}




测试程序

#include <muduo/net/inspect/Inspector.h>
#include <muduo/net/EventLoop.h>
#include <muduo/net/EventLoopThread.h>

using namespace muduo;
using namespace muduo::net;

int main()
{
  EventLoop loop;
  EventLoopThread t;    // 监控线程 ,这里“线程该没有真正创建,t.startLoop()函数才是真正创建”
  /*Inspector 的loop 和main thread 的loop是不一样的,也就是说,现在已经有两个loop了*/
  Inspector ins(t.startLoop(), InetAddress(12345), "test");
  loop.loop();
}


在浏览器中输入127.0.0.1:12345/proc/status

Name:   inspector_test
State:  S (sleeping)
Tgid:   2030
Pid:    2030
PPid:   1907
TracerPid:  0
Uid:    1000    1000    1000    1000
Gid:    1000    1000    1000    1000
FDSize: 256
Groups: 4 20 24 46 116 118 124 1000 
VmPeak:    12348 kB
VmSize:    12348 kB
VmLck:         0 kB
VmHWM:      1580 kB
VmRSS:      1580 kB
VmData:     8408 kB
VmStk:       136 kB
VmExe:       860 kB
VmLib:      2868 kB
VmPTE:        32 kB
VmSwap:        0 kB
Threads:    2
SigQ:   0/9772
SigPnd: 0000000000000000
ShdPnd: 0000000000000000
SigBlk: 0000000000000000
SigIgn: 0000000000001000
SigCgt: 0000000180000000
CapInh: 0000000000000000
CapPrm: 0000000000000000
CapEff: 0000000000000000
CapBnd: ffffffffffffffff
Cpus_allowed:   3
Cpus_allowed_list:  0-1
Mems_allowed:   1
Mems_allowed_list:  0
voluntary_ctxt_switches:    22
nonvoluntary_ctxt_switches: 14



详细muduo的使用(二)——安装与使用 Muduo是什么? muduo是陈硕大神个人开发的C++的TCP网络编程库。muduo基于Reactor模式实现。Reactor模式也是目前大多数Linux端高性能网络编程框架和网络应用所选择的主要架构,例如内存数据库Redis和Java的Netty库等。 陈硕的《Linux多线程服务器端编程》一书对muduo整个架构进行了非常详尽的介绍和分析,可以说是学习muduo源码和设计理念最好的资料了... 阅读详情

相关推荐

python: inspect模块各函数的用法

inspect模块也被称为 检查现场对象。这里的重点在于“现场”二字,也就是当前运行的状态。inspect模块提供了一些函数来了解现场对象,包括 模块、类、实例、函数和方法。inspect函数主要用于以下四个方面。

weixin_43915090的博客 939

muduo网络库学习之muduo_inspect 库涉及到的类

muduo inspect 库通过HTTP方式为服务器提供监控接口, 现在只实现进程相关信息的监控,通过成员ProcessInspector 实现。 ProcessInspector // 通过ProcessInfo返回进程信息      ProcessInfo // 获取进程相关信息 我们可以参照 ProcessInspector 实现一个如TcpStateInspector,作为

Meditation 3918

muduo库手册

感觉不错,分享一下,它的使用手册,希望它对你有帮助

(P41)muduo_inspect库源码分析:muduo_inspect库通过HTTP方式为服务器提供监控接口

文章目录1.muduo_inspect库通过HTTP方式为服务器提供监控接口 1.muduo_inspect库通过HTTP方式为服务器提供监控接口 接受了多少个TCP连接 当前有多少个活动连接 一共响应了多少次请求 每次请求的平均响应时间多少毫秒 。。。 Inspector // 包含了一个HttpServer对象 ProcessInspector // 通过ProcessInfo返回进程信息 ProcessInfo // 获取进程相关信息,muduo_base库中已做了解

喜欢打篮球的普通人 528

inspect

检查 小型CLI可以查询图像的元数据以尝试确定其来源。 当前仅适用于生成到GitHub的比较链接。 安装 brew tap garethjevans/tap brew install inspect 可以将其与以下内容一起用于docker容器: docker run -it garethjevans/inspect 用法 检查图像 inspect image <image> 例如 inspect image jenkinsciinfra/terraform 将产生类似于以下内容的输出: +------------------------------------------+----------------------------------------------------------------+ | LABEL

微软inspect.exe(类似 Spy++、UI Spy)

请使用最新版解压缩软件解压,或者使用7-zip解压 微软的东西 在Windows 8.1 SDK中有带 类似 Spy++ / UI Spy,可以查看窗口及其内各种元素,可pinvoke 详细描述在此链接 http://technet.microsoft.com/zh-cn/magazine/dd318521(en-us,VS.85).aspx 用Dependency Walker看了下DLL依赖,都是系统的,应该可以直接用的吧,谁用过后回复反馈一下

python模块—inspect_inspect

inspect的主要作用。

m0_60634964的博客 1477

微软inspect x86

微软控件识别工具X86,支持大部分控件,自动化测试的必备工具

发布一个基于 Reactor 模式的 C++ 网络库

Muduo 是我在业余时间编写的基于 Reactor 模式的 C++ 网络库,适用于 Linux 平台,支持多线程。本文主要介绍 muduo 网络库的使用。其设计与实现将有另文讲解。

陈硕的Blog 9万+

Ubuntu系统下搭建QtCreator开发环境详细过程(Qt简介;Linux下安装QtCreator;Ubuntu安装后闪退蓝屏的解决方法)

Qt 是一个跨平台的 C++开发库。主要用来开发图形用户界面 (Graphical User Interface,简称 GUI) 程序。Qt 支持的操作系统有很多,例如通用操作系统 Windows、Linux、Unix、智能手机系统Android、ios、WinPhone, 嵌入式系统 QNX、VxWorks 等。

分享学习成长历程,Winter is coming! 8575

NXP KW45开发实战:如何在MCUXpresso IDE中正确配置J-Link并解决设备识别问题

本文详细介绍了在MCUXpresso IDE中配置J-Link以支持NXP KW45无线MCU开发的实战指南。从环境准备到J-Link深度配置,再到烧录流程优化和高级调试技巧,帮助开发者高效解决设备识别问题并提升开发效率。特别推荐使用JLinkRemoteServer.exe进行多设备并行调试,并提供了烧录速度优化和低功耗调试的实用技巧。

gray5的博客 864

第十八章:语言工具-inspect:检查现场对象-获取源代码

18.4.6 获取源代码 如果可以得到一个模块的.py文件,则可以使用getsource()和getsourcelines()获取类或方法的原始源代码。 import inspect import example print(inspect.getsource(example.A)) 传入一个类时,输出中会包含这个类的所有方法。 要获取一个方法的源代码,可以将这个方法引用传入getsourc...

编程菜鸟iglesias的博客 552

【PADS-开发指南】

馒头破戒大师

sinat_23896491的博客 3225

[Vue CLI 3] vue inspect 的源码设计实现

首先,请记住: 它在新版本的脚手架项目里面非常重要 它有什么用呢? inspect internal webpack config 能快速地在控制台看到对应生成的 webpack 配置对象。 首先它是 vue 的一个扩展命令,在文件 @vue/cli/bin/vue.js 中定义了 command 还是依赖了工具包 commander c...

weixin_33695082的博客 591

EDK2多平台编译实战:从树莓派到RK3399的UEFI固件构建

本文提供了从树莓派到RK3399的EDK2多平台编译实战指南。详细介绍了UEFI固件构建的环境搭建、交叉编译配置、平台描述文件修改及常见问题排查,帮助开发者掌握为不同ARM架构硬件构建标准化启动固件的核心技能。

weixin_29193259的博客 836

XS5018A TX ISP芯片同轴高清模拟输出图像处理芯片用户指南

当看门狗开启且系统异常,看门狗模块得不到喂狗信号时,自动产生看门狗复位信号,复位​​​​​​​。分量上与人眼对光谱的响应通常是有偏差的,通常通过一个色彩校正矩阵校正光谱响应的交叉效。但实际上由于电路的作用,复位模块对整个芯片及各个功能模块的复位进行统一管理,负责产生各个模块的软复位信。每个区域前景色可以在 16 个色表中查找,单个区域的前景色统一,各个区域的前。较弱的坏点被放大成刺眼的白点和明显的黑点,强烈推荐开启动态坏点校正功能。四种制式,通过寄存器配置,可以很方便的实现四种制式的切换、且可以对截止。

2403_88922712的博客 1961

muduo网络库学习之muduo_http 库涉及到的类

1、http request: request line + header + body (header分为普通报头,请求报头与实体报头) header与body之间有一空行(CRLF) 请求方法有: Get, Post, Head, Put, Delete等 协议版本1.0、1.1 常用请求头 Accept:浏览器可接受的

Meditation 6123

Claude Code(7):从零创建你的第一个 Skill

在前面的文章,我们介绍了 Skills 的基本概念,这篇我们深入实战,从零开始创建你的第一个 Skill,并逐步解锁所有配置项,让 Claude Code 真正熟悉你的测试规范和习惯。

blues_C的博客 649

Python自省(反射)指南

在笔者,也就是我的概念里,自省和反射是一回事,当然其实我并不十分确定一定以及肯定,所以如果这确实是两个不同的概念的话,还请多多指教 :) 转载请注明作者、出处并附上原文链接,多谢!update 2011-3-10: 更正函数的func_globals属性含义。 首先通过一个例子来看一下本文中可能用到的对象和相关概念。 #coding: UTF-8 import sys # 模块...

weixin_30813225的博客 1210

华为OD机考真题 新系统 - 小菊的寻宝之旅(Java/Py/C/C++/Js/Go)

华为OD机试真题 新系统【小菊的寻宝之旅】,提供C语言、Java、C++、Python、Go、JavaScript源码实现,并提供完整思路讲解,以及在线OJ模拟做题。华为OD机试真题 【小菊的寻宝之旅】多语言题解。

qq_45776114的博客 624

蓝牙模块HM-10手册

蓝牙模块HM-10手册,该手册对蓝牙模块HM-10进行了详细的介绍和使用方法以及一些AT指令。

标准库-inspect

import inspect import os #basename求一长串路径中,最后一个路径的名字 binary = os.path.basename(inspect.stack()[-1][1]) print (binary) binary1 = (inspect.stack()) print (binary1) binary2 = (inspect.stack()[-1][1]) p...

you 922

VC中sqlite的使用

在VC中对sqlite的使用,实现数据库大批数据的快速插入,读取数据库中的数据在ListCtrl中显示

muduo的研究目录

发布一个基于 Reactor 模式的 C++ 网络库 https://blog.youkuaiyun.com/solstice/article/details/5848547 – – – – – – – – – – – – – – – – – – – ...

喜欢打篮球的普通人 1298

迈瑞BS系列全自动生化分析仪LIS协议接口手册(内含HL7和ASTM)

迈瑞BS系列全自动生化分析仪LIS协议接口手册,内含HL7和ASTM)

SIMATIC S7-1200, S7-1500 PID 控制[手册]

SIMATIC S7-1200, S7-1500 PID 控制[手册]

EventLoop再分析之IO线程(29)

EventLoop IO 线程的简单描述 进程(线程)wait/notify pipe socketpair eventfd eventfd 是一个比 pipe 更高效的线程间事件通知机制,一方面它比 pipe 少用一个 file descripor,节省了资源;另一方面,eventfd 的缓冲区管理也简单得多,全部“buffer” 只有定

Riak, Spark, Golang, Erlang, 云存储, 云计算, 数据挖掘 6126

http实现源码(40-1)

HttpRequest头文件(包含实现) HttpResquest.h // Copyright 2010, Shuo Chen. All rights reserved. // http://code.google.com/p/muduo/ // // Use of this source code is governed by a BSD-style license // that ca

Riak, Spark, Golang, Erlang, 云存储, 云计算, 数据挖掘 3144
上一篇: http实现源码(40-1)
下一篇: 文件传输服务器(43)
laohan_
博客等级 码龄15年 112粉丝 224原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值