Spring Boot(三) 之搭建Spring Cloud Eureka服务注册中心(单机模式和集群模式)

Spring Boot官网:点击打开链接

Spring Cloud官网:点击打开链接

本文,我们开始学习SpringCloud另一个知识,服务注册中心。在开始之前我觉着有必要先看看这两个。

(一)背景信息

Spring Cloud Eureka 是 Spring Cloud Netflix 微服务套件的一部分,基于 Netflix Eureka 做了二次封装,主要负责完成微服务架构中的服务治理功能,服务治理可以说是微服务架构中最为核心和基础的模块,他主要用来实现各个微服务实例的自动化注册与发现。

  • 服务注册在服务治理框架中,通常都会构建一个注册中心,每个服务单元向注册中心登记自己提供的服务,将主机与端口号、版本号、通信协议等一些附加信息告知注册中心,注册中心按照服务名分类组织服务清单,服务注册中心还需要以心跳的方式去监控清单中的服务是否可用,若不可用需要从服务清单中剔除,达到排除故障服务的效果。
  • 服务发现由于在服务治理框架下运行,服务间的调用不再通过指定具体的实例地址来实现,而是通过向服务名发起请求调用实现。

简介:

Spring Cloud Eureka 使用 Netflix Eureka 来实现服务注册与发现,即包括了服务端组件,也包含了客户端组件,并且服务端和客户端均采用Java编写,所以Eureka主要适用与通过Java实现的分布式系统,或是与JVM兼容语言构建的系统,但是,由于Eureka服务端的服务治理机制提供了完备的RESTful API,所以他也支持将非Java语言构建的微服务纳入Eureka的服务治理体系中来。

  • Eureka服务端我们也称为服务注册中心,他同其他服务注册中心一样,支持高可用配置。
  • Eureka客户端主要处理服务的注册和发现,客户端服务通过注册和参数配置的方式,嵌入在客户端应用程序的代码中,在应用程序运行时,Eureka客户端向注册中心注册自身提供的服务并周期性的发送心跳来更新他的服务租约。

Eureka服务治理体系

(1)服务治理

服务治理是微服务架构中最为核心和基础的模块,它主要用来实现各个微服务实例的自动化注册和发现。

Spring Cloud Eureka是Spring Cloud Netflix微服务套件中的一部分,它基于Netflix Eureka做了二次封装。主要负责完成微服务架构中的服务治理功能。


(2)服务注册

在服务治理框架中,通常都会构建一个注册中心,每个服务单元向注册中心登记自己提供的服务,包括服务的主机与端口号、服务版本号、通讯协议等一些附加信息。注册中心按照服务名分类组织服务清单,同时还需要以心跳检测的方式去监测清单中的服务是否可用,若不可用需要从服务清单中剔除,以达到排除故障服务的效果。

(3)服务发现

在服务治理框架下,服务间的调用不再通过指定具体的实例地址来实现,而是通过服务名发起请求调用实现。服务调用方通过服务名从服务注册中心的服务清单中获取服务实例的列表清单,通过指定的负载均衡策略取出一个服务实例位置来进行服务调用。

(二)Eureka服务注册中心搭建(单机模式)

一、搭建Eureka服务端

Eureka服务端,就是Eureka的服务注册中心了,客户端可以通过在自己的项目的.yml或者.properties文件中配置上服务端Eureka服务端的地址,就可以将项目注册到Eureka上面了。

开发工具:idea 

  1. idea中新建一个maven项目
  2. 在pom.xml文件中添加相关的依赖
  3. 新建一个启动类,并在启动类上面添加相应的注解
  4. 在resources文件夹下面新建一个bootstrap.yml文件,并在里面配置上该项目的服务端口号
  5. 运行启动类
  6. 输入相应的本机ip地址和项目端口号,查看服务器端的启动情况

(1)idea中新建一个maven项目

打开idea,选择File->New->Project->Maven 新建一个Maven项目


填写上GroupId和ArtifactId


填写上项目名和项目保存目录,点击Finish创建完成


