SSE 推送技术

实战揭秘:深入解析SSE结合EventBus实现消息定向推送 EventBus 是一个广泛应用于开发中的轻量级事件发布/订阅框架,它的核心设计理念是简化应用程序内部各组件间的通信。通过采用发布/订阅(Publish/Subscribe)设计模式,EventBus能够有效地降低组件之间的耦合度,提高代码可读性和维护性。 阅读详情

1、简介

Server-Sent Events(SSE)技术,它是一种用于实现服务器向客户端实时单向推送数据的Web技术。
SSE基于HTTP协议,允许服务器将数据以事件流(Event Stream)的形式发送给客户端。客户端通过建立持久的HTTP连接,并监听事件流,可以实时接收服务器推送的数据。
之前分享了一篇关于websocket技术的文章。本篇算是之前内容的一个补充。

官网摘要:

2、SSE和WebSocket的区别

WebSocket是另一种用于实现实时双向通信的Web技术。

  • 数据推送方面

    • SSE 是服务端像客户端的单向通信的技术。

    • WebSocket是双向通讯的技术

  • 协议方面

    • SSE是基于HTTP协议的长连接,超时后可以自动重连

    • WebSocket是基于ws协议的,建立双向连接实现通讯的

3、SSE的使用

SSE的使用无需引入特别的包,因为是一个Web技术,只要应为web对应的依赖即可。SSE的客户端是SseEmitter

@RestController
@RequestMapping("/foo")
public class FooController {

    Map<Integer,SseEmitter> map = Maps.newConcurrentMap();
    Map<Integer,SseEmitter> doneMap = Maps.newConcurrentMap();
    String curentContext = "";

    @GetMapping(value = "/sse", produces = {MediaType.TEXT_EVENT_STREAM_VALUE})
    public SseEmitter sseEmitter(HttpServletRequest request) throws IOException {
        String messageId = request.getHeader("Last-Event-ID");
        System.out.println("Last-Event-ID 重新连接:" + messageId);
        Integer sseEmitterId = RandomUtils.nextInt();
        SseEmitter sseEmitter = new SseEmitter(15000L);
        System.out.println("sseEmitter 建立连接... sseEmitterId=" + sseEmitterId);
        map.put(sseEmitterId, sseEmitter);
        if (StringUtils.isNotBlank(messageId)) {
            if (!doneMap.containsKey(Integer.valueOf(messageId))) {
                sseEmitter.send(SseEmitter.event().id(messageId).data("来自客户端【" + messageId + "】补发的信息:" + curentContext));
            }
        }

        sseEmitter.onCompletion(() -> {
            System.out.println("sseEmitter 结束... sseEmitterId=" + sseEmitterId);
            map.remove(sseEmitterId);
        });
        return sseEmitter;
    }

    @GetMapping("/sseSend")
    public String sseSend() {
        System.out.println("获取的sseEmitter客户端:" + JSON.toJSONString(map));
        curentContext = "测试SSE" + DateFormatUtils.format(new Date(), "yyyy-MM-dd HH:mm:ss");
        if (!map.isEmpty()) {
            doneMap.clear();
            map.forEach((key, value) -> {
                try {
                    value.send(SseEmitter.event().id(String.valueOf(key)).data("来自客户端【" + key + "】的信息:" + curentContext));
                    doneMap.put(key, value);
                    // value.send("来自客户端【" + key + "】的信息-----------:" + j);
                    Thread.sleep(1000);
                } catch (Exception e) {
                    throw new RuntimeException(e);
                }
            });
        }

        return "发送成功!";
    }

    @GetMapping("/closeWindow")
    public void test05() {
        System.out.println("closeWindow 窗口被关闭了....");
    }
}
3.1 订阅方法说明(/foo/sse)

/foo/sse 为订阅的方法。客户端打开页面,订阅该接口。返回的SseEmitter 为当前页面专属的连接,可以通过该连接推送消息。

