云原生API网关全生命周期管理Apache APISIX探究实操

26 篇文章 0 订阅
20 篇文章 1 订阅

概述

定义

Apache APISIX官网地址 https://apisix.apache.org/ 最新版本3.2.0

Apache APISIX官网文档地址 https://apisix.apache.org/docs/apisix/getting-started/

Apache APISIX源码地址 https://github.com/apache/apisix

Apache APISIX 是一个动态、实时、高性能、可扩展的云原生 API 网关,提供了如负载均衡、动态上游、灰度发布(金丝雀发布)、服务熔断、身份认证、可观测性等丰富的流量管理功能。可以使用APISIX API Gateway来处理传统的南北向流量,也可以用来处理服务之间的东西向流量;还可以用作k8s入口控制器。

Apache APISIX是第一个包含内置低代码仪表板的开源API网关,它为开发人员提供了强大而灵活的UI;云原生时代,动态和可观测性成为衡量 API 网关的标准之一;Apache APISIX 自诞生之初就一直跟随着云原生的脚步前行。

备注:南北向流量可以简单理解客户端到服务端,也即是我们传统上使用Nginx;东西向流量更多的是后台微服务之间的通信。

NGINX 与 Kong 的痛点

在单体服务时代,使用 NGINX 可以应对大多数的场景,而到了云原生时代,NGINX 因为其自身架构的原因则会出现两个问题:

  • 首先是 NGINX 不支持集群管理。几乎每家互联网厂商都有自己的 NGINX 配置管理系统,系统虽然大同小异但是一直没有统一的方案。
  • 其次是 NGINX 不支持配置的热加载。很多公司一旦修改了配置,重新加载 NGINX 的时间可能需要半个小时以上。并且在 Kubernetes 体系下,上游会经常发生变化,如果使用 NGINX 来处理就需要频繁重启服务,这对于企业是不可接受的。

而 Kong 的出现则解决了 NGINX 的痛点,但是又带来了新的问题:

  • Kong 需要依赖于 PostgreSQL 或 Cassandra 数据库,这使 Kong 的整个架构非常臃肿,并且会给企业带来高可用的问题。如果数据库故障了,那么整个 API 网关都会出现故障。
  • Kong 的路由使用的是遍历查找,当网关内有超过上千个路由时,它的性能就会出现比较急剧的下降。

APISIX 的技术优势

而 APISIX 的出现则解决了上述所有问题,成为了云原生时代最完美的 API 网关。Apache APISIX 和 Kong相比在技术方面的主要优势大部分都是在底层模块上的优化和创新;在简单的 PoC 上并不一定能够体现出这些技术的优势,但在复杂的生产环境中,Apache APISIX 的这些优势将会造成巨大的差距。

  • 无数据库依赖:在 APISIX 项目出现之前,也有非常多的商业 API 网关或开源 API 网关产品,但这些产品大多数都把 API 数据、路由、证书和配置等信息存放在一个关系型数据库中。网关作为一个基础中间件处理了所有来自客户端的流量,因此对于可用性的要求便会非常高。如果API 网关依赖了一个关系型数据库,也就意味着关系型数据库一旦出现了故障(比如宕机、丢失数据),API 网关也会因此受到影响,整个业务系统的可用性也会大打折扣。
  • 高性能路由匹配算法和 IP 匹配算法:Apache APISIX 使用的是 RadixTree,它提供了 KV 存储查找的数据结构并对只有一个子节点的中间节点进行了压缩,因此它又被称为压缩前缀树;高效且支持模糊匹配的搜索数据结构。

特性

