SpringBoot_1

1.微服务(Microservice)是什么?
SOA(面向服务的架构)
微服务,是与之前的服务化思路和实践相比较而来的。
早些年的服务实现和实施思路是将很多功能从开发到交付都打包成一个很大的服务单元(一般称为 Monolith),而微服务实现和实施思路则更强调功能趋向单一,服务单元小型化和微型化。
如果用“茶壶煮饺子”来打比方的话,原来我们是在一个茶壶里煮很多个饺子,现在(微服务化之后)则基本上是在一个茶壶煮一个饺子,而这些饺子就是服务的功能,茶壶则是将这些服务功能打包交付的服务单元,如图 1 所示。

所以,从思路和理念上来讲,微服务就是要倡导大家尽量将功能进行拆分,将服务粒度做小,使之可以独立承担对外服务的职责,沿着这个思路开发和交付的软件服务实体就叫作“微服务”,而围绕着这个思路和理念构建的一系列基础设施和指导思想,笔者将它称为“微服务体系”。
微服务是怎么来的?
微服务的概念我们应该大体了解了,那么微服务又是怎么来的?原来将很多功能打包为一个很大的服务单元进行交付的做法不能满足需求吗?
实际上,并非原来“大一统”(Monolith)的服务化实践不能满足要求,也不是不好,只是,它有自己存在的合理场景。
对于 Monolith 服务来说,如果团队不大,软件复杂度不高,那么,使用 Monolith 的形式进行服务化治理是比较合适的,而且,这种方式对运维和各种基础设施的要求也不高。
但是,随着软件系统的复杂度持续飙升,软件交付的效率要求更高,投入的人力以及各项资源越来越多,基于 Monolith 的服务化思路就开始“捉襟见肘”。
在开发阶段,如果我们遵循 Monolith 的服务化理念,通常会将所有功能的实现都统一归到一个开发项目下,但随着功能的膨胀,这些功能一定会分发给不同的研发人员进行开发,造成的后果就是,大家在提交代码的时候频繁冲突并需要解决这些冲突,单一的开发项目成为了开发期间所有人的工作瓶颈。
为了减轻这种苦恼,我们自然会将项目按照要开发的功能拆分为不同的项目,从而负责不同功能的研发人员就可以在自己的代码项目上进行开发,从而解决了大家无法在开发阶段并行开发的苦恼。
到了软件交付阶段,如果我们遵循 Monolith 的服务化理念,那么,我们一定是将所有这些开发阶段并行开发的项目集合到一起进行交付。
这就涉及服务化早期实践中比较有名的“火车模型”,即交付的服务就像一辆火车,而这个服务相关的所有功能对应的项目成果,就是要装上火车车厢的一件件货物,交付的列车只有等到所有项目都开发测试完成后才可以装车出发,完成整个服务的交付。
很显然,只要有一个车厢没有准备好货物(即功能项目未开发测试完成),火车就不能发车,服务就不能交付,这大大降低了服务的交付效率。如果每个功能项目可以各自独立交付,那么就不需要都等同一辆火车,各自出发就可以了。
顺着这个思路,自然而然地,大家逐渐各自独立,每一个功能或者少数相近的功能作为单一项目开发完成后将作为一个独立的服务单元进行交付,从而在服务交付阶段,大家也能够并行不悖,各自演化而不受影响。
所以,随着服务和系统的复杂度逐渐飙升,为了能够在整个软件的交付链路上高效扩展,将独立的功能和服务单元进行拆分,从而形成一个一个的微服务是自然而然发生的事情。
这就像打不同的战役一样,在双方兵力不多、战场复杂度不高的情况下,Monolith 的统一指挥调度方式是合适的。而一旦要打大的战役(类似于系统复杂度提升),双方一定会投入大量的兵力(软件研发团队的规模增长),如果还是在狭小甚至固定的战场上进行厮杀,显然施展不开!
所以,小战役有小战役的打法,大战役有大战役的战法,而微服务实际上就是一种帮助扩展组织能力、提升团队效率的应对“大战役”的方法,它帮助我们从软件开发到交付,进而到团队和组织层面多方位进行扩展。
总的来说,一方面微服务可以帮助我们应对飙升的系统复杂度;另一个方面,微服务可以帮助我们进行更大范围的扩展,从开发阶段项目并行开发的扩展,到交付阶段并行交付的扩展,再到相应的组织结构和组织能力的扩展,皆因微服务而受惠。
早期我们从开发到交付,都是单一应用结构,所有功能都集中在一个项目中,统一管理和开发。【雕版印刷】
微服务就是一个项目中的功能,独立的分成多个子项目,并行开发。【活字印刷】
微服务其实是一种架构风格,提倡一个应用应该是一组小型服务组成;可以通过HTTP的方式进行互通。
在没有微服务之前我们软件架构方式称为单体应用:ALL IN ONE。就是所有的内容统一集中在一个应用程序中。