注意事项:

  • 订阅的返回值必须是SseEmitter ,返回的数据类型为事件流。执行返回类型的的话需要配置produces = {MediaType.TEXT_EVENT_STREAM_VALUE} 。也可以不配置,请求会自动匹配。

  • 消息的发送,必须通过返回的SseEmitter,调用send()方法。由于需要实时推送,所以需要将创建的SseEmitter 缓存起来,随时推送消息。

  • SseEmitter 空参构造函数默认的超时时间为60s,也可以通过构造参数设置超时时间。案例中超时时间15s。如果设置成0,则表示永不超时。

  • Header中Last-Event-ID 参数为当前连接最新推送消息的ID,该ID可以自定义。消息推送之后,客户端重连之后,Header中会自动携带此参数(Last-Event-ID)。

  • SseEmitter 连接可以注册onCompletion【关闭】,onTimeOut【超时】,onError【错误】事件的回调。

3.2 模拟消息推送(/foo/sseSend)

/foo/sseSend 模拟消息推送的方法。获取创建的SseEmitter 连接,然后逐个推送消息。

注意事项:

  • map里面存放客户端的ID和连接。
  • 因为SSE连接会超时,超时的连接关闭之后会通过回调删除连接,所以重新连接的连接不会受到消息的推送。所以使用doneMap记录已经推送的客户端。自动连接的新连接补发消息。
3.3 页面关闭事件(/foo/closeWindow)

/foo/closeWindow 是页面被关闭时的请求连接。正常的逻辑里面,应该删除服务端保存的连接。案例中没有去实现,因为页面关闭有兼容性问题。只做演示。

  • 页面关闭的事件有兼容性问题,不能保证一定会触发

  • 页面关闭后,推送消息的连接应该被清除,否则容易引起OOM

  • 设置超时时间,通过回调可以避免这种问题。但是如果设置成永不超时,则会必须处理页面被关闭后连接的清除。

  • 关闭连接也可以使用客户端close方法直接关闭,但是如果发型消息的话会报错

    Caused by: java.io.IOException: 你的主机中的软件中止了一个已建立的连接。
        at sun.nio.ch.SocketDispatcher.write0(Native Method) ~[na:1.8.0_202]
        at sun.nio.ch.SocketDispatcher.write(SocketDispatcher.java:51) ~[na:1.8.0_202]
        at sun.nio.ch.IOUtil.writeFromNativeBuffer(IOUtil.java:93) ~[na:1.8.0_202]
        at sun.nio.ch.IOUtil.write(IOUtil.java:65) ~[na:1.8.0_202]
        at sun.nio.ch.SocketChannelImpl.write(SocketChannelImpl.java:471) ~[na:1.8.0_202]
        at org.apache.tomcat.util.net.NioChannel.write(NioChannel.java:135) ~[tomcat-embed-core-9.0.65.jar:9.0.65]
        at org.apache.tomcat.util.net.NioEndpoint$NioSocketWrapper.doWrite(NioEndpoint.java:1424) ~[tomcat-embed-core-9.0.65.jar:9.0.65]
        at org.apache.tomcat.util.net.SocketWrapperBase.doWrite(SocketWrapperBase.java:768) ~[tomcat-embed-core-9.0.65.jar:9.0.65]
        at org.apache.tomcat.util.net.SocketWrapperBase.flushBlocking(SocketWrapperBase.java:732) ~[tomcat-embed-core-9.0.65.jar:9.0.65]
        at org.apache.tomcat.util.net.SocketWrapperBase.flush(SocketWrapperBase.java:716) ~[tomcat-embed-core-9.0.65.jar:9.0.65]
        at org.apache.coyote.http11.Http11OutputBuffer$SocketOutputBuffer.flush(Http11OutputBuffer.java:573) ~[tomcat-embed-core-9.0.65.jar:9.0.65]
        at org.apache.coyote.http11.filters.ChunkedOutputFilter.flush(ChunkedOutputFilter.java:157) ~[tomcat-embed-core-9.0.65.jar:9.0.65]
        at org.apache.coyote.http11.Http11OutputBuffer.flush(Http11OutputBuffer.java:221) ~[tomcat-embed-core-9.0.65.jar:9.0.65]
        at org.apache.coyote.http11.Http11Processor.flush(Http11Processor.java:1255) [tomcat-embed-core-9.0.65.jar:9.0.65]
        at org.apache.coyote.AbstractProcessor.action(AbstractProcessor.java:402) ~[tomcat-embed-core-9.0.65.jar:9.0.65]
        at org.apache.coyote.Response.action(Response.java:209) ~[tomcat-embed-core-9.0.65.jar:9.0.65]
        at org.apache.catalina.connector.OutputBuffer.doFlush(OutputBuffer.java:306) ~[tomcat-embed-core-9.0.65.jar:9.0.65]
        ... 67 common frames omitted
    

