Spring Cloud Gateway

Spring Cloud Gateway

Gateway是Spring Cloud 第二代网关,第一代是Zuul。

Spring Cloud Gateway 简介

Spring Cloud Gateway 是什么

Spring Cloud Gateway是Spring官网基于Spring 5.0、 Spring Boot 2.0、Project Reactor等技术开发等网关
Spring Cloud Gateway为微服务架构提供简单、有效且统一的API路由管理方式。
Spring Cloud Gateway基于Filter链提供网关基本功能:安全、监控/埋点、限流等。
Spring Cloud Gateway是替代Netflix Zuul的一套解决方案。

Spring Cloud Gateway 核心概念

Spring Cloud Gateway设计以降低管理成本和安全风险,包含协议适配、协议转发、安全策略(WAF)、防刷、流量、监控日志等功能
下面介绍Spring Cloud Gateway中重要的概念:
在这里插入图片描述

  • 路由(route)
    路由信息的组成:
    由一个ID、一个目的URL、一组断言工厂、一组Filter组成
    如果路由断言为真,说明请求URL和配置路由匹配
  • 断言(Predicate)
    Java 8中的断言函数
    Spring Cloud Gateway中的断言函数输入类型是Spring 5.0框架中的ServerWebExchange。Spring Cloud Gateway的断言函数允许开发者去定义匹配来自于Http Request中的任何信息比如请求头和参数
  • 过滤器(Filter)
    一个标准的Spring WebFilter
    Spring Cloud Gateway中的Filter分为两种类型的Filter,分别是Gateway FilterGlobal Filter。过滤器Filter将会对请求和响应进行修改处理。

Spring Cloud Gateway 工作原理

在这里插入图片描述
如上图所示,有几个知识点:
Gateway的客户端会向Spring Cloud Gateway发送请求,请求
首先被HttpWebHandlerAdapter进行提取组装成网关上下文.
然后网关的上下文会传递到DispatcherHandler。 DispatcherHandler是所有请求的分发处理器.

  • DispatcherHandler
    DispatcherHandler主要负责分发请求对应的处理器,比如将请求分发到对应RoutePredicate-HandlerMapping(路由断言处理映射器)
  • RoutePredicate-HandlerMapping(路由断言处理映射器)
    路由断言处理映射器主要用于路由的查找,以及找到路由返回对应的FilteringWebHandler
  • FilteringWebHandler
    FilteringWebHandler主要负责组装Filter链表并调用Filter执行一系列Filter处理,然后把请求转到后端对应的代理服务处理,处理完毕之后将Response返回到Gateway客户端
  • Filter类型
    Spring Cloud Gateway和Zuul类似,有prepost两种方式的filter。客户端的请求先经过“pre”类型的filter,然后将请求转发到具体的业务服务,比如一个service,收到业务服务的响应之后,再经过“post”类型的filter处理,最后返回响应到客户端。类似在zuul中指定入口filter和出口filter

注:在配置路由的时候,如果不指定端口的话,http默认设置端口为80,https默认设置端口为443。Spring Cloud Gateway的启动容器目前只支持Netty。

Spring Cloud Gateway 案例

网关最重要的功能是协议适配协议转发,协议转发即基本的路由信息转发,本节演示最简单的Spring Cloud Gateway协议转发的实现。

使用Application.javayml两种方式分别展示基础路由

Application.java

创建项目cloud-gty-basic-application

  • pom
<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-gateway</artifactId>
        <version>2.0.0.RELEASE</version>
    </dependency>
</dependencies>
  • yml
server:
  port: 8080
spring:
  application:
    name: spring-cloud-gateway
# Spring Cloud Gateway 日志配置
logging:
  level:
    org.springframework.cloud.gateway: TRACE
    org.springframework.http.server.reactive: DEBUG
    org.springframework.web.reactive: DEBUG
    reactor.ipc.netty: DEBUG
  • *Application.java
