几段代码,带你理解RPC

解密瑞数6.5的sign生成逻辑:从RPC调用到完整cookie处理流程 本文深入剖析了瑞数6.5防护体系的核心机制,重点解密其sign生成逻辑与完整cookie处理流程。文章系统性地介绍了从首次请求的412状态码、环境检测,到通过RPC(远程过程调用)技术高效Hook关键函数以获取sign值的实战方案,并探讨了精细化补环境的高级技巧,为开发者提供了一套从原理到实践的逆向工程完整思路。 阅读详情

RPC

RPC 远程过程调用可以说是分布式系统的基础,本文将通过 Java 演示一次普通的 rpc 调用到底发生了什么。

咸鱼君曾经在网上看到有人提问,为什么 RPC 要叫作远程过程调用,而不叫作 RMC 远程方法调用。但咸鱼君为 RPC 的叫法才是合理的,远程调用的是某个过程,不一定是一个具体的方法。(你只要看过后面第一个版本的代码就能懂了)

image

这整个过程可以用一句话概括:机器 A 通过网络与机器B建立连接,A 发送一些参数给 B,B 执行某个过程,并把结果返回给 A。

在写代码之前,先说一个前置背景,假设我们有一个商品类

public class Product implements Serializable {
    private Integer id;
    private String name;
​
    public Product(Integer id, String name) {
        this.id = id;
        this.name = name;
    }
​
    //toString()
    //get set 方法
}

有一个商品服务接口

public interface IProductService {
  Product getProductById(Integer id);
}

服务端有商品服务接口的实现类

public class ProductServiceImpl implements IProductService {
    @Override
    public Product getProductById(Integer id) {
        //实际上这里应该去查询数据库获得数据,下面简化了
        return new Product(id, "手机");
    }
}

下面我们通过客户端发送一个商品 id 到服务端,服务端获得 id 后通过商品服务类获取商品信息,返回给客户端

public class Client {
​
    public static void main(String[] args) throws Exception {
        //建立Socket
        Socket socket = new Socket("127.0.0.1", 8888);
        //获取输出流
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        DataOutputStream dos = new DataOutputStream(baos);
        //把商品Id通过网络传到服务端
        dos.writeInt(123);
​
        socket.getOutputStream().write(baos.toByteArray());
        socket.getOutputStream().flush();
​
        //读取服务端返回的商品信息
        DataInputStream dis = new DataInputStream(socket.getInputStream());
        Integer id = dis.readInt();     //商品id
        String name = dis.readUTF();    //商品名称
        Product product = new Product(id, name);//通过服务端返回的商品信息生成商品
​
        System.out.println(product);
        
        //关闭流资源为了方便阅读,没有做try-catch处理
        dos.close();
        baos.close();
        socket.close();
    }
}
​
public class Server {
    private static boolean running = true;
​
    public static void main(String[] args) throws Exception {
        //建立服务端Socket
        ServerSocket ss = new ServerSocket(8888);
        //不断监听,处理客户端请求
        while (running) {
            Socket socket = ss.accept();
            process(socket);
            socket.close();
        }
        ss.close();
    }
​
    private static void process(Socket socket) throws Exception {
        InputStream is = socket.getInputStream();
        OutputStream os = socket.getOutputStream();
        DataInputStream dis = new DataInputStream(is);
        DataOutputStream dos = new DataOutputStream(os);
​
        //读取客户端发过来的id
        Integer id = dis.readInt();
        //调用服务类生成商品
        IProductService service = new ProductServiceImpl();
        Product product = service.getProductById(id);
        //把商品的信息写回给客户端
        dos.writeInt(id);
        dos.writeUTF(product.getName());
        dos.flush();
​
        dos.close();
        dis.close();
        os.close();
        is.close();
    }
}

上面的是 RPC 远程调用的原始简易版本,可以看到联网的代码写死在了客户端中,网络部分代码和getProductById()耦合在了一起,如果想把别的方法改为远程调用还要重新写一遍联网的代码,非常麻烦。

image

在实际的使用中,我们会编写各种各样的远程调用,打个比方,IProductService接口以后可能会扩展成这样:

