nginx 变量的保存结构和优化

Nginx多HTTPS证书配置精简:SNI与变量化路径实战 在Web服务器部署中,HTTPS证书配置是保障通信安全的基础环节。其核心原理是通过SSL/TLS协议对传输数据进行加密,而SNI(服务器名称指示)扩展技术则解决了单一IP地址承载多个HTTPS域名的关键难题,允许服务器在握手阶段根据客户端请求的域名选择正确的证书。这项技术的工程价值在于,它为实现配置的集中化与自动化管理提供了可能,从而显著提升运维效率和系统可靠性。在实际应用场景中,面对服务器托管数十上百个域名的需求,传统的‘一域一配’模式会导致配置文件臃肿且难以维护。通过引入变量化证书路径,结合Nginx的 阅读详情
在使用nginx lua 时经常会使用ngx.var.varname 来获取变量,随着业务的越复杂,变量越来越多,发现服务的速度明显变慢了
本文分析了整个变量的获取和保存逻辑
  1. ngx_lua 获取变量

首先ngx_lua 获取变量调用的是 ngx_http_lua_var_get(lua_State *L)

46-static int
 47ngx_http_lua_var_get(lua_State *L)
 48{
 49    ngx_http_request_t          *r;
 50    u_char                      *p, *lowcase;
 51    size_t                       len;
 52    ngx_uint_t                   hash;
 53    ngx_str_t                    name;
 54    ngx_http_variable_value_t   *vv;
 55
 56#if (NGX_PCRE)
 57    u_char                      *val;
 58    ngx_uint_t                   n;
 59    LUA_NUMBER                   index;
 60    int                         *cap;
 61#endif
 62
 63    r = ngx_http_lua_get_req(L);
 64    if (r == NULL) {
 65        return luaL_error(L, "no request object found");
 66    }
 ...
 111    p = (u_char *) lua_tolstring(L, -1, &len);
112
113    lowcase = lua_newuserdata(L, len);
114
115    hash = ngx_hash_strlow(lowcase, p, len);
116
117    name.len = len;
118    name.data = lowcase;
119    dd("variable name: %.*s", (int) len, lowcase);
120    vv = ngx_http_get_variable(r, &name, hash);

可见就是调用nginx的变量获取接口, 可以看到前面将变量名全部变为小写然后计算hash,ngx_lua是不区分大小写的

  1. nginx 变量的获取
546-ngx_http_variable_value_t *
 547ngx_http_get_variable(ngx_http_request_t *r, ngx_str_t *name, ngx_uint_t key)
 548{
 549    ngx_http_variable_t        *v;
 550    ngx_http_variable_value_t  *vv;
 551    ngx_http_core_main_conf_t  *cmcf;
 552
 553    cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);
 554
 555    v = ngx_hash_find(&cmcf->variables_hash, key, name->data, name->len);
 556
  1. nginx 变量的保存结构
    前面已经猜到nginx 的变量是保存在hash表中的, 主要的结构保存在ngx_hash.c 中
    这里主要分析两个函数ngx_hash_find 和 ngx_hash_init

ngx_hash_find 就是典型的拉链式hash表的处理方式,先定位bucket, 然后挨个对比,那么效率就在于hash bucket的构建上了

12-void *
  13ngx_hash_find(ngx_hash_t *hash, ngx_uint_t key, u_char *name, size_t len)
  14{
  15    ngx_uint_t       i;
  16    ngx_hash_elt_t  *elt;
  17
  18#if 0
  19    ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0, "hf:\"%*s\"", len, name);
  20#endif
  21
  22    elt = hash->buckets[key % hash->size];
  23
  24    if (elt == NULL) {
  25        return NULL;
  26    }
  27    
  28    while (elt->value) {
  29        
  30        if (len != (size_t) elt->len) {
  31            goto next;
  32        }
  33
  34        for (i = 0; i < len; i++) {
  35            if (name[i] != elt->name[i]) {
  36                goto next;
  37            }
  38        }
  39        
  40        return elt->value;
  41
  42    next:
  43
  44        elt = (ngx_hash_elt_t *) ngx_align_ptr(&elt->name[0] + elt->len,
  45                                               sizeof(void *));
  46        continue;
  47    }
  48    
  49    return NULL;
  50}

