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

Java微服务资料入门教程:从零开始学习微服务

概述

本文详细介绍了Java微服务的概念、架构设计、必备框架以及安全性最佳实践,提供了Spring Boot和Spring Cloud等框架的示例代码,帮助开发者快速搭建和部署微服务应用。文中分享了实战案例和部署运维建议,全面覆盖了Java微服务开发的各个方面。

Java微服务简介

微服务的概念

微服务是一种架构风格,它将一个大型复杂的软件应用构建为一组小型、独立的、可部署的服务。每个服务都专注于完成一个特定的业务功能,通常运行在自己的进程中,并通过轻量级的API(通常是HTTP RESTful API)进行通信。每个服务都围绕一个特定的业务功能构建,且应拥有独立的数据库和独立的部署机制。

微服务架构的核心思想在于通过将复杂的单体应用拆分成一系列离散的服务,使得这些服务能够独立开发、测试、部署和扩展。这种架构风格能够显著提高软件开发的灵活性、可维护性和可扩展性。

微服务与传统单体应用的区别

微服务与传统单体应用相比,具有以下区别:

特性 微服务架构 单体架构
服务单元 拆分成多个独立的、可独立部署的服务 一个庞大的单体应用
代码量 每个服务的代码量较小 整体代码量庞大
部署 可以单独部署某一个或一组服务 需要部署整个应用
扩展性 可以独立扩展单个服务 需要整体扩展应用
技术栈 可以使用不同的编程语言和技术栈 统一的技术栈
独立部署 可以独立部署和升级服务 需要整体部署和升级应用
服务间通信 通过轻量级的API进行通信 内部通信较为紧密

Java在微服务中的应用

Java作为最流行的编程语言之一,其生态系统非常庞大。Java在微服务中广泛使用,包括Spring Boot和Spring Cloud等框架,大大简化了微服务的开发过程。

Spring Boot和Spring Cloud是Java微服务开发中最常用的框架之一。Spring Boot提供了快速构建独立的、生产级别的应用的能力,而Spring Cloud则提供了构建分布式系统的工具和服务,使微服务架构更容易实现。

示例代码:使用Spring Boot创建一个简单的Web应用

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@SpringBootApplication
public class HelloWorldApplication {

    public static void main(String[] args) {
        SpringApplication.run(HelloWorldApplication.class, args);
    }

    @RestController
    public class HelloWorldController {

        @GetMapping("/")
        public String index() {
            return "Hello, World!";
        }
    }
}
必备的Java微服务框架

Spring Boot与Spring Cloud介绍

Spring Boot简介

Spring Boot是Spring框架的一个模块,它简化了传统的Spring应用开发流程,提供了约定优于配置的原则,使得开发者能够快速搭建独立的、生产级别的应用。

Spring Boot的优势:

  • 自动化配置:通过约定优于配置的原则,极大地简化了配置过程。开发者只需关注业务逻辑,大部分配置可以直接使用默认配置。
  • 无需XML配置:大多数情况下,Spring Boot可以使用注解和Java配置来替代传统的XML配置。
  • 自动装配:Spring Boot会自动将项目中依赖的组件进行装配,减少手动配置的复杂性。
  • 快速构建独立应用:可以将应用打包成独立的可执行Jar文件,包含所有依赖库,简化了部署过程。

Spring Cloud简介

Spring Cloud是一系列框架的有序集合,它用于开发分布式系统和服务治理工具。Spring Cloud基于Spring Boot编程模型,使用Spring Boot的约定优于配置的方式,简化开发和配置。

Spring Cloud的核心组件:

  • Eureka:服务注册与发现组件。
  • Ribbon:客户端负载均衡组件。
  • Feign:声明式服务调用组件。
  • Hystrix:断路器组件,用于容错处理。
  • Zuul:API Gateway组件,提供路由和过滤器功能。

示例代码:使用Spring Cloud Gateway实现API Gateway

