突破PHP性能瓶颈:Goridge实现PHP与Golang的超高性能IPC/RPC通信

突破PHP性能瓶颈:Goridge实现PHP与Golang的超高性能IPC/RPC通信

【免费下载链接】goridge 🧙 High-performance PHP-to-Golang IPC/RPC bridge 【免费下载链接】goridge 项目地址: https://gitcode.com/gh_mirrors/go/goridge

引言:当PHP遇见Golang——性能革命的开端

你是否还在为PHP应用的性能瓶颈而烦恼?是否在寻找一种无需重写整个项目就能显著提升性能的解决方案?Goridge(Go + Bridge的组合词)为你带来了曙光。作为一款高性能的PHP-to-Golang IPC/RPC桥接库,Goridge能够让你在保留现有PHP代码库的同时,将计算密集型任务无缝迁移到Golang,从而获得数量级的性能提升。

读完本文后,你将能够:

  • 理解Goridge的核心原理和架构设计
  • 掌握Goridge的安装与配置方法
  • 实现PHP与Golang之间的双向通信
  • 优化RPC调用性能,处理大型数据传输
  • 在实际项目中应用Goridge解决性能瓶颈

Goridge简介:重新定义PHP与Golang通信

什么是Goridge?

Goridge是一个高性能的PHP-to-Golang IPC(进程间通信)/RPC(远程过程调用)桥接库。它通过原生PHP套接字(Socket)和Golang的net/rpc包实现通信,允许你从PHP调用Go服务的方法,同时保持最小的性能开销,支持复杂数据结构和二进制数据传输。

Goridge的核心优势

特性描述优势
零依赖无需外部服务,仅需原生PHP扩展部署简单,维护成本低
低开销每个消息仅增加12字节头部网络传输效率高,减少延迟
CRC32校验头部包含CRC32校验确保数据完整性,自动检测传输错误
多传输方式支持TCP、Unix套接字(Socket)和标准管道(Pipe)适应不同部署场景,灵活选择
超高性能在Ryzen 1700X上,20线程可达到300k次/秒调用满足高并发需求,显著提升系统吞吐量
原生集成与Golang的net/rpc包无缝集成无需学习新的RPC框架,直接使用标准库
跨平台支持Linux、macOS和Windows适应不同开发和生产环境
数据类型支持JSON结构化数据、二进制[]byte传输满足复杂业务数据交换需求

Goridge的应用场景

Goridge特别适合以下场景:

  • PHP应用中计算密集型任务的卸载(如图像处理、数据分析)
  • 服务解耦,将核心业务逻辑迁移到Golang以提升性能
  • 构建微服务架构,PHP作为前端控制器,Golang处理后端业务
  • 需要高并发处理的API服务
  • 实时数据处理和分析系统

Goridge架构深度解析

整体架构

Goridge的架构设计遵循简洁高效的原则,主要由以下几个部分组成:

mermaid

协议格式

Goridge定义了一种高效的二进制协议格式,用于PHP和Golang之间的数据交换。协议格式如下:

+----------------+----------------+----------------+----------------+
| 版本 (1字节)    | 标志 (1字节)    | 选项长度 (2字节) | 负载长度 (4字节) |
+----------------+----------------+----------------+----------------+
| CRC32校验 (4字节) | 选项数据        | 负载数据        |
+----------------+----------------+----------------+

其中:

  • 版本:协议版本号,目前为1
  • 标志:包含编解码器类型、错误状态等信息
  • 选项长度:选项数据的长度
  • 负载长度:实际业务数据的长度
  • CRC32校验:整个头部的校验和,确保数据完整性

数据编解码流程

Goridge支持多种编解码方式,以适应不同的数据类型和性能需求:

mermaid

快速开始:Goridge环境搭建与基础使用

环境要求

环境要求
PHP7.2+ (64位版本,需安装ext-sockets扩展)
Golang1.16+
操作系统Linux, macOS, Windows

安装Goridge

安装Golang端库
GO111MODULE=on go get github.com/roadrunner-server/goridge/v3
安装PHP客户端
composer require spiral/goridge

第一个示例:Hello World

下面我们将创建一个简单的示例,演示如何从PHP调用Golang的方法。

1. 创建Golang服务端

