记一次“Axis2客户端调用产生大量CLOSE_WAIT连接”的解决过程

背景: 程序使用1.6.2版本的axis2做WebService客户端调用。
代码:
public static Object sendRequest(Object req) throws AxisFault
{
	String returnMsg = "";
	RPCServiceClient serviceClient = null;
	serviceClient = new RPCServiceClient();
	Options options = serviceClient.getOptions();
	EndpointReference targetEPR = new EndpointReference(PropertiesUtils.getproperty("send.ws.targetEPR", ""));
	
	options.setTo(targetEPR);
	options.setTimeOutInMilliSeconds(Long.valueOf(PropertiesUtils.getproperty("send.ws.timeout", "30000")));
	//设置客户端关闭连接
	//options.setProperty(HTTPConstants.HEADER_CONNECTION, HTTPConstants.HEADER_CONNECTION_CLOSE);
	
	QName opAddEntry = new QName(PropertiesUtils.getproperty("send.ws.namespace", ""), PropertiesUtils.getproperty("send.ws.method", ""));
	Object[] opAddEntryArgs = new Object[] { req };
	Class[] classes = new Class[] { String.class };
	log.info("发送请求XML:"+req);
	returnMsg = (String) serviceClient.invokeBlocking(opAddEntry, opAddEntryArgs, classes)[0];
	log.info("收到返回XML:"+returnMsg);
	return returnMsg;

}
 现象:程序上线后,观察WebService的调用情况,发现服务端响应很慢(服务端是另一家厂商,动不了他),每个请求延时都在5秒左右,在使用netstate查看连接数的时候,意外发现了程序产生大量CLOSE_WAIT状态的连接,而且数量稳定增长,一个小时就接近300个连接。
#netstat -tnp|grep 7011|grep CLOSE_WAIT|wc -l
#271

 

折腾:发现这个问题后,就开始baidu.然后各种折腾。axis2的HTTPConstants.HEADER_CONNECTION ,

HEADER_CONNECTION_KEEPALIVE参数沾边的都试了遍,无效果。

两个小时过去了,没有进展。

 

看着一直增长的连接数,好象血管在一直流血一样。这样下去内存应该也会增长吧。看看jvm内存是不是有对象泄漏,导致HttpClient没有回收(axis2底层使用这个类发送WebService请求)?

 

jmap -histo 18041 |grep com.xxxxxxx
果然,程序new出来的对象,每一分钟增长一次(对应每分钟循环一次)。

 

 

此时心中有一个大大的问号:  为什么GC没有回收掉?

如果GC回收了,TCP连接会Close吗? 带着这个疑问,用tcpdump监控下tcp连接。

 

jmap -histo:live 18041 |grep com.xxxxxx # live会引起GC:没有找到根据,但确实是这样。
 在GC的瞬间,tcpdump看到客户端发出的reset包,同时CLOSE_WAIT连接都消失了。 

 

 

15:27:50.152698 IP WOOS1.46262 > 137.32.25.62.talon-disc: R 1384067195:1384067195(0) ack 4094862191 win 72 <nop,nop,timestamp 536888568 1094736905>
15:27:50.152710 IP WOOS1.44490 > 137.32.25.62.talon-disc: R 1983758145:1983758145(0) ack 4199192243 win 72 <nop,nop,timestamp 536888568 1094821918>
15:27:50.152723 IP WOOS1.58174 > 137.32.25.62.talon-disc: R 1982916452:1982916452(0) ack 4178184596 win 72 <nop,nop,timestamp 536888568 1094806915>
15:27:50.152738 IP WOOS1.32940 > 137.32.25.62.talon-disc: R 1748662783:1748662783(0) ack 4206843128 win 72 <nop,nop,timestamp 536888568 1094826919>
15:27:50.152762 IP WOOS1.36913 > 137.32.25.62.talon-disc: R 815070216:815070216(0) ack 4200525319 win 72 <nop,nop,timestamp 536888568 1094826919>
15:27:50.152775 IP WOOS1.40743 > 137.32.25.62.talon-disc: R 4116878055:4116878055(0) ack 4209835792 win 72 <nop,nop,timestamp 536888568 1094836922>
15:27:50.152788 IP WOOS1.33267 > 137.32.25.62.talon-disc: R 2893572903:2893572903(0) ack 4218047967 win 72 <nop,nop,timestamp 536888568 1094841922>
15:27:50.152802 IP WOOS1.38644 > 137.32.25.62.talon-disc: R 1232744431:1232744431(0) ack 12002951 win 72 <nop,nop,timestamp 536888568 1094931936>
 