@SpringBootApplication
public class CloudGatewayBasic1Application {
    /**
     * 通过JAVA流式API自定义RouteLocatorBuilder方式定义Spring Cloud Gateway路由
     * @param builder
     * @return
     */
    @Bean
    public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
        return builder.routes()
                // basic proxy
                .route(r -> r.path("/jd")
                                .uri("http://jd.com:80/")
                                    .id("jd_route")
                        ).build();
    }
    public static void main(String[] args) {
        SpringApplication.run(CloudGatewayBasic1Application.class, args);
    }
}

服务启动,访问 http://localhost:8080/jd 会自动跳转到https://www.jd.com/

yml

基本与上方代码一致,Application和yml有所变化:

  • Application(只是一个启动程序,没有特殊东西)
@SpringBootApplication
public class CloudGatewayBasic2Application {
    public static void main(String[] args) {
        SpringApplication.run(CloudGatewayBasic2Application.class, args);
    }
}
  • yml
server:
  port: 8080
spring:
  application:
    name: spring-cloud-gateway
  cloud:
    gateway:
      #当访问http://localhost:8080/baidu,直接转发到https://www.baidu.com/
      routes:
      - id: baidu_route
        uri: http://baidu.com:80/
        predicates:
        - Path=/baidu
# Spring Cloud Gateway 日志配置
logging:
  level:
    org.springframework.cloud.gateway: TRACE
    org.springframework.http.server.reactive: DEBUG
    org.springframework.web.reactive: DEBUG
    reactor.ipc.netty: DEBUG

yml中体现了Spring Cloud Gateway的配置

总结

至此, 两种简单的路由方式已经演示完毕,实际中我选择用yml的方式。
代码:

项目端口描述GIthub地址
cloud-gty-basic-application8080通过启动程序Java代码实现网关路由https://github.com/FrankCy/cloud-master/tree/master/cloud-gty-basic-application
cloud-gty-basic-yml8080通过yml配置文件实现网关路由https://github.com/FrankCy/cloud-master/tree/master/cloud-gty-basic-yml

开启端点

Spring Cloud Gateway提供gateway actuator,该EndPiont提供关于Filter及routes的信息查询及指定route信息更新的Rest API接口。配置后即可查看路由情况
需在yml中添加如下配置(我修改了cloud-gty-basic-yml的yml达到演示效果):

......
        uri: http://baidu.com:80/
        predicates:
        - Path=/baidu
management:
  endpoints:
    web:
      exposure:
        include: '*'
    enabled: false
# Spring Cloud Gateway 日志配置
logging:
 ......

新增management这段代码,然后访问地址:
http://localhost:8080/actuator/gateway/routes 查看到路由情况
在这里插入图片描述

Spring Cloud Gateway 路由断言

Spring Cloud Gateway的路由匹配的功能是以Spring WebFlux中的Handler Mapping为基础实现的
Spring Cloud Gateway也是由许多的路由断言工厂组成的
Http Request请求进入Spring Cloud Gateway的时候,网关路由断言工厂对请求进行断言匹配。匹配成功允许进入,失败则返回错误信息。

断言定义

  • 维基百科:
    在程序设计中,断言(assertion)是一种放在程序中的一阶逻辑(如一个结果为真或是假的逻辑判断式),目的是为了标示与验证程序开发者预期的结果-当程序运行到断言的位置时,对应的断言应该为真。若断言不为真时,程序会中止运行,并给出错误消息

After 路由断言工厂

After Route Predicate Factory 中获取一个UTC时间格式的参数, 当请求的当前时间在配置的UTC时间之后,则会成功匹配,否则不能成功匹配。我们通过例子说明情况:
创建项目cloud-gty-after-route

  • pom省略
  • Application
@SpringBootApplication
public class CloudGatewayAfterRouteApplication {
    public static void main(String[] args) {
        SpringApplication.run(CloudGatewayAfterRouteApplication.class, args);
    }
    @Bean
    public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
        ZonedDateTime minusTime = LocalDateTime.now().minusHours(1).atZone((ZoneId.systemDefault()));
        System.out.println("minusTime : " + minusTime);
        return builder.routes().route("after_route", r -> r.after(minusTime).uri("http://baidu.com")).build();
    }
}
  • yml