4、客户端的使用

不需要引入任何js,直接使用EventSource 建立连接。

案例:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>测试SSE</title>
</head>
<body>
  <h1>测试SSE</h1>
  <div id="stock-price"></div>
  <div id="closeConnect">关闭连接</div>
</body>
<script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.min.js"></script>
<script>
    let eventSource;
    function init() {
        eventSource = new EventSource('/foo/sse');
        eventSource.onmessage = function (event) {
            console.info(event);
            document.getElementById('stock-price').innerHTML = event.data;
        };

        eventSource.onerror = function (event) {
            console.info(event.data + "::::exception");
            // if (event.target.readyState === EventSource.CLOSED) {
            //     init();
            // }
        };
        eventSource.addEventListener('test', e => {
            console.log(`message-data: ${e.data}`);
        }, false);
    }
    init();

    window.onbeforeunload  = function(e) {
        $.get("/foo/closeWindow", {});
    };

    $("#closeConnect").click(function(){
        console.info("close connection");
        eventSource.close();
    });
</script>
</html>
4.1 建立连接

new EventSource('/foo/sse') 建立连接/订阅消息,页面打开,方法执行会根据订阅的路径请求服务端获取连接。

4.2 监听消息

  • eventSource.onmessage 监听推送的消息,event.data直接可以获取推送的消息。

  • eventSource.onerror 监听异常的消息

  • eventSource.close() 关闭连接

  • eventSource.addEventListener 监听自定义时间,服务端通过SseEmitter.event().name(xxx)来设置事件的名称。

4.3 页面关闭的事件

window.onbeforeunload 监听页面的关闭,但是存在兼容性问题,或者页面异常的关闭都不会触发该方法。所以此方法不可靠。

5、案例演示

5.1 客户端页面

页面的跳转,订阅/foo/sse接口,等待消息推送。

5.2 模拟消息的推送

模拟推送消息/foo/sseSend

6、小结

  • SSE的使用要注意过期时间的设置,使用了过期时间,就要考虑客户端重连的消息的丢失问题。
  • 使用了永不过期就要考虑防止客户端连接过多造成的OOM

7、参考文档

https://zh.javascript.info/server-sent-events

https://javascript.info/server-sent-events

https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events

服务端实时推送技术之SSE(Server-Send Events) 服务端推送,也称为消息推送或通知推送,是一种允许应用服务器主动将信息发送到客户端的能力,为客户端提供了实时的信息更新和通知,增强了用户体验。 阅读详情

相关推荐

服务端推送SSE技术

SSE(Server-Sent Events)是一种用于实现服务器主动向客户端推送数据的技术,也被称为“它基于 HTTP 协议,利用了其长连接特性,在客户端与服务器之间建立一条持久化连接,并通过这条连接实现服务器向客户端的实时数据推送。SSE 基于 HTTP 协议,利用了其特性,通过浏览器向服务器发送一个 HTTP 请求,建立一条持久化的连接。而 WebSocket 则是通过特殊的升级协议(HTTP/1.1 Upgrade 或者 HTTP/2)建立新的 TCP 连接,与传统 HTTP 连接不同。

apple_52957499的博客 972

消息推送技术(sse、websocket)