这种单体应用的优点:开发测试简单,不会涉及多个应用之间的互联互调,应用部署也简单,只需要一个war包就OK,不会对运维造成太大负担,还有就是应用程序的水平扩展也简单,就是新增模块简单。当并发量高的时候,我们可以将这个单体应用多复制几份部署在多个服务器上,通过负载均衡机制控制运行,可以提高并发访问。
他的缺点也很明显,那就是到我们需要对程序进行修改的时候就需要重新打包,重新部署,重新运行,这就是一个牵一发动全身的情况,当然更大的挑战是来自于日益增长的软件需求。

目前我们随便做一个软件都需要提供很多更能,不管这些功能有用没有都得实现,这个一个小应用就都成一个大型应用。而大型应用我们不可能将所有功能全部都写在一个应用中,因为分工合作麻烦,维护量大,而且这个应用到时候可能谁都不知道这个应用会有多大。所以我们才要学习使用微服务开发,来避免这样的问题。

可以这样理解:单体应用就是雕版印刷
微服务就是活字印刷
微服务:每一个功能元素最终都是一个可独立替换和独立升级的软件单元;
那么利用微服务搭建起来的项目长什么样?

这种类似于神经网络一样的结构就是最终微服务构建起来的项目,每一个圆圈就是服务,服务于服务之间通过http协议访问连接。
那么这种庞大微服务应用程序如何构建和部署整合呢,这时我们的SPringBoot就有用武之地。

通过SpringBoot快速构建微服务应用,通过SpringCloud进行分布式互联互调,形成分布式结构,通过SpringCloudDataFlow进行流式数据运算和批处理操作。
2.Spring Boot简介
SpringBoot是Spring团队在2014年,伴随Spring4.0版本推出的一个新的框架。
https://spring.io/


主要作用:

SpringBoot使创建独立的、生产级的、基于Spring的应用程序变得容易,您可以“只运行”。
就是帮助我们快速的创建出基于Spring的应用程序。
想一想在你没有SpringBoot的时候我们使用Spring创建应用程序,或者是整合SpringMVC、MyBatyis,往往都需要先导入很多的依赖包,然后编写大量的配置文件,然后开发业务逻辑,最后测试,接下来就是打war包,部署到服务器,部署之前还得配置好服务器环境,这样才完成工作。所以在以前没有SpringBoot的时候,我们对java EE程序的开发都是比较笨重的开发,需要繁多的配置,带来低下的开发效率和复杂的部署流程,还有第三方技术集成难度大。因此上SpringBoot的出现实际上就是用来简化我们对java EE程序的开发,帮助我们快速的创建出基于Spring的应用程序。
SpringBoot的底层实际上还是使用的Spring技术,他其实就是通过整合Spring提供的针对不同领域的开发框架来达到简化Java EE 程序的开发步骤的
Spring提供的针对不同领域的开发框架:
Spring Framework:提供IOC容器
Spring Cloud:分布式框架
Spring Data:数据库访问框架
Spring Security:安全校验框架
等等…