import org.springframework.cloud.gateway.route.RouteLocator;
import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class GatewayConfig {

    @Bean
    public RouteLocator myRouteLocator(RouteLocatorBuilder builder) {
        return builder.routes()
                .route("service1", r -> r.path("/service1/**")
                        .uri("lb://SERVICE1"))
                .route("service2", r -> r.path("/service2/**")
                        .uri("lb://SERVICE2"))
                .build();
    }
}

其他框架简介(如Dubbo、gRPC)

Dubbo简介

Dubbo是一个高性能的Java RPC框架,它基于Java SPI、动态代理和序列化机制,提供了服务的注册与发现、负载均衡、容错等服务治理能力。Dubbo支持多种序列化协议,如Hessian、Java序列化、ProtoBuf等,支持多种传输协议,如HTTP、TCP、RPC等。Dubbo使用简单、扩展性强,适合构建大规模分布式服务架构。

示例代码:使用Dubbo创建一个简单的服务

import com.alibaba.dubbo.config.ApplicationConfig;
import com.alibaba.dubbo.config.RegistryConfig;
import com.alibaba.dubbo.config.springlus.DubboConfigLoader;
import com.alibaba.dubbo.config.springlus.DubboConfigProperties;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class SimpleServiceApplication {

    public static void main(String[] args) {
        SpringApplication.run(SimpleServiceApplication.class, args);
        ApplicationConfig applicationConfig = new ApplicationConfig();
        applicationConfig.setName("simple-service");
        RegistryConfig registryConfig = new RegistryConfig();
        registryConfig.setAddress("zookeeper://localhost:2181");
        DubboConfigLoader.load(applicationConfig, registryConfig);
    }
}

gRPC简介

gRPC是一个高性能、开源和通用的RPC框架,面向移动和HTTP/2设计。gRPC支持多种平台,如Java、C++、Python等,并且使用Protocol Buffers作为接口定义语言,提供高性能、跨语言的RPC支持。

示例代码:使用gRPC创建一个简单的服务

import io.grpc.Server;
import io.grpc.ServerBuilder;

public class HelloWorldService {

    private static final String SERVICE_NAME = "HelloWorld";

    public static void main(String[] args) throws Exception {
        Server server = ServerBuilder.forPort(50051)
                .addService(new HelloWorldServiceImpl())
                .build();
        server.start();
        System.out.println(SERVICE_NAME + " server started, listening on 50051");
        server.awaitTermination();
    }
}

选择合适的框架

选择合适的框架需要考虑以下几个方面:

  • 项目规模及复杂度:对于中小型项目,Spring Boot和Spring Cloud是很好的选择;对于大型分布式系统,Dubbo和gRPC等更专业的RPC框架可能更适合。
  • 开发语言:Spring Boot和gRPC支持多种语言,而Dubbo主要支持Java。
  • 性能要求:如果对性能有较高要求,可以选择gRPC、Dubbo等高性能框架。
  • 社区支持:Spring Boot和Spring Cloud拥有庞大的社区支持和丰富的插件生态系统。
  • 学习曲线:对于新项目,选择易于上手的框架可以加快开发速度。
微服务架构设计

API Gateway的设计与实现

API Gateway是微服务架构中的一个核心组件,它作为所有服务请求的入口,负责请求路由、负载均衡、协议转换等,提高了系统的稳定性和可维护性。

API Gateway的关键功能:

  • 路由与转发:根据API请求的路由规则,将请求转发到相应的后端服务。
  • 负载均衡:通过负载均衡算法,将请求分发到不同的服务实例。
  • 协议转换:支持不同协议间的转换,如将HTTP请求转换为gRPC请求。
  • 认证与权限管理:对访问API的请求进行认证和权限校验。
  • 服务熔断与降级:当服务出现故障时,API Gateway可以自动熔断相关服务,保证系统的稳定性。

示例代码