Apache APISIX的特性较多,下面只是列出部分特性和说明。

  • 平台支持:APISIX 提供了平台级解决方案,不但支持裸机运行,也支持在 Kubernetes 中云原生使用。

  • 众多协议支持:如TCP/UDP Proxy、Dubbo Proxy、Dynamic MQTT Proxy、gRPC proxy、HTTP(S) Forward Proxy等

  • 全动态能力

    • Apache APISIX提供了热更新和热插件,可以在不重新启动的情况下持续更新配置,节省了开发时间和压力。
    • 健康检查:开启上游节点健康检查功能,负载均衡时自动过滤不健康节点,保证系统稳定。
    • 断路器:智能跟踪不健康的上游服务。
    • 流量分流:可以在不同的上游之间增量地引导流量的百分比。
  • 精细化路由

    • Apache APISIX Gateway提供了编写自己的自定义插件的能力,在平衡器阶段使用自定义负载平衡算法进行扩展,自定义路由算法用于对路由进行精细控制;支持全路径匹配和前缀匹配。
    • 支持所有Nginx内置变量作为路由条件,可以使用cookie, args等作为路由条件来实现金丝雀发布,A/B测试。
    • 支持自定义路由匹配功能。
  • 安全

    • 丰富的身份验证和授权支持。Apache APISIX Gateway提供了多个用于身份验证和API验证的安全插件,包括CORS、JWT、Key Auth、OpenID Connect (OIDC)、Keycloak等。
    • IP黑白名单、Referer黑白名单。
    • Anti-ReDoS(正则表达式拒绝服务):无需配置,内置防ReDoS策略。
    • 防CSRF攻击。
  • 运维友好

    • 支持与多种工具和平台如Zipkin、SkyWalking、Consul、Nacos、Eureka、Zookeeper、Prometheus、HashiCorp Vault等集成。
    • 通过使用APISIX Dashboard,运维人员可以通过友好且直观的 UI 配置 APISIX。
    • 版本控制:支持操作回滚。
    • 命令行:通过命令行启动/停止/重新加载APISIX。
  • 高可扩展

    • 自定义插件:允许挂载常见阶段,如重写,访问,头过滤器,主体过滤器和日志。插件可用Java/Go/Python编写,也可用代理Wasm SDK编写。
    • 自定义负载均衡算法:可以在均衡器阶段使用自定义负载均衡算法。
    • 自定义路由:支持用户自行实现路由算法。
  • 多语言支持:Apache APISIX是一个用于插件开发的多语言网关,并通过RPC和Wasm提供支持。

    image-20230313142939499

  • Serverless:支持与Lua函数、AWS Lambda、Azure Functions、Apache OpenWhisk等云服务集成。

架构

Apache APISIX基于NGINX和etcd。与传统的API网关相比,APISIX具有动态路由、热加载插件等特性。

image-20230313133735575

APISIX 的架构主要分成如下两部分“

  • 数据面:真正去处理来自客户端请求的一个组件,去处理用户的真实流量,包括像身份验证、证书卸载、日志分析和可观测性等功能。数据面本身并不会存储任何数据,所以它是一个无状态结构。
  • 控制面:APISIX 在控制面上并没有使用传统的类似于像 MySQL 去做配置存储,而是选择使用 etcd。这样做的好处在于:
    • 与产品架构的云原生技术体系更统一
    • 更贴合 API 网关存放的数据类型
    • 能更好地体现高可用特性
    • 拥有低于毫秒级别的变化通知

应用场景

APISIX 目标是统一代理基础设施,其核心为高性能代理服务,自身不绑定任何环境属性。当它演变为 Ingress、服务网格等产品时,都是外部服务与 APISIX 配合,变化的是外部程序而不是 APISIX 自身。

  • Load Balancer 和 API 网关:针对传统的 LB 和 API 网关场景, APISIX 基于 NGINX + LuaJIT 实现天然具备高性能、安全等特性,并且原生支持了动态 SSL 证书卸载、SSL 握手优化等功能,在负载均衡的服务能力上也更优秀。从 NGINX 切换到 APISIX 不仅性能不会下降,而且可以享受到动态、统一管理等特性带来的管理效率的提升。
  • 微服务网关:APISIX 目前支持多种语言编写扩展插件,可以解决东西向微服务 API 网关面临的主要问题——异构多语言和通用问题。内置支持的服务注册中心有 Nacos、etcd、Eureka 等,还有标准的 DNS 方式,可以平滑替代 Zuul、Spring Cloud Gateway、Dubbo 等微服务 API 网关。
  • Kubernetes Ingress: K8s 官方 Kubernetes Ingress Controller 项目主要基于 NGINX 配置文件的方式,所以在路由能力和加载模式上稍显不足,并且存在一些明显劣势。比如添加、修改任何 API 时,需要重启服务才能完成新 NGINX 配置的更新,但重启服务,对线上流量的影响是非常大的。而 APISIX Ingress Controller 则完美解决了上面提到的所有问题:支持全动态,无需重启加载。同时继承了 APISIX 的所有优势,还支持原生 Kubernetes CRD,方便用户迁移。