解决问题:
     既然GC可以关闭连接,只要能让jvm即时GC就可以解决问题。 考虑到程序时各种大量的List , Map的对象互相引用,是否是这个原因导致 不能及时GC?(这是工作以来,第一次遇到GC的问题,脑子里关于GC的知识太少)
    于是修改程序,在List , Map 超出作用域时,clear掉。 用jmap观察程序,对象不再无限制增长:增长到一定值后大减,被GC掉。同时连接数也不再无限制增长,最大到30左右连接就会被关闭掉。
教训:
代码中没有考虑GC效率问题,尤其是map,list对象很多,对象间引用关系复杂,导致GC无法及时回收过期对象。   对于java的GC策略也不了解。看来要花些时间了解GC了。
 
 
%% 初始化与环境清理 clc; clear; close all; % 增加close all确保干净的图形环境 %% 数据加载与参数预设 try load('1.mat'); % 使用try-catch处理文件加载错误 catch ME error('数据文件加载失败: %s', ME.message); end % 定义模型参数(添加参数说明注释) D1 = 2.582; % 转轮直径(m) Nb = 375; % 额定转速(rpm) Qb = 89.73; % 额定流量(m³/s) Hb = 440; % 额定扬程(m) Mb = abs((357*0.957460219777722*1e6/(Nb*pi/30))); % 额定转矩(N·m) K1 = 10; % 转矩修正系数 K2 = 0.8; % 流量修正系数 Cy = 0.2; % 导叶开度修正系数 Ch = 0.5; % 扬程修正系数 %% 数据预处理与向量化计算 % 创建进度条(提升用户体验) wait_h = waitbar(0,'数据处理进度'); % 预分配结构体数组内存 Chazhipump(14) = struct('x',[], 'Wh',[], 'Wm',[]); for i = 1:14 % 向量化计算(提升计算效率) N11 = Pump_data(i).N11; M11 = Pump_data(i).M11; Q11 = Pump_data(i).Q11; % 批量计算特征参数 M = M11 * D1^3 * Hb; Q = Q11 * D1^2 * sqrt(Hb); N = N11 * sqrt(Hb) / D1; Y = Pump_data(i).y / 2; % Suter变换(优化角度计算) numerator = K2 + Q/Qb; denominator = N/Nb; theta = atan2(numerator, denominator); x = theta + pi*(N <= 0); % 使用逻辑索引替代条件判断 % 计算无量纲参数(避免重复计算) common_denominator = (N/Nb).^2 + (Q/Qb).^2 + Ch; Wh = (Y + Cy).^2 ./ common_denominator; Wm = ((M/Mb) + K1) .* Wh; % 数据存储(添加有效性检查) valid_idx = isfinite(x) & isfinite(Wh) & isfinite(Wm); Chazhipump(i).x = x(valid_idx); Chazhipump(i).Wh = Wh(valid_idx); Chazhipump(i).Wm = Wm(valid_idx); waitbar(i/14, wait_h); end close(wait_h); %% 网格化与插值优化 num = 1000; % 加密采样点 X = linspace(0.2, 3.6, num); y = 0:2:26; % 导叶开度向量 % 创建统一网格 [X_grid, Y_grid] = meshgrid(X, y); % 改进的插值方案(处理边界值) interp_method = 'makima'; % 改用makima插值算法 extrap_method = 'nearest'; % 边界外推方法 % 预分配结果矩阵 Wm_grid = nan(length(y), num); Wh_grid = nan(length(y), num); for i = 1:14 % 获取有效数据范围 x_data = Chazhipump(i).x; wh_data = Chazhipump(i).Wh; wm_data = Chazhipump(i).Wm; % 执行插值并处理异常值 if numel(x_data) >= 3 % 保证插值可行性 Wh_grid(i,:) = interp1(x_data, wh_data, X, interp_method, extrap_method); Wm_grid(i,:) = interp1(x_data, wm_data, X, interp_method, extrap_method); end % 数据平滑处理(可选) Wh_grid(i,:) = smoothdata(Wh_grid(i,:), 'movmean', 50); Wm_grid(i,:) = smoothdata(Wm_grid(i,:), 'movmean', 50); end %% 增强可视化 set(0,'defaultfigurecolor',[1 1 1]) % 设置默认背景色 cmap = turbo(256); % 改用turbo颜色映射 % 创建统一图形窗口 mainFig = figure('Name','泵全特性分析系统',... 'Position',[100 100 1440 700],... 'NumberTitle','off'); % Wh特性曲面 subplot(2,2,1) [~, h1] = contourf(X_grid, Y_grid, Wh_grid, 30); set(h1, 'EdgeColor', 'none') colormap(cmap) colorbar('southoutside') title('Wh全特性曲面','FontSize',12,'FontWeight','bold') xlabel('角度参数x (rad)','FontSize',10) ylabel('导叶开度Y (mm)','FontSize',10) grid on axis tight % Wm特性曲面 subplot(2,2,2) [~, h2] = contourf(X_grid, Y_grid, Wm_grid, 30); set(h2, 'EdgeColor', 'none') colormap(cmap) colorbar('southoutside') title('Wm全特性曲面','FontSize',12,'FontWeight','bold') xlabel('角度参数x (rad)','FontSize',10) grid on axis tight % 三维可视化 subplot(2,2,[3,4]) surf_h = surf(X_grid, Y_grid, Wh_grid, 'EdgeColor','none'); shading interp light('Position',[0 1 1],'Style','infinite') lighting gouraud material dull colorbar view(-35, 60) title('Wh三维特性曲面','FontSize',12,'FontWeight','bold') xlabel('角度参数x (rad)','FontSize',10) ylabel('导叶开度Y (mm)','FontSize',10) zlabel('无量纲扬程系数','FontSize',10) % 添加统一注释 annotation(mainFig,'textbox',[0.4 0.93 0.2 0.05],... 'String','水泵全特性分析报告',... 'FontSize',14,'FontWeight','bold',... 'EdgeColor','none','HorizontalAlignment','center'); %% 数据保存(可选) save('ProcessedData.mat','Chazhipump','X_grid','Y_grid','Wh_grid','Wm_grid'); 上述的这套代码运行后出现以下报错,请你做出正确的修改 报错:错误使用 interp1>parseinputs (第 391 行) 字符向量参数无效。是否指 'pp' 或 'extrap'? 出错 interp1 (第 112 行) parseinputs(X,V,penultimate,last,nargin,ndataarg,pp); 出错 M11_Q1122 (第 86 行) Wh_grid(i,:) = interp1(x_data, wh_data, X, interp_method, extrap_method);
最新发布
03-13
<think>嗯,我现在需要解决这个Matlab代码运行时的错误。用户提供的代码在运行时报错,错误信息是关于interp1函数的参数无效,具体提示是字符向量参数无效,可能指的是'pp'或'extrap'。这个问题出现在第86行,也就是调用interp1的时候。让我仔细看一下相关部分的代码。 首先,用户代码中的interp1调用是这样的: Wh_grid(i,:) = interp1(x_data, wh_data, X, interp_method, extrap_method); 这里传递了五个参数。根据Matlab的文档,interp1的基本语法是interp1(X,V,Xq,method,extrapolation)。也就是说,第五个参数应该是extrapolation方法,比如'extrap'或者其他指定的值。然而,用户可能在传递参数时出现了问题。 查看用户定义的变量: interp_method = 'makima'; % 改用makima插值算法 extrap_method = 'nearest'; % 边界外推方法 然后他们在interp1中同时传递了这两个方法作为第四和第五个参数。不过,根据Matlab的文档,interp1的参数顺序应该是method之后跟着extrapolation。正确的调用方式应该是将method和extrapolation作为两个独立的参数传入,并且需要显式地设置extrapolation参数。例如: interp1(x_data, wh_data, X, interp_method, 'extrap', extrap_method); 或者可能用户在使用旧版本的Matlab,其中extrapolation参数可能需要通过不同的方式指定。也有可能用户的写法不符合当前版本的语法。 在Matlab中,interp1的语法在较新版本中可能有所变化。比如,在R2013a之后,interp1允许在第四个参数指定方法,第五个参数指定外推方式。例如: vq = interp1(x,v,xq,method,extrapolation) 这里的extrapolation可以是标量值或者'extrap',或者一个特定的方法。但用户在这里可能误用了参数。例如,用户可能将第四个参数作为方法,第五个作为外推方法,但正确的做法是当需要指定外推值时,应使用'extrap'参数,并在其后指定具体的外推方法或值。 或者,用户可能混淆了外推参数的使用方式。正确的做法是,当使用interp1时,如果要在方法参数之后指定外推方式,需要将extrapolation参数设置为'extrap',然后再跟具体的方法或值。例如,正确的调用方式应该是: interp1(x_data, wh_data, X, interp_method, 'extrap', extrap_method); 但Matlab的interp1函数是否支持这样的参数呢?实际上,在Matlab的帮助文档中,interp1的语法是: vq = interp1(x,v,xq,method, extrapolation) 这里的extrapolation可以是一个标量(用于填充外推点的值)或者字符串'extrap',表示使用插值方法进行外推。如果用户想要使用不同的外推方法,可能需要用其他方式处理。 例如,用户可能希望当数据超出范围时使用'nearest'方法进行外推。但interp1的extrapolation参数不支持直接指定另一种方法,只能指定一个标量或者'extrap'。例如,如果用户想要在超出区域使用最近的邻点,可能需要将extrapolation参数设置为'extrap',并且interp_method本身需要支持外推,或者用户需要自行处理外推值。 或者,用户可能误将外推方法作为第五个参数传入,但正确的做法是,当需要外推时,应该设置extrapolation参数为'extrap',然后interp1会使用method指定的方法进行外推。例如,如果interp_method是'makima',则外推时会使用makima方法进行外推。如果用户希望外推时使用nearest方法,可能需要将interp_method设置为'nearest',或者需要分开处理。 回到用户的代码,他们可能想将插值方法设置为'makima',而外推方法设置为'nearest'。但Matlab的interp1函数不允许在同一个调用中指定两种不同的方法。这时候,可能需要先进行插值,然后在数据点之外使用其他方法处理,或者使用fillmissing等函数进行后处理。 因此,用户当前的代码中,interp1的参数顺序可能不正确。正确的做法是,如果用户想要指定外推值,比如使用'nearest'来填充超出范围的点,应该将第五个参数设置为一个标量,或者使用'extrap',让函数使用插值方法本身进行外推。或者,用户可能需要改变参数传递的方式。 例如,用户原来的代码中,第五个参数是extrap_method,即'nearest'。但interp1函数中,第五个参数如果是字符向量的话,可能被误认为是其他的参数。例如,在旧版本的Matlab中,可能不支持在interp1中同时指定方法和外推参数,或者需要不同的语法。 查看Matlab的报错信息:“字符向量参数无效。是否指 'pp' 或 'extrap'?”,这说明在调用interp1时,输入的参数可能被解析为其他选项,例如'pp'代表分段多项式,或者需要显式使用'extrap'关键字参数。 可能正确的调用方式应该是,当想要启用外推时,需要显式设置'extrap'参数。例如,正确的调用应该是: interp1(..., 'makima', 'extrap') 或者,当需要指定外推值时,第五个参数应为一个标量,或者'extrap'。比如,用户可能应该这样调用: Wh_grid(i,:) = interp1(x_data, wh_data, X, interp_method, 'extrap'); 这样,interp1会使用'makima'方法进行外推。但用户希望外推方法为'nearest',这可能无法直接实现。因为'makima'和'nearest'是不同的插值方法,无法在一个调用中同时使用。因此,用户可能需要选择其中一种方法进行外推,或者在外推时采用其他处理方式。 可能的解决方案是,将外推参数改为'extrap',这样interp1会使用interp_method(如'makima')进行外推。或者,如果用户希望外推时使用'nearest',则可能需要将插值方法也改为'nearest',但这显然不符合用户原本的意图。 或者,用户可能混淆了interp1的参数顺序。例如,正确的参数顺序应该是x, v, xq, method, extrapolation。在这里,extrapolation可以是一个标量或者'extrap'。例如,当使用interp_method = 'makima',并且想在外推时使用'nearest',这似乎是不可能的,因为interp1不允许在同一个调用中使用两种不同的插值方法。所以用户可能需要重新考虑如何处理外推的情况。 基于此,用户当前的错误可能出现在将extrap_method作为第五个参数传递,而正确的参数应是在指定方法后,使用'extrap'来启用外推,或者传递一个标量值。例如,正确的调用可能是: Wh_grid(i,:) = interp1(x_data, wh_data, X, interp_method, 'extrap'); 这样,外推时会使用'makima'方法。如果用户想要在外推时使用其他方法,可能需要分两步处理:首先进行插值,然后对超出范围的数据使用其他方法填充。 或者,用户可能应该将第五个参数设置为一个标量值(如NaN),然后后续用fillmissing函数填充为'nearest'。例如: Wh_grid(i,:) = interp1(x_data, wh_data, X, interp_method, NaN); Wh_grid(i,:) = fillmissing(Wh_grid(i,:), 'nearest'); 但用户可能希望外推时使用nearest方法,这可能需要不同的处理方式。或者,用户可能误以为'interp1'允许在第五个参数中指定外推方法,但实际上这并不正确。 因此,问题出在用户将extrap_method作为第五个参数传递给interp1,而正确的做法可能是:如果要使用外推,需要设置第五个参数为'extrap',此时interp1会使用method参数指定的方法进行外推。或者,将第五个参数设置为一个标量值,用于填充外推点。 根据用户的代码,他们的extrap_method是'nearest',这可能不符合interp1的参数要求。正确的做法可能是将第五个参数设为'extrap',或者使用其他方式处理。例如: 原来的代码: Wh_grid(i,:) = interp1(x_data, wh_data, X, interp_method, extrap_method); 错误的原因是,在interp1中,第五个参数被识别为无效的字符向量。正确的参数可能是'extrap'或者一个标量。例如,如果用户想在外推时使用'nearest'方法,这可能需要将插值方法也设置为'nearest',或者分步骤处理。 可能的解决方案是将第五个参数改为'extrap',这样interp1会使用method参数指定的方法(如'makima')进行外推。例如: Wh_grid(i,:) = interp1(x_data, wh_data, X, interp_method, 'extrap'); 这样,外推时就会使用'makima'方法。这可能与用户原来的意图不符,因为用户想在外推时使用'nearest'方法。或者,用户可能误以为可以将两个方法参数传递给interp1,但实际不行。 因此,正确的修改应该是将第五个参数替换为'extrap',从而启用外推,并使用method指定的方法进行外推。这样,当数据点超出范围时,会使用'makima'插值方法进行外推,而不是'nearest'。这可能更符合用户原本的意图,或者可能需要调整参数。 因此,用户需要修改interp1的调用方式,将第五个参数改为'extrap',从而允许外推,并利用method指定的方法进行外推。例如: 原代码中的: Wh_grid(i,:) = interp1(x_data, wh_data, X, interp_method, extrap_method); 改为: Wh_grid(i,:) = interp1(x_data, wh_data, X, interp_method, 'extrap'); 或者,如果用户希望在外推时使用特定的值(比如nearest的值),可能需要先找到超出范围的区域,并使用不同的方法处理。例如,首先进行插值,然后对于超出原始数据范围的X点,使用nearest方法填充。但这样做可能需要更复杂的处理步骤。 另一种可能性是,用户可能将extrap_method变量设置为'extrap',而不是'nearest'。例如,在用户原来的代码中,extrap_method被定义为'nearest',但interp1的第五个参数期望的是'extrap'或者标量。因此,正确的做法是设置extrap_method为'extrap',并确保插值方法支持外推。例如,某些插值方法(如'spline'、'pchip'、'makima')支持外推,而其他方法(如'linear'、'nearest')可能默认返回NaN,除非指定外推方式。 因此,总结错误原因:在调用interp1时,第五个参数是'nearest',而interp1期望的是'extrap'或者一个标量值,导致参数无效。解决方法是将第五个参数改为'extrap',让interp1使用method参数指定的插值方法进行外推,或者使用一个标量值填充外推点。 因此,修改后的代码应为: 将原来的: Wh_grid(i,:) = interp1(x_data, wh_data, X, interp_method, extrap_method); Wm_grid(i,:) = interp1(x_data, wm_data, X, interp_method, extrap_method); 改为: Wh_grid(i,:) = interp1(x_data, wh_data, X, interp_method, 'extrap'); Wm_grid(i,:) = interp1(x_data, wm_data, X, interp_method, 'extrap'); 这样,当使用interp_method(如'makima')进行插值时,允许外推,并使用同样的方法进行外推。如果用户确实需要在外推时使用'nearest',则需要将插值方法设置为'nearest',或者在调用interp1后,手动处理外推部分的数据。 但根据用户原来的注释:“改进的插值方案(处理边界值)”,可能他们希望在外推时使用'nearest',但这是无法通过interp1直接实现的。因此,可能需要分两步处理:首先进行插值,不允许外推(得到NaN),然后用'nearest'填充这些NaN值。例如: Wh_grid(i,:) = interp1(x_data, wh_data, X, interp_method); Wh_grid(i,:) = fillmissing(Wh_grid(i,:), 'nearest', 2); 同样适用于Wm_grid。这需要将外推部分的数据用'nearest'填充。这时,用户需要调整代码,先进行插值,然后填充NaN值。 因此,可能的正确修改是: 将原来的interp1调用改为: Wh_grid(i,:) = interp1(x_data, wh_data, X, interp_method); Wh_grid(i,:) = fillmissing(Wh_grid(i,:), 'nearest'); 同样适用于Wm_grid。这样,当插值结果为NaN时,用最近的数值填充。这可能更符合用户希望的效果。 综合以上分析,用户的问题在于将外推方法参数错误地传递给了interp1,正确的处理方式取决于用户的需求。根据用户提供的extrap_method = 'nearest',可能的正确修改是分两步处理:先插值,然后填充外推值为最近的数值。因此,修改后的代码部分可能如下: 原代码中的: Wh_grid(i,:) = interp1(x_data, wh_data, X, interp_method, extrap_method); Wm_grid(i,:) = interp1(x_data, wm_data, X, interp_method, extrap_method); 修改为: Wh_grid(i,:) = interp1(x_data, wh_data, X, interp_method); Wh_grid(i,:) = fillmissing(Wh_grid(i,:), 'nearest', 2); % 假设第二个维度是列 同样适用于Wm_grid。但具体需要看数据维度。或者,用户可能需要先进行插值,然后处理NaN值。 总结错误原因:用户试图在interp1中同时指定插值方法和外推方法,但interp1不支持这种参数传递方式。解决方法是分开处理,先进行插值,然后填充外推的NaN值为最近的数值。 因此,正确的修改方式是将interp1的调用改为使用插值方法,并在之后用fillmissing填充NaN为'nearest'。</think>错误出现在`interp1`函数调用时参数传递不当。MATLAB的`interp1`函数在指定外推方法时,需要显式使用`'extrap'`参数,且外推值应与插值方法兼容。以下是修正后的代码段: ```matlab %% 改进的插值方案(处理边界值) interp_method = 'makima'; % 主插值算法 extrap_method = 'nearest'; % 边界处理策略 for i = 1:14 if numel(x_data) >= 3 % 分步处理:先插值再填充外推值 Wh_temp = interp1(x_data, wh_data, X, interp_method); Wh_grid(i,:) = fillmissing(Wh_temp, extrap_method); Wm_temp = interp1(x_data, wm_data, X, interp_method); Wm_grid(i,:) = fillmissing(Wm_temp, extrap_method); end end ``` **错误原因解释**: 1. `interp1`的第五个参数应接受标量值或`'extrap'`,不能直接传递外推方法名称 2. `'nearest'`外推需要分两步实现:先进行主插值,再用`fillmissing`处理`NaN`值 3. 原代码试图在单个`interp1`调用中同时指定两种方法,不符合MATLAB语法 **修正方案特点**: 1. 使用`fillmissing`独立处理外推逻辑 2. 保持`makima`插值方法的高精度特性 3. 通过`fillmissing`实现边界`nearest`填充 4. 增强代码对不同MATLAB版本的兼容性
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值