HTTP/2 简介

HTTP/2 协议(Wireshark 抓取浏览器加密的 HTTP2) HTTP/2 协议介绍 1.HTTP/1.1 发明以来发生的变化 从几 KB 大小的消息,到几 MB 大小的消息 每个页面小于 10 个资源,到每页面 100 多个资源 从文本为主的内容,到富媒体(如图片、声音、视频)为主的内容 对页面内容实时性要求的应用越来越多 2.HTTP/1.1 的高延迟问题 高延迟带来页面加载速度的降低 随着带宽的增加,延迟并没有显著下降 并发连接有限 同一连接同时只能在完成一个 HTTP 事务(请求/响应)才能处理下一个事务 3.高延迟 VS 高带宽 単连接上的串行请求 阅读详情
AI摘要

HTTP/2是HTTP协议的重大升级,引入了二进制分帧、多路复用等特性,提高了网络性能和效率。二进制分帧层使得消息以帧的形式传输,支持数据流优先级和流控,解决了HTTP/1.x的连接和响应队列问题。多路复用允许在一个TCP连接上并发处理多个请求,减少了延迟和资源消耗。此外,HTTP/2还支持服务端推送和头部压缩,进一步优化了用户体验。通过设置如禁用服务端推送、流控窗口大小等,可以精细控制连接行为。

HTTP/2,简称h2,是HTTP协议在1999年发布了HTTP1.1之后的首个更新。它主要基于谷歌的SPDY协议,于2015年5月正式发表,大多数主流浏览器已经在2015年底前支持了该协议。HTTP/2的最显著的改进主要体现在多路复用、允许设置请求优先级、压缩算法、二进制传输等等,这些特性能够让使用HTTP/2的应用获得更快、更简单、更稳定的效果。目前流行的RPC框架gRPC也采用了HTTP/2作为传输方式。

HTTP/2协议没有改动HTTP原来的应用层语义。之前熟悉的请求方法、状态码、URI、http headers等概念还是一模一样的。但HTTP/2修改了数据包的格式以及数据在客户端与服务端之间传递的方式。所以只要服务端和客户端(浏览器)支持,应用不需要经过任何修改就可以切换到新协议下运行。