image-20230314113407964

  • 服务网格:APISIX 也提前基于云原生模式架构下的服务网格架构,通过调研和技术分析后,APISIX 已经支持了 xDS 协议,APISIX Mesh 就此诞生,在服务网格领域 APISIX 也拥有了一席之地。

image-20230314113352818

主要概念

APISIX 主要分为两个部分:

  1. APISIX 核心:包括 Lua 插件、多语言插件运行时(Plugin Runner)、Wasm 插件运行时等;
  2. 功能丰富的各种内置插件:包括可观测性、安全、流量控制等。

image-20230313175747815

概念/组件描述
Route通过路由定义规则来匹配客户端请求,根据匹配结果加载并执行相应的插件,最后把请求转发给到指定的上游应用。
Upstream上游的作用是按照配置规则对服务节点进行负载均衡,它的地址信息可以直接配置到路由或服务上。
Admin API用户可以通过 Admin API 控制 APISIX 实例。

插件加载流程

image-20230314134242441

插件内部结构

image-20230314134310069

部署

快速入门

quickstart安装

# quickstart 脚本快速安装并启动,该命令在本地安装并运行了基于 Docker 的 APISIX 和 etcd 容器,其中 APISIX 采用 etcd 保存和同步配置信息。APISIX 和 etcd 容器使用 host 的 Docker 网络模式,因此可以从本地直接访问。
curl -sL https://run.api7.ai/apisix/quickstart | sh
# 请确保其他系统进程没有占用 9080、9180、9443 和 2379 端口。可以通过 curl 来访问正在运行的 APISIX 实例。比如,你可以发送一个简单的 HTTP 请求来验证 APISIX 运行状态是否正常。
curl "http://127.0.0.1:9080" --head | grep Server

image-20230314094832456

Admin API创建路由

# 以httpbin.org这个简单的HTTP请求和响应服务作为演示,详细可以直接查阅http://httpbin.org/,使用httpbin.org测试接口来体验下,如果希望这个接口在本地运行,可采用docker部署,docker Run -p 80:80 kennethreitz/httpbin
curl --location --request GET "http://httpbin.org/get?param1=value1&param2=value2"

image-20230314094929020

  • 创建路由,使用 Admin API 创建一个 Route并与Upstream绑定,当一个请求到达 APISIX 时,APISIX 会将请求转发到指定的上游服务中。
curl "http://127.0.0.1:9180/apisix/admin/routes/1" -X PUT -d '
{
  "methods": ["GET"],
  "host": "mytest.com",
  "uri": "/anything/*",
  "upstream": {
    "type": "roundrobin",
    "nodes": {
      "httpbin.org:80": 1
    }
  }
}'

image-20230314102545705

# 当路由创建完成后,可以通过以下命令访问上游服务,该请求将被 APISIX 转发到 http://httpbin.org:80/anything/foo?arg=10
curl -i -X GET "http://127.0.0.1:9080/anything/foo?arg=10" -H "Host: mytest.com"

image-20230314102658741

也可以配置hosts映射,然后在浏览器直接访问http://mytest.com:9080/anything/foo?arg=10

image-20230314102805540

  • 使用上游服务创建路由,可以通过下面命令创建一个上游,并在路由中使用它,而不是想上面示例直接将其配置在路由中