public interface IProductService {
​
    Product getProductById(Integer id);
    
    Product getProductByName(String name);
    
    Product getMostExpensiveProduct();
}

我们总不可能为每个方法都编写一段网络连接的代码吧,我们得想到一种办法为所有的方法都嵌入一段共用的网络连接代码。

那具体应该怎样嵌入呢?这里我们可以用到代理模式。

在 Java 中许多优秀的框架都用到了代理模式做代码嵌入,比如说 Mybatis。它把 JDBC 连接部分的代码通过代理模式嵌入到 sql 语句的周围,让我们专注于写 sql。

首先,服务端的代码要进行修改,由于多个方法共用一套联网的代码,我们要在服务端识别调用的是哪一个方法:

public class Server {
​
    private static boolean running = true;
​
    public static void main(String[] args) throws Exception {
        //......
    }
​
    private static void process(Socket socket) throws Exception {
        //获取输入流,输出流
        InputStream is = socket.getInputStream();
        OutputStream os = socket.getOutputStream();
        ObjectInputStream ois = new ObjectInputStream(is);
        ObjectOutputStream oos = new ObjectOutputStream(os);
        //获取本次远程调用的方法名
        String methodName = ois.readUTF();
        //获取本次远程调用方法的参数类型
        Class[] parameterTypes = (Class[]) ois.readObject();
        //获取具体的参数对象
        Object[] args = (Object[]) ois.readObject();
        
        //创建商品服务类实例,(往后这里可以继续优化)
        IProductService service = new ProductServiceImpl();
        //根据远程获取的方法名和参数,调用相应的方法
        Method method = service.getClass().getMethod(methodName, parameterTypes);
        Product product = (Product) method.invoke(service, args);
        //把结果写回给客户端
        oos.writeObject(product);
​
        oos.close();
        ois.close();
        socket.close();
    }
}

然后在客户端,我们创建一个新的代理类,对外提供一个 getStub 获取代理类的方法。使用 JDK 的动态代理需要三个参数,一个是类加载器,一个是接口的 class 类,最后一个是InvocationHandler实例。

JDK 动态代理背后的逻辑是这样的:JVM 会根据接口的 class 类动态创建一个代理类对象,这个代理对象实现了传入的接口,也就是说它拥有了接口中所有方法的实现。方法具体的实现可以由用户指定,也就是调用InvocationHandler的invoke方法。

在invoke方法中有三个参数,分别是proxy代理类,method调用的方法,args调用方法的参数。我们可以在invoke方法中对具体的实现方法进行增强,在本案例中就是进行网络调用。

public class Stub {
​
    public static IProductService getStub() {
​
        InvocationHandler h = new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                //和服务端建立Socket连接
                Socket socket = new Socket("127.0.0.1", 8888);
                ObjectOutputStream oos = new ObjectOutputStream(socket.getOutputStream());
                //拿到远程调用的方法名
                String methodName = method.getName();
                //拿到远程调用方法的参数类型
                Class[] parametersTypes = method.getParameterTypes();
                //把方法名传递给服务端
                oos.writeUTF(methodName);
                //把方法参数类型传递给服务端
                oos.writeObject(parametersTypes);
                //把方法参数传递给服务端
                oos.writeObject(args);
                oos.flush();
                //获取远程调用的返回结果
                ObjectInputStream ois = new ObjectInputStream(socket.getInputStream());
                Product product = (Product) ois.readObject();
​
                ois.close();
                oos.close();
                socket.close();
                return product;
            }
        };
        Object o = Proxy.newProxyInstance(IProductService.class.getClassLoader(), new Class[]{IProductService.class}, h);
        return (IProductService) o;
    }
}

这个新版本比第一个版本又美好了一些,但是其实还可以继续优化。现在我们的代理只能够返回IProductService的实现类,得想办法让它返回任意类型的服务实现类。

image

思路和远程调用方法相似,在远程调用方法时,我们把方法的名称,参数类型,参数传递给服务端;现在要动态创建服务类,我们可以把服务接口的名字传给服务端。服务端拿到远程接口的名字后,就可以从服务注册表中找到对应服务实现类。

