为了账号安全,请及时绑定邮箱和手机立即绑定

SpringCloud Alibaba入门教程:快速搭建微服务项目

概述

SpringCloud Alibaba 是阿里巴巴基于SpringCloud开发的微服务工具包,集成了多种阿里巴巴的分布式中间件,提供了服务注册与发现、配置管理、分布式事务等功能。借助SpringCloud Alibaba,开发者可以轻松构建高可用、高性能的微服务应用,大幅简化开发流程。

SpringCloud Alibaba简介

SpringCloud Alibaba 是阿里巴巴开源的,基于SpringCloud的微服务开发工具包。它集成了阿里巴巴的一系列分布式中间件,为开发者提供了构建微服务应用的完整解决方案。这些中间件包括服务注册与发现、配置中心、分布式事务管理、消息驱动等,使得开发人员可以更加方便地构建高可用、高性能的微服务应用。

使用SpringCloud Alibaba的优势

  1. 一站式解决方案:SpringCloud Alibaba提供了一站式的微服务解决方案,涵盖了服务注册与发现、配置管理、分布式事务等多个方面,大幅度减少了微服务开发的复杂性。例如,一个典型的微服务应用可以通过SpringCloud Alibaba快速集成Nacos进行服务注册与发现,以及配置管理。
  2. 高性能:SpringCloud Alibaba集成了阿里巴巴自主研发的分布式中间件,能够提供高性能的服务支持。例如,通过使用Nacos作为服务注册中心,可以实现服务的高效发现和调用。
  3. 高可用性:通过使用Nacos、Seata等组件,SpringCloud Alibaba能够确保系统的高可用性,防止单点故障导致服务中断。例如,当一个服务节点发生故障时,Nacos能够迅速通知其他健康节点进行服务切换,确保应用的正常运行。
  4. 易用性:SpringCloud Alibaba的组件提供了丰富的配置选项和接口,使得开发者可以更加容易地集成和使用这些组件。例如,通过简单的配置,开发者可以轻松地将Seata集成到Spring Cloud应用中,实现分布式事务管理。
  5. 社区支持:由于SpringCloud Alibaba的开源性和阿里巴巴的强大技术支持,开发者可以享受到丰富的社区资源和及时的技术支持。例如,开发者可以在GitHub上找到大量的示例代码和开发文档,帮助解决开发过程中遇到的问题。

SpringCloud Alibaba的主要组件介绍

SpringCloud Alibaba包含了许多核心组件,分别用于解决不同的微服务开发问题。以下是几个主要的组件:

  1. Nacos:一个用于动态服务发现、配置管理和服务管理的平台。它可以帮助开发者实现服务的注册与发现,同时也可以集中管理所有服务的配置信息。
  2. Sentinel:一个轻量级的、高性能的Java服务保护框架。它能够对微服务进行实时监控,并提供流量控制、资源监控等功能。
  3. Seata:一个开源的分布式事务解决方案,支持多种编程语言和框架。它可以确保分布式环境下事务的一致性。
  4. RocketMQ:一个分布式消息系统,可以保证大规模系统中消息的可靠传输。它支持多种消息模式,包括同步、异步和延迟消息等。
  5. Dubbo:一个高性能的Java RPC框架,可以实现服务之间的远程调用。它支持多种通信协议,并且具有良好的扩展性和灵活性。

环境搭建

在进行SpringCloud Alibaba项目的开发之前,需要先搭建好开发环境,并快速创建一个SpringBoot项目,然后引入SpringCloud Alibaba的相关依赖。

开发环境配置

为了顺利开发和运行SpringCloud Alibaba项目,需要确保以下开发环境已经配置好:

  1. Java开发环境:确保Java环境已安装,建议使用Java 8或更高版本。
  2. IDE:推荐使用IntelliJ IDEA或Eclipse等IDE工具,这些工具提供了丰富的开发支持。
  3. Maven或Gradle:使用Maven或Gradle管理项目依赖,确保已经正确安装配置。
  4. 本地仓库:配置好本地Maven或Gradle仓库路径。
  5. Git(可选):使用Git进行版本控制,提高代码的管理效率。