SpringBoot的优点
2.1快速创建独立运行的Spring项目以及与主流框架集成
2.2使用嵌入式的Servlet容器,应用无需打成WAR包
2.3 starters自动依赖与版本控制
2.4大量的自动配置,简化开发,也可修改默认值
2.5无需配置XML,无代码生成,开箱即用
2.6准生产环境的运行时应用监控
2.7 与云计算的天然集成
在学习使用的SpringBoot之前我们需要我一下的一些基本内容:
1.熟练使用Spring框架
2.熟练使用Maven进行项目的构建和依赖包的管理
熟练使用Eclipse或者IDEA
约束环境
1.jdk1.8
2.maven3.x
3.IntelliJ IDEA 2017
4.Spring Boot 1.5.9.RELEASE
3.Spring Boot HelloWorld
需求:浏览器发送hello请求,服务器接受请求并处理,响应Hello World字符串。
创建一个maven工程;(jar)



一路next直至项目创建成功!
3.pom文件配置

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

4.在src/main/java下创建控制器类

package com.wangxing.springboot.controller;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
public class TestController {
    @RequestMapping(value = "/test")
    @ResponseBody
    public String  testController(){
      String info="hello,网星";
      return info;
    }
}

5.由于我们自己创建的控制器类与SpringBoot的主类不在同一个包中,所以我们需要给主类上添加@ComponentScan(basePackages = “com.wangxing.springboot”),如果我们自己创建的控制器类与SpringBoot的主类在同一个包中,那么这个注解不用加。

package com.wangxing.springboot.springbootdemo1;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;

@SpringBootApplication
@ComponentScan(basePackages = "com.wangxing.springboot")
public class Springbootdemo1Application {
    public static void main(String[] args) {
        SpringApplication.run(Springbootdemo1Application.class, args);
    }
}

6.运行主类中的主方法,应用程序自动部署,内嵌的Servlet容器自动启动

7.打开浏览器访问
http://localhost:8080/test

8.将上面的用户程序打成jar包,通过java -jar xxxxx.jar命令来执行

成功以后会在当前项目的target中生成对应的jar包

将这个jar包复制出去,编写一个与之对应的批处理文件,双击就可运行。


打开浏览器访问
4.解析pom文件
4.1.父项目

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.4.0</version>
    <relativePath/> <!-- lookup parent from repository -->
</parent>

上面的配置是我们自己项目需要依赖的父项目【spring‐boot‐starter‐parent】
我们现在新建的SpringBoot项目是【spring‐boot‐starter‐parent】项目下的一个子项目,我们的SpringBoot项目继承了【spring‐boot‐starter‐parent】父项目。
【spring‐boot‐starter‐parent】父项目继承了【spring-boot-dependencies】

spring‐boot‐starter‐parent-2.4.0.pom
<parent>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-dependencies</artifactId>
  <version>2.4.0</version>
</parent>

【spring‐boot‐starter‐parent】父项目是【spring-boot-dependencies】的子项目
spring-boot-dependencies-2.4.0.pom

<properties>
  <activemq.version>5.16.0</activemq.version>
  <antlr2.version>2.7.7</antlr2.version>
  <appengine-sdk.version>1.9.83</appengine-sdk.version>
  <artemis.version>2.15.0</artemis.version>
  <aspectj.version>1.9.6</aspectj.version>
  <assertj.version>3.18.1</assertj.version>
  <atomikos.version>4.0.6</atomikos.version>
  <awaitility.version>4.0.3</awaitility.version>
  <bitronix.version>2.1.4</bitronix.version>
......
</properties>

上面的< properties>元素中配置的是我们所创建的项目的所有依赖包版本