消息推送-ajax长短轮循、sse技术、websocket技术

码农之路,向死而生 7002

SSE(Sever Sent Event)服务端推送技术

可以看到ChatGPT的输出是逐字输出的打字效果,这里应用到了SSE(SeverSideEvent)服务端推送的技术。

EmotionComputer 835

SSE推送技术

SSE超简单的推送方案

玩转Java 3782

VSCode 1.85便携版安装与配置:解决远程服务器glibc版本过低报错的保姆级指南

本文详细介绍了如何通过VSCode 1.85便携版解决远程服务器glibc版本过低导致的兼容性问题。通过环境隔离和版本固化的方法,开发者可以在老旧服务器上稳定运行Remote-SSH插件,同时保留主力VSCode的配置。文章提供了从下载安装到深度配置的完整指南,帮助用户快速搭建稳定的开发环境。

weixin_30279315的博客 578

BUCK型DC/DC变换器的建模与仿真

BUCK型DC-DC变换器的建模与仿真摘要1 文献调研1.1 研究背景1.2 Buck电路的应用场合2 原理分析2.1 模型介绍2.2 模型分析2.3 频域和时域响应2.4 参数指标要求3 参数设计3.1 校正器选择3.2 校正器参数整定3.2.1 积分器3.2.2 一阶微分环节3.2.3 超前校正器3.3 改进4 结论与展望参考文献 摘要   目前工业自动化水平已成为衡量各行各业现代化水平的一个重要标志。如何对一个系统进行建模分析、补偿校正变得越来越重要。   本文选择BUCK型DC/DC变换器作为载体,

屠龙归来仍是少年 2万+

服务器端实时推送技术之SSE

精选30+云产品,助力企业轻松上云!>>> 前言 ...

qq_18537055的博客 1731

用Docker-Compose三节点部署生产级Doris集群:从配置文件到故障转移

本文详细介绍了如何使用Docker-Compose部署一个生产级的三节点Apache Doris集群。内容涵盖从核心架构理解、配置文件编写到集群初始化验证的全过程,并重点讲解了如何实现高可用、数据持久化、健康检查以及故障转移等生产级运维关键点,为中小团队提供了一种在容器化便利性与系统稳定性间取得平衡的可靠部署方案。

kkk56的博客 858

springboot的服务端推送技术SSE

一.常见定时推送实现方式 1.客户端轮询:ajax定时拉取 2.服务端主动推送:WebSocket 全双工的,本质上是一个额外的tcp连接,建立和关闭时握手使用http协议,其他数据传输不使用http协议,更加复杂一些,适用于需要进行复杂双向数据通讯的场景。 3.服务端主动推送:SSE (Server Send Event): html5新标准,用来从服务端实时推送数据到浏览器端, 直接建立在当前http连接上,本质上是...

健康平安的活着的专栏 3300

全志T113s3主线linux开发(0)--基础准备

嵌入式Linux设备的启动大概是三步:1)启动芯片+内存:常见的是Uboot,不过全志的Uboot主线对SPI NAND 不太友好,用TF卡就方便。对于T113S芯片,推荐使用Awboot,简陋但快速。开发阶段用这个就方便好多,对SPINAND支持也方便。2)启动linux内核:主线内核以下通篇都用6.13以上的内核。对比tina5.0还在用5.4 ,主要是性能、安全上有变化。6.0以上内核在devicetree的编写具体格式上有了变化,网上大量tina5.0和5.4内核的编写方式不适用。

2301_79234138的博客 2249

最小二乘法(看完即会)

其中,X是数据矩阵,每一行是一个数据点,每一列是一个自变量(包括截距项),Y是观测值向量,β是待求的系数向量。为了找到使S最小的a和b的值,我们需要对S求关于a和b的偏导数,并令它们等于零。最小二乘法的数学公式通常用于线性回归问题,其中目标是找到一条直线(或更高维度的超平面),使得这条直线与给定数据点的误差平方和最小。同样地,通过求偏导数和令偏导数等于零,我们可以得到一个线性方程组,解这个方程组可以找到最优的b_0, b_1, …+ b_m*x_m,其中b_0是截距,b_1到b_m是各个自变量的系数。