至于服务实现类如何注册到服务注册表,这里提供一个思路:可以考虑使用 Spring 的注解注入。这和我们平时写 spring 代码是相似的,在创建完服务实现类后我们会加上注解 @Service,这样我们就可以在收到远程调用后,遍历使用了 @Service的Bean,找到对应的实现类。

总结

如果想要搭建一个简易的RPC框架,有4个点:

  1. 代理问题
  2. 服务实例化问题
  3. 序列化问题
  4. 通信问题

本篇文章的侧重点是前两个,代理问题和服务实例化问题,我们回顾一下:

  1. 首先通过代理模式解决了每个远程方法联网的问题
  2. 通过传递方法名,方法参数类型,参数,以及后面提及的直接传接口名字的形式解决服务实例化问题,这背后的原理是反射,传这些参数也是给反射用的。

关于另外两个点的延伸

在网络上传输数据避免不了序列化问题。Java 自带的序列化方式效率比较低,如果要造一个 RPC 框架的话,我们可以考虑引入其他第三方的序列化框架。

通信问题也很重要,现有的 RPC 框架使用的通信协议也不相同,各有各的优缺点。但这部分手动实现可能比较困难,可以根据语言自身封装好的 API 进行选择。


参考:马士兵 rpc 的演化过程公开课 https://www.bilibili.com/video/BV1Ug4y1875i?p=2

最后

如果大家喜欢我们的文章,欢迎大家转发,点击在看让更多的人看到。也欢迎大家热爱技术和学习的朋友加入的我们的知识星球当中,我们共同成长,进步。

本文参考公众号 “java极客技术”

请关注我的订阅号

订阅号.png

四台Ryzen AI Max+ 395组网:llama.cpp RPC分布式推理集群实战 大模型推理的算力与内存瓶颈,始终是本地部署绕不开的课题。单机统一内存虽能承载百亿级模型,但面对数百亿参数的开源模型时,容量和带宽的双重限制会迅速暴露。分布式推理技术应运而生,它通过将模型按层切分到多台设备协同计算,实现内存池和算力的横向扩展。其中,基于llama.cpp的RPC后端方案,凭借低网络依赖和GGUF生态兼容性,成为入门多机推理的优选路径。该方案利用流水线并行原理,让每台机器仅持有部分层权重,在降低单机内存压力的同时,显著提升多用户并发吞吐。实际工程中,通过25G以太网互联四台AMD Ryzen 阅读详情

相关推荐

高分六号GF-6 WFV数据处理全流程:从原始数据解压到经纬度投影转换

本文详细解析了高分六号(GF-6)宽幅相机(WFV)遥感数据的全流程处理方法。从原始数据解压与自动拼接开始,逐步指导完成基于RPC参数和DEM的正射校正,以消除几何畸变;并重点阐述了针对超宽幅影像,如何将UTM投影转换为通用的WGS-84地理坐标系(经纬度),确保数据跨区域使用的准确性。文中还分享了数据源选择、DEM精度影响及性能优化等实战经验与避坑指南。

weixin_29081703的博客 273

rpc框架简易模拟+源码解释--马士兵

要想了解微服务 先把RPC搞懂 模拟客户端 服务端之间进行数据交互进化历史 01 最基础二进制传递 02 简化客户端的流程 引入stub 客户端存根(Client Stub):存放服务端地址信息,将客户端的请求参数数据信息打包成网络消息,再通过网络传输发送给服务端。 ...

ss123mlk的博客 1889

全套坦克大战源码

是网上比较经典的java大战源码,由浅入深,一步步实现各种功能,最后将整个项目写出,而不是因为过多的代码,导致思路不明确,不知道从何入手,是java必会经典题

RPC、RMI基本介绍

定义RPC全称为remote procedure call,即远程过程调用。借助RPC可以做到像本地调用一样调用远程服 务,是一种进程间的通信方式。RPC并不是一个具体的技术,而是指整个网络远程调 用过程。本地调用 和 RPC调用 区别RPC过程时序图一个完整的RPC架构里面包含了四个核心的组件,分别是Client,Client Stub,Server以及Server Stub,这个Stub可以理解为存根。客户端(Client),服务的调用方。