<dependencyManagement>
  <dependencies>
    <dependency>
      <groupId>org.apache.activemq</groupId>
      <artifactId>activemq-amqp</artifactId>
      <version>${activemq.version}</version>
    </dependency>
    <dependency>
      <groupId>org.apache.activemq</groupId>
      <artifactId>activemq-blueprint</artifactId>
      <version>${activemq.version}</version>
    </dependency>
    <dependency>
      <groupId>org.apache.activemq</groupId>
      <artifactId>activemq-broker</artifactId>
      <version>${activemq.version}</version>
    </dependency>
.......
</dependencies>
</dependencyManagement>

上面的< dependencies >元素中配置的是我们所创建的项目的所有依赖包的导入
这个【spring‐boot‐dependencies】项目实际是用来真正管理Spring Boot应用里面的所有依赖包。有了【spring‐boot‐dependencies】项目以后我们新建的SpringBoot项目所需要的依赖包导入就不要在再配置版本和< dependencies >元素导入依赖,从【spring‐boot‐dependencies】项目中继承就可以了。
4.2SpringBoot启动器

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
</dependency>

命名:spring-boot-starter-xxxx [SpingBoot的启动器]
spring-boot-starter:帮助我们导入springboot基础依赖包
spring-boot-starter-web:帮我们导入了web模块正常运行所依赖包。
spring-boot-starter-test:帮我们导入了单元测试模块正常运行所依赖包。
Spring Boot将所有的功能的依赖包都抽取出来,做成一个个的starters(启动器),只需要在项目里面引入这些starter 那么实现相关功能的所有依赖都会自动导入进来,且没有版本冲突。要用什么功能就导入什么场景的启动器就可以了 。
基本的启动器
Spring Boot应用启动器基本的一共有44种,具体如下:
1)spring-boot-starter:这是Spring Boot的核心启动器,包含了自动配置、日志和YAML。
2)spring-boot-starter-actuator:帮助监控和管理应用。
3)spring-boot-starter-amqp:通过spring-rabbit来支持AMQP协议(Advanced Message Queuing Protocol)。
4)spring-boot-starter-aop:支持面向方面的编程即AOP,包括spring-aop和AspectJ。
5)spring-boot-starter-artemis:通过Apache Artemis支持JMS的API(Java Message Service API)。
6)spring-boot-starter-batch:支持Spring Batch,包括HSQLDB数据库。
7)spring-boot-starter-cache:支持Spring的Cache抽象。
8)spring-boot-starter-cloud-connectors:支持Spring Cloud Connectors,简化了在像Cloud Foundry或Heroku这样的云平台上连接服务。
9)spring-boot-starter-data-elasticsearch:支持ElasticSearch搜索和分析引擎,包括spring-data-elasticsearch。
10)spring-boot-starter-data-gemfire:支持GemFire分布式数据存储,包括spring-data-gemfire。
11)spring-boot-starter-data-jpa:支持JPA(Java Persistence API),包括spring-data-jpa、spring-orm、Hibernate。
12)spring-boot-starter-data-mongodb:支持MongoDB数据,包括spring-data-mongodb。
13)spring-boot-starter-data-rest:通过spring-data-rest-webmvc,支持通过REST暴露Spring Data数据仓库。
14)spring-boot-starter-data-solr:支持Apache Solr搜索平台,包括spring-data-solr。
15)spring-boot-starter-freemarker:支持FreeMarker模板引擎。
16)spring-boot-starter-groovy-templates:支持Groovy模板引擎。
17)spring-boot-starter-hateoas:通过spring-hateoas支持基于HATEOAS的RESTful Web服务。
18)spring-boot-starter-hornetq:通过HornetQ支持JMS。
19)spring-boot-starter-integration:支持通用的spring-integration模块。
20)spring-boot-starter-jdbc:支持JDBC数据库。
21)spring-boot-starter-jersey:支持Jersey RESTful Web服务框架。
22)spring-boot-starter-jta-atomikos:通过Atomikos支持JTA分布式事务处理。
23)spring-boot-starter-jta-bitronix:通过Bitronix支持JTA分布式事务处理。
24)spring-boot-starter-mail:支持javax.mail模块。
25)spring-boot-starter-mobile:支持spring-mobile。
26)spring-boot-starter-mustache:支持Mustache模板引擎。
27)spring-boot-starter-redis:支持Redis键值存储数据库,包括spring-redis。
28)spring-boot-starter-security:支持spring-security。
29)spring-boot-starter-social-facebook:支持spring-social-facebook
30)spring-boot-starter-social-linkedin:支持pring-social-linkedin
31)spring-boot-starter-social-twitter:支持pring-social-twitter
32)spring-boot-starter-test:支持常规的测试依赖,包括JUnit、Hamcrest、Mockito以及spring-test模块。
33)spring-boot-starter-thymeleaf:支持Thymeleaf模板引擎,包括与Spring的集成。
34)spring-boot-starter-velocity:支持Velocity模板引擎。
35)spring-boot-starter-web:支持全栈式Web开发,包括Tomcat和spring-webmvc。
36)spring-boot-starter-websocket:支持WebSocket开发。
37)spring-boot-starter-ws:支持Spring Web Services。
Spring Boot应用启动器面向生产环境的还有2种,具体如下:
1)spring-boot-starter-actuator:增加了面向产品上线相关的功能,比如测量和监控。
2)spring-boot-starter-remote-shell:增加了远程ssh shell的支持。
最后,Spring Boot应用启动器还有一些替换技术的启动器,具体如下:
1)spring-boot-starter-jetty:引入了Jetty HTTP引擎(用于替换Tomcat)。
2)spring-boot-starter-log4j:支持Log4J日志框架。
3)spring-boot-starter-logging:引入了Spring Boot默认的日志框架Logback。
4)spring-boot-starter-tomcat:引入了Spring Boot默认的HTTP引擎Tomcat。
5)spring-boot-starter-undertow:引入了Undertow HTTP引擎(用于替换Tomcat)
5.SpringBoot的主类