qlkaicx的博客 2万+

SSE技术:实时数据推送全解析

摘要:Server-Sent Events(SSE)是一种基于HTTP的服务器推送技术,支持服务器向客户端单向实时传输文本数据。其核心特性包括长连接、自动重连和简单的事件处理机制,适用于新闻推送、股票行情等场景。相比WebSocket,SSE更轻量但功能单一,仅支持单向通信且无法传输二进制数据。实现上,服务器需设置text/event-stream响应头,客户端通过EventSource监听数据。SSE是HTML5规范的一部分,为轻量级实时应用提供了高效解决方案。(149字)

kcarly的专栏 632

【笔记6】SP3485自动收发电路设计分析

则TXD路为1,MOS管导通,2/3引脚为0,芯片实际上事与愿违的进入了接收模式。但是,由于我们采用单根双绞线与上位机通信,在数据发出去前数据线上没有任何信号传输,所以AB处于高阻态,进而受到R1下拉与R2上拉的影响,差分信号为1,所以输出1;此外,RXD内路也被上拉,485总线B端上拉,A端下拉。此时TXD由于没有数据传输,所以一直处于1的状态,因而MOS管导通,2/3引脚为0,芯片处于正常接收状态。总而言之就是,RE#与 DE只能有一个是1,1代表微控制器向外发送,0代表微控制器接收。

Haytham233的博客 3147

如果让你实现实时消息推送你会用什么技术?轮询、websocket还是sse

在很久很久以前,前端一般使用轮询来进行服务端向客户端进行消息的_伪推送_,为什么说轮询是伪推送?因为_轮询本质上还是通过客户端向服务端发起一个单项传输的请求_,服务端对这个请求做出响应而已。通过不断的请求来实现服务端向客户端推送数据的错觉。并不是服务端主动向客户端推送数据。显然,轮询一定是上述三个方法里最下策的决定。首先轮询需要不断的发起请求,_每一个请求都需要经过http建立连接的流程_(比如三次握手,四次挥手),是没有必要的消耗。客户端需要从页面被打开的那一刻开始就_一直处理请求_。

广漂程序猿DevinRock 1601

HighTec安装使用

HighTec安装使用

qq_43552564的博客 3836

STM32 BOOTLOADER配置以及APP跳转实现(裸机)

Bootloader是硬件启动的引导程序,是运行操作系统的前提。在操作系统内核或用户应用程序运行之前运行的一段小代码。对硬件进行相应的初始化和设定,最终为操作系统准备好环境。APP就是我们的应用程序,经过硬件引导程序之后跳转到系统程序运行。

吻等离子的博客 3358

基于SSE的实时数据推送技术

前言在当今的互联网应用中,实时数据推送技术扮演着至关重要的角色。无论是股票行情、新闻更新还是在线聊天,实时性都极大地提升了用户体验。今天,我们将深入探讨一种基于W3C标准的实时数据推送技术——Server-Sent Events(SSE)。SSE是一种简单而高效的单向通信机制,允许服务器向客户端推送数据流。通过Spring框架中的SseEmitter类,我们可以轻松实现SSE功能。接下来,我们将通过一个完整的示例来展示如何在Spring Boot项目中使用SSE。

m0_74337424的博客 796

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

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

服务器推送技术之——SSE

一 点睛 服务器推送技术在日常开发中较为常用。 SSE:Server send Event:服务端发送事件。 本项目推送技术是基于:当客户端向服务端发送请求,服务端会抓住这个请求不放,等有数据更新的时候才返回给客户端,当客户端接收到消息后,再向服务端发送请求,周而复始。这种方式的好处是减少了服务器的请求数量,大大减少了服务器的压力。 二 实战 1 控制器 package com.wi...

实践求真知 2702

Data-Structures-and-Algorithm-Analysis-in-Java.pdf