curl "http://127.0.0.1:9180/apisix/admin/upstreams/1" -X PUT -d '
{
  "type": "roundrobin",
  "nodes": {
    "httpbin.org:80": 1
  }
}'

然后在通过以下命令绑定到指定路由

curl "http://127.0.0.1:9180/apisix/admin/routes/1" -X PUT -d '
{
  "uri": "/get",
  "host": "httpbin.org",
  "upstream_id": "1"
}'

image-20230314103951527

可以通过下面命令访问上游服务,该请求将被 APISIX 转发到 http://httpbin.org:80/anything/foo?arg=10

curl -i -X GET "http://127.0.0.1:9080/get?foo1=bar1&foo2=bar2" -H "Host: httpbin.org"

image-20230314104103200

RPM安装

安装etcd

APISIX使用etcd保存和同步配置,在安装APISIX之前需要安装etcd。

ETCD_VERSION='3.5.7'
wget https://github.com/etcd-io/etcd/releases/download/v${ETCD_VERSION}/etcd-v${ETCD_VERSION}-linux-amd64.tar.gz
tar -xvf etcd-v${ETCD_VERSION}-linux-amd64.tar.gz
cd etcd-v${ETCD_VERSION}-linux-amd64
cp -a etcd etcdctl /usr/bin/
nohup etcd >/tmp/etcd.log 2>&1 &

RPM安装

APISIX RPM安装适用于CentOS 7和CentOS 8

# 如果没有安装OpenResty,可运行以下命令同时安装OpenResty和APISI仓库yum install -y https://repos.apiseven.com/packages/centos/apache-apisix-repo-1.0-1.noarch.rpm# 如果安装了OpenResty,下面的命令将安装APISIX存储库:yum-config-manager --add-repo https://repos.apiseven.com/packages/centos/apache-apisix.repo# 安装APISIXyum install apisix# 还可以通过指定APISIX来安装特定版本的APISIXyum install apisix-3.1.0# 管理APISIX服务器,安装APISIX后,可以通过以下命令初始化配置文件和etcdapisix init# 检查配置文件是否争取apisix test

image-20230314153608997

# 启动apisixapisix start# 正常关闭apisix,确保停止之前完成所有收到的请求apisix quit# 强制关闭apisix并丢弃所有请求apisix stop

配置apisix

更改配置文件,路径在/usr/local/apisix/conf/config.yaml

# 在启动APISIX时,通过使用--config或-c标志将路径传递给配置文件,APISIX将使用此配置文件中添加的配置,如果没有配置任何内容,则将退回到默认配置。apisix start -c <path to config file>

警告注意:

  • APISIX的默认配置可以在conf/config-default.yaml中找到,这个文件不应该被修改,它与源代码绑定,只能通过上面提到的config.yaml更改配置。
  • conf/nginx.conf文件是自动生成的,不可修改。

建议修改Admin API密钥以保证安全性,修改conf/config.yaml,修改后重启apisix

deployment:  admin:    admin_key      -        name: "admin"        key: edd1c9f034335f136f87ad84b625c123        role: admin      
# 访问Admin API,你可以使用新的密码curl http://127.0.0.1:9180/apisix/admin/routes?api_key=edd1c9f034335f136f87ad84b625c123 -i

image-20230314155628797

Docker安装

docker-compose安装

# docker
git clone https://github.com/apache/apisix-docker.git
# 进入目录
cd apisix-docker/example
# 可以查看和修改文件,这里将apisix-dashboard暴露本机端口改为19000
vim docker-compose.yml
# 使用docker-compose启动APISIX
docker-compose -p docker-apisix up -d

image-20230314161245987

访问http://hadoop2:19000,输入用户名密码admin/admin即可登录成功(密码配置在docker-compose指定的配置文件dashboard_conf/conf.yaml中)

image-20230314161843748

apisix-dashboard简单使用

仪表板

dashboard 可视化 WEB 控制台,可以很直观的进行 router 、upstream 等配置。登录后的首页即为仪表板,这是通过在iframe中引用监视器页面来支持它可以配置grafana的地址,比如配置http://grafanaip:3000,快捷操作grafana