使用Spring Cloud Gateway实现API Gateway

import org.springframework.cloud.gateway.route.RouteLocator;
import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class GatewayConfig {

    @Bean
    public RouteLocator myRouteLocator(RouteLocatorBuilder builder) {
        return builder.routes()
                .route("service1", r -> r.path("/service1/**")
                        .uri("lb://SERVICE1"))
                .route("service2", r -> r.path("/service2/**")
                        .uri("lb://SERVICE2"))
                .build();
    }
}

服务发现与注册中心

服务发现是指在服务运行时,动态地发现和获取服务的地址和状态的能力。服务注册中心负责管理服务实例的注册与发现,通过维护一个服务实例的列表,使得其他服务能够快速找到目标服务的位置。

服务发现的关键特性:

  • 服务注册:服务实例启动时向服务注册中心注册自己的地址和端口。
  • 服务发现:客户端通过服务注册中心获取服务实例的地址列表,从而实现服务之间的通信。
  • 负载均衡:服务注册中心提供负载均衡机制,将请求均匀地分发到不同的服务实例上。
  • 健康检查:服务注册中心定期检查服务实例的健康状况,如果检测到故障,及时将故障实例从服务列表中移除。

示例代码

使用Spring Cloud Eureka实现服务注册与发现

服务提供者

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@SpringBootApplication
@EnableEurekaClient
public class ServiceProviderApplication {

    public static void main(String[] args) {
        SpringApplication.run(ServiceProviderApplication.class, args);
    }

    @RestController
    public class HelloWorldController {

        @GetMapping("/hello")
        public String index() {
            return "Hello, Service Provider!";
        }
    }
}

服务消费者

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@SpringBootApplication
@EnableEurekaClient
@EnableFeignClients
public class ServiceConsumerApplication {

    public static void main(String[] args) {
        SpringApplication.run(ServiceConsumerApplication.class, args);
    }

    @RestController
    public class HelloWorldController {

        @GetMapping("/hello")
        public String index() {
            return "Hello, Service Consumer!";
        }
    }
}

分布式配置与服务跟踪

分布式配置

分布式配置中心用于集中管理微服务的配置信息,支持动态更新配置,使得配置管理更灵活。Spring Cloud Config提供了强大的分布式配置支持,可以将配置信息存储在多种存储系统中,如Git、SVN等。

分布式配置的关键特性:

  • 集中化管理:将配置信息集中存储,简化配置管理。
  • 动态更新:配置信息可以实时更新,无需重启应用。
  • 版本控制:支持配置文件的版本控制,方便回溯历史版本。
  • 多环境支持:支持不同环境(如开发、测试、生产)的配置隔离。

服务跟踪

服务跟踪是指在分布式系统中,跟踪整个请求的处理过程,以便快速定位和诊断问题。常见的服务跟踪工具包括Zipkin、Sleuth等。

服务跟踪的关键特性:

  • 追踪链路:跟踪请求从客户端到服务端的完整调用链路。
  • 异常检测:检测请求处理中的异常,提供报警机制。
  • 性能监控:统计各服务的处理时间和响应时间,辅助性能优化。
  • 调用关系:展示服务之间的调用关系,帮助理解系统架构。

示例代码

使用Spring Cloud Sleuth实现服务跟踪

服务提供者

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.sleuth.sampler.AlwaysSampler;
import org.springframework.cloud.sleuth.trace.SleuthTracer;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@SpringBootApplication
@EnableEurekaClient
public class ServiceProviderApplication {

    public static void main(String[] args) {
        SpringApplication.run(ServiceProviderApplication.class, args);
    }

    @RestController
    public class HelloWorldController {

        @GetMapping("/hello")
        public String index() {
            return "Hello, Service Provider!";
        }
    }
}

服务消费者

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.cloud.sleuth.sampler.AlwaysSampler;
import org.springframework.cloud.sleuth.trace.SleuthTracer;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@SpringBootApplication
@EnableEurekaClient
@EnableFeignClients
public class ServiceConsumerApplication {