快速创建SpringBoot项目

快速创建一个SpringBoot项目,可以使用Spring Initializr来帮助我们快速搭建项目结构。Spring Initializr提供了一个用户友好的Web界面来生成Spring Boot项目。

步骤一:访问Spring Initializr网站

访问Spring Initializr网站(https://start.spring.io/)。

步骤二:选择项目配置
  1. Project:选择Maven项目。
  2. Language:选择Java。
  3. Spring Boot:选择你需要的Spring Boot版本。
  4. Project Metadata
    • Group:输入项目组名,如 com.example
    • Artifact:输入项目名称,如 demo
    • Name:输入项目名,如 spring-cloud-alibaba-demo
    • Description:输入项目描述,如 SpringCloud Alibaba Demo
  5. Packaging:选择JAR。
  6. Java:选择Java版本,如11。
步骤三:添加依赖

添加SpringCloud Alibaba的依赖。选择 Dependencies 标签页,添加以下依赖:

  • Spring Cloud Dependency
  • Spring Web
  • Spring Boot Devtools

如果你希望先创建一个简单的Spring Boot项目,可以只添加Spring Web和Spring Boot Devtools。

步骤四:生成项目

点击 Generate 按钮,生成项目压缩包。下载并解压该压缩包,导入到IDE中。

引入SpringCloud Alibaba依赖

在项目中添加SpringCloud Alibaba的依赖。在 pom.xml 文件中添加以下内容:

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.7.4</version>
    <relativePath/>
</parent>

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>
</dependencies>

<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-dependencies</artifactId>
            <version>2021.0.2</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-alibaba-dependencies</artifactId>
            <version>2022.0.0</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

<build>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
        </plugin>
    </plugins>
</build>

Nacos服务注册与发现

Nacos 是SpringCloud Alibaba中的核心组件之一,主要用于服务注册与发现。Nacos能够帮助开发者集中管理服务实例,并实现服务之间的动态调用。

什么是Nacos

Nacos 是一个动态服务发现、配置管理和服务管理平台。它具有以下功能:

  1. 服务注册与发现:提供服务注册与发现的功能,支持多种服务注册方式,例如直接使用Nacos自带的注册中心,或者通过Nacos与Spring Cloud整合的方式。
  2. 动态配置管理:支持配置的动态更新,配置变更后能够实时推送至客户端,实现配置的热更新。
  3. 服务管理:提供服务的Dashboard界面,可以查看各个服务实例的状态和相关元数据。

Nacos的核心作用是作为服务注册中心,它能够帮助服务提供者和消费者进行服务发现和调用。通过Nacos,服务提供者可以注册自己的服务实例信息,服务消费者可以根据服务名获取到可用的服务实例列表。

如何使用Nacos进行服务注册与发现

使用Nacos进行服务注册与发现的基本步骤如下:

  1. 服务提供者注册:服务提供者启动时向Nacos注册自己的服务实例。
  2. 服务消费者发现:服务消费者启动时从Nacos获取服务实例列表。
  3. 服务实例动态变更:支持服务实例的动态变更,如服务下线、上线等操作。

以下是具体的做法:

  1. 引入Nacos依赖:确保在项目的 pom.xml 文件中添加以下依赖:

    <dependency>
       <groupId>org.springframework.cloud</groupId>
       <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
    </dependency>
  2. 配置Nacos服务器地址:在项目的 application.ymlapplication.properties 文件中配置Nacos服务器地址:

    spring:
     cloud:
       nacos:
         discovery:
           server-addr: 127.0.0.1:8848
  3. 启用服务注册与发现功能:在Spring Boot启动类上添加 @EnableDiscoveryClient 注解:

    @SpringBootApplication
    @EnableDiscoveryClient
    public class ServiceApplication {
       public static void main(String[] args) {
           SpringApplication.run(ServiceApplication.class, args);
       }
    }

服务提供者和消费者示例代码

以下是一个简单的Nacos服务注册与发现示例代码:

服务提供者代码: ServiceApplication.java

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@SpringBootApplication
@EnableDiscoveryClient
public class ServiceApplication {
    public static void main(String[] args) {
        SpringApplication.run(ServiceApplication.class, args);
    }

    @RestController
    public static class ServiceController {
        @GetMapping("/service")
        public String service() {
            return "Hello from Nacos service!";
        }
    }
}

服务消费者代码: ConsumerApplication.java

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.web.client.RestTemplate;

@SpringBootApplication
@EnableDiscoveryClient
public class ConsumerApplication {
    public static void main(String[] args) {
        SpringApplication.run(ConsumerApplication.class, args);
    }

    @RestController
    public static class ConsumerController {
        @Autowired
        private RestTemplate restTemplate;

        @GetMapping("/consumer")
        public String consumer() {
            return restTemplate.getForObject("http://SERVICE_NAME/service", String.class);
        }
    }
}

Seata分布式事务管理

Seata 是一个开源的分布式事务解决方案,可以确保分布式环境下事务的一致性。它可以集成到SpringCloud Alibaba项目中,提供分布式事务管理的支持。

Seata的基本概念

Seata的核心概念包括:

  1. Transaction Service:事务服务,负责管理和协调分布式事务的执行。
  2. Transaction Branch:事务分支,代表参与分布式事务的一个独立事务。
  3. Transaction Log:事务日志,用于记录各个事务分支的状态和相关操作。

Seata的运作方式如下:

  1. 事务开始:客户端发起一个分布式事务请求,Seata事务服务会创建一个全局事务ID(Global Transaction ID,XID)。
  2. 事务分支执行:各个服务执行本地事务,并通过Seata的事务分支管理模块进行注册。
  3. 事务提交/回滚:当所有分支事务执行完毕后,客户端决定是否提交或回滚全局事务。Seata会根据提交/回滚指令协调各个服务执行相应的操作。
  4. 事务日志管理:Seata会记录各个分支事务的状态和操作,确保在发生异常时能够正确恢复事务。

Seata的安装与配置

安装与配置Seata主要步骤如下:

  1. 下载Seata Server:从GitHub下载Seata Server的压缩包,并解压到指定目录。
  2. 配置Seata Server:在Seata Server的 config 目录下,修改 file.conf 文件,配置相关的参数,例如端口号、数据库类型和连接信息等。

    # file.conf
    server {
       port = 8091
       nacos {
           server-addr = 127.0.0.1:8848
       }
    }
  3. 引入Seata依赖:在Spring Boot项目中添加Seata的依赖,以支持事务管理。

    <dependency>
       <groupId>com.alibaba.cloud</groupId>
       <artifactId>spring-cloud-starter-alibaba-seata</artifactId>
    </dependency>
  4. 配置Seata客户端:修改项目的 application.ymlapplication.properties 文件,配置Seata客户端的参数。

    spring:
     cloud:
       seata:
         tx-service-group: default
         registry:
           type: nacos
           server-addr: 127.0.0.1:8848

实现一个简单的分布式事务示例

以下是一个简单的Spring Boot应用,使用Seata实现一个分布式事务:

服务提供者代码: ServiceApplication.java

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
public class ServiceApplication {
    public static void main(String[] args) {
        SpringApplication.run(ServiceApplication.class, args);
    }

    @RestController
    public class ServiceController {
        @GetMapping("/service")
        public String service() {
            return "Hello from Seata service!";
        }
    }
}

服务消费者代码: ConsumerApplication.java

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
public class ConsumerApplication {
    public static void main(String[] args) {
        SpringApplication.run(ConsumerApplication.class, args);
    }

    @RestController
    public class ConsumerController {
        @Autowired
        private ServiceClient serviceClient;

        @GetMapping("/consumer")
        public String consumer() {
            return serviceClient.service();
        }
    }

    @FeignClient(name = "SERVICE_NAME")
    interface ServiceClient {
        @GetMapping("/service")
        String service();
    }
}

分布式事务代码: DistributedTransactionDemo.java

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;

@RestController
public class DistributedTransactionDemo {
    @Autowired
    private ServiceClient serviceClient;

    @GetMapping("/distributed-transactions")
    public String distributedTransactions() {
        // 开始全局事务
        System.out.println("Begin global transaction");
        serviceClient.service();
        System.out.println("Commit global transaction");
        return "Distributed Transactions Completed!";
    }
}

@FeignClient(name = "SERVICE_NAME")
interface ServiceClient {
    @GetMapping("/service")
    String service();
}

使用RocketMQ实现消息驱动

RocketMQ 是一个开源的分布式消息系统,用于实现大规模系统中的消息可靠传输。它可以支持多种消息模式,包括同步、异步和延迟消息等。

RocketMQ简介

RocketMQ的核心功能包括:

  1. 消息发送与接收:支持多种消息发送方式,如同步、异步和单向发送。
  2. 消息过滤:支持定制化的消息过滤规则,确保消息能够精确地到达目标消费者。
    3.. 消息存储与查询:具备高可靠性的消息存储机制,支持消息的持久化存储和查询。
  3. 集群部署:支持主从复制和多活部署模式,具备良好的扩展性和容错性。

RocketMQ的核心组件包括以下几个:

  1. Broker:消息中间件,负责消息的存储和转发。
  2. NameServer:命名服务,维护Broker的地址信息。
  3. Producer:消息发送者,负责将消息发送到指定的Topic和分区。
  4. Consumer:消息接收者,从Broker获取消息并进行处理。
  5. Client SDK:提供Java和C++语言的客户端SDK,支持多种开发框架。

如何使用RocketMQ进行消息的发送与接收

使用RocketMQ进行消息的发送与接收,主要涉及以下几个步骤:

  1. 安装RocketMQ:从GitHub下载RocketMQ的压缩包并解压,然后按照官方文档进行安装。
  2. 启动NameServer:NameServer是RocketMQ的命名服务,负责维护Broker的地址信息。启动后可以查看日志确认启动成功。
  3. 启动Broker:Broker是RocketMQ的核心组件,负责消息的存储和转发。启动后需要配置Broker的配置文件,确保其能够连接到NameServer。
  4. 配置生产者和消费者:在Spring Boot应用中,添加RocketMQ的依赖,并配置生产者和消费者的属性。

以下是具体的配置步骤:

  1. 引入RocketMQ依赖:在Spring Boot项目的 pom.xml 文件中添加以下依赖:

    <dependency>
       <groupId>org.apache.rocketmq</groupId>
       <artifactId>rocketmq-spring-boot-starter</artifactId>
       <version>2.2.2</version>
    </dependency>
  2. 配置RocketMQ属性:在项目的 application.ymlapplication.properties 文件中配置RocketMQ的相关属性:

    rocketmq:
     name-server: 127.0.0.1:9876
     producer:
       group: group1
       topic: topic1

生产者和消费者示例代码

生产者代码: ProducerController.java

import org.apache.rocketmq.spring.core.RocketMQTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.messaging.Message;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class ProducerController {
    @Autowired
    private RocketMQTemplate rocketMQTemplate;

    @GetMapping("/send")
    public void sendMessage() {
        rocketMQTemplate.convertAndSend("topic1", "Hello RocketMQ!");
    }
}

消费者代码: ConsumerService.java

import org.apache.rocketmq.spring.annotation.RocketMQMessageListener;
import org.apache.rocketmq.spring.core.RocketMQListener;
import org.springframework.stereotype.Service;

@Service
@RocketMQMessageListener(
        topic = "topic1",
        consumerGroup = "group1"
)
public class ConsumerService implements RocketMQListener<String> {
    @Override
    public void onMessage(String message) {
        System.out.println("Received message: " + message);
    }
}

实战案例:整合多个组件实现微服务项目

在实际开发中,通常需要将Nacos、Seata、RocketMQ等多个组件整合到一个微服务项目中,实现服务的注册与发现、分布式事务管理和消息驱动等功能。

综合使用Nacos、Seata、RocketMQ等组件

在构建微服务项目时,可以按照以下步骤整合多个组件:

  1. 服务提供者:实现服务的提供和注册。
  2. 服务消费者:实现服务的发现和调用。
  3. 分布式事务管理:确保分布式环境下事务的一致性。
  4. 消息驱动:实现服务之间的异步通信。
  5. 集成配置管理:通过Nacos进行动态配置管理。

项目结构设计与代码实现

以下是一个简单的微服务项目结构设计与代码实现示例:

项目结构

src
├── main
│   ├── java
│   │   └── com
│   │       └── example
│   │           └── service
│   │               ├── config
│   │               │   └── ApplicationConfig.java
│   │               ├── consumer
│   │               │   └── ConsumerApplication.java
│   │               ├── nacos
│   │               │   └── NacosApplication.java
│   │               ├── producer
│   │               │   └── ProducerApplication.java
│   │               └── seata
│   │                   └── SeataApplication.java
│   └── resources
│       └── application.yml

配置文件

在项目的 application.yml 文件中配置相关组件的属性:

spring:
  cloud:
   nacos:
      discovery:
         server-addr: 127.0.0.1:8848
      config:
         server-addr: 127.0.0.1:8848
         file-extension: yaml
         refresh-enabled: true
         refresh-group: DEFAULT_GROUP
   seata:
      tx-service-group: default
      registry:
         type: nacos
         server-addr: 127.0.0.1:8848
rocketmq:
   name-server: 127.0.0.1:9876
   producer:
      group: group1
      topic: topic1

服务提供者

ProducerApplication.java 文件中实现服务提供者的逻辑:

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
public class ProducerApplication {
    public static void main(String[] args) {
        SpringApplication.run(ProducerApplication.class, args);
    }

    @RestController
    public class ProducerController {
        @GetMapping("/producer")
        public String producer() {
            return "Hello from producer!";
        }
    }
}

服务消费者

ConsumerApplication.java 文件中实现服务消费者的逻辑:

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
public class ConsumerApplication {
    public static void main(String[] args) {
        SpringApplication.run(ConsumerApplication.class, args);
    }

    @RestController
    public class ConsumerController {
        @Autowired
        private ProducerClient producerClient;

        @GetMapping("/consumer")
        public String consumer() {
            return producerClient.producer();
        }
    }

    @FeignClient(name = "PRODUCER_SERVICE_NAME")
    public interface ProducerClient {
        @GetMapping("/producer")
        String producer();
    }
}

分布式事务

SeataApplication.java 文件中实现分布式事务的逻辑:

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
public class SeataApplication {
    public static void main(String[] args) {
        SpringApplication.run(SeataApplication.class, args);
    }

    @RestController
    public class SeataController {
        @Autowired
        private ProducerClient producerClient;

        @GetMapping("/seata")
        public String seata() {
            // 开始全局事务
            System.out.println("Begin global transaction");
            producerClient.producer();
            System.out.println("Commit global transaction");
            return "Seata Transactions Completed!";
        }
    }

    @FeignClient(name = "PRODUCER_SERVICE_NAME")
    public interface ProducerClient {
        @GetMapping("/producer")
        String producer();
    }
}

消息驱动

NacosApplication.java 文件中实现消息驱动的逻辑:

import org.apache.rocketmq.spring.core.RocketMQTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.messaging.Message;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

@RestController
public class NacosController {
    @Autowired
    private RocketMQTemplate rocketMQTemplate;

    @GetMapping("/send")
    public void sendMessage() {
        rocketMQTemplate.convertAndSend("topic1", "Hello Nacos!");
    }

    @Autowired
    private RocketMQListener<String> rocketMQListener;

    @GetMapping("/subscribe")
    public void subscribe() {
        ExecutorService executor = Executors.newSingleThreadExecutor();
        executor.submit(() -> {
            rocketMQListener.onMessage("Hello Nacos!");
        });
    }
}
点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
微信客服

购课补贴
联系客服咨询优惠详情

帮助反馈 APP下载

慕课网APP
您的移动学习伙伴

公众号

扫描二维码
关注慕课网微信公众号

举报

0/150
提交
取消