package com.wangxing.springboot.springbootdemo1;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;

@SpringBootApplication
@ComponentScan(basePackages = "com.wangxing.springboot")
public class Springbootdemo1Application {
    public static void main(String[] args) {
        SpringApplication.run(Springbootdemo1Application.class, args);
    }
}

之所以说Springbootdemo1Application 是主类是因为它里面包含了主方法
public static void main(String[] args) {}
在这个主方法中有
SpringApplication.run(Springbootdemo1Application.class, args);
SpringApplication 将一个典型的 Spring 应用启动的流程“模板化”(这里是动词),在没有特殊需求的情况下,默认模板化后的执行流程就可以满足需求了但有特殊需求也没关系,SpringApplication 在合适的流程结点开放了一系列不同类型的扩展点,我们可以通过这些扩展点对 SpringBoot 程序的启动和关闭过程进行扩展。
通过SpringApplication设置修改启动图标
1.在resources文件夹中创建banner.txt
banner.txt中的内容
‘##::::’##:’########:’##:::::::’##::::::::’#######::
##:::: ##: ##…:: ##::::::: ##:::::::’##… ##:
##:::: ##: ##::::::: ##::::::: ##::::::: ##:::: ##:
#########: ######::: ##::::::: ##::::::: ##:::: ##:
##… ##: ##…:::: ##::::::: ##::::::: ##:::: ##:
##:::: ##: ##::::::: ##::::::: ##::::::: ##:::: ##:
##:::: ##: ########: ########: ########:. #######::
…:::::…::…::…::…:::…:::
2.在主类中通过SpringApplication来设置启动图标

package com.wangxing.springboot.springboot3;

import org.springframework.boot.Banner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class Springboot3Application {

    public static void main(String[] args) {
        SpringApplication bootstrap=new SpringApplication(Springboot3Application.class);
        bootstrap.setBannerMode(Banner.Mode.CONSOLE);
        bootstrap.run(args);

    }

}

执行结果