CAUC_lin的博客 2894

【华为2021校招】笔试题【通用硬件】

时间:2020/07/15 19:00 时长:60min 题型:40单选+5多选 难度:中等 考察范围:模电、数电、SI、EMC、电源、微机原理、计算机网络、材料

Jkcia的博客 4万+

Amos实战:如何用Bootstrap法检验中介效应?附完整操作步骤

本文详细介绍了如何使用Amos软件通过Bootstrap法检验中介效应,包括模型设定、参数配置和结果解读。Bootstrap方法因其不依赖正态分布假设、统计功效高等优势,成为中介效应分析的首选方法。文章提供了完整的操作步骤和实用建议,帮助研究者准确评估变量间的中介作用机制。

zz67890的博客 1437

RPC、RMI、Http 相关介绍

可能大家对RPC、REST、Dubbo、HTTP、RMI等概有些了解,但是对于这些名词之间的区别和联系却一直没有一个系统性的认识。下面对其做些总结。 一、概念介绍 RPC(Remote Procedure Call)是远程过程调用,是一种思想,也是一种协议规范。简单地说就是能使应用像调用本地方法一样的调用远程的过程或服务,可以应用在分布式服务、分布式计算、远程服务调用等许多场景。 RPC 的实现方式多样化:如http、webservice、rmi 此外业界有很多开源的优秀 RPC 框架,例如 Dubbo、T

Ruishine的博客 2081

Maven的下载安装配置教程(详细图文)

目录 一、简单了解一下什么是Maven 二、maven的下载 三、maven的安装 四、maven的环境变量配置 五、setting文件配置 六、开发工具配置Maven 一、简单了解一下什么是Maven Maven就是一款帮助程序员构建项目的工具,我们只需要告诉Maven需要哪些Jar 包,它会帮助我们下载所有的Jar,极大提升开发效率。 1.Maven翻译为“专家“, ”内行”的意思,是著名Apache公司下基于Java开发的开源项目。 2.Maven项目对象模型(POM)是一..

bakelFF的博客 12万+

通过几段 Java 代码带你理解 RPC

每天早上七点三十,准时推送干货Photo byMatthew SichkarukonUnsplashRPC 远程过程调用可以说是分布式系统的基础,本文将通过 Java 演示一次普通...

Java极客技术 518

IPMI的SOL (Serial Over LAN) 和 UART

UART全名是Universal Asynchronous Receiver/Transmitter(通用異步接收/发送器),它是一个常用的異步串行传输协议。透过UART传输前,接收方须要先知道传输方的鲍率(Baud rate):单位时间内,讯号状态变化的次数。例如 115200 bps,表示每秒会传送115200个bits--> 因此读取率就是 1/115200=8.6微秒读取一次讯号。封包内的资料长度:通常是 8bits。

yeiris的博客 1万+

pentaho-kettle依赖拉取失败?试试这个Maven仓库配置的终极解决方案

本文详细解析了Pentaho Kettle依赖拉取失败的常见原因,并提供了三种Maven仓库配置方案,包括基础pom.xml配置、全局settings.xml设置以及企业级私有镜像仓库搭建。通过实战验证的方法和常见问题排查指南,帮助开发者彻底解决pentaho-kettle依赖问题,提升项目构建效率。

weixin_30275415的博客 202

RPC发展(马士兵rpc笔记总结)

rpc01客户端等待服务器端连接,客户端将数据通过二进制传给服务器端。服务器端收到数据后,在数据库进行查询,得到一个对象。然后讲对象中的每个属性通过二进制传递给客户端。客户端读取每一个属性后,new为一个新的对象。(最原始的方法,很不灵活)rpc02从客户端分离出一个stub类,作为代理,从而屏蔽一些网络细节。(缺点:在这个stub类里只能代理一个方法,并返回这一个类)rpc03stub类通过动态代理返回了一个动态对象。这个对象所属的类实现了IUserService接口。在这个接口实在的方法屏蔽了一些...

weixin_44057219的博客 817

VisionPro 机器视觉案例 之 彩色保险丝个数统计