(2)在pom.xml文件中添加相关的依赖

由于我们是spring boot项目因此还需要额外加上一个spring boot父依赖。

pom.xml文件

<?xml version="1.0" encoding="UTF-8"?>
<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>EurekaServer</artifactId>
    <version>1.0-SNAPSHOT</version>
    <!-- 引入spring boot的依赖 -->
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.4.5.RELEASE</version>
    </parent>
    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-eureka-server</artifactId>
        </dependency>
    </dependencies>
    <!-- 引入spring cloud的依赖 -->
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>Camden.SR1</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

</project>

(3)新建一个启动类,并在启动类上面添加相应的注解


@SpringBootApplication:spring boot项目启动类注解
@EnableEurekaServer:开启Eureka Server服务注解

package com.example.eurekaserver;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;

/**
 * Created by sunwei on 2017/12/29 Time:10:28
 */
@SpringBootApplication
@EnableEurekaServer
public class SpringCloudEurekaServer {
    public static void main(String[] args) {
        SpringApplication.run(SpringCloudEurekaServer.class,args);
        System.out.println("EurekaServer begins starting");
    }
}

(4)在resources文件夹下面新建一个bootstrap.yml文件,并在里面配置上该项目的服务端口号


bootstrap.yml文件可以配置如下信息

#服务注册中心端口号

server.port: 

#服务注册中心实例的主机名,一般可以是自己的本机ip地址,我的是10.3.99.29

eureka.instance.hostname: 

#默认配置下,eureka会将自己作为客户端,从而尝试将自己也注册到上面去,所以我们在这里配置false,不让它注册

register-with-eureka: false  

#是否检索服务

fetch-registry:false 

#客户端配置的服务注册中心的地址,指定服务注册中心的位置

eureka.client.serviceUrl.defaultZone=http://${eureka.instance.hostname}:${server.port}/eureka/

server:
  port: 1111
eureka:
  instance:
    hostname: 10.3.99.29
  client:
    register-with-eureka: false
    fetch-registry: false
    service-url:
    #    注册服务中心的地址模板
      defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/

(5)基本配置已经完成,现在我们可以运行启动类了。

运行完就会显示如下


(6)如果觉着控制台输出还不直观,此时你可以输入入相应的本机ip地址和项目端口号,查看服务器端的启动情况

我的本机是10.3.99.29

端口号是:1111

Google浏览器输入地址和端口号后可以看到如下界面


此时还没有客户端注册到上面来,因此Instance currently registered with Eureka 一栏,显示No instances available

好了以上就是服务注册中心的搭建了,客户端的搭建其实也比较简单,就是你的具体开发项目了,在这里我可以写一个demo来充当客户端,然后注册到刚才搭建好的Eureak注册中心上面去。

二、搭建客户端

搭建步骤其实和建立Eureka项目类似,在这里我们可以直接建立一个spring boot项目。我的项目是建立在一个父spring boot项目下的module子项目。但是创建spring boot项目的步骤都一样。

第一步:点击File->New ->Project-Spring Initializr,选择JDK


输入Group Artifact package等等信息


再输入项目名,点击Finish完成创建


新建好了之后你打开pom.xml文件,你会发现,里面已经为你添加上了一些依赖,这个就是spring boot项目的好处,Spring Boot 可以轻松创建独立,生产级的基于Spring 的应用程序。

Spring Boot 默认生成的是application.properties,我这里把它改成了bootstrap.yml文件,其实两个都可以,只是里面的配置信息,书写格式不一样。我个人偏向于bootsrap.yml文件,因为这个文件里面的写法格式,看着比较清晰。


第二步:添加依赖

除了自动添加的依赖,我们还需要添上Eureka依赖,开启WEB服务依赖

另外大家如果不知道依赖的具体信息,例如版本号,大家可以百度搜索

Maven Repository

网址:点击打开链接

比如咱们需要Eureka相关的依赖,我们就是可以在搜素框里面搜索Eureka


搜索了之后,我们可以发现第二个就是咱们需要的spring-cloud-starter-eureka,点击进入


