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

SpringCloud项目开发学习:从入门到实践

概述

本文档详细介绍了springCloud项目开发学习的全过程,包括开发环境搭建、核心组件介绍、实战项目开发、以及常见问题与解决方法。通过这些内容,读者可以系统地掌握Spring Cloud的使用方法和最佳实践。

SpringCloud简介

SpringCloud是什么

Spring Cloud是一组框架的集合,用于简化分布式系统中常见模式的实现,提供了服务发现、配置管理、服务路由、负载均衡、断路器等微服务架构下常用的功能。Spring Cloud构建在Spring Boot之上,能够方便地与Spring Boot项目集成,使得开发者可以快速构建分布式的微服务系统。

SpringCloud的优势

  • 简化配置管理:Spring Cloud Config提供了集中式的配置管理,可以通过Git等版本控制系统管理配置文件。
  • 服务发现与注册:通过Eureka等组件实现服务注册与发现,简化了服务间的协作。
  • 负载均衡与路由:Ribbon、Zuul等组件提供了强大的负载均衡和路由功能。
  • 服务容错:Hystrix等组件可以提供断路器功能,有效避免了服务雪崩效应。
  • 统一入口:Zuul等组件提供了统一的API网关,简化了客户端的调用流程。
  • 分布式调用:Feign等组件提供了声明式的HTTP客户端,简化了服务间的调用流程。

SpringCloud架构概览

Spring Cloud架构主要由多个组件构成,每个组件负责不同的功能,如下所示:

  • Eureka:服务注册与发现。
  • Ribbon:客户端负载均衡。
  • Feign:声明式服务调用。
  • Hystrix:断路器。
  • Zuul:API网关。

开发环境搭建

JDK环境搭建

Java开发工具包(JDK)是开发Java应用所必需的环境。确保安装最新的JDK版本,可以从Oracle官方网站或其他第三方网站下载JDK并安装。安装完成后,可以通过命令行验证是否安装成功:

java -version

输出应显示安装的JDK版本信息。

IDE环境搭建

推荐使用 IntelliJ IDEA 或 Eclipse 等集成开发环境(IDE)。IDEA和Eclipse都提供了强大的代码编辑、调试、重构等功能,适合Java开发。安装IDE之后,需要配置IDE的JDK环境,确保IDE能够识别到本地安装的JDK。

  1. IntelliJ IDEA

    • 打开IntelliJ IDEA,选择 File -> Project Structure
    • Project 选项卡中,选择 Project SDK,选择本地的JDK安装路径。
  2. Eclipse
    • 打开Eclipse,选择 Window -> Preferences
    • Java -> Installed JREs 选项卡中,添加本地的JDK安装路径。

Maven配置与SpringBoot项目创建

Maven是一个项目管理和构建工具,用于管理Java项目的依赖和构建过程。Spring Boot项目可以使用Maven来管理其依赖和构建流程。

  1. 配置Maven

    • 下载Maven并解压到本地目录,如 /usr/local/apache-maven
    • 设置环境变量 MAVEN_HOMEPATH。例如:
    export MAVEN_HOME=/usr/local/apache-maven
    export PATH=$PATH:$MAVEN_HOME/bin
  2. 创建Spring Boot项目

    • 使用Spring Initializr或其他工具创建一个新的Spring Boot项目。例如,通过Maven命令行创建一个新的项目:
    mvn archetype:generate -DgroupId=com.example -DartifactId=demo -DarchetypeArtifactId=maven-archetype-quickstart -DinteractiveMode=false
    • 更改 pom.xml 文件,添加Spring Boot的父依赖和启动器:
    <parent>
       <groupId>org.springframework.boot</groupId>
       <artifactId>spring-boot-starter-parent</artifactId>
       <version>2.5.4</version>
    </parent>
    
    <dependencies>
       <dependency>
           <groupId>org.springframework.boot</groupId>
           <artifactId>spring-boot-starter-web</artifactId>
       </dependency>
    </dependencies>

SpringCloud相关依赖的引入

在项目中引入Spring Cloud的相关依赖。修改 pom.xml 文件,添加Spring Cloud的依赖:

<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-dependencies</artifactId>
            <version>Hoxton.SR9</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

<dependencies>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
    <!-- 省略中间部分 -->
    </dependency>