CogColorSegmenterTool CogColorMatchTool

机器视觉李小白的博客 2352

就靠这几段代码,带你玩转rpc通信协议,不信你学不明白

RPC(Remote Procedure Call Protocol)——远程过程调用协议,它是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的协议。该协议允许运行于一台计算机的程序调用另一台计算机的子程序,而程序员无需额外地为这个交互作用编程。RPC协议假定某些传输协议的存在,如TCP或UDP,为通信程序之间携带信息数据。在OSI网络通信模型中,RPC跨越了传输层和应用层。RPC使得开发包括网络分布式多程序在内的应用程序更加容易。 2.基本概念 RPC(Remote Procedur

Java架构师联盟 968

C++ Qt6 qml CMakeLists.txt如何添加qrc文件

本文介绍了在Qt6项目中如何通过CMake管理QML图片资源。相比Qt5的qmake,CMake需要手动创建qrc文件并配置CMakeLists.txt。关键步骤包括:1)创建resources.qrc文件并添加图片资源;2)在CMakeLists.txt中设置CMAKE_AUTORCC为ON开启自动资源处理;3)使用qt_add_resources将qrc文件绑定到项目。配置完成后,可通过"qrc:alias"方式引用资源图片,解决了相对路径管理复杂的问题。

琅嬛福地 1453

深入解读WT软件湍流强度计算与分析

深入解读WT软件湍流强度计算与分析

weixin_46713695的博客 1021

几段代码,轻松玩转rpc通信协议