作者: Weiss, Mark A. 出版社: Addison Wesley Data Structures and Algorithm Analysis in Java is an “advanced algorithms” book that fits between traditional CS2 and Algorithms Analysis courses. In the old ACM Curriculum Guidelines, this course was known as CS7. This text is for readers who want to learn good programming and algorithm analysis skills simultaneously so that they can develop such programs with the maximum amo...

STM32F407中文手册

STM32f407中文手册 本参考手册面向应用开发人员,提供有关使用 STM32F405xx/07xx、STM32F415xx/17xx、 STM32F42xxx 和 STM32F43xxx 微控制器存储器与外设的完整信息。STM32F405xx/07xx、STM32F415xx/17xx、STM32F42xxx 和 STM32F43xxx 构成一个微 控制器系列,各产品具有不同的存储器大小、封装和外设。有关订购信息以及器件的机械与电气特性,请参见数据手册。有关 ARM Cortex™-M4F 内核的信息,请参见《Cortex™-M4F 技术参考手册》。

HTML5中的SSE(服务器推送技术)

本文原链接:https://cloud.tencent.com/developer/article/1194063 SSE技术详解:一种全新的HTML5服务器推送事件技术 前言 概述 基本介绍 与WebSocket的比较 SSE(Server-sent Events)在HTML 5中的技术规范和定义 SSE实战示例:服务器端和浏览器端实现 IE上的兼容性问题 结束语 参...

weixin_33676492的博客 1301

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

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

主动推送数据到前端的SSE技术原理以及如何使用

首先,SSE全称是Server-Sent Events,是一种服务器向客户端单向推送数据的技术。它基于HTTP协议,使用长连接,允许服务器主动发送消息到浏览器。相比WebSocket,SSE更简单,但只支持单向通信,适合服务器向客户端推送实时数据的场景,比如实时通知、股票行情等。Spring MVC提供了对SSE的支持,主要是通过SseEmitter类。需要创建一个Controller,返回SseEmitter对象,然后通过这个对象发送数据。

qq_39126115的博客 1774

【SpringBoot】服务器端主动推送SSE技术讲解

=====================16、高级篇幅之SpringBoot2.0服务器端主动推送SSE技术讲解 ============================ 1、服务端推送常用技术介绍 简介:服务端常用推送技术介绍,如websocket,sse轮询等 1、客户端轮询:ajax定时拉取 2、服务端主动推送:WebSocket 全双工的,本质上是一个额外的tcp连接...

weixin_30894583的博客 623

如果让你实现实时消息推送你会用什么技术?轮询、websocket还是sse

大厂技术高级前端Node进阶点击上方程序员成长指北,关注公众号 回复1,加入高级Node交流群前言在日常的开发中,我们经常能碰见服务端需要主动推送给客户端数据的业务场景,比如_数据大屏的实时数据_,比如_消息中心的未读消息_,比如_聊天功能_等等。本文主要介绍SSE的使用场景和如何使用SSE。image.png服务端向客户端推送数据的实现方案有哪几种?我们常规实现这些需求的方案有以下三种...

程序员成长指北 455

用Go实现 SSE 实时推送消息(消息通知)——思悟项目技术4

SSE(Server - Sent Events)是一种允许服务器向客户端实时推送更新的 Web 技术。是一种基于 HTTP 协议的单向通信机制,服务器可以在客户端建立连接后,持续不断地向客户端发送事件流。客户端只需发起一次请求,服务器就能随时向客户端推送新的数据,无需客户端反复请求。

m0_72874409的博客 1373

SpringBoot2.0实现服务器主动推送SSE技术

服务器主动推送SSE技术介绍以及SpringBoot2.0实现方式

学习记录和总结 1万+
上一篇: Spring源码解析-构造函数
下一篇: Spring源码解析-注册配置信息
智_永无止境
博客等级 码龄10年 803粉丝 406原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

打赏作者

智_永无止境

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

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

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

打赏作者

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

抵扣说明:

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

余额充值