</dependencies>

SpringCloud核心组件介绍

Eureka服务注册与发现

Eureka是Netflix公司开源的一个服务发现组件,用于实现服务的注册与发现。服务启动后,会将自己的信息注册到Eureka Server,其他服务通过Eureka Server发现并调用这些服务。

  1. 配置Eureka Server

    • 创建一个新的Spring Boot项目,并添加Eureka Server依赖。
    <dependency>
       <groupId>org.springframework.cloud</groupId>
       <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
    </dependency>
    • 在主类中添加 @EnableEurekaServer 注解,启动Eureka Server。
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
    
    @SpringBootApplication
    @EnableEurekaServer
    public class EurekaServerApplication {
       public static void main(String[] args) {
           SpringApplication.run(EurekaServerApplication.class, args);
       }
    }
    • 配置 application.properties 文件,设置Eureka Server的相关配置。
    server.port=8761
    eureka.client.register-with-eureka=false
    eureka.client.fetch-registry=false
  2. 配置Eureka Client

    • 在服务提供者服务中添加Eureka Client依赖。
    <dependency>
       <groupId>org.springframework.cloud</groupId>
       <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
    </dependency>
    • 在主类中添加 @EnableDiscoveryClient 注解,启动Eureka Client。
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
    
    @SpringBootApplication
    @EnableDiscoveryClient
    public class ServiceProviderApplication {
       public static void main(String[] args) {
           SpringApplication.run(ServiceProviderApplication.class, args);
       }
    }
    • 配置 application.properties 文件,设置Eureka Client的相关配置。
    server.port=8081
    spring.application.name=service-provider
    eureka.client.service-url.defaultZone=http://localhost:8761/eureka/
    • 实现服务提供者的API。
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    @RestController
    @RequestMapping("/api")
    public class ServiceProviderController {
    
       @GetMapping("/hello")
       public String hello() {
           return "Hello, World!";
       }
    }

Ribbon负载均衡

Ribbon是Netflix公司开源的一个客户端负载均衡器组件,用于在多个服务实例之间实现负载均衡。

  1. 配置Ribbon

    • 创建一个新的Spring Boot项目,并添加Ribbon依赖。
    <dependency>
       <groupId>org.springframework.cloud</groupId>
       <artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
    </dependency>
    • 使用Ribbon进行服务调用。
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.cloud.client.loadbalancer.LoadBalanced;
    import org.springframework.context.annotation.Bean;
    import org.springframework.web.client.RestTemplate;
    
    @Configuration
    public class RibbonConfig {
    
       @Bean
       @LoadBalanced
       public RestTemplate restTemplate() {
           return new RestTemplate();
       }
    }
    • 在服务消费者中使用 RestTemplate 进行服务调用。
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    import org.springframework.web.client.RestTemplate;
    
    @RestController
    @RequestMapping("/api")
    public class ServiceConsumerController {
    
       @Autowired
       private RestTemplate restTemplate;
    
       @Autowired
       private LoadBalancerClient loadBalancerClient;
    
       @GetMapping("/call")
       public String callService() {
           // 使用负载均衡客户端获取服务实例
           String serviceInstanceId = loadBalancerClient.choose("SERVICE-PROVIDER").getUri().toString();
           return restTemplate.getForObject(serviceInstanceId + "/hello", String.class);
       }
    }

Feign声明式服务调用

Feign是Netflix公司开源的一个声明式HTTP客户端,用于简化服务间的调用流程。Feign提供了注解驱动的方式,使得编写服务调用代码更加简单。

  1. 配置Feign

    • 创建一个新的Spring Boot项目,并添加Feign依赖。
    <dependency>
       <groupId>org.springframework.cloud</groupId>
       <artifactId>spring-cloud-starter-openfeign</artifactId>
    </dependency>
    • 在主类中添加 @EnableFeignClients 注解,启动Feign Client。
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
    import org.springframework.cloud.openfeign.EnableFeignClients;
    
    @SpringBootApplication
    @EnableEurekaClient
    @EnableFeignClients
    public class ServiceConsumerApplication {
       public static void main(String[] args) {
           SpringApplication.run(ServiceConsumerApplication.class, args);
       }
    }
    • 在服务消费者中定义Feign接口。
    import org.springframework.cloud.openfeign.FeignClient;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RequestParam;
    
    @FeignClient(value = "SERVICE-PROVIDER", url = "http://SERVICE-.provid")
    public interface ServiceProviderClient {
    
       @GetMapping("/hello")
       String hello(@RequestParam("name") String name);
    }
    • 在控制器中调用Feign接口。
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RequestParam;
    import org.springframework.web.bind.annotation.RestController;
    
    @RestController
    @RequestMapping("/api")
    public class ServiceConsumerController {
    
       @Autowired
       private ServiceProviderClient serviceProviderClient;
    
       @GetMapping("/call")
       public String callService(@RequestParam("name") String name) {
           return serviceProviderClient.hello(name);
       }
    }