创建文件main.go

package main

import (
	"fmt"
	"net"
	"net/rpc"

	goridgeRpc "github.com/roadrunner-server/goridge/v3/pkg/rpc"
)

// App 定义我们的服务结构体
type App struct{}

// Hi 定义一个可以被PHP调用的方法
// 注意:Golang的RPC方法必须满足以下条件:
// 1. 方法是导出的(首字母大写)
// 2. 方法有两个参数,都是导出类型或内建类型
// 3. 第二个参数是指针类型
// 4. 方法返回error类型
func (s *App) Hi(name string, r *string) error {
	*r = fmt.Sprintf("Hello, %s!", name)
	return nil
}

func main() {
	// 在TCP端口6001上监听
	ln, err := net.Listen("tcp", ":6001")
	if err != nil {
		panic(err)
	}
	defer ln.Close()

	// 注册我们的服务
	if err := rpc.Register(new(App)); err != nil {
		panic(err)
	}

	fmt.Println("Goridge server started on :6001")

	// 接受并处理连接
	for {
		conn, err := ln.Accept()
		if err != nil {
			fmt.Printf("Accept error: %v\n", err)
			continue
		}
		
		// 使用Goridge编解码器处理连接
		go rpc.ServeCodec(goridgeRpc.NewCodec(conn))
	}
}
2. 运行Golang服务端
go run main.go

如果一切正常,你将看到输出:Goridge server started on :6001

3. 创建PHP客户端

创建文件client.php

<?php
require __DIR__ . '/vendor/autoload.php';

use Spiral\Goridge\RPC\RPC;
use Spiral\Goridge\SocketRelay;

// 创建一个Socket中继,连接到Golang服务
$relay = new SocketRelay("127.0.0.1", 6001);
$rpc = new RPC($relay);

// 调用Golang服务的App.Hi方法
$result = $rpc->call("App.Hi", "World");

echo $result . "\n"; // 输出: Hello, World!
4. 运行PHP客户端
php client.php

如果一切正常,你将看到输出:Hello, World!

恭喜!你已经成功搭建了Goridge环境并实现了第一个PHP与Golang的通信示例。

深入Goridge:核心组件与高级用法

连接类型详解

Goridge支持多种连接类型,适用于不同的部署场景:

1. TCP连接

TCP连接是最常用的方式,适用于PHP和Golang服务运行在不同进程或不同服务器上的场景。

Golang端:

ln, err := net.Listen("tcp", ":6001")

PHP端:

$relay = new SocketRelay("127.0.0.1", 6001);
2. Unix套接字连接

Unix套接字适用于PHP和Golang服务运行在同一台服务器上的场景,性能优于TCP。

Golang端:

ln, err := net.Listen("unix", "/tmp/goridge.sock")

PHP端:

$relay = new SocketRelay("/tmp/goridge.sock", null);
3. 标准管道连接

标准管道适用于PHP作为父进程,Golang作为子进程的场景,如PHP-FPM与Golang服务协同工作。

Golang端:

// 使用标准输入输出作为管道
relay := pipe.NewPipeRelay(os.Stdin, os.Stdout)
codec := rpc.NewCodec(relay)
// ...

PHP端:

// 使用proc_open创建子进程并建立管道
$descriptors = [
    0 => ['pipe', 'r'],  // 标准输入
    1 => ['pipe', 'w'],  // 标准输出
    2 => ['pipe', 'w']   // 标准错误
];

$process = proc_open(
    'go run main.go', 
    $descriptors, 
    $pipes, 
    __DIR__
);

$relay = new PipeRelay($pipes[0], $pipes[1]);
$rpc = new RPC($relay);

数据类型与编解码器

Goridge支持多种数据类型和编解码器,以满足不同的业务需求:

支持的数据类型

Goridge支持几乎所有常见的数据类型:

  • 基本类型:整数、浮点数、布尔值、字符串
  • 复杂类型:数组、映射(关联数组)、对象
  • 二进制数据:字节数组
编解码器选择

Goridge提供多种编解码器,可根据数据类型和性能需求选择:

编解码器特点适用场景
GobGolang原生序列化格式,支持复杂类型Golang与Golang通信,或需要传输复杂Golang类型
JSON通用数据交换格式,人类可读跨语言通信,需要数据可读性
Protocol Buffers高效二进制格式,需要预定义 schema高性能需求,数据结构固定的场景
MessagePack高效紧凑的二进制格式对传输大小有严格要求的场景
Raw原始二进制数据传输已序列化的数据或二进制文件
使用Protocol Buffers示例
  1. 定义proto文件(message.proto):
syntax = "proto3";

package example;

message User {
    string name = 1;
    int32 age = 2;
    repeated string hobbies = 3;
}

message UserResponse {
    string greeting = 1;
    bool success = 2;
}
  1. 生成Golang代码:
protoc --go_out=. message.proto
  1. Golang服务端实现:
import (
    // ...
    "github.com/golang/protobuf/proto"
    "your_package_path/example"
)

type App struct{}

func (s *App) GreetUser(user *example.User, resp *example.UserResponse) error {
    resp.Greeting = fmt.Sprintf("Hello, %s! You are %d years old.", user.Name, user.Age)
    resp.Success = true
    return nil
}

// ... 注册服务等代码 ...
  1. PHP客户端调用(需要安装protobuf扩展):
// 加载proto定义
$descriptor = \Google\Protobuf\DescriptorPool::getGeneratedPool()->getDescriptorByClassName('Example\User');
$user = new \Example\User();
$user->setName("John Doe");
$user->setAge(30);
$user->setHobbies(["reading", "coding"]);

// 调用Golang服务,指定使用Protobuf编解码器
$result = $rpc->call("App.GreetUser", $user, RPC::CODEC_PROTOBUF);

echo $result->getGreeting(); // 输出: Hello, John Doe! You are 30 years old.

错误处理机制

Goridge提供多层次的错误处理机制,确保你能够准确诊断和处理通信过程中的问题:

1. 传输层错误

传输层错误通常与网络连接相关,如连接超时、断开等。

try {
    $result = $rpc->call("App.Hi", "World");
} catch (\Spiral\Goridge\Exception\RelayException $e) {
    // 处理传输层错误
    echo "传输错误: " . $e->getMessage();
}
2. 服务层错误

服务层错误发生在Golang服务处理请求时,如方法不存在、参数错误等。

try {
    $result = $rpc->call("App.NonExistentMethod", []);
} catch (\Spiral\Goridge\Exception\ServiceException $e) {
    // 处理服务层错误
    echo "服务错误: " . $e->getMessage();
    echo "错误代码: " . $e->getCode();
}
3. 业务逻辑错误

业务逻辑错误由Golang服务方法返回,需要在PHP中根据返回结果处理。

func (s *App) Divide(a, b float64, result *float64) error {
    if b == 0 {
        return errors.New("division by zero")
    }
    *result = a / b
    return nil
}
try {
    $result = $rpc->call("App.Divide", [10, 0]);
} catch (\Spiral\Goridge\Exception\ServiceException $e) {
    // 捕获Golang返回的业务错误
    echo "业务错误: " . $e->getMessage(); // 输出: division by zero
}

性能优化策略

Goridge本身已经高度优化,但以下策略可以帮助你进一步提升性能:

1. 连接池

对于高并发场景,使用连接池可以避免频繁创建和关闭连接的开销。

$pool = new \Spiral\Goridge\RPC\ConnectionPool(
    function () {
        return new \Spiral\Goridge\RPC\RPC(
            new \Spiral\Goridge\SocketRelay("127.0.0.1", 6001)
        );
    },
    10 // 连接池大小
);

// 从连接池获取RPC客户端
$rpc = $pool->get();
$result = $rpc->call("App.Hi", "World");
// 将客户端归还给连接池
$pool->put($rpc);
2. 批量请求

对于多个独立的RPC调用,可以使用批量请求减少网络往返。

$batch = $rpc->createBatch();
$batch->add("App.Hi", "Alice");
$batch->add("App.Hi", "Bob");
$batch->add("App.Add", [2, 3]);

// 执行批量请求
$results = $batch->execute();

// 处理结果
echo $results[0]; // Hello, Alice!
echo $results[1]; // Hello, Bob!
echo $results[2]; // 5
3. 选择合适的编解码器

根据数据类型和性能需求选择合适的编解码器:

编解码器速度数据大小可读性复杂类型支持
Gob中等不可读优秀
JSON可读良好
Protocol Buffers很快不可读优秀(需预定义)
MessagePack很快不可读良好
Raw最快原始大小取决于内容无(需自行序列化)

Goridge实战:解决实际业务问题

场景一:图片处理服务

PHP在处理大型图片时性能较差,我们可以使用Goridge将图片处理任务交给Golang。

1. Golang图片处理服务
package main

import (
	"image"
	"image/jpeg"
	"image/png"
	"io/ioutil"
	"net"
	"net/rpc"
	"os"

	"github.com/nfnt/resize"
	goridgeRpc "github.com/roadrunner-server/goridge/v3/pkg/rpc"
)

type ImageProcessor struct{}

// Resize 调整图片大小
func (s *ImageProcessor) Resize(params map[string]interface{}, result *[]byte) error {
	// 从参数获取图片数据、宽度和高度
	imgData := []byte(params["data"].(string))
	width := uint(params["width"].(float64))
	height := uint(params["height"].(float64))
	format := params["format"].(string)

	// 解码图片
	img, _, err := image.Decode(bytes.NewReader(imgData))
	if err != nil {
		return err
	}

	// 调整图片大小
	resizedImg := resize.Resize(width, height, img, resize.Lanczos3)

	// 编码为指定格式
	buf := new(bytes.Buffer)
	switch format {
	case "jpeg", "jpg":
		err = jpeg.Encode(buf, resizedImg, &jpeg.Options{Quality: 80})
	case "png":
		err = png.Encode(buf, resizedImg)
	default:
		return errors.New("unsupported image format")
	}
	if err != nil {
		return err
	}

	// 返回处理后的图片数据
	*result = buf.Bytes()
	return nil
}

func main() {
	ln, err := net.Listen("tcp", ":6002")
	if err != nil {
		panic(err)
	}
	defer ln.Close()

	_ = rpc.Register(new(ImageProcessor))

	for {
		conn, err := ln.Accept()
		if err != nil {
			continue
		}
		go rpc.ServeCodec(goridgeRpc.NewCodec(conn))
	}
}
2. PHP客户端调用
<?php
require __DIR__ . '/vendor/autoload.php';

use Spiral\Goridge\RPC\RPC;
use Spiral\Goridge\SocketRelay;

// 读取本地图片
$imageData = file_get_contents('large-image.jpg');

// 创建RPC客户端
$relay = new SocketRelay("127.0.0.1", 6002);
$rpc = new RPC($relay);

// 调用图片处理服务
$start = microtime(true);
$result = $rpc->call("ImageProcessor.Resize", [
    "data" => base64_encode($imageData),
    "width" => 800,
    "height" => 600,
    "format" => "jpg"
]);
$end = microtime(true);

echo "图片处理完成,耗时: " . ($end - $start) . "秒\n";

// 保存处理后的图片
file_put_contents('resized-image.jpg', $result);

通过这种方式,我们将耗时的图片处理任务交给Golang,显著提升了PHP应用的响应速度。

场景二:数据分析与计算

Golang在处理数值计算和数据分析方面比PHP更高效,我们可以利用Goridge将这些任务交给Golang处理。

1. Golang数据分析服务
package main

import (
	"encoding/csv"
	"io"
	"net"
	"net/rpc"
	"strconv"

	goridgeRpc "github.com/roadrunner-server/goridge/v3/pkg/rpc"
)

type DataAnalyzer struct{}