RPC(Remote Procedure Call Protocol)——远程过程调用协议,它是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的协议。该协议允许运行于一台计算机的程序调用另一台计算机的子程序,而程序员无需额外地为这个交互作用编程。RPC协议假定某些传输协议的存在,如TCP或UDP,为通信程序之间携带信息数据。在OSI网络通信模型中,RPC跨越了传输层和应用层。RPC使得开发包括网络分布式多程序在内的应用程序更加容易。 2.基本概念 RPC(Remote Proced

weixin_50205273的博客 236

Statistics and Machine Learning Toolbox

Statistics and Machine Learning Toolbox(带书签),MATLAB统计学习工具

一起写RPC框架(四)RPC网络模块的搭建二 Netty的编码器和译码器

上一节,我们说到写一个RPC,很核心的一个模块也是基础就是网络通讯部分,定义一个通用的网络传输对象,会方便我们的对业务层面的编码,我们会花费很少的心思在底层的字节流的网络传输上。   因为我们选用的是Netty,写过Netty的或者有一点Netty基础的,都知道我们需要C/S端写一个编码器和译码器,因为我们知道不管你传输对象定义的有多好,有多么的通用,在网络上传输的全部都是正常人看不懂的字节,而

BazingaLyncc 4050

复分析 阿尔福斯(Ahlfors) 第三版答案 英文

答案分为十章,有部分习题没有答案,全英文复分析阿尔福斯(Ahlfors) 第三版答案

C# WinForm 基于DirectShow.dll编写的程序,可边预览边保存视频,音频和图片

基于微软的DirectShow.dll的二次开发1.自动识别连接的摄像头跟麦克风且可以对其进行选择(默认选中第一个)2.可录制wmv和avi两种格式的视频并且都带有音频,avi可以录制的分辨率和帧数进行选择,wmv默认进行了淸晰度优化如不喜欢可去除代码中有相关提示3.可在预览点开之后进行拍照,也可边录制视频边拍照4.添加了视频和音频的压缩功能(酌情使用)这次二次开发发现C#对DirectShow的相关信息太少,这次做完也不敢藏私共享出来,还望大家指正

从零实现 C++ Json-Rpc(四):项目框架设计、LV 协议与消息分发

本文为《从零实现 C++ Json-Rpc》系列第四篇,开始进入 RPC 框架整体设计。主要梳理 Network → Protocol → Dispatcher → 业务模块 的处理流程,并介绍 TCP 消息边界、LV 协议以及 Dispatcher 与各业务模块之间的关系,为后续协议与核心模块实现打下基础。

2301_79973359的博客 168

廖雪峰Python教程[完整版]

廖雪峰的python教程整理的,已经排好版了,而且还是完整的,适合小白

go-plugin 从入门到精通:基于 net/rpc 与 gRPC 的插件系统完整实战教程

本教程以 go-plugin 仓库官方文档 [docs/extensive-go-plugin-tutorial.md](https://link.gitcode.com/i/b000b1673c5885294f15a425b9af78c4) 为骨架,带领你从零理解 HashiCorp 插件系统的核心设计:宿主进程通过本地网络上的 RPC 与子进程插件通信,从而获得"插件崩溃不影响宿主、任意语言都

gitblog_06818的博客 67

从零手写Java简易RPC框架:核心原理与踩坑实战

RPC是分布式系统服务间通信的基石,其核心目标是让远程调用像本地方法一样透明。一次完整的RPC调用涉及动态代理、序列化、网络传输与反射调用等关键环节:客户端通过代理对象将方法调用封装为结构化请求,经序列化后传输至服务端,服务端再以反射方式精准定位并执行目标方法。理解这套底层机制,不仅是阅读Dubbo、gRPC等成熟框架源码的捷径,更能直接指导分布式架构中的性能调优与服务治理实践。本文从最小闭环设计出发,仅依赖JDK原生能力,逐步构建一个包含服务注册、代理生成、协议封装和反射调用的简易RPC框架,并针对实测中

weixin_30431445的博客 77

Agent生态建设四大支柱:RPC、SDK、Web与安全

AI Agent正从单体工具走向开放平台,其价值在于被外部系统集成。要让Agent具备可复用性,必须打通跨语言通信层,RPC是基础;SDK封装降低接入门槛;Web管理界面提供可视化观测;安全边界则保障自主调用工具时的可信可控。基于Pi Agent实践,从进程内函数调升级到gRPC双向流、SSE事件推送、细粒度权限Token与提示词注入防御的具体取舍,为构建可持续生长的Agent生态提供参考。

weixin_26985923的博客 230

Codex-X海鸥3.0破甲五步离线激活:装好发一句“在吗“就能验证

海鸥3.0破甲是 Codex-X 随附的 5 套内置指令模板之一:中文多域操作员人格、技能路由、黑话到技术轨道的映射打成一个复合体,写进 Codex 的指令文件而不是对话,自带一句逐字固定应答用来机械验证生效与否。源文件:[examples/海鸥3.0破甲.md](https://link.gitcode.com/i/6f126b51e83d54ae74109d77f8c1a552)。 [![C

gitblog_01107的博客 268

RPC框架优化实战:从800ms到80ms的拆解与调优

在微服务架构中,RPC调用的性能瓶颈常隐藏在连接管理、线程池配置、序列化选型等看似基础的环节。TCP_NODELAY、有界队列、二进制序列化等手段,能有效降低P99延迟并提升吞吐量。通过可观测性与压测方法定位长尾问题,结合熔断与重试设计保障稳定性,最终将一个高并发服务的P99从800ms降至80ms。本文从工程实践出发,拆解RPC框架优化的完整路径,为性能调优提供可复用的方法论。

weixin_42405592的博客 180

C++微服务落地指南:从RPC选型到延迟优化实战

微服务架构已成为大规模分布式系统的主流形态,而语言选型直接决定链路的延迟上限与资源效率。C++凭借可控的内存管理、编译期强约束和低资源占用,在支付交易、实时风控等延迟敏感场景中具备独特优势,但工程化门槛也远高于Java与Go。从RPC框架与序列化协议的选择,到镜像瘦身、优雅退出和崩溃治理,每一环都影响服务的稳定性与性能表现。本文结合支付链路真实案例,梳理C++微服务从架构拆分、同步异步调用到容器化部署的完整实践路径,重点覆盖gRPC、protobuf、etcd等关键组件,并给出延迟长尾分析与容量估算的实用方

weixin_29185199的博客 275
上一篇: 两个对象List根据属性取交集和差集
下一篇: 39. 从零开始学springboot-好用的开源工具类
码哥说
博客等级 码龄8年 5645粉丝 172原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

码哥说

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

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

抵扣说明:

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

余额充值