    public static void main(String[] args) {
        SpringApplication.run(ServiceConsumerApplication.class, args);
    }

    @RestController
    public class HelloWorldController {

        @GetMapping("/hello")
        public String index() {
            return "Hello, Service Consumer!";
        }
    }
}
构建第一个Java微服务应用

快速搭建开发环境

搭建Java微服务开发环境通常需要以下工具:

  • Java环境:确保安装了Java 8或更高版本。
  • IDE:建议使用IntelliJ IDEA或Eclipse作为开发IDE。
  • Maven或Gradle:用于构建项目。
  • Spring Boot和Spring Cloud依赖:在项目的pom.xml或build.gradle文件中添加相关依赖。

示例代码

pom.xml

<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.example</groupId>
    <artifactId>microservice-example</artifactId>
    <version>1.0.0-SNAPSHOT</version>
    <packaging>jar</packaging>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <version>2.7.5</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
            <version>2021.0.6</version>
        </dependency>
    </dependencies>
</project>

创建第一个微服务项目

在IDE中创建一个新的Spring Boot项目,选择Spring Web和Spring Cloud Eureka Client依赖。

示例代码

HelloWorldApplication.java

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@SpringBootApplication
@EnableEurekaClient
public class HelloWorldApplication {

    public static void main(String[] args) {
        SpringApplication.run(HelloWorldApplication.class, args);
    }

    @RestController
    public class HelloWorldController {

        @GetMapping("/")
        public String index() {
            return "Hello, World!";
        }
    }
}

测试与部署

测试微服务应用时,可以先使用IDE自带的内置服务器进行调试,确保应用正常运行后,可以将其打包为可执行的Jar文件,方便部署到生产环境。

示例代码

打包并启动应用

mvn clean package
java -jar target/microservice-example-1.0.0-SNAPSHOT.jar
微服务中的安全性

身份认证与授权

微服务架构中,安全性是一个重要的话题。身份认证和授权是实现安全性的重要手段。常见的身份认证协议有OAuth2、JWT、OpenID Connect等。

身份认证与授权的关键特性:

  • 身份认证:识别用户身份,验证用户是否合法。
  • 授权:基于身份认证的结果,决定用户可以访问哪些资源。
  • 访问令牌:通过访问令牌验证请求的合法性,保护资源的安全。

示例代码

使用Spring Security实现基本的身份认证

pom.xml

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

SecurityConfig.java

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .authorizeRequests()
                .antMatchers("/").permitAll()
                .anyRequest().authenticated()
            .and()
            .formLogin()
                .loginPage("/login")
                .permitAll()
            .and()
            .logout()
                .permitAll();
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
}

API保护

在微服务架构中,API保护是确保服务安全的重要手段。常见的API保护措施包括:

  • Throttle API调用频率:限制客户端请求的频率,防止恶意攻击。
  • Token验证:在每个API调用前,验证请求中的访问令牌。
  • IP黑名单/白名单:限制或允许特定IP地址的请求。
  • 数据加密:对敏感数据进行加密,保护数据不被窃取。

示例代码

使用Spring Security实现基于JWT的API保护

pom.xml

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt</artifactId>
    <version>0.9.1</version>
</dependency>

SecurityConfig.java

import io.jsonwebtoken.security.Keys;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    private final JwtTokenFilter jwtTokenFilter;

    public SecurityConfig(JwtTokenFilter jwtTokenFilter) {
        this.jwtTokenFilter = jwtTokenFilter;
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .csrf().disable()
            .sessionManagement()
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
            .and()
            .addFilterBefore(jwtTokenFilter, WebAuthenticationDetailsSource.class)
            .authorizeRequests()
                .antMatchers("/login").permitAll()
                .anyRequest().authenticated();
    }
}