Hystrix断路器

Hystrix是Netflix公司开源的一个延迟和容错库,用于实现断路器模式,用于防止服务雪崩效应。

  1. 配置Hystrix

    • 创建一个新的Spring Boot项目,并添加Hystrix依赖。
    <dependency>
       <groupId>org.springframework.cloud</groupId>
       <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
    </dependency>
    • 在服务提供者中添加Hystrix断路器。
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
    import org.springframework.cloud.netflix.hystrix.EnableHystrix;
    
    @SpringBootApplication
    @EnableEurekaClient
    @EnableHystrix
    public class ServiceProviderApplication {
       public static void main(String[] args) {
           SpringApplication.run(ServiceProviderApplication.class, args);
       }
    }
    • 在服务提供者中提供容错逻辑。
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RequestParam;
    import org.springframework.web.bind.annotation.RestController;
    import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
    
    @RestController
    @EnableCircuitBreaker
    public class ServiceProviderController {
    
       @GetMapping("/hello")
       public String hello(@RequestParam("name") String name) {
           if ("service-broken".equals(name)) {
               throw new RuntimeException("Service is broken");
           }
           return "Hello, " + name;
       }
    }

Zuul服务网关

Zuul是Netflix开源的一个API网关组件,用于统一服务的入口。Zuul提供了路由、过滤等功能,可以简化客户端的调用流程。

  1. 配置Zuul

    • 创建一个新的Spring Boot项目,并添加Zuul依赖。
    <dependency>
       <groupId>org.springframework.cloud</groupId>
       <artifactId>spring-cloud-starter-netflix-zuul</artifactId>
    </dependency>
    • 在主类中添加 @EnableZuulProxy 注解,启动Zuul网关。
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
    import org.springframework.cloud.netflix.zuul.EnableZuulProxy;
    
    @SpringBootApplication
    @EnableEurekaClient
    @EnableZuulProxy
    public class ZuulGatewayApplication {
       public static void main(String[] args) {
           SpringApplication.run(ZuulGatewayApplication.class, args);
       }
    }
    • 配置路由规则。
    zuul.routes.provider.path=/provider/**
    zuul.routes.provider.url=http://localhost:8081
    • 使用Zuul网关进行服务调用。
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    @RestController
    @RequestMapping("/api")
    public class ServiceConsumerController {
    
       @GetMapping("/call")
       public String callService() {
           return "Hello, Service Provider";
       }
    }

实战项目开发

创建基础服务注册中心(使用Eureka)

创建一个Eureka Server服务,作为服务注册与发现的中心。

  1. 创建项目

    • 使用Spring Initializr创建一个新的Spring Boot项目,并添加Eureka Server依赖。
  2. 配置Eureka Server

    • 修改主类,添加 @EnableEurekaServer 注解。
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
    
    @SpringBootApplication
    @EnableEurekaServer
    public class EurekaServerApplication {
       public static void main(String[] args) {
           SpringApplication.run(EurekaServerApplication.class, args);
       }
    }
    • 配置 application.properties 文件,设置Eureka Server的相关配置。
    server.port=8761
    eureka.client.register-with-eureka=false
    eureka.client.fetch-registry=false

实现服务提供者与消费者

创建一个服务提供者和一个服务消费者,实现服务的注册、发现和调用。

  1. 服务提供者

    • 创建一个新的Spring Boot项目,并添加Eureka Client依赖。
    <dependency>
       <groupId>org.springframework.cloud</groupId>
       <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
    </dependency>
    • 修改主类,添加 @EnableDiscoveryClient 注解。
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
    
    @SpringBootApplication
    @EnableEurekaClient
    public class ServiceProviderApplication {
       public static void main(String[] args) {
           SpringApplication.run(ServiceProviderApplication.class, args);
       }
    }
    • application.properties 文件中设置服务的注册信息。
    server.port=8081
    spring.application.name=service-provider
    eureka.client.service-url.defaultZone=http://localhost:8761/eureka/
    • 实现服务接口。
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    @RestController
    @RequestMapping("/api")
    public class ServiceProviderController {
    
       @GetMapping("/hello")
       public String hello() {
           return "Hello, World!";
       }
    }
  2. 服务消费者

    • 创建一个新的Spring Boot项目,并添加Eureka Client依赖。
    <dependency>
       <groupId>org.springframework.cloud</groupId>
       <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
    </dependency>
    • 修改主类,添加 @EnableDiscoveryClient 注解。
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
    
    @SpringBootApplication
    @EnableEurekaClient
    public class ServiceConsumerApplication {
       public static void main(String[] args) {
           SpringApplication.run(ServiceConsumerApplication.class, args);
       }
    }
    • application.properties 文件中设置服务的注册信息。
    server.port=8082
    spring.application.name=service-consumer
    eureka.client.service-url.defaultZone=http://localhost:8761/eureka/
    • 实现服务调用。
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    import org.springframework.web.client.RestTemplate;
    
    @RestController
    @RequestMapping("/api")
    public class ServiceConsumerController {
    
       @Autowired
       private RestTemplate restTemplate;
    
       @Autowired
       private LoadBalancerClient loadBalancerClient;
    
       @GetMapping("/call")
       public String callService() {
           // 使用负载均衡客户端获取服务实例
           String serviceInstanceId = loadBalancerClient.choose("SERVICE-PROVIDER").getUri().toString();
           return restTemplate.getForObject(serviceInstanceId + "/hello", String.class);
       }
    }

应用负载均衡与服务降级

在服务提供者和消费者中实现负载均衡和断路器功能。

  1. 负载均衡

    • 在服务消费者中使用 RestTemplateLoadBalancerClient 实现负载均衡。
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    import org.springframework.web.client.RestTemplate;
    
    @RestController
    @RequestMapping("/api")
    public class ServiceConsumerController {
    
       @Autowired
       private RestTemplate restTemplate;
    
       @Autowired
       private LoadBalancerClient loadBalancerClient;
    
       @GetMapping("/call")
       public String callService() {
           // 使用负载均衡客户端获取服务实例
           String serviceInstanceId = loadBalancerClient.choose("SERVICE-PROVIDER").getUri().toString();
           return restTemplate.getForObject(serviceInstanceId + "/hello", String.class);
       }
    }
  2. 服务降级

    • 在服务提供者中实现服务降级逻辑。
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RequestParam;
    import org.springframework.web.bind.annotation.RestController;
    import org.springframework.cloud.netflix.hystrix.EnableHystrix;
    
    @RestController
    @EnableHystrix
    public class ServiceProviderController {
    
       @GetMapping("/hello")
       public String hello(@RequestParam("name") String name) {
           if ("service-broken".equals(name)) {
               throw new RuntimeException("Service is broken");
           }
           return "Hello, " + name;
       }
    
       @GetMapping("/fallback")
       public String fallbackMethod() {
           return "Fallback method called";
       }
    }

配置服务网关与路由规则

使用Zuul作为API网关,配置路由规则,简化客户端的调用流程。

  1. 配置Zuul网关

    • 创建一个新的Spring Boot项目,并添加Zuul依赖。
    <dependency>
       <groupId>org.springframework.cloud</groupId>
       <artifactId>spring-cloud-starter-netflix-zuul</artifactId>
    </dependency>
    • 修改主类,添加 @EnableZuulProxy 注解。
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
    import org.springframework.cloud.netflix.zuul.EnableZuulProxy;
    
    @SpringBootApplication
    @EnableEurekaClient
    @EnableZuulProxy
    public class ZuulGatewayApplication {
       public static void main(String[] args) {
           SpringApplication.run(ZuulGatewayApplication.class, args);
       }
    }
    • 配置路由规则。
    zuul.routes.provider.path=/provider/**
    zuul.routes.provider.url=http://localhost:8081
    • 在控制器中定义路由规则。
    import org.springframework.cloud.netflix.zuul.filters.route.ZuulRoute;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    
    import java.util.List;
    
    @Configuration
    public class ZuulConfig {
    
       @Bean
       public List<ZuulRoute> zuulRoutes() {
           return List.of(
               new ZuulRoute("provider", "/provider/**", "SERVICE-PROVIDER", "DEFAULT")
           );
       }
    }

项目打包与部署

使用Maven进行项目打包

使用Maven进行项目打包,生成可用于部署的JAR或WAR文件。

  1. 打包项目

    • 打开项目根目录下的 pom.xml 文件,确保包含 maven-jar-plugin 插件配置。
    <build>
       <plugins>
           <plugin>
               <groupId>org.apache.maven.plugins</groupId>
               <artifactId>maven-jar-plugin</artifactId>
               <version>3.2.0</version>
               <configuration>
                   <archive>
                       <manifest>
                           <addClasspath>true</addClasspath>
                           <mainClass>com.example.Application</mainClass>
                       </manifest>
                   </archive>
               </configuration>
           </plugin>
       </plugins>
    </build>
    • 执行Maven命令进行项目打包。
    mvn clean package

    打包完成后,项目根目录下的 target 目录会生成一个 *.jar 文件。

本地测试部署

将打包好的JAR或WAR文件部署到本地服务器上,进行功能测试

  1. 部署到本地

    • 使用 java -jar 命令启动打包好的JAR文件。
    java -jar target/myapp.jar
    • 访问服务,验证功能是否正常。

部署到云服务器

将项目部署到云服务器上,确保服务能够正常运行。

  1. 上传文件

    • 使用SCP或FTP将打包好的JAR或WAR文件上传到云服务器。
    scp target/myapp.jar user@server:/path/to/deploy
  2. 部署到云服务器

    • 在云服务器上启动服务。
    java -jar /path/to/deploy/myapp.jar

常见问题与解决方法

SpringCloud项目中常见错误

  • 服务无法注册

    • 检查服务是否正确配置了Eureka Server地址。
    • 检查Eureka Server是否正常运行。
    • 确认Eureka Server的网络配置是否正确。
  • 服务调用失败

    • 检查服务是否正确注册。
    • 检查服务调用的代码是否正确。
    • 检查网络连接是否正常。
  • 负载均衡问题

    • 检查是否启用了负载均衡功能。
    • 检查服务实例是否正确注册。
    • 检查负载均衡配置是否正确。
  • 断路器异常
    • 检查是否启用了断路器功能。
    • 检查服务调用是否有异常抛出。
    • 检查服务降级逻辑是否正确实现。

问题排查与解决技巧

  • 查看日志

    • 查看服务的日志文件,寻找异常信息。
    • 使用 logback-spring.xml 配置日志级别,输出更详细的信息。
  • 使用调试工具

    • 使用IDE的调试功能,设置断点,逐步执行代码。
    • 使用网络调试工具,如Wireshark或Fiddler,查看网络请求。
  • 配置环境变量
    • 设置环境变量,如 JAVA_OPTS,调整JVM参数。
    • 设置 SPRING_PROFILES_ACTIVE,切换不同环境的配置文件。

日志分析与监控

  • 查看Spring Boot Actuator

    • 启用Spring Boot Actuator,查看服务的健康状态和运行信息。
    • 访问 /actuator 接口,查看详细的运行时数据。
  • 使用Prometheus和Grafana

    • 集成Prometheus和Grafana,监控服务的运行状态。
    • 使用Prometheus抓取服务的运行数据,使用Grafana展示监控图表。
  • 配置日志文件
    • 使用 logback-spring.xml 配置日志文件的位置和格式。
    • 使用 logging.file 配置文件路径,使用 logging.pattern.file 配置日志格式。

总结:
通过以上步骤,可以完成Spring Cloud项目的开发、部署和维护。掌握Spring Cloud的核心组件和常见问题解决方法,能够帮助开发者更高效地开发分布式微服务系统。

点击查看更多内容
TA 点赞

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

评论

作者其他优质文章

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

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消