image-20230314170404993

配置测试上游
# 现在本地搭建httpbin服务
docker run -p 5080:80 kennethreitz/httpbin

点击上游菜单,点击创建按钮,目标节点添加httpbin.org和上面我们刚部署内网httpbin,其他暂时默认,点击下一步然后提交。

image-20230315154133554

配置测试路由

点击路由菜单,点击创建按钮,目标节点添加httpbin.org和上面我们刚部署内网httpbin,其他暂时默认,点击下一步然后提交。

image-20230315155140997

设置上游服务选择前面创建的,其他先保持默认,最后提交

image-20230315155521134

执行访问测试

curl -i -X GET "http://127.0.0.1:9080/anything/foo?arg=10"

image-20230315170044136

整合Nacos
  • 先准备演示订单微服务并向Nacos注册,关于Nacos可翻看前面的文章

image-20230316144809624

  • 在apisix 的conf/config.yaml中添加以下配置,由于这里是用docker部署,路径在apisix-docker/example/apisix_conf
discovery:
  nacos:
    host:
      - "http://nacos:nacos@192.168.3.113:8848"
    prefix: "/nacos/v1/"
    fetch_interval: 30    # default 30 sec
    weight: 100           # default 100
    timeout:
      connect: 2000       # default 2000 ms
      send: 2000          # default 2000 ms
      read: 5000          # default 5000 ms
  • 配置文件修改后重启apisix
docker-compose -p docker-apisix down
docker-compose -p docker-apisix up -d
  • 在添加order上游,输入服务类型信息点击下一步和提交

image-20230316145533719

添加order路由,输入路径信息点击下一步

image-20230316145737362

路由中选择前面添加order上游,其他默认最终提交

image-20230316145825346

访问apisix监听地址,http://hadoop2:9080/order/add,最后成功返回订单微服务的接口数据

image-20230316150351498

开启SkyWalking插件链路追踪
  • 部署skywalking,详细可以翻看前面关于skywalking的文章
  • 同样在配置文件添加以下配置
plugins:
  - skywalking          # 启用skywalking插件
  
plugin_attr:
  skywalking: # 配置skywalking的属性
    service_name: APISIX_GATEWAY
    service_instance_name: "APISIX_INSTANCE_GATEWAY"
    endpoint_addr: http://192.168.3.113:12800  # skywalking的地址,本机默认是这个,可自行修改

image-20230316164024479

  • 配置文件修改后重启apisix
  • 在前面创建的order路由中开启SkyWalking插件,点击可观察性找到skywalking点击启动按钮

image-20230316152740828

在弹出的页面的插件配置中点击启用按钮然后提交,然后一直下一步最后提交

image-20230316152828372

返回路由列表也可以通过操作-更多-查看,显示数据编辑器内容

image-20230316153058621

  • 在订单微服务中添加依赖
        <dependency>
            <groupId>org.apache.skywalking</groupId>
            <artifactId>apm-toolkit-trace</artifactId>
            <version>8.14.0</version>
        </dependency>
        <dependency>
            <groupId>org.apache.skywalking</groupId>
            <artifactId>apm-toolkit-logback-1.x</artifactId>
            <version>8.14.0</version>
        </dependency>

在logback-spring.xml添加打印skywalking链路日志信息

<configuration debug="false" scan="false">
	<springProperty scop="context" name="spring.application.name" source="spring.application.name" defaultValue=""/>
	<property name="log.path" value="logs/${spring.application.name}"/>
	<!-- 彩色日志格式 -->
	<property name="CONSOLE_LOG_PATTERN"
			  value="${CONSOLE_LOG_PATTERN:-%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr(${PID:- }){magenta} %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}}"/>
	<!-- 彩色日志依赖的渲染类 -->
	<conversionRule conversionWord="clr" converterClass="org.springframework.boot.logging.logback.ColorConverter"/>
	<conversionRule conversionWord="wex"
					converterClass="org.springframework.boot.logging.logback.WhitespaceThrowableProxyConverter"/>
	<conversionRule conversionWord="wEx"
					converterClass="org.springframework.boot.logging.logback.ExtendedWhitespaceThrowableProxyConverter"/>
	<!-- Console log output -->
	<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
		<encoder>
			<pattern>${CONSOLE_LOG_PATTERN}</pattern>
		</encoder>
	</appender>

	<!-- Log file debug output -->
	<appender name="debug" class="ch.qos.logback.core.rolling.RollingFileAppender">
		<file>${log.path}/debug.log</file>
		<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
			<fileNamePattern>${log.path}/%d{yyyy-MM, aux}/debug.%d{yyyy-MM-dd}.%i.log.gz</fileNamePattern>
			<maxFileSize>50MB</maxFileSize>
			<maxHistory>30</maxHistory>
		</rollingPolicy>
		<encoder>
			<pattern>%date [%thread] %-5level [%logger{50}] %file:%line - %msg%n</pattern>
		</encoder>
	</appender>

	<!-- Log file error output -->
	<appender name="error" class="ch.qos.logback.core.rolling.RollingFileAppender">
		<file>${log.path}/error.log</file>
		<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
			<fileNamePattern>${log.path}/%d{yyyy-MM}/error.%d{yyyy-MM-dd}.%i.log.gz</fileNamePattern>
			<maxFileSize>50MB</maxFileSize>
			<maxHistory>30</maxHistory>
		</rollingPolicy>
		<encoder>
			<pattern>%date [%thread] %-5level [%logger{50}] %file:%line - %msg%n</pattern>
		</encoder>
		<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
			<level>ERROR</level>
		</filter>
	</appender>
	<appender name="stdout" class="ch.qos.logback.core.ConsoleAppender">
		<encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">
			<layout class="org.apache.skywalking.apm.toolkit.log.logback.v1.x.TraceIdPatternLogbackLayout">
				<Pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%tid] [%thread] %-5level %logger{36} -%msg%n</Pattern>
			</layout>
		</encoder>
	</appender>

	<appender name="grpc" class="org.apache.skywalking.apm.toolkit.log.logback.v1.x.log.GRPCLogClientAppender">
		<encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">
			<layout class="org.apache.skywalking.apm.toolkit.log.logback.v1.x.mdc.TraceIdMDCPatternLogbackLayout">
				<Pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%X{tid}] [%thread] %-5level %logger{36} -%msg%n</Pattern>
			</layout>
		</encoder>
	</appender>
	<!--nacos 心跳 INFO 屏蔽-->
	<logger name="com.alibaba.nacos" level="OFF">
		<appender-ref ref="error"/>
	</logger>

	<!-- Level: FATAL 0  ERROR 3  WARN 4  INFO 6  DEBUG 7 -->
	<root level="INFO">
    <!-- <appender-ref ref="console"/>-->
		<appender-ref ref="debug"/>
		<appender-ref ref="error"/>
		<appender-ref ref="stdout"/>
		<appender-ref ref="grpc"/>
	</root>
</configuration>

将agent.config 拷贝到订单微服务的resources目录下,库存微服务修改agent.config下面两个配置,详细查阅skywalking前面的文章

agent.service_name=${SW_AGENT_NAME:ecom-storage-service}
collector.backend_service=${SW_AGENT_COLLECTOR_BACKEND_SERVICES:192.168.3.113:11800}
# 启动jvm参数增加
-javaagent:F:\commoms\skywalking-agent\skywalking-agent.jar
-Dskywalking_config=F:\dev\simple-ecommerce\ecom-storage-service\src\main\resources\agent.config

访问http://hadoop2:9080/order/add多次看下,在普通服务就可以看到订单微服务和APISIX_GATEWAY。

image-20230316164629360

通过Trace可以看到日志链路经过了APISIX_GATEWAY和订单微服务,至此基本整合演示完毕

image-20230316164542799

  • 本人博客网站IT小神 www.itxiaoshen.com
  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值