下面我们来看一下 hash bucket的构建

// 用于计算存储一个变量所需要的空间
249#define NGX_HASH_ELT_SIZE(name)                                               \
 250    (sizeof(void *) + ngx_align((name)->key.len + 2, sizeof(void *)))
 251
 252-ngx_int_t
 253ngx_hash_init(ngx_hash_init_t *hinit, ngx_hash_key_t *names, ngx_uint_t nelts)
 254{

NGX_HASH_ELT_SIZE 是计算每个key 所占用的空间
每一个bucket 中key的保存如下
ngx_hash_elt_t

valuelenname
point to ngx_http_core_srv_conf_tkey.len(short)key.data
NULL(队尾)key.len(short)key.data

因为key.len 是short 类型的所以需要在+2 然后对齐

nginx 对于变量的保存是存在hash 表中, 在配置中有两个参数
server_names_hash_bucket_size 每个bucket 的大小,决定着每个bucket 最多能放入多少个对象
server_names_hash_max_size 最大的bucket 数量
nginx 用拿现有的变量列表从一个hash size开始尝试, 及 尝试把所有符合条件 hash(key)%size 的key放到一个bucket 中,不过不能则增大size 直到 max_size,让然不行就会报错。
这中间就会有两个问题
1 这种尝试会使nginx 启动变慢
2 如果bucket size 过大会使太多的单个bucket 中存放了太多的对象导致,在运行中获取变量时的开销变大,相应的bucket 太小又会使size 很大消耗更多的内存
nginx 在这方面是选择了在参数内尽可能的减少内存的使用,但是在实际环境中增大内存来加快变量的访问时非常可取的也是。

 252-ngx_int_t
 253ngx_hash_init(ngx_hash_init_t *hinit, ngx_hash_key_t *names, ngx_uint_t nelts)
 {
 ... 
 // 1 首先check 一下当前bucekt size 是不是能放下所有的对象
  270    for (n = 0; n < nelts; n++) {
 271        ngx_uint_t var_size = NGX_HASH_ELT_SIZE(&names[n]) + sizeof(void *);
 272        if (hinit->bucket_size < NGX_HASH_ELT_SIZE(&names[n]) + sizeof(void *))
 273        {
 274            ngx_log_error(NGX_LOG_EMERG, hinit->pool->log, 0,
 275                          "could not build %s, you should "
 276                          "increase %s_bucket_size: %i",
 277                          hinit->name, hinit->name, hinit->bucket_size);
 278            return NGX_ERROR;
 279        }
 280        
 283        if (NULL == names[n].key.data){
 284            ngx_log_error(NGX_LOG_EMERG, hinit->pool->log, 0, "ngx_hash_init error var %i", n);
 285            continue;
 286        }
        }
       ...
       // 接下来开始, 顺序尝试,看能否确定一个 size 后, 看能不能把符合hash%size的key 放入同一个桶中, 如果不能则继续增大size 直至max_size
 311    if (hinit->max_size > 10000 && nelts && hinit->max_size / nelts < 100) {
 312        start = hinit->max_size - 1000;
 313    }
 314    ngx_log_error(NGX_LOG_EMERG, hinit->pool->log, 0,
 315                          "ngx_hash_init start confirm size... "
 316                          "name %s start(%i) max_size(%i)",
 317                      hinit->name, start, hinit->max_size);
 318    if (hinit->max_size < 20*nelts)
 319        hinit->max_size = 20*nelts;
 320    for (size = start; size <= hinit->max_size; size++) {
 321
 322        ngx_memzero(test, size * sizeof(u_short));
 323
 324        for (n = 0; n < nelts; n++) {
 325            if (names[n].key.data == NULL) {
 326                continue;
 327            }
 328
 329            key = names[n].key_hash % size;
 330            test[key] = (u_short) (test[key] + NGX_HASH_ELT_SIZE(&names[n]));
 331
 332#if 0
 333            ngx_log_error(NGX_LOG_ALERT, hinit->pool->log, 0,
 334                          "%ui: %ui %ui \"%V\"",
 335                          size, key, test[key], &names[n].key);
 336#endif
 337
 338            if (test[key] > (u_short) bucket_size) {
 339                goto next;
 340            }
 341        }
 342        ngx_log_error(NGX_LOG_EMERG, hinit->pool->log, 0,
 343                          "ngx_hash_init confirm size "
 344                          "name %s bucket_cnt(%i) var_cnt(%i) bucket_size(%i) max_size(%i)",
 345                      hinit->name, size, nelts, hinit->bucket_size, hinit->max_size);
 346        goto found;
 347
 348    next:
 349
 350        continue;
 351    }
 352
 353    size = hinit->max_size;
 354
 355    ngx_log_error(NGX_LOG_WARN, hinit->pool->log, 0,
 356                  "could not build optimal %s, you should increase "
 357                  "either %s_max_size: %i or %s_bucket_size: %i; "
 358                  "ignoring %s_bucket_size",
 359                  hinit->name, hinit->name, hinit->max_size,
 360                  hinit->name, hinit->bucket_size, hinit->name);
 361
 362found:
 364    for (i = 0; i < size; i++) {      // 每一个bucket 都至少有一个元素,没有value
 365        test[i] = sizeof(void *);
 366    }
... 
// 确定了size 后面就开始根据元素来分配空间,创建hash表, 填充空间了
 }

nginx 的hash 是静态表不能运行是添加或删除, 一种优化的方案是在构造hash表时根据所需要存的变量计算bucket size, 例如是平均值的几倍,这样避免业务变化后需要重新调整这参数,也避免了参数不匹配造成的性能问题。另一个就是在尝试size时 初始尝试一个较大值, 毕竟size 过小也会使单个bucket 中的元素过多影响访问性能,同事也能尽快的找到合适的size 加速度nginx 启动。
后续的填充逻辑有机会再详细分析。

docker镜像介绍_Dockerfile详解、构建nginx镜像、镜像优化 docker镜像介绍一、镜像的分层结构二、镜像的构建1、docker commit 构建2、Dockerfile 构建3、查看镜像的分层结构三、Dockerfile详解1、Dockerfile常用指令2、快捷删除镜像四、镜像构建nginx 一、镜像的分层结构 1、共享宿主机的kernel 2、base镜像提供的是最小的Linux发行版 3、同一docker主机支持运行多种Linux发行版 4、采用分层结构的最大好处是:共享资源 5、Copy-on-Write 容器层可写 6、容器层以下所有镜像层都是只读的 阅读详情

相关推荐

Nginx 内存管理源码分析

文章目录Nginx 介绍Nginx 内存管理用到的数据数据结构 Nginx 介绍 Nginx是一款轻量级的Web 服务器/反向代理服务器及电子邮件(IMAP/POP3)代理服务器,并在一个BSD-like 协议下发行。其特点是占有内存少,并发能力强,事实上nginx的并发能力确实在同类型的网页服务器中表现较好,中国大陆使用nginx网站用户有:百度、京东、新浪、网易、腾讯、淘宝等。 Nginx 内...

qq_43390943的博客 1824

ngx_lua 模块API说明

#ngx_lua模块API说明 #参考文档: http://wiki.nginx.org/HttpLuaModuleZh#Core_constants ngx_lua官方文档 http://blog.youkuaiyun.com/imlsz/article/details/42915473  对官方API的主要内容翻译 http://jinnianshilongnian.iteye.com/blog/

D調の學痞 1万+

Nginx架构及配置详解

概述 Nginx采用C进行编写,是俄罗斯程序员开发的一款轻量级的可构建Web 服务器/反向代理服务器及电子邮件(IMAP/POP3)代理服务器的软件, Nginx服务器是一个高性能的HTTP和反向代理web服务器,同时也可提供了IMAP/POP3/SMTP服务。nginx常用作跑静态和做负载反向代理,动态php交给apache处理,因后者比较稳定,jsp交给tomcat、resin或jboss。n......

企业实战系列集 ●●● https://ximenjianxue.blog.youkuaiyun.com 7069

Nginx 变量

从这个例子我们可以窥见的另一个重要特性是,Nginx变量名的可见范围虽然是整个配置,但每个请求都有所有变量的独立副本,或者说都有各变量用来存放值的容器的独立副本,彼此互不干扰。Nginx变量的创建和赋值操作发生在全然不同的时间阶段,Nginx变量的创建只能发生在Nginx配置加载的时候,或者说Nginx启动的时候,而赋值操作则只会发生在请求实际处理的时候。一个例子是arg_name,这个变量的值是当前请求中名为name的参数的值,而且还是未解码的原始形式的值。...

PercyXuBiao的博客 8834

Nginx日志配置实战:Ubuntu下logrotate轮转与结构化日志设计

日志是Web服务可观测性的基石,Nginx作为主流反向代理服务器,其日志机制直接影响故障定位、性能分析与安全审计能力。理解日志的异步缓冲原理、字段取值时机及格式可扩展性,是实现精准采集的前提;而Ubuntu系统层的logrotate工具通过时间/大小策略、压缩归档与安全权限控制,保障日志的可持续性与磁盘健康。技术价值在于将原始文本转化为结构化、可追溯、可压缩、可分级的运维资产——既支撑ELK等平台接入,也适配shell级快速分析。典型应用场景包括电商接口超时排查、CDN回源异常识别、爬虫行为审计及GDPR合

weixin_30372371的博客 408

分享最新版 nginx内置变量 大全

nginx内置变量 内置变量存放在  ngx_http_core_module 模块中,变量的命名方式和apache 服务器变量是一致的。总而言之,这些变量代表着客户端请求头的内容,例如$http_user_agent, $http_cookie, 等等。下面是nginx支持的所有内置变量: $arg_name 请求中的的参数名,即“?”后面的arg_name=arg_value形式的arg_name $args 请求中的参数值 $binary_remote_addr 客户端地址的二进制形式, 固定长度为4个字节 $body_bytes_sent 传输给客户端的字节数,响应头不计算在内;这个

Nginx内置变量详解

1. 请求值 - $args (参数) - $query_string (请求参数) - $is_args (参数判断) - $arg_PARAMETER - $request (客户端请求) - $request_body (客户端请求的报文体) - $request_body_file - $request_filename (请求文件路径) - $request_method (请求的方法) - $uri (请求URI) - $request_uri (请求的URI,带参数) 2. 客户端信息 - $

架构+开发+运维 6597

DPABINet保姆级教程:零代码完成脑网络分析与图论计算

本文提供DPABINet的零代码脑网络分析全流程指南。该工具通过图形化界面集成时间序列提取、连接矩阵构建、图论指标计算及统计分析,大幅降低技术门槛。重点解析了阈值选择、二值化与小世界属性计算等关键步骤,帮助研究者快速掌握脑网络分析与图论计算,提升科研效率与可重复性。

j2k3l4的博客 667

Nginx变量漫谈

至此,本系列教程对Nginx变量的介绍终于可以告一段落了. 我们在这个过程中接触到了许多标准的和第三方的Nginx模块, 这些模块让我们得以很轻松地构造出许多有趣的小例子, 从而可以深入探究Nginx变量的各种行为和特性. 在后续的教程中, 我们还会有很多机会与这些模块打交道.

wang11876的博客 1988

2024.6.10丨基于DIAMOND与VFDB数据库的致病菌毒力因子高效注释指南

本文详细介绍了基于DIAMOND与VFDB数据库的致病菌毒力因子高效注释方法,涵盖工具安装、参数优化、数据库选择及实战案例解析。通过DIAMOND的快速比对和VFDB的全面毒力因子数据,显著提升微生物基因组研究效率,适用于临床检测、食品安全监测等多个场景。

weixin_33712987的博客 375

电子技术设计——16×16点阵汉字显示设计

同时,设计还应注重提升显示品质,以保证汉字的稳定展示和观感舒适。本设计的目的是构建一个能够准确在LED点阵上展示汉字的系统,并允许用户通过输入设备来选择所需的汉字,我们将验证设计方案的实际操作性和效果,以确保其满足设计目标。在LED点阵显示和汉字信息处理技术的结合下,研究者们着手研究如何在LED点阵上有效地显示汉字,并提出了一系列显示算法和优化策略,旨在实现清晰且易于阅读的汉字显示效果。首先,你需要准备16×16的LED点阵面板,通常这些点阵是8×8点阵的两倍,因此可以由两个8×8的点阵组合而成。

weixin_68094467的博客 1532

突破性能瓶颈:Nginx变量三剑客set/map/split实战指南

你是否还在为复杂的请求处理逻辑编写冗长的Nginx配置?是否遇到过无法高效分流流量的困境?本文将系统讲解Nginx变量体系中最强大的三个工具——set指令、map模块和split_clients模块,通过12个实战案例带你掌握请求改写、动态分流、A/B测试的核心技巧,让你的配置文件效率提升300%。读完本文你将能够:实现URL智能重写、构建基于用户特征的动态路由、设计零代码的流量分配系统。 ##...

gitblog_00649的博客 438

STM32F4三重ADC+DMA双缓冲实战:用CubeMX搞定同步采样,告别数据覆盖烦恼

本文详细介绍了STM32F4三重ADC与DMA双缓冲技术的实战应用,通过CubeMX配置实现同步采样,解决数据覆盖问题。内容涵盖硬件架构解析、CubeMX工程配置、双缓冲实现技巧及调试优化方法,适用于电机控制、电力监测等需要高精度多通道信号采集的场景。

weixin_30627341的博客 357

HTTP 和 Nginx

实现防篡改CA 使用私钥对 TBS(待签名数据) 计算的哈希摘要进行签名,将签名算法和签名值追加到 TBS 后方生成数字证书。

corey93的博客 531

DEA-Malmquist指数模型实战:从理论到应用的全流程解析

本文全面解析了DEA-Malmquist指数模型从理论到应用的全流程。文章详细阐述了该模型如何动态衡量效率随时间的变化,并将其分解为技术效率变化与技术进步,从而精准定位效率变动的根源。通过实战步骤演示了数据准备、软件操作(如DEAP 2.1)及结果解读,并结合水资源环境效率等案例,展示了其在包含非期望产出时的应用价值,为研究者进行动态效率分析提供了清晰指南。

b0c1d2的博客 220

DDR5内存Training全解析:从信号完整性到CS训练模式的实战指南

本文深入解析了DDR5内存Training的全过程,从信号完整性挑战到CS训练模式(CSTM)的实战应用。文章详细阐述了Training在DDR5高频时代的关键作用,对比了In-Band与OOB两种架构,并拆解了Intel MRC的核心训练步骤,为硬件与固件工程师提供了诊断与优化系统稳定性的实用指南。

weixin_29252171的博客 1024

PHP之linux、apache和nginx与安全优化面试题

微服务架构是一种软件架构设计风格,将一个大型的应用程序拆分成一组小型、自治的服务。每个服务都可以独立部署、扩展和管理,并通过轻量级的通信机制进行交互。每个服务专注于解决特定的业务问题,通过独立开发、部署和运维来提高开发效率、灵活性和可伸缩性。拆分性:将一个大型的应用程序拆分成多个小型的服务,每个服务关注单一的业务功能。独立性:每个服务都是独立部署和运行的,可以使用不同的技术栈和开发语言。松耦合:服务之间通过轻量级的通信机制进行交互,如RESTful API、消息队列等。

php-yyds 768

Allegro PCB设计实战:5分钟搞定DDR信号线的Match Group等长规则设置

本文详细介绍了在Allegro PCB设计中,如何快速为DDR信号线设置Match Group等长规则。通过实战演练,解析了从理解相对传输延迟原理、在规则管理器中创建Match Group,到设置合理容差和利用动态绕线指示的全流程,帮助工程师在5分钟内高效完成高速信号时序约束,提升设计可靠性与效率。

gg901的博客 1052

Nginx 安装目录 和 编译参数

安装目录详解 查看安装nginx之后总共生成了哪些文件 rpm -ql nginx 在上面的文件中包括配置文件和日志文件 /etc/logrotate.d/nginx 类型:配置文件 作用:Nginx日志轮转,用于logrotate服务的日志切割(比如按天切割日志) /etc/nginx /etc/nginx/nginx.conf /etc/nginx/co...

weixin_34265814的博客 174

uniapp [全端兼容] - 详细实现拍照或相册选取图片后插入水印功能,手机拍照或相册上传图像并添加自定义水印及指定位置显示水印,uniApp给图片加水印后上传到后端服务器或保存到本机相册详细教程!

uni-app,h5移动端网页,微信小程序,支付宝小程序,Android安卓,苹果ios,app,nvue,图片水印,上传照片带水印,从相册选择图片加水印功能,拍完照片给图片加水印,添加照片水印,uniapp图片加水印,为图片添加自定义水印,uni-app上传图片并添加水印,上传图片加水印,uni-app如何对拍摄的照片加水印,uni-app实现图片上传添加水印操作,给上传的图片添加上水印,图片加完水印后如何上传到服务器,水印相机,拍照图片加水印,相册选取后给图片加上水印,渲染失败,无法保存,文字水印,日期

🏆 前端领域优质创作者 1万+

汉书HE8,好用的汇编代码编写工具

汉书HE8,好用的汇编代码编写工具早期编写汇编代码所用功能强大

nginx的工作原理与nginx的配置

nginx的工作原理与nginx的配置 1.nginx的工作原理 nginx的模块直接被编译进nginx,因此属于静态编译方式。 启动nginx后,nginx的模块被自动加载,与Apache不一样,首先将模块编译为一个so文件,然后在配置文件中指定是否进行加载。 在解析配置文件时,nginx的每个模块都有可能去处理某个请求,但是同一个处理请求只能由一个模块来完成。 nginx的进程架构: 启动nginx时,会启动一个Master进程,这个进程不处理任何客户端的请求,主要用来产生worker线程,一个work

爱情买卖 3538

jre9下载 jdk9

(Java SE Runtime Environment 9.0) 9.0u175 官方最新版 【x86|x64】

Nginx对post请求的优化

在nginx配置文件中设置: client_body_buffer_size 256k(足够大); client_body_in_single_buffer on; 这样才能在r->request_body->bufs->buf里存放完整的request_body,要不然,就依赖数据到达的顺序,存放在r->request_body->bufs这个ngx_chain_t这个链表里了

jiujiu372的博客 4418

android 蓝牙通讯demo

android 下蓝牙通讯demo,实现了发现蓝牙,使能蓝牙,通过蓝牙发送消息。

win10 跳过U8环境检测的IE WebControls.rar

win10 安装u8的时候 提示IE WebControls 不通过,这个工具可以欺骗u8的检查。 跳过U8环境检测的IE WebControls。提示导入失败后,重新检测通过。

Nginx 服务的配置语法、基本配置项及解析

Nginx 服务的配置语法、基本配置项及解析

weixin_42576071的博客 1169

Nginx服务优化与防盗链

本章结构 Nginx服务优化 Nginx深入优化 配置Nginx隐藏版本号 ■隐藏Nginx版本号,避免安全漏洞泄漏 ■Nginx隐藏版本号的方法 ●修改配置文件法 ●修改源码 修改配置文件法2-1 ■将Nginx配置文件中server_ tokens选项的值设置为off [root@www conf]# vi nginx.conf ...... server_ tokens off; ###关闭版本号 [root@www conf]# nginx -t 重启服务,访问网站使用curl -|命令检测

Houtieyu的博客 684

Nginx 过滤模块

Nginx 过滤模块 过滤模块简介 执行时间和内容 过滤(filter)模块是过滤响应头和内容的模块,可以对回复的头和内容进行处理。它的处理时间在获取回复内容之后,向用户发送响应之前。它的处理过程分为两个阶段,过滤HTTP回复的头部和主体,在这两个阶段可以分别对头部和主体进行修改。 在代码中有类似的函数: ngx_http_top_header_filter(r); ngx_h...

YunWisdom 570

nginx源码分析—hash结构ngx_hash_t(v1.0.4)

本博客(http://blog.youkuaiyun.com/livelylittlefish )贴出作者(阿波)相关研究、学习内容所做的笔记,欢迎广大朋友指正!Content0.序1.hash结构1.1ngx_hash_t结构1.2ngx_hash_init_t结构1.3ngx_hash_

学习,思考,记录,分享。 1万+
上一篇: filesystem测试工具之filebench
下一篇: CentOS7下部署ceph-12 (luminous)--单机集群
for_tech
博客等级 码龄11年 50粉丝 37原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值