可以看到各个版本的依赖,你可以选择一个点击进入,比如我点击第一个


点击进入后可以看到,红框里面的就是我们需要的依赖了,选择复制后添加到我们的pom.xml文件里面。由于我的项目是建立在一个父spring boot项目下的module子项目

如果是子module项目,一般会把<parent></parent>放到父项目中的pom文件中。

父项目pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<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>spring-cloud-demo</groupId>
    <artifactId>spring-cloud-demo</artifactId>
    <packaging>pom</packaging>
    <version>1.0-SNAPSHOT</version>
    <modules>
        <module>spring-cloud-eureka-server</module>
        <module>spring-cloud-config-server</module>
        <module>spring-cloud-eureka-client</module>
    </modules>

    <!-- 引入spring boot的依赖 -->
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.4.5.RELEASE</version>
    </parent>

    <!--Maven依赖统一管理,子项目中的相关Spring cloud 依赖可以不加版本号-->
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>Camden.SR1</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>
</project>

eureka-client项目pom.xml就可以写成这样了

eureka-client项目pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<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>spring-cloud-eureka-client</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>

    <name>spring-cloud-eureka-client</name>
    <description>Demo project for Spring Boot</description>

    <parent>
        <groupId>spring-cloud-demo</groupId>
        <artifactId>spring-cloud-demo</artifactId>
        <version>1.0-SNAPSHOT</version>
    </parent>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
    </properties>

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

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <!--为web可访问的接口依赖,缺少这个项目启动类无法启动-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <exclusions>
                <exclusion>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-starter-logging</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

        <!--spring cloud依赖-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-eureka</artifactId>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>


</project>

第三步:

在bootstrap.yml文件里面添加基本的配置信息

server:
  port: 8888
spring:
  application:
    name: spring-cloud-eureka-client
eureka:
  instance:
    preferIpAddress: true
    #这样写eureka服务注册中心会显示本机ip+端口号
    instanceId: ${spring.cloud.client.ipAddress}:${server.port}
    #这样写eureka服务注册中心会显示项目名和端口号
    #instanceId: ${spring.application.name}:${server.port}
  client:
    serviceUrl:
      defaultZone: http://10.3.99.29:1111/eureka/  #注册中心地址

注意:

(1)这里的yml文件的写法要注意,每句话后面都要保证没有多余的空格,

(2)InstanceId在这里的写法有两种,个人觉着第一种比较好,可以显示每个人的ip,这样在多人同时注册时,也方便查看是谁注册到Eureka上面了。

第四步:在启动类上面加上@EnableDiscoveryClient或者@EnableEurekaClient注解。

两者的用法其实都是差不多的,只能说@EnableEurekaClient注解更加明确。

spring cloud中discovery service有许多种实现(eureka、consul、zookeeper等等),@EnableDiscoveryClient基于spring-cloud-commons, @EnableEurekaClient基于spring-cloud-netflix。

简单的说,如果选用的注册中心是eureka,那么就推荐@EnableEurekaClient,如果是其他的注册中心,那么推荐使用@EnableDiscoveryClient

我感觉@EnableDiscoveryClient的使用更加广泛一些,它可以在使用在别的注册中心时使用,而如果你用了@EnableEurekaClient注解那么你的所要注册的注册中心则只能是Eureka了

在这里我们就选择@EnableEurekaClient注解吧


第五步:运行启动类

运行后显示我们可以看到:


已经启动完成了,并且已经注册到我们之前建立好的Eureka服务注册中心上面了。

这个时候我们可以查看一下,在浏览器中输入

10.3.99.29:1111

我们可以看到如下界面


上面已经出现我们的项目了,项目名是 spring-cloud-eureka-client 后面是对应的你的本机ip和端口号。

好了所有的讲解完成,以上就是我们搭建Eureka服务注册中心(单机模式)的步骤,包括服务端和客户端。

(三)Eureka服务注册中心搭建(集群模式)

一、背景:

考虑到发生故障的情况,服务注册中心发生故障必将会造成整个系统的瘫痪,因此需要保证服务注册中心的高可用。