有人可能会问,为什么这个要叫做HTTP/2而不是HTTP/1.2呢?原因主要是HTTP/2引入了一个二进制分帧层(Binary Framing Layer,这层是无法和之前的HTTP/1.x的服务器和客户端兼容的,所以HTTP的协议的主版本就升级到了2。那接下来我们就来了解一下这个二进制分帧层。

二进制分帧层(Binary Framing Layer)

新的二进制分帧层是HTTP/2性能增强的核心,他定义了消息的封装格式与传输方式
在这里插入图片描述
为了说明这个传输的过程,我们需要先理解3个概念
数据流(Stream):数据流定义了在一条TCP连接内的逻辑上的双向数据流,在一个数据流上可以承载一条或多条消息。一个TCP连接上可以承载很多数量的双向数据流。
消息:与HTTP请求或响应消息对应的一系列帧,这个和传统的request/response是对应的。
帧(frame):HTTP/2通信的最小单位,每个帧都包含帧头,帧上会标明自己所属的数据流。来自不同数据流的帧可以交错发送。在HTTP/2中定义了很多种类的帧,最主要的有HEADER帧与DATA帧,对应了原来的http header和body。
在这里插入图片描述

简单的说,HTTP/2协议将HTTP/1.x中使用文本方式传递的请求与响应消息分解成为了二进制编码的帧的传递。所有这些帧能够复用一个TCP连接,通过任意数量的逻辑数据流进行传输。这也是HTTP/2协议性能与功能改进的基础。

连接多路复用与共享

在HTTP/1.x中,如果要并发请求多个资源,那必须使用多个TCP连接,现代浏览器对单个网站能同时发起的连接数都做了限制。HTTP1.1虽然允许在同一个TCP连接上一次发送多个请求,但是他规定了服务器的响应的发送要根据请求被接收的顺序进行排队,也就是说,先接收到的请求的响应也要先发送。这样如果先收到的请求处理时间长的话,就会阻塞后面的请求的响应的发送。而HTTP/2彻底解决了这个问题,所有请求和响应都可以分成互不依赖的帧在同一TCP连接上通过不同的逻辑数据流自由发送,由接收端收到帧之后再组装起来。

在这里插入图片描述
在这个图中我们可以看到,有三个数据流在同时传输。

通过多路复用,我们就可以做到一个origin一个连接。实际应用场景中,大多数HTTP请求都是短暂的,而TCP协议却是为了长时间的数据传输设计的,所以复用一个TCP连接可以显著的提高性能,降低成本。对TLS连接来说,也可以减少加密连接建立的开销。

数据流的优先级

将HTTP消息分解为多个独立帧之后,这些帧就可以在客户端和服务器之间以任意顺序发送,那帧的传输顺序的先后和依赖关系就会很大地影响性能。HTTP/2通过定义数据流的优先级和依赖关系来解决了这个问题。HTTP/2支持给每个数据流赋值一个1~256之间的整数权重,并且可以定义一个数据流和另一个数据流之间的依赖关系。数据流的权重和依赖关系可以让数据流之间形成一个依赖关系树。客户端和服务端可以根据这个依赖关系树来决定发送的优先级。举例来说,在一个网页中,我们就可以设置HTML文件为高优先级,css与js为中优先级,图片为最低优先级。但这里需要注意的是,这个优先级只是一个偏好,而不是强制的,所以优先级并不能保证高优先级的一定比低优先级的先传输。这个也是非常合理的,因为我们不希望一个低优先级的资源因为一个高优先级的资源处理和传输比较慢而被完全阻塞住。

流控

在某些场景下,接收方负载比较重或者比较忙的时候,可能不希望服务端继续发送响应数据过来。比方说在比较长的视频的播放场景下,用户暂停了或者数据传输速度远大于播放速度,那我们可能不想提前缓冲太多的数据下来。另外,在代理服务器的场景下,如果下游的连接速度比较快,上游的速度比较慢,那代理也会希望能控制下游发送请求的速度,以免太多的请求在代理处堆积。TCP协议本身也支持流控,但是因为HTTP/2支持在一个TCP连接中复用多个数据流,所以仅仅依靠TCP协议的流控显然是不够的。因此HTTP/2引入了一个简单的流控机制,主要特点有:

  • 流控是双向的,接收端和发送端都可以设置自己的流控窗口大小
  • 流控是基于信任的,接收端在创建连接的时候会告知对方自己的流控窗口字节大小,每当发送端发送一个DATA帧的时候,这个窗口会相应减少,如果窗口减少到0,发送端需要主动停止发送数据,接收端可以通过发送新的WINDOW_UPDATE帧来修改当前流控窗口的字节数
  • 流控无法被禁用。当连接建立的时候,双方会交换SETTINGS帧,定义了双方的流控窗口字节大小,默认的流控字节数是65,536字节,最大可以设置成2的32次方-1
  • 流控是基于直接节点之间的,不是端到端的。所以中间层(代理)也可以来对上下游进行流控

基于上面的简单机制,HTTP/2就可以实现比较复杂的流控,比方说可以通过让浏览器设置窗口为0,使浏览器暂停某些资源的提取,等用户要求继续之后再开放窗口继续传输。

服务端推送

HTTP/2另一个强大的功能是可以允许服务端对一个请求发送多个响应,也就是说服务端可以在没有明确请求的情况下向客户端推送额外的资源。比方说请求一个网页的时候,涉及到很多资源,有静态页面、图片、css、js,传统的方式需要对每个资源分别请求,但如果服务端知道请求某个静态页面的时候都是要同时请求相对应的其他资源的,通过这种服务端推送的机制就能主动的把资源一起返回,从而节省客户端的很多请求时间。比方说一个网页请求style.css的时候,服务端就可以同时把style.js也推送给客户端,这样客户端在需要style.js的时候就可以直接从缓存读取,而不需要再通过网络请求了。

所有的服务端推送数据都是由PUSH_PROMISE帧发起的,它标明了服务端推送的资源的意图,并且该帧要在请求推送资源的响应帧之前传输。这个做法应该是比较好理解的,因为这样能避免客户端重复请求服务端主动推送的数据。服务端推送的使用方式由客户端掌握主动权,客户端可以限制并行推送的数量、调整流控窗口、或者禁用服务端推送。这些优先级都是在连接建立初期通过SETTINGS帧传输的,后续也可以随时更新。

头部压缩

在HTTP/1.x中,请求和响应头部始终是以纯文本方式传输,而不能被压缩的。这样每个请求和响应都会增加一些开销,特别是在启用了cookie的时候,可能每次会要增加几k字节的开销。HTTP/2采用了HPACK压缩格式来压缩请求和响应头。这种压缩格式不仅采用静态霍夫曼编码对头字段进行编码,还要求发送和接收方都维护一个动态header字段的索引的列表,通过这个索引列表,后续传输重复值的时候就可以只传输索引值,而不需要重复传输完整的信息。

在这里插入图片描述

TLS传输层加密

虽然理论上HTTP/2也是支持非加密连接传输的(这种非加密连接的HTTP/2简称为h2c),但实际上目前主流浏览器厂商都只实现了加密连接的模式,所以https变成了HTTP/2的事实上的标准。

一个例子

下面我们来看一个具体的例子,例子程序通过gRPC向服务端发起一次调用,并得到返回结果,从客户端的Netty日志中,我们可以看到HTTP/2连接建立与包传输的完整过程

[1]  [id: 0x3c68f114, L:/127.0.0.1:57280 - R:localhost/127.0.0.1:50051] OUTBOUND SETTINGS: ack=false settings={ENABLE_PUSH=0, MAX_CONCURRENT_STREAMS=0, INITIAL_WINDOW_SIZE=1048576, MAX_HEADER_LIST_SIZE=8192} 
[2]  [id: 0x3c68f114, L:/127.0.0.1:57280 - R:localhost/127.0.0.1:50051] OUTBOUND WINDOW_UPDATE: streamId=0 windowSizeIncrement=983041 
[3]  [id: 0x3c68f114, L:/127.0.0.1:57280 - R:localhost/127.0.0.1:50051] INBOUND SETTINGS: ack=false settings={MAX_CONCURRENT_STREAMS=2147483647, INITIAL_WINDOW_SIZE=1048576, MAX_HEADER_LIST_SIZE=8192} 
[4]  [id: 0x3c68f114, L:/127.0.0.1:57280 - R:localhost/127.0.0.1:50051] OUTBOUND SETTINGS: ack=true 
[5]  [id: 0x3c68f114, L:/127.0.0.1:57280 - R:localhost/127.0.0.1:50051] INBOUND WINDOW_UPDATE: streamId=0 windowSizeIncrement=983041 
[6]  [id: 0x3c68f114, L:/127.0.0.1:57280 - R:localhost/127.0.0.1:50051] INBOUND SETTINGS: ack=true 
[7]  [id: 0x3c68f114, L:/127.0.0.1:57280 - R:localhost/127.0.0.1:50051] OUTBOUND HEADERS: streamId=3 headers=GrpcHttp2OutboundHeaders[:authority: localhost:50051, :path: /helloworld.Greeter/SayHello, :method: POST, :scheme: http, content-type: application/grpc, te: trailers, user-agent: grpc-java-netty/1.36.0, grpc-accept-encoding: gzip] streamDependency=0 weight=16 exclusive=false padding=0 endStream=false 
[8]  [id: 0x3c68f114, L:/127.0.0.1:57280 - R:localhost/127.0.0.1:50051] OUTBOUND DATA: streamId=3 padding=0 endStream=true length=5 bytes=0000000000 2021-07-16 14:27:27,889 [9] DEBUG i.g.n.s.i.g.n.NettyClientHandler - [id: 0x3c68f114, [9]  [id: 0x3c68f114, L:/127.0.0.1:57280 - R:localhost/127.0.0.1:50051] INBOUND HEADERS: streamId=3 headers=GrpcHttp2ResponseHeaders[:status: 200, content-type: application/grpc, grpc-encoding: identity, grpc-accept-encoding: gzip] padding=0 endStream=false 
[10] [id: 0x3c68f114, L:/127.0.0.1:57280 - R:localhost/127.0.0.1:50051] INBOUND DATA: streamId=3 padding=0 endStream=false length=13 bytes=00000000080a0648656c6c6f20 
  • [1] 发送了一个SETTINGS帧,设置了禁用服务端推送、最大并发STEAM条数为0,初始流控窗口大小为1048576(1M)字节,最大Header大小为8192(8k)字节。
  • [2] 发送一个WINDOW_UPDATE帧,数据流Id为0,将流控窗口大小增加983041字节
  • [3] 收到服务端发送的一个SETTINGS帧,设置为最大并发数据流数量为2147483647,初始流控窗口大小为1048576(1M)字节,最大Header大小为8192(8k)字节。
  • [4] 发送SETTINGS帧,对服务端发送的SETTINGS进行ack
  • [5] 收到服务端WINDOW_UPDATE帧,数据流Id为0,将流控窗口大小增加983041字节
  • [6] 收到服务端SETTINGS帧,对客户端之前发送的SETTINGS进行ack
  • [7] 发送请求头HEADERS帧,可以看到header中的:method, content-type, user-agent等等都是和HTTP/1.x中是一样的
  • [8] 发送请求的DATA数据帧
  • [9] 收到响应的HEADERS帧,有传统的:status, content-type等响应头字段
  • [10] 收到响应的DATA数据帧

在nginx上开启HTTP/2

nginx提供了非常简便的开启HTTP/2的方式,只要nginx版本在1.9.5及以上,启用了http_v2_module和http_ssl_module模块,并且为网站配置了https。然后只需要修改

server {
	# 添加 http2
	listen 443 ssl http2;
	...
}

重新加载配置后,网站就支持HTTP/2的方式访问了。

网络协议——HTTP/1&HTTP/2&HTTP/3 极客时间——浏览器中的网络 HTTP 是浏览器中最重要且使用最多的协议,是浏览器和服务器之间的通信语言。 即将完成使命的HTTP/1 超文本传输协议HTTP/0.9: 被浏览器推动的HTTP/1.0: 新特性: ①引入了请求头和响应头,以Key-Value的形式保存。 请求头: 期望服务器返回的文件的类型 表示期望服务器采用的文件压缩方式 期望服务器返回的文件的编码方式 期望页面的优先语言 响应头: 服务器采用的压缩方法 服务器返回的文件类型 服务器返回的文件的编码方式 ②引入了状态码 通过响应行的方式 阅读详情

相关推荐

http/2.0简介

http/2.0简介HTTP/2四大特性二进制流HTTP2.0 多路复用服务器推送http2 demo :http1.1和http/2性能对比 HTTP/2四大特性 新的二进制格式(Binary Format),HTTP1.x的解析是基于文本。基于文本协议的格式解析存在天然缺陷,文本的表现形式有多样性,要做到健壮性考虑的场景必然很多,二进制则不同,只认0和1的组合。基于这种考虑HTTP2.0的协议...

lhZero_F的博客 3182

HTTP2 详解

维基百科关于 HTTP/2 的介绍,可以看下定义和发展历史: Wiki RFC 7540 定义了 HTTP/2 的协议规范和细节,本文的细节主要来自此文档,建议先看一遍本文,再回过头来照着协议大致过一遍 RFC,如果想深入某些细节再仔细翻看 RFC RFC7540 Why use it ? HTTP/1.1 存在的问题: 1、TCP 连接数限制 对于同一个域名,浏览器最多只能同时创建 6~8 个 TCP 连接 (不同浏览器不一样)。为了解决数量限制,出现了 域名分片 技术,其实就是资源分域,将

z69183787的专栏 2万+

杂记 - HTTP/2 和 HTTP/3 简介

HTTP/2 HTTP/2 的核心为 Binary framing layer “layer” 表明 binary framing layer 是一个新的层级, 该层级处于 socket interface 和 HTTP API 之间, 作为中间层级 binary framing layer 不会改变 HTTP 请求/响应的语义: 如我们常说的头, 方法 binary framing layer 只改变了传输过程中的编码方式 HTTP/1.x 明文传输通过换行符分割的信息 HTTP/2 将信息分割为二进制

XieJay97的博客 625

HTTP2 协议完整解析

HTTP 协议自诞生以来经历了多次修订和更改, HTTP/2 相比于 HTTP/1.0 和 HTTP/1.1 有了革命性的变更, 从它的大版本号由 1 变为 2 也可以看出这是一次巨大的升级, HTTP/2 主要解决 HTTP/1.x 协议交互效率低下的问题, HTTP 使用 TCP 作为传输层协议, 在 HTTP/1.0。

thlzjfefe的博客 2561

SAP S/4HANA 物料主数据里的默认行业部门、视图、组织层级和配置文件

物料主数据维护的效率痛点在于高频重复操作,而非字段复杂度。SAP 通过默认行业部门(Industry Sector)、视图(Views)、组织层级(Organizational Levels)和配置集(Profiles)来优化这一过程。这些预设能根据用户角色自动填充常用值,减少重复选择。行业部门还影响屏幕顺序和字段控制,一旦设定不可修改;默认视图可避免漏选关键业务视图;组织层级预设则需谨慎,避免在多组织场景中出错。这些功能共同实现了维护路径的稳定性,特别适合大批量、标准化物料创建场景,但需根据企业实际情况灵

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 817

头歌工程实训答案-机器学习篇之手写体识别篇

第2关:激活函数'''x:负无穷到正无穷的实数'''if x<=0:return 0else:return x#***第3关:反向传播算法import os。

m0_71537260的博客 2518

HTTP/2 h2 协议简介

随着web技术的飞速发展,1999年制定的HTTP 1.1已经无法满足大家对性能的要求,Google推出协议SPDY,旨在解决HTTP 1.1中广为人知的性能问题。SPDY得到了Chrome、Firefox和Opera的支持,很多大型网站(如谷歌、Twitter、Facebook、淘宝)都对兼容客户端使用SPDY。SPDY在被行业采用并证明能够大幅提升性能之后,已经具备了成为一个标准的条件。 ...

whatday的专栏 1万+

DIY便携式PEMF磁脉冲治疗仪:百元成本自制生物物理疗法设备

脉冲电磁场(PEMF)疗法是一种基于生物物理原理的非侵入性技术,它通过特定频率的电磁脉冲作用于细胞层面,影响细胞膜电位和离子通道活动,从而可能调节细胞代谢与功能。其技术价值在于提供了一种物理干预手段,在健康科技和生物黑客领域被探索用于放松、恢复等场景。实现PEMF的核心是通过信号发生与功率放大驱动电磁线圈产生可控磁场。本文以蓝牙音频模块和智能手机App为切入点,详细解析了如何利用低成本电子元件(如DFRobot XY-WRBT模块和漆包线线圈)构建一个可自定义频率的便携式PEMF设备,涵盖了从电路设计、线圈

weixin_31476015的博客 352

HTTP/2简介

1.HTTP/2的定义: HTTP/2即超文本传输协议2.0,是HTTP/1.1下一代的协议。是由互联网工程任务组(IETF)的Hypertext Transfer Protocol Bis (httpbis)工作小组进行开发。 是自1999年HTTP/1.1发布后的首个更新。HTTP/2.0在2013年8月进行首次合作共事性测试。在开放互联网上HTTP/2.0将只用于"https://" 网...

AsunHunt的博客 108

Oracle数据库安装图文教程 附百度云oracle Database 11g 安装包

一,Oracle数据库安装包下载 1.从Oracle官网下载所需的数据库安装包【下载需要注册登录Oracle账户】     http://www.oracle.com/technetwork/database/enterprise-edition/downloads/index.html 2.此处有下载好的Oracle Database 11g Release 2  百度云网盘  提取码:z...

向来如此,就一定是对的吗 6万+

ABAP 平台注册 OAuth 2.0 Client 的完整实践指南(覆盖 Authorization Code、SAML Bearer 与 PKCE)

本文详述了在ABAP平台上注册入站OAuth 2.0 Client的关键技术和操作要点。文章面向SAP技术人员,系统梳理了前置条件(TLS/SSL、技术用户、授权对象)、配置路径(SOAUTH2事务)、授权类型差异(授权码、SAML Bearer、PKCE)等重要内容。特别强调入站与出站配置的区别,并提供了保密客户端和公有客户端的配置指引。文中所有事务码和参数均参考SAP官方文档,包含多个实用案例和权限配置建议,为ABAP系统作为OAuth 2.0授权服务器的实施提供全面指导。

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 297

HTTP/2 简介及https原理

注:以下内容节选自《高性能浏览器网络》(出版社:O'Reilly,作者:Ilya Grigorik)。 要了解完整版本和相关内容,请访问hpbn.co。 HTTP/2 可以让我们的应用更快、更简单、更稳定 - 这几词凑到一块是很罕见的!HTTP/2 将很多以前我们在应用中针对 HTTP/1.1 想出来的“歪招儿”一笔勾销,把解决那些问题的方案内置在了传输层中。 不仅如此,它...

weixin_30564901的博客 1166

《Python星球日记》 第43天:机器学习概述与Scikit-learn入门

《Python星球日记》 第43天:机器学习概述与Scikit-learn入门

Code_流苏:在代码中寻诗意,在实践中觅真知 933

Java 9中的HTTP / 2支持简介

1.简介 IETF流媒体小组于2015年(即HTTP / 1.1发布后的16年)批准了HTTP / 2协议。 HTTP / 2有望降低延迟,并且使许多替代方法变得过时,而这些替代方法是HTTP / 1.1所必需的,以便能够满足当今的响应时间要求。 在本文中,我将简要介绍HTTP / 2以及它如何更新基于文本的HTTP / 1.1,然后研究Java 9中即将推出的HTTP / 2支持。 2. HTTP / 1.1的延迟优化技术 人们越来越多地在Internet上住院,但是他们不会注意到,如果响应时间低.

蝉之洞 939

零基础基于U-Net网络实战眼底图像血管提取

文章目录1 前言2 血管提取任务概述3 U-Net架构简介4 眼底图像血管分割代码5 结果评估可视化(ROC曲线)6 改进U-Net网络完成眼底图像血管提取任务思路 1 前言 本文基于U-Net架构完成了视网膜血管的提取任务,基于Keras框架更简单的实现分割,网络、工具类、训练和预测的代码一目了然

NJU phd 在读。 5203

[QMT量化交易小白入门]-九十五、多因子选股和趋势择时股票量化交易策略,年化收益率40.61%

本策略采用“双层过滤”机制:第一层基于多维度财务指标进行股票池筛选(多因子选股),第二层运用均线系统和MACD指标实施动态择时(趋势跟踪)。这种设计既保证了组合的质量下限,又能灵活应对市场波动。因子配置科学性:涵盖估值、成长、盈利、动量四大类因子风险控制精细化:设置动态止损与仓位管理机制信号验证严格性:多重条件交叉验证提升胜率。

木头大左的博客 800

HTTP/2帧格式简介

HTTP/2 ,超文本传输协议的第二版。相对于HTTP/1.x 协议的文本传输格式,HTTP/2以二进制的格式进行数据传输。因此,具有更小的传输体积以及负载,相比于文本解析,二进制解析更加方便、高校。

浴血重生-学习空间 1万+

一文弄懂Word2Vec之skip-gram(含详细代码)

目录 前言 一、什么是Skip-gram算法 二、目标是什么 三、定义表示法 3.1 one-hot向量 3.2 词向量(word vector) 3.3 单词矩阵 3.4 单词相似度 3.5 softmax函数 3.6算法过程 3.7 求softmax 四、skipgram代码实现 4.1 如何把词转换为向量 4.2 CBOW和Skip-gram的算法实现 Skip-gram的理想实现 Skip-gram的实际实现 4.3 使用Pytorch实现Skip-gram .

Python,数据分析,机器学习,深度学习 1万+
上一篇: 通过Java使用opencv识别二维码
下一篇: 记一次springboot应用偶发502错误的排查过程
帅帅兔子
博客等级 码龄24年 7粉丝 29原创
评论 3
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

打赏作者

帅帅兔子

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

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

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

打赏作者

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

抵扣说明:

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

余额充值