server:
  port: 8081
logging:
  level:
    org.springframework.cloud.gateway: TRACE
    org.springframework.http.server.reactive: DEBUG
    org.springframework.web.reactive: DEBUG
    reactor.ipc.netty: DEBUG
#开启actuator管理api,后面要关闭
management:
  endpoints:
    web:  # http://localhost:8080/actuator/gateway/routes
      exposure:
        include: "*"
## ---- yml配置方式 ----
#spring:
#  cloud:
#    gateway:
#      routes:
#      - id: after_route
#        uri: http://baidu.com
#        predicates:
#        - After=2019-01-22T17:27:58.909+08:00[Asia/Shanghai]

After时间可以由下面代码生成:

public static String getMinTime() {
    String minTime = ZonedDateTime.now().minusHours(1).format(DateTimeFormatter.ISO_ZONED_DATE_TIME);
    return minTime;
}

访问 http://localhost:8081/actuator/gateway/routes 显示如下:
在这里插入图片描述
访问 http://localhost:8081 直接跳转到 http://www.baidu.com,因为请求的当前时间在配置的UTC时间之后,否则不能成功转发!

Before 路由断言工厂

Before Route Predicate Factory 中获取一个UTC时间格式的参数, 当请求的当前时间在配置的UTC时间之前,则会成功匹配,否则不能成功匹配
与After Route Predicate Factory 正好相反,例子只展出核心代码,并且不包含效果:

  • Application.java
@SpringBootApplication
public class CloudGatewayBeforeRouteApplication {
    public static void main(String[] args) {
        SpringApplication.run(CloudGatewayBeforeRouteApplication.class, args);
    }
    @Bean
    public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
        ZonedDateTime dateTime = LocalDateTime.now().plusDays(1).atZone(ZoneId.systemDefault());
        return builder.routes()
                        .route("before_route", r -> r.before(dateTime)
                            .uri("http://baidu.com"))
                        .build();
    }
}
  • yml 版
server:
  port: 8081
logging:
  level:
    org.springframework.cloud.gateway: TRACE
    org.springframework.http.server.reactive: DEBUG
    org.springframework.web.reactive: DEBUG
    reactor.ipc.netty: DEBUG
#开启actuator管理api,后面要关闭
management:
  endpoints:
    web:  # http://localhost:8080/actuator/gateway/routes
      exposure:
        include: "*"
## ---- yml配置方式 ----
#spring:
#  cloud:
#    gateway:
#      routes:
#      - id: before_route
#        uri: http://baidu.com
#        predicates:
#        - After=2022-01-22T17:27:58.909+08:00[Asia/Shanghai]

After的时间一定要在请求时间这之后!

Between 路由断言工厂

Between Route Predicate Factory 中获取一个UTC时间格式的参数, 当请求的当前时间在配置的UTC时间之间,则会成功匹配,否则不能成功匹配
After Before相近,有2个UTC时间,例子只展出核心代码,并且不包含效果:

  • Application.java
@SpringBootApplication
public class CloudGatewayBetweenRouteApplication {
    public static void main(String[] args) {
        SpringApplication.run(CloudGatewayBetweenRouteApplication.class, args);
    }
    @Bean
    public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
        ZonedDateTime dateTime1 = LocalDateTime.now().minusDays(1).atZone(ZoneId.systemDefault());
        ZonedDateTime dateTime2 = LocalDateTime.now().plusDays(1).atZone(ZoneId.systemDefault());
        return builder.routes()
                .route("before_route", r -> r.between(dateTime1, dateTime2)
                        .uri("http://baidu.com"))
                .build();
    }
}
  • yml
server:
  port: 8081
logging:
  level:
    org.springframework.cloud.gateway: TRACE
    org.springframework.http.server.reactive: DEBUG
    org.springframework.web.reactive: DEBUG
    reactor.ipc.netty: DEBUG