SpringApplication的run方法的执行流程
SpringApplication 的 run 方法的实现是我们本次运行的主要线路,该方法的主要流程大体可以归纳如下:
1)如果我们使用的是 SpringApplication 的静态 run 方法,那么,这个方法里面首先需要创建一个 SpringApplication 对象实例,然后调用这个创建好的 SpringApplication 的实例 run方 法。在 SpringApplication 实例初始化的时候,它会提前做几件事情:
根据 classpath 里面是否存在某个特征类(org.springframework.web.context.ConfigurableWebApplicationContext)来决定是否应该创建一个为 Web 应用使用的 ApplicationContext 类型,还是应该创建一个标准 Standalone 应用使用的 ApplicationContext 类型。
使用 SpringFactoriesLoader 在应用的 classpath 中查找并加载所有可用的 ApplicationContextInitializer。
使用 SpringFactoriesLoader 在应用的 classpath 中查找并加载所有可用的 ApplicationListener。
推断并设置 main 方法的定义类。
2)SpringApplication 实例初始化完成并且完成设置后,就开始执行 run 方法的逻辑了,方法执行伊始,首先遍历执行所有通过 SpringFactoriesLoader 可以查找到并加载的 SpringApplicationRunListener,调用它们的 started() 方法,告诉这些 SpringApplicationRunListener,“嘿,SpringBoot 应用要开始执行咯!”。
3)创建并配置当前 SpringBoot 应用将要使用的 Environment(包括配置要使用的 PropertySource 以及 Profile)。
4)遍历调用所有 SpringApplicationRunListener 的 environmentPrepared()的方法,告诉它们:“当前 SpringBoot 应用使用的 Environment 准备好咯!”。
5)如果 SpringApplication的showBanner 属性被设置为 true,则打印 banner(SpringBoot 1.3.x版本,这里应该是基于 Banner.Mode 决定 banner 的打印行为)。这一步的逻辑其实可以不关心,我认为唯一的用途就是“好玩”(Just For Fun)。
6)根据用户是否明确设置了applicationContextClass 类型以及初始化阶段的推断结果,决定该为当前 SpringBoot 应用创建什么类型的 ApplicationContext 并创建完成,然后根据条件决定是否添加 ShutdownHook,决定是否使用自定义的 BeanNameGenerator,决定是否使用自定义的 ResourceLoader,当然,最重要的,将之前准备好的 Environment 设置给创建好的 ApplicationContext 使用。
7)ApplicationContext 创建好之后,SpringApplication 会再次借助 Spring-FactoriesLoader,查找并加载 classpath 中所有可用的 ApplicationContext-Initializer,然后遍历调用这些 ApplicationContextInitializer 的 initialize(applicationContext)方法来对已经创建好的 ApplicationContext 进行进一步的处理。
8)遍历调用所有 SpringApplicationRunListener 的 contextPrepared()方法,通知它们:“SpringBoot 应用使用的 ApplicationContext 准备好啦!”
9)最核心的一步,将之前通过 @EnableAutoConfiguration 获取的所有配置以及其他形式的 IoC 容器配置加载到已经准备完毕的 ApplicationContext。
10)遍历调用所有 SpringApplicationRunListener 的 contextLoaded() 方法,告知所有 SpringApplicationRunListener,ApplicationContext “装填完毕”!
11)调用 ApplicationContext 的 refresh() 方法,完成 IoC 容器可用的最后一道工序。
12)查找当前 ApplicationContext 中是否注册有 CommandLineRunner,如果有,则遍历执行它们。
13)正常情况下,遍历执行 SpringApplicationRunListener 的 finished() 方法,告知它们:“搞定!”。(如果整个过程出现异常,则依然调用所有 SpringApplicationRunListener 的 finished() 方法,只不过这种情况下会将异常信息一并传入处理)。
至此,一个完整的 SpringBoot 应用启动完毕!
整个过程看起来冗长无比,但其实很多都是一些事件通知的扩展点,如果我们将这些逻辑暂时忽略,那么,其实整个 SpringBoot 应用启动的逻辑就可以压缩到极其精简的几步,如图 1 所示。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值