JwtTokenFilter.java

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@Component
public class JwtTokenFilter extends OncePerRequestFilter {

    private final UserDetailsService userDetailsService;
    private final String secret = "secret-key";

    public JwtTokenFilter(UserDetailsService userDetailsService) {
        this.userDetailsService = userDetailsService;
    }

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
            throws ServletException, IOException {
        String jwt = request.getHeader("Authorization");
        if (jwt != null && jwt.startsWith("Bearer ")) {
            String token = jwt.substring(7);
            Claims claims = Jwts.parser().setSigningKey(secret.getBytes()).parseClaimsJws(token).getBody();
            String username = claims.getSubject();
            UserDetails userDetails = userDetailsService.loadUserByUsername(username);
            SecurityContextHolder.getContext().setAuthentication(new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities()));
        }
        chain.doFilter(request, response);
    }
}

安全性最佳实践

  • 使用HTTPS:确保所有通信通过HTTPS加密,保护数据不被窃听。
  • 定期更新依赖库:定期检查并更新项目中使用的依赖库,防止安全漏洞。
  • 安全编码:遵循安全编码的最佳实践,防止常见的安全漏洞,如SQL注入、XSS攻击等。
  • 日志和审计:记录重要的操作日志,方便审计和回溯。
  • 权限最小化:确保每个服务和用户拥有最小必要的权限,减少风险。
实战案例分享

小项目实战

假设我们正在开发一个图书管理系统,该系统包括以下几个微服务:

  • 图书服务:提供图书的增删改查功能。
  • 用户服务:提供用户的注册、登录、权限管理功能。
  • 订单服务:提供订单的创建、查询、取消功能。

图书服务示例代码

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@SpringBootApplication
public class BookServiceApplication {

    public static void main(String[] args) {
        SpringApplication.run(BookServiceApplication.class, args);
    }

    @RestController
    public class BookController {

        @GetMapping("/books")
        public String getBooks() {
            return "List of books";
        }
    }
}

用户服务示例代码

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@SpringBootApplication
public class UserServiceApplication {

    public static void main(String[] args) {
        SpringApplication.run(UserServiceApplication.class, args);
    }

    @RestController
    public class UserController {

        @GetMapping("/users")
        public String getUsers() {
            return "List of users";
        }
    }
}

部署与运维建议

部署建议

  • 容器化部署:使用Docker和Kubernetes等容器化工具,实现应用的快速部署和管理。
  • CI/CD:集成持续集成和持续部署,确保代码的自动化测试和部署。
  • 灰度发布:通过灰度发布,逐步将新版本应用部署到生产环境。

运维建议

  • 监控与报警:使用Prometheus、Grafana等工具进行实时监控,设置报警规则。
  • 日志管理:使用ELK(Elasticsearch、Logstash、Kibana)或EFK(Elasticsearch、Fluentd、Kibana)等工具进行日志管理和检索。
  • 负载均衡:使用Nginx、HAProxy等负载均衡器,提高系统的可用性和稳定性。

常见问题与解决方案

问题1:服务间通信不稳定

  • 原因:网络延迟、服务延迟或服务不可达。
  • 解决方案
    • 网络优化:改善网络基础设施,提高网络传输速度。
    • 服务优化:优化服务逻辑,减少服务延迟。
    • 熔断降级:使用Hystrix等组件实现服务熔断和降级,提高系统稳定性。

问题2:微服务版本管理复杂

  • 原因:微服务数量多,版本更新频繁。
  • 解决方案
    • 版本控制:使用Git等版本控制系统,实现代码的版本控制。
    • 灰度发布:通过灰度发布,逐步将新版本应用部署到生产环境。
    • 服务发现:使用服务注册中心进行服务发现,减少版本管理的复杂性。
点击查看更多内容
TA 点赞

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

评论

作者其他优质文章

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

100积分直接送

付费专栏免费学

大额优惠券免费领

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

举报

0/150
提交
取消