#开启actuator管理api,后面要关闭
management:
  endpoints:
    web:  # http://localhost:8080/actuator/gateway/routes
      exposure:
        include: "*"
## ---- yml配置方式 ----
#spring:
#  cloud:
#    gateway:
#      routes:
#      - id: between_route
#        uri: http://baidu.com
#        predicates:
#        - name: Between
#          args:
#            datetime1: 2022-01-22T17:27:58.907+08:00[Asia/Shanghai]
#            datetime2: 2022-01-22T17:27:58.909+08:00[Asia/Shanghai]

如果在这个区间,就可以正常访问

Cookie 路由断言工厂

Cookie Route Predicate Factory 会取两个参数(Header中以“Cookie”命名的名称,对应的Key和Value)。当请求携带的cookie和Cookie断言工厂配置的一致,则路由匹配成功,否则匹配失败

以下用实例表明
通过2个工程来说明Cookie路由断言工厂

工程说明
cloud-gty-cookie-routeCookie断言工厂程序,这里进行判断Cookie的K/V,然后路由到对应的服务
cloud-gty-route-server被路由到的服务

cloud-gty-cookie-route

  • Application
@SpringBootApplication
public class CloudGatewayCookieRoute {
    public static void main(String[] args) {
        SpringApplication.run(CloudGatewayCookieRoute.class, args);
    }
    @Bean
    public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
        //判断Cookie K(chocolate)对应的V是不是(ch.p),是的话路由到http://localhost:8071/test/cookie地址
        return builder.routes()
                .route("cookie_route", r -> r.cookie("chocolate", "ch.p")
                        .uri("http://localhost:8071/test/cookie"))
                .build();
    }
}
  • yml
server:
  port: 8081
spring:
  application:
    name: spring-cloud-gateway
logging:
  level:
    org.springframework.cloud.gateway: TRACE
    org.springframework.http.server.reactive: DEBUG
    org.springframework.web.reactive: DEBUG
    reactor.ipc.netty: DEBUG

#开启actuator管理api,后面要关闭
management:
endpoints:
web: # http://localhost:8080/actuator/gateway/routes
exposure:
include: “*”

## ---- yml配置方式 ----
#spring:
# cloud:
# gateway:
# routes:
# - id: cookie_route
# uri: http://localhost:8071/test/cookie
# predicates:
# - Cookie=chocolate, ch.p

cloud-gty-route-server
Application与yml为常规启动程序,不粘贴上来

  • Controller
/**
 * 测试Cookies路由断言工厂
 * @param request
 * @param response
 * @return
 */
@GetMapping("/test/cookie")
public String testGateway(HttpServletRequest request, HttpServletResponse response){
    Cookie[] cookies = request.getCookies();
    if (cookies != null) {
        for (Cookie cookie : cookies) {
            System.out.println(cookie.getName()+":"+cookie.getValue());
        }
    }
    return "Spring Cloud Gateway,Hello world!";
}

分别顺序启动cloud-gty-cookie-routecloud-gty-route-server

  • 打开接口测试工具(我用的PAW),在Header中录入Cookie和对应的值,并访问 http://localhost:8081,效果如下:
    在这里插入图片描述
    注:当Cookie设置的值是我们对应配置的,才会路由匹配成功,否则路由匹配失败

Header 路由断言工厂

Header Route Predicate Factory 根据配置的路由Header信息进行断言匹配路由,匹配成功进行转发,否则不进行转发

以下用实例表明
通过2个工程来说明Cookie路由断言工厂

工程说明
cloud-gty-header-routeHeader断言工厂程序
cloud-gty-route-server被路由到的服务

cloud-gty-cookie-route

  • Application
@SpringBootApplication
public class CloudGatewayHeaderRouteApplication {
    public static void main(String[] args) {
        SpringApplication.run(CloudGatewayHeaderRouteApplication.class, args);
    }
    @Bean
    public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
        //判断header 对应的K/V
        return builder.routes()
                .route("header_route", r -> r.cookie("X-Request-Id", "frank")
                        .uri("http://localhost:8071/test/head"))
                .build();
    }
}
  • yml
server:
  port: 8081
spring:
  application:
    name: spring-cloud-gateway
logging:
  level:
    org.springframework.cloud.gateway: TRACE
    org.springframework.http.server.reactive: DEBUG
    org.springframework.web.reactive: DEBUG
    reactor.ipc.netty: DEBUG
#开启actuator管理api,后面要关闭
management:
  endpoints:
    web:  # http://localhost:8080/actuator/gateway/routes
      exposure:
        include: "*"
## ---- yml配置方式 ----
#spring:
#  cloud:
#    gateway:
#      routes:
#      - id: header_route
#        uri: http://localhost:8071/test/cookie
#        predicates:
#        - Header=X-Request-Id, frank

cloud-gty-route-server
Application与yml为常规启动程序,不粘贴上来

  • Controller
/**
 * 测试Head路由断言工厂
 * @param request
 * @param response
 * @return
 */
@GetMapping("/test/head")
public String testGatewayHead(HttpServletRequest request, HttpServletResponse response){
    String head=request.getHeader("X-Request-Id");
    return "return head info:"+head;
}

分别顺序启动cloud-gty-header-routecloud-gty-route-server
在这里插入图片描述

Host 路由断言工厂

Host Route Predicate Factory 根据配置的Host,对请求中的Host进行断言处理,断言匹配成功进行转发,否则不进行转发
核心代码展示:

  • Application
@Bean
public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
    //判断header 对应的K/V
    return builder.routes()
            .route("host_route", r -> r.host("**.baidu.com:8080")
                    .uri("http://jd.com"))
            .build();
}
  • yml
## ---- yml配置方式 ----
#spring:
#  cloud:
#    gateway:
#      routes:
#      - id: host_route
#        uri: http://jd.com
#        predicates:
#        - Host=**.baidu.com:8080
  • 说明
    配置主机名的时候,如果Gateway端口为80则把80端口省略,如果Gateway有端口,如上所示需要配置Host=**.baidu.com:8080,需要加上端口
  • 运行
    • 通过端口配置工具 SwitchHosts配置本地二级域名vip.baidu.com来解析本地ip 127.0.0.1:
      在这里插入图片描述
    • 启动cloud-gty-host-route,并访问地址 http://vip.baidu.com:8081 ,将会自动转发到https://www.jd.com。

Method 路由断言工厂

Method Route Predicate Factory 根据路由信息配置的Method对请求方式是Get或者Post等进行断言匹配,断言匹配成功进行转发,否则不进行转发
核心代码展示:

  • Application
@Bean
public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
    //判断header 对应的K/V
    return builder.routes()
            .route("method_route", r -> r.method("GET")
                    .uri("http://jd.com"))
            .build();
}
  • yml
## ---- yml配置方式 ----
#spring:
#  cloud:
#    gateway:
#      routes:
#      - id: method_route
#        uri: http://jd.com
#        predicates:
#        - Method=GET
  • 运行
    请求http://localhost:8081,由于是GET请求,会自动断言路由到https://www.jd.com。

Query 路由断言工厂

Query Route Predicate Factory 根据请求中的两个参数进行断言匹配,断言匹配成功进行转发,否则不进行转发
例如地址:http://localhost:8081?flag=main,此地址后面的参数flag=main,在应用程序中配置对应的匹配,匹配则通过,不匹配则不通过,如下代码:
核心代码展示:

  • Application
@Bean
public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
    //判断header 对应的K/V
    return builder.routes()
            .route("query_route", r -> r.query("flag", "main")
                    .uri("http://baidu.com"))
            .build();
}
  • yml
## ---- yml配置方式 ----
#spring:
#  cloud:
#    gateway:
#      routes:
#      - id: query_route
#        uri: http://jd.com
#        predicates:
#        - Query=flag, main
  • 运行
    请求http://localhost:8081?flag=main,根据query请求断言配置,匹配参数,会自动断言路由到https://www.baidu.com。

RemoteAddr 路由断言工厂