Eureka Server在设计的时候就考虑了高可用设计,在Eureka服务治理设计中,所有节点既是服务的提供方,也是服务的消费方,服务注册中心也不例外。

Eureka Server的高可用实际上就是将自己做为服务向其他服务注册中心注册自己,这样就可以形成一组互相注册的服务注册中心,以实现服务清单的互相同步,达到高可用的效果。

二、高可用服务注册中心搭建

1.首先新建一个Maven项目。

(1)新建父项目

新建步骤和上文新建Eureka服务注册中心的步骤相同,新建好了之后,我们选择src目录,右键删除掉这个文件夹,只保留pom.xml文件。


删除掉之后,是这个样子的。


(2)新建三个子module,名字分别为:

spring-cloud-eureka-server01

spring-cloud-eureka-server02

spring-cloud-eureka-server03


(3)添加相关依赖。

父项目pom.xm依赖

<?xml version="1.0" encoding="UTF-8"?>
<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>spring-cloud-demo</groupId>
    <artifactId>spring-cloud-demo</artifactId>
    <packaging>pom</packaging>
    <version>1.0-SNAPSHOT</version>
    <modules>
        <module>spring-cloud-eureka-server</module>
        <module>spring-cloud-config-server</module>
        <module>spring-cloud-eureka-client</module>
        <module>spring-cloud-eureka-server01</module>
        <module>spring-cloud-eureka-server02</module>
        <module>spring-cloud-eureka-server03</module>
    </modules>

    <!-- 引入spring boot的依赖 -->
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.4.5.RELEASE</version>
    </parent>

    <!--Maven依赖统一管理,子项目中的相关Spring cloud 依赖可以不加版本号-->
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>Camden.SR1</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>
</project>

父项目中可以看到它的子module,每个子module项目,都是用<module><module>包括的

<!-- 引入spring boot的依赖 -->
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.4.5.RELEASE</version>
    </parent>

父项目中已经包含了spring boot parent依赖,所以我们的子moudle中的<parent><parent>只需要依赖父项目名字就好了。

<parent>
        <artifactId>spring-cloud-demo</artifactId>
        <groupId>spring-cloud-demo</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>

子项目

spring-cloud-eureka-server01

spring-cloud-eureka-server02

spring-cloud-eureka-server03

依赖都是一样的在这里我粘贴一份,比如,我们可以看下spring-cloud-eureka-server01项目的pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<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">
    <parent>
        <artifactId>spring-cloud-demo</artifactId>
        <groupId>spring-cloud-demo</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>spring-cloud-eureka-server</artifactId>
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <!--Eureka服务端依赖-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-eureka-server</artifactId>
        </dependency>
    </dependencies>
</project>

我们可以看到,子项目的module,<parent></parent>自动依赖为父项目了。

(四)创建启动类,bootstrap.yml文件中添加配置信息。

注意:创建启动类之前别忘了新建一个package,因为spring boot的启动类是要扫描包路径的,如果没有包路径,那么spring boot会无法启动成功。

eureka相关属性:

1.失效剔除

有些时候,我们的服务实例并不一定会正常下线,可能由于内存溢出、网络故障等原因使服务不能正常运作。而服务注册中心并未收到“服务下线”的请求,为了从服务列表中将这些无法提供服务的实例剔除,Eureka Server在启动的时候会创建一个定时任务,默认每隔一段时间(默认为60秒)将当前清单中超时(默认为90秒)没有续约的服务剔除出去。

2.自我保护

服务注册到Eureka Server后,会维护一个心跳连接,告诉Eureka Server自己还活着。Eureka Server在运行期间会统计心跳失败的比例在15分钟以之内是否低于85%,如果出现低于的情况,Eureka Server会将当前实例注册信息保护起来,让这些实例不会过期。这样做会使客户端很容易拿到实际已经不存在的服务实例,会出现调用失败的情况。因此客户端要有容错机制,比如请求重试、断路器。

以下是自我保护相关的属性:

eureka.server.enableSelfPreservation=true. 可以设置改参数值为false,以确保注册中心将不可用的实例删除

3.region(地域和可用区)和zone(可用区)

region和zone(或者Availability Zone)均是AWS的概念。在非AWS环境下,我们可以简单地将region理解为地域,zone理解成机房。一个region可以包含多个zone,可以理解为一个地域内的多个不同的机房。不同地域的距离很远,一个地域的不同zone间距离往往较近,也可能在同一个机房内。

region可以通过配置文件进行配置,如果不配置,会默认使用us-east-1。同样Zone也可以配置,如果不配置,会默认使用defaultZone

Eureka Server通过eureka.client.serviceUrl.defaultZone属性设置Eureka的服务注册中心的位置。

指定region和zone的属性如下:

(1)eureka.client.availabilityZones.myregion=myzone# myregion是region

(2)eureka.client.region=myregion

     Ribbon的默认策略会优先访问通客户端处于同一个region中的服务端实例,只有当同一个zone中没有可用服务端实例的时候才会访问其他zone中的实例。所以通过zone属性的定义,配合实际部署的物理结构,我们就可以设计出应对区域性故障的容错集群。

4.用户认证

给Eureka Server加上安全的用户认证.

(1)pom文件中引入依赖

<dependency> 

   <groupId>org.springframework.boot</groupId> 

   <artifactId>spring-boot-starter-security</artifactId> 

</dependency> 

(2)serviceurl中加入安全校验信息

eureka.client.serviceUrl.defaultZone=http://<username><password>@${eureka.instance.hostname}:${server.port}/eureka/


spring-cloud-eureka-server01子项目bootstrap.yml

server:
  port: 2001
spring:
  application:
    name: eurekaserver1
eureka:
  server:
    enable-self-preservation: false
  instance:
    hostname: server1
    #注册到服务注册中心的实例显示的是ip
    prefer-ip-address: true
    #元数据
    metadataMap:
      instanceId: ${spring.application.name}:${random.value}
  client:
    #是否检索服务
    fetch-registry: false
    #是否允许Eureka自己尝试注册自己
    register-with-eureka: false
    serviceUrl:
      defaultZone:  http://server2:2002/eureka/,http://server3:2003/eureka/

spring-cloud-eureka-server02子项目bootstrap.yml

server:
  port: 2002
spring:
  application:
    name: eurekaserver2
eureka:
  server:
    enable-self-preservation: false
  instance:
    hostname: server2
    #注册到服务注册中心的实例显示的是ip
    prefer-ip-address: true
    #元数据
    metadataMap:
      instanceId: ${spring.application.name}:${random.value}
  client:
    #是否检索服务
    fetch-registry: false
    #是否允许Eureka自己尝试注册自己
    register-with-eureka: false
    serviceUrl:
      defaultZone:  http://server1:2001/eureka/,http://server3:2003/eureka/

spring-cloud-eureka-server03子项目bootstrap.yml

server:
  port: 2003
spring:
  application:
    name: eurekaserver3
eureka:
  server:
    enable-self-preservation: false
  instance:
    hostname: server3
    #注册到服务注册中心的实例显示的是ip
    prefer-ip-address: true
    #元数据
    metadataMap:
      instanceId: ${spring.application.name}:${random.value}
  client:
    #是否检索服务
    fetch-registry: false
    #是否允许Eureka自己尝试注册自己
    register-with-eureka: false
    serviceUrl:
      defaultZone:  http://server1:2001/eureka/,http://server2:2002/eureka/

运行三个子项目的启动类,启动集群,启动后我们可以浏览器随意打开任意一个子项目的ip+端口号查看。

输入10.3.99.29:2001,我们可以看到上面有server2和server3的服务注册到上面了。


输入10.3.99.29:2002,我们可以看到上面有server1和server3的服务注册到上面了。


输入10.3.99.29:2003,我们可以看到上面有server1和server2的服务注册到上面了。


好了,到这里,eureka集群的搭建,也完成了。

具体项目可以查看我的github:点击打开链接

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值