// AnalyzeCSV 分析CSV数据,计算统计信息
func (s *DataAnalyzer) AnalyzeCSV(csvData string, result *map[string]interface{}) error {
	reader := csv.NewReader(strings.NewReader(csvData))
	headers, err := reader.Read()
	if err != nil {
		return err
	}

	// 初始化统计结果
	*result = make(map[string]interface{})
	stats := make(map[string]map[string]float64)
	for _, header := range headers[1:] { // 跳过第一列(标签列)
		stats[header] = map[string]float64{
			"sum": 0, "count": 0, "min": 1e18, "max": -1e18, "avg": 0,
		}
	}

	// 读取并分析数据
	rowCount := 0
	for {
		row, err := reader.Read()
		if err == io.EOF {
			break
		}
		if err != nil {
			return err
		}
		rowCount++

		for i, header := range headers[1:] {
			value, err := strconv.ParseFloat(row[i+1], 64)
			if err != nil {
				continue // 跳过非数值数据
			}

			stat := stats[header]
			stat["sum"] += value
			stat["count"]++
			if value < stat["min"] {
				stat["min"] = value
			}
			if value > stat["max"] {
				stat["max"] = value
			}
		}
	}

	// 计算平均值
	for _, header := range headers[1:] {
		stat := stats[header]
		if stat["count"] > 0 {
			stat["avg"] = stat["sum"] / stat["count"]
		} else {
			stat["min"] = 0
			stat["max"] = 0
		}
	}

	(*result)["stats"] = stats
	(*result)["row_count"] = rowCount
	return nil
}

func main() {
	ln, err := net.Listen("tcp", ":6003")
	if err != nil {
		panic(err)
	}
	defer ln.Close()

	_ = rpc.Register(new(DataAnalyzer))

	for {
		conn, err := ln.Accept()
		if err != nil {
			continue
		}
		go rpc.ServeCodec(goridgeRpc.NewCodec(conn))
	}
}
2. PHP客户端调用
<?php
// 读取CSV文件内容
$csvData = file_get_contents('large-data.csv');

// 创建RPC客户端
$relay = new SocketRelay("127.0.0.1", 6003);
$rpc = new RPC($relay);

// 调用数据分析服务
$start = microtime(true);
$result = $rpc->call("DataAnalyzer.AnalyzeCSV", $csvData);
$end = microtime(true);

echo "数据分析完成,耗时: " . ($end - $start) . "秒\n";
echo "处理行数: " . $result['row_count'] . "\n";
echo "统计结果:\n";
print_r($result['stats']);

Goridge高级应用:构建高性能微服务架构

负载均衡与服务发现

在生产环境中,单一的Golang服务实例可能无法满足高并发需求。我们可以通过负载均衡和服务发现来扩展系统容量。

使用Nginx作为TCP负载均衡器
stream {
    upstream goridge_servers {
        server 127.0.0.1:6001;
        server 127.0.0.1:6002;
        server 127.0.0.1:6003;
    }

    server {
        listen 6000;
        proxy_pass goridge_servers;
        proxy_timeout 300s;
        proxy_connect_timeout 1s;
    }
}

启动多个Golang服务实例,分别监听6001、6002、6003端口,然后PHP客户端连接到Nginx的6000端口,实现负载均衡。

与PHP框架集成

Goridge可以轻松集成到主流PHP框架中,如Laravel、Symfony等。

Laravel集成示例
  1. 创建配置文件config/goridge.php
return [
    'default' => [
        'host' => env('GORIDGE_HOST', '127.0.0.1'),
        'port' => env('GORIDGE_PORT', 6000),
        'timeout' => env('GORIDGE_TIMEOUT', 3),
    ],
];
  1. 创建服务提供者app/Providers/GoridgeServiceProvider.php
<?php

namespace App\Providers;

use Illuminate\Support\ServiceProvider;
use Spiral\Goridge\RPC\RPC;
use Spiral\Goridge\SocketRelay;

class GoridgeServiceProvider extends ServiceProvider
{
    public function register()
    {
        $this->app->singleton('goridge', function ($app) {
            $config = $app['config']['goridge.default'];
            $relay = new SocketRelay($config['host'], $config['port']);
            return new RPC($relay);
        });
    }
}
  1. 在控制器中使用:
<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;

class ImageController extends Controller
{
    public function resize(Request $request)
    {
        $image = $request->file('image')->getContent();
        
        // 使用Goridge处理图片
        $result = app('goridge')->call("ImageProcessor.Resize", [
            "data" => base64_encode($image),
            "width" => $request->input('width', 800),
            "height" => $request->input('height', 600),
            "format" => $request->input('format', 'jpg')
        ]);
        
        return response($result, 200, [
            'Content-Type' => 'image/' . $request->input('format', 'jpg')
        ]);
    }
}

安全最佳实践

在生产环境中,确保Goridge通信安全至关重要:

1. 使用Unix套接字并限制权限
# 创建套接字文件
touch /tmp/goridge.sock
# 设置权限,仅允许PHP和Golang进程访问
chmod 0600 /tmp/goridge.sock
2. 实现认证机制

在Golang服务中添加简单的认证:

func (s *App) AuthenticatedMethod(token string, params map[string]interface{}, result *interface{}) error {
    // 验证令牌
    if !validateToken(token) {
        return errors.New("authentication failed")
    }
    
    // 处理业务逻辑
    // ...
    
    return nil
}

PHP客户端调用时传递认证令牌:

$result = $rpc->call("App.AuthenticatedMethod", [
    "token" => "your-secure-token",
    "data" => "sensitive-data"
]);

Goridge性能调优与监控

性能测试与基准测试

Goridge提供了基准测试工具,可以帮助你评估和优化系统性能。

# 克隆Goridge仓库
git clone https://gitcode.com/gh_mirrors/go/goridge
cd goridge/benchmarks

# 运行基准测试
go run main.go

基准测试将输出不同传输方式和数据大小下的性能指标,如每秒调用次数、延迟等。

监控与日志

为确保系统稳定运行,需要对Goridge通信进行监控和日志记录。

Golang服务日志
import (
    "log"
    "os"
    "time"
)

// 创建日志文件
logFile, err := os.OpenFile("goridge.log", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
if err != nil {
    panic(err)
}
defer logFile.Close()

// 设置日志输出和格式
logger := log.New(logFile, "[Goridge] ", log.Ldate|log.Ltime|log.Lmicroseconds|log.Lshortfile)

// 在处理请求时记录日志
func (s *App) Hi(name string, r *string) error {
    start := time.Now()
    defer func() {
        logger.Printf("Hi method called with name=%s, duration=%v", name, time.Since(start))
    }()
    
    *r = fmt.Sprintf("Hello, %s!", name)
    return nil
}
PHP客户端监控
// 使用Monolog记录RPC调用
$logger = new \Monolog\Logger('goridge');
$logger->pushHandler(new \Monolog\Handler\FileHandler('goridge-php.log'));

$start = microtime(true);
try {
    $result = $rpc->call("App.Hi", "World");
    $duration = microtime(true) - $start;
    $logger->info("RPC调用成功", [
        "method" => "App.Hi",
        "duration" => $duration,
        "success" => true
    ]);
} catch (\Exception $e) {
    $duration = microtime(true) - $start;
    $logger->error("RPC调用失败", [
        "method" => "App.Hi",
        "duration" => $duration,
        "success" => false,
        "error" => $e->getMessage()
    ]);
}

总结与展望

Goridge的价值与应用前景

Goridge为PHP和Golang开发者提供了一个高性能、低开销的通信桥梁,它的价值主要体现在:

  1. 性能提升:将计算密集型任务从PHP转移到Golang,显著提升系统性能
  2. 技术整合:充分利用PHP的快速开发优势和Golang的高性能特性
  3. 平滑迁移:无需重写整个PHP项目,可逐步将关键组件迁移到Golang
  4. 架构升级:为PHP应用向微服务架构演进提供了可行路径

随着Web应用对性能要求的不断提高,以及Golang在后端开发领域的普及,Goridge的应用前景将更加广阔。未来,我们可以期待Goridge支持更多的数据格式、提供更丰富的服务发现机制,并与更多的PHP框架和Golang库深度集成。

下一步学习建议

  1. 深入Goridge源码:了解Goridge的底层实现,掌握自定义编解码器的开发
  2. 探索高级特性:如异步调用、流式传输等高级功能的应用
  3. 参与社区贡献:为Goridge项目提交bug修复、功能增强或文档改进

Goridge为PHP开发者打开了通往高性能后端开发的大门,通过将PHP的灵活性与Golang的性能优势相结合,你可以构建出既易于开发又能处理高并发的现代Web应用。


如果你觉得本文对你有帮助,请点赞、收藏并关注,以便获取更多关于Goridge和PHP性能优化的高级技巧!

下期预告:《Goridge在微服务架构中的实践:从单体到分布式》

【免费下载链接】goridge 🧙 High-performance PHP-to-Golang IPC/RPC bridge 【免费下载链接】goridge 项目地址: https://gitcode.com/gh_mirrors/go/goridge

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值