RemoteAddr Route Predicate Factory 配置一个IPv4或者IPv6网段的字符串或者IP。当请求IP地址在网段之内或者和配置的IP相同,断言匹配成功进行转发,否则不进行转发
如下所示都是可以通过的:

IP说明
192.168.0.1IP地址
192.168.0.16子网掩码,属同一个网段即有效

核心代码展示:

  • Application
@Bean
public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
    //判断网段,在127.0.0.1内的都可以断言路由成功
    return builder.routes()
            .route("remoteaddr_route", r -> r.remoteAddr("127.0.0.1")
                    .uri("http://baidu.com"))
            .build();
}
  • yml
## ---- yml配置方式 ----
#spring:
#  cloud:
#    gateway:
#      routes:
#      - id: remoteaddr_route
#        uri: http://baidu.com
#        predicates:
#        - RemoteAddr=127.0.0.1
  • 运行
    请求http://localhost:8081,根据remoteaddr请求断言配置,是127.0.0.1网段下,匹配成功,会自动断言路由到https://www.baidu.com。

总结

路由断言项目列表
项目描述GIthub地址
cloud-gty-before-route请求的当前时间在配置的UTC时间之前https://github.com/FrankCy/cloud-master/tree/master/cloud-gty-before-route
cloud-gty-after-route请求的当前时间在配置的UTC时间之后https://github.com/FrankCy/cloud-master/tree/master/cloud-gty-after-route
cloud-gty-between-route请求的当前时间在配置的UTC时间之间https://github.com/FrankCy/cloud-master/tree/master/cloud-gty-between-route
1. cloud-gty-cookie-route

2. cloud-gty-route-server
1. 取两个参数(Header中以“Cookie”命名的名称,对应的Key和Value)。当请求携带的cookie和Cookie断言工厂配置的一致,则路由匹配成功,否则匹配失败

2. 作为路由匹配成功转发节点
1.https://github.com/FrankCy/cloud-master/tree/master/cloud-gty-cookie-route

2.https://github.com/FrankCy/cloud-master/tree/master/cloud-gty-route-server
1. cloud-gty-header-route

2. cloud-gty-route-server
1.根据配置的路由Header信息进行断言匹配路由,匹配成功进行转发,否则不进行转发

2. 作为路由匹配成功转发节点
1.https://github.com/FrankCy/cloud-master/tree/master/cloud-gty-header-route

2.https://github.com/FrankCy/cloud-master/tree/master/cloud-gty-route-server
cloud-gty-host-route根据配置的Host,对请求中的Host进行断言处理https://github.com/FrankCy/cloud-master/tree/master/cloud-gty-host-route
cloud-gty-method-route根据路由信息配置的Method对请求方式是Get或者Post等进行断言匹配https://github.com/FrankCy/cloud-master/tree/master/cloud-gty-method-route
cloud-gty-query-route根据请求中的两个参数进行断言匹配https://github.com/FrankCy/cloud-master/tree/master/cloud-gty-query-route
cloud-gty-remote-addr-route配置一个IPv4或者IPv6网段的字符串或者IP。当请求IP地址在网段之内或者和配置的IP相同,断言匹配成功进行转发,否则不进行转发https://github.com/FrankCy/cloud-master/tree/master/cloud-gty-remote-addr-route

Spring Cloud Gateway 内置Filter

就是过滤器,修改请求报文和响应报文,可以用在鉴权之类的功能上

AddRequestHeader 过滤器工厂

AddRequestHeader过滤器工厂用于对匹配上对请求加上header

AddRequestParameters 过滤器

AddRequestParameters过滤器对匹配上的请求路由添加参数

RewritePath 过滤器

RewritePath过滤器去掉前缀

AddResponseHeader 过滤器

AddResponseHeader过滤器工厂对从网关返回的响应添加Header

StripPrefix 过滤器

StripPrefix过滤器去掉前缀

Retry 过滤器

Retry过滤器,设置重试机制

Hystrix 过滤器

Hystrix过滤器,熔断器

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值