Nacos配置中心的使用

11 篇文章 0 订阅
1 篇文章 0 订阅

Nacos简介

什么是Nacos?

Nacos是阿里巴巴提供的一套帮助开发者快速实现动态服务发现服务配置服务元数据及流量管理等集一身的服务注册中心。

Nacos的特性

Nacos 的关键特性包括:

服务发现和服务健康监测

Nacos 支持基于 DNS 和基于 RPC 的服务发现。服务提供者使用 原生SDK、OpenAPI、或一个独立的Agent TODO注册 Service 后,服务消费者可以使用DNS TODO 或HTTP&API查找和发现服务。

Nacos 提供对服务的实时的健康检查,阻止向不健康的主机或服务实例发送请求。Nacos 支持传输层 (PING 或 TCP)和应用层 (如 HTTP、MySQL、用户自定义)的健康检查。 对于复杂的云环境和网络拓扑环境中(如 VPC、边缘网络等)服务的健康检查,Nacos 提供了 agent 上报模式和服务端主动检测2种健康检查模式。Nacos 还提供了统一的健康检查仪表盘,帮助您根据健康状态管理服务的可用性及流量。

动态配置服务

动态配置服务可以让您以中心化、外部化和动态化的方式管理所有环境的应用配置和服务配置。

动态配置消除了配置变更时重新部署应用和服务的需要,让配置管理变得更加高效和敏捷。

配置中心化管理让实现无状态服务变得更简单,让服务按需弹性扩展变得更容易。

Nacos 提供了一个简洁易用的UI (控制台样例 Demo) 帮助您管理所有的服务和应用的配置。Nacos 还提供包括配置版本跟踪、金丝雀发布、一键回滚配置以及客户端配置更新状态跟踪在内的一系列开箱即用的配置管理特性,帮助您更安全地在生产环境中管理配置变更和降低配置变更带来的风险。

动态 DNS 服务

动态 DNS 服务支持权重路由,让您更容易地实现中间层负载均衡、更灵活的路由策略、流量控制以及数据中心内网的简单DNS解析服务。动态DNS服务还能让您更容易地实现以 DNS 协议为基础的服务发现,以帮助您消除耦合到厂商私有服务发现 API 上的风险。同时还可以使得不同种异构微服务直接组合使用。

Nacos 提供了一些简单的 DNS APIs TODO 帮助您管理服务的关联域名和可用的 IP:PORT 列表.

服务及其元数据管理

Nacos 能让您从微服务平台建设的视角管理数据中心的所有服务及元数据,包括管理服务的描述、生命周期、服务的静态依赖分析、服务的健康状态、服务的流量管理、路由及安全策略、服务的 SLA 以及最首要的 metrics 统计数据。

Nacos生态图

在这里插入图片描述

Nacos的安装部署

Nacos官方文档:点击直达

1.预备环境准备

Nacos 依赖 Java 环境来运行。如果您是从代码开始构建并运行Nacos,还需要为此配置 Maven环境,请确保是在以下版本环境中安装使用:

  1. 64 bit OS,支持 Linux/Unix/Mac/Windows,推荐选用 Linux/Unix/Mac。
  2. 64 bit JDK 1.8+;下载 & 配置。
  3. Maven 3.2.x+;下载 & 配置。
2.下载源码或者安装包
从 Github 上下载源码方式
git clone https://github.com/alibaba/nacos.git
cd nacos/
mvn -Prelease-nacos -Dmaven.test.skip=true clean install -U  
ls -al distribution/target/

// change the $version to your actual path
cd distribution/target/nacos-server-$version/nacos/bin
下载编译后压缩包方式

您可以从 最新稳定版本 下载 nacos-server-$version.zip包。

  unzip nacos-server-$version.zip 或者 tar -xvf nacos-server-$version.tar.gz
  cd nacos/bin
3.启动服务器

Linux/Unix/Mac
启动命令(standalone代表着单机模式运行,非集群模式):

sh startup.sh -m standalone

如果您使用的是ubuntu系统,或者运行脚本报错提示[[符号找不到,可尝试如下运行:

bash startup.sh -m standalone

Windows
启动命令(standalone代表着单机模式运行,非集群模式):

startup.cmd -m standalone
4.服务注册&发现和配置管理

服务注册

curl -X POST 'http://127.0.0.1:8848/nacos/v1/ns/instance?serviceName=nacos.naming.serviceName&ip=20.18.7.10&port=8080'

服务发现

curl -X GET 'http://127.0.0.1:8848/nacos/v1/ns/instance/list?serviceName=nacos.naming.serviceName'

发布配置

curl -X POST "http://127.0.0.1:8848/nacos/v1/cs/configs?dataId=nacos.cfg.dataId&group=test&content=HelloWorld"

获取配置

curl -X GET "http://127.0.0.1:8848/nacos/v1/cs/configs?dataId=nacos.cfg.dataId&group=test"
5.关闭服务器

Linux/Unix/Mac

sh shutdown.sh

Windows

shutdown.cmd

或者双击shutdown.cmd运行文件。

外部mysql数据库支持

单机模式时nacos默认使用嵌入式数据库实现数据的存储,若想使用外部mysql存储nacos数据,需要进行以下步骤:

  1. 安装数据库,版本要求:5.6.5+ ,mysql 8 以下
  2. 初始化mysql数据库,新建数据库nacos_config,数据库初始化文件:${nacoshome}/conf/nacosmysql.sql
  3. 修改${nacoshome}/conf/application.properties文件,增加支持mysql数据源配置(目前只支持
    mysql),添加mysql数据源的url、用户名和密码。
spring.datasource.platform=mysql
db.num=1
db.url.0=jdbc:mysql://xxxxxx:3306/nacos_config?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true
db.user=数据库用户名
db.password=数据库密码
配置管理
1. 发布配置

首先在nacos发布配置。
浏览器访问 http://127.0.0.1:8848/nacos ,默认用户名和密码都是nacos,打开nacos控制台,并点击菜单配置管理->配置列表:
在Nacos添加如下的配置:

#Data ID: nacos‐demo.yaml
#Group : DEFAULT_GROUP
#配置格式: YAML
common:
	config: test

Note: 注意dataid是以 properties(默认的文件扩展名方式)为扩展名,这里使用yaml。

第一步:点击新增配置
在这里插入图片描述
第二步:配置信息
在这里插入图片描述
在这里插入图片描述

Java代码获取配置【非整合SpringBoot】

引入Maven坐标

<dependency>
<groupId>com.alibaba.nacos</groupId>
<artifactId>nacos‐client</artifactId>
<version>1.1.3</version>
</dependency>

编写代码

public class SimpleDemoMain {
	public static void main(String[] args) throws NacosException {
		//nacos 地址
		String serverAddr = "127.0.0.1:8848";
		//Data Id
		String dataId = "nacos‐demo.yaml";
		//Group
		String group = "DEFAULT_GROUP";
		Properties properties = new Properties();
		properties.put("serverAddr",serverAddr);
		ConfigService configService = NacosFactory.createConfigService(properties);
		//获取配置,String dataId, String group, long timeoutMs
		String content = configService.getConfig(dataId, group, 5000);
		System.out.println(content);
	}
}

启动SimpleDemoMain,控制台得到以下内容:

common:
	config: test
Nacos配置管理模型

对于Nacos配置管理,通过Namespace、group、Data ID能够定位到一个配置集。
在这里插入图片描述
配置集(Data ID)
在系统中,一个配置文件通常就是一个配置集,一个配置集可以包含了系统的各种配置信息,例如,一个配置集可能包含了数据源、线程池、日志级别等配置项。每个配置集都可以定义一个有意义的名称,就是配置集的ID即DataID。

配置项
配置集中包含的一个个配置内容就是配置项。它代表一个具体的可配置的参数与其值域,通常以 key=value 的形式存在。例如我们常配置系统的日志输出级别(logLevel=INFO|WARN|ERROR) 就是一个配置项。

配置分组(Group)
配置分组是对配置集进行分组,通过一个有意义的字符串(如 Buy 或 Trade )来表示,不同的配置分组下可以有相同的配置集(Data ID)。当您在 Nacos 上创建一个配置时,如果未填写配置分组的名称,则配置分组的名称默认采用 DEFAULT_GROUP 。配置分组的常见场景:可用于区分不同的项目或应用,例如:学生管理系统的配置集可以定义一个group为:STUDENT_GROUP。

命名空间(Namespace)
命名空间(namespace)可用于进行不同环境的配置隔离。例如可以隔离开发环境、测试环境和生产环境,因为它们的配置可能各不相同,或者是隔离不同的用户,不同的开发人员使用同一个nacos管理各自的配置,可通过namespace隔离。不同的命名空间下,可以存在相同名称的配置分组(Group) 或 配置集。

最佳实践
Nacos抽象定义了Namespace、Group、Data ID的概念,具体这几个概念代表什么,取决于我们把它们看成什么:"
Namespace:代表不同环境,如开发、测试、生产环境。
Group:代表某项目,如XX项目
DataId:每个项目下往往有若干个工程,每个配置集(DataId)是一个工程的主配置文件

获取某配置集的代码:
获取配置集需要指定:

  1. nacos服务地址,必须指定
  2. namespace,如不指定默认public
  3. group,如不指定默认 DEFAULT_GROUP
  4. dataId,必须指定
    代码如下:
// 初始化配置服务,
String serverAddr = "127.0.0.1:8848";
String namespace = "8b69db17-ccbc-44a8-b376-c87f22d507b7"; //开发环境
String group = "DEFAULT_GROUP"; //默认组
String dataId = "nacos‐demo.yaml";
Properties properties = new Properties();
properties.put("serverAddr", serverAddr);
properties.put("namespace", namespace);
ConfigService configService = NacosFactory.createConfigService(properties);
//获取配置,并输出控制台
String content = configService.getConfig(dataId, group, 5000);
System.out.println(content);

看懂即可不用运行。
以上代码说明将从地址为127.0.0.1:8848的nacos配置中心获取配置,通过以下信息定位配置集:
namespace:ee247dde-d838-425c-b371-029dab26232f
注意:namespace需要指定id。
group:DEFAULT_GROUP
Data Id:nacos-demo.yaml

2.命名空间管理

前面已经介绍过,命名空间(Namespace)是用于隔离多个环境的(如开发、测试、生产),而每个应用在不同环境的同一个配置(如数据库数据源)的值是不一样的。因此,我们应针对企业项目实际研发流程、环境进行规划。如某软件公司拥有开发、测试、生产三套环境,那么我们应该针对这三个环境分别建立三个namespace。
在这里插入图片描述
建立好所有namespace后,在配置管理与服务管理模块下所有页面,都会包含用于切换namespace(环境)的tab按钮,如下图:在这里插入图片描述
在这里插入图片描述
如果您在编写程序获取配置集过程中没有感知到这个参数的输入,那么 nacos 统一会使用一个默认的 namespace作为输入,nacos config 会使用一个空字符串作为默认的参数来初始化,对应界面上就是public命名空间。

Note: namesace 为 public 是 nacos 的一个保留空间,如果您需要创建自己的 namespace,不要和 public 重名,以一个实际业务场景有具体语义的名字来命名,以免带来字面上不容易区分自己是哪一个 namespace。

Note:在编写程序获取配置集时,指定的namespace参数一定要填写命名空间ID,而不是名称

注意:nameSpace的值根据自己的环境确定。

// 初始化配置服务,
String serverAddr = "127.0.0.1:8848";
String namespace = "8b69db17-ccbc-44a8-b376-c87f22d507b7"; //开发环境
String group = "DEFAULT_GROUP"; //默认组
String dataId = "nacos‐demo.yaml";
Properties properties = new Properties();
properties.put("serverAddr", serverAddr);
properties.put("namespace", namespace);
ConfigService configService = NacosFactory.createConfigService(properties);
//获取配置,并输出控制台
String content = configService.getConfig(dataId, group, 5000);
System.out.println(content);
3. 监听查询

Nacos提供配置订阅者即监听者查询能力,同时提供客户端当前配置的MD5校验值,以便帮助用户更好的检查配置变更是否推送到 Client 端。
在这里插入图片描述

public class SimpleDemoMainListener {
	public static void main(String[] args) throws NacosException {
	//nacos 地址
	String serverAddr = "127.0.0.1:8848";
	//Data Id
	String dataId = "nacos‐demo.yaml";
	//Group
	String group = "DEFAULT_GROUP";
	Properties properties = new Properties();
	properties.put("serverAddr",serverAddr);
	ConfigService configService = NacosFactory.createConfigService(properties);
	//获取配置,String dataId, String group, long timeoutMs
	String content = configService.getConfig(dataId, group, 5000);
	System.out.println(content);
	//添加监听String dataId, String group, Listener listener
	configService.addListener(dataId, group, new Listener() {
		public Executor getExecutor() {
			return null;
		}
	public void receiveConfigInfo(String s) {
	//当配置发生变化时的响应
	System.out.println(s);
		}
	});
// 测试让主线程不退出,因为订阅配置是守护线程,主线程退出守护线程就会退出。 正式代码中无需下面代码
		while (true) {
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
}
4. 登录管理

Nacos当前版本支持简单的登录功能,默认用户名/密码为: nacos/nacos 。
修改默认用户名/密码方法
加入坐标依赖

<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring‐security‐core</artifactId>
<version>5.1.4.RELEASE</version>
</dependency>

编写PasswordEncoderUtil类,生成加密后的密码,采用BCrypt加密方法在每次生成密码时会加随机盐,所以生成密码每次可能不一样。把获得的加密后的密码,复制更新在nacos的数据库中

public class PasswordEncoderUtil {
	public static void main(String[] args) {
		System.out.println(new BCryptPasswordEncoder().encode("123"));
	}
}

创建用户名或者密码的时候,用指定用户名密码即可。将上边程序输出的密码更新到数据库。

INSERT INTO users (username, password, enabled) VALUES ('nacos1',
'$2a$10$SmtL5C6Gp2sLjBrhrx1vj.dJAbJLa4FiJYZsBb921/wfvKAmxKWyu', TRUE);
INSERT INTO roles (username, role) VALUES ('nacos1', 'ROLE_ADMIN');

以上是老版本的操作,新版本提供权限控制列表:
在这里插入图片描述

关闭登录功能
由于部分公司自己开发控制台,不希望被nacos的安全filter拦截。因此nacos支持定制关闭登录功能找到配置文件${nacoshome}/conf/application.properties , 替换以下内容即可。

## spring security config
### turn off security
spring.security.enabled=false
management.security=false
security.basic.enabled=false
nacos.security.ignore.urls=/**
#nacos.security.ignore.urls=/,/**/*.css,/**/*.js,/**/*.html,/**/*.map,/**/*.svg,/**/*.png,/**/*.
ico,/console‐
fe/public/**,/v1/auth/login,/v1/console/health,/v1/cs/**,/v1/ns/**,/v1/cmdb/**,/actuator/**
微服务获取配置【整合SpringBoot】

导入依赖

<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.3.7.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <groupId>com.example</groupId>
    <artifactId>demo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>demo</name>

    <properties>
        <java.version>1.8</java.version>
        <nacos.version>2.2.6.RELEASE</nacos.version>
        <spring-cloud.version>Hoxton.SR11</spring-cloud.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
            <version>${nacos.version}</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
            <version>${nacos.version}</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <dependency>
                <groupId>com.alibaba.cloud</groupId>
                <artifactId>spring-cloud-alibaba-dependencies</artifactId>
                <version>2.2.5.RELEASE</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <excludes>
                        <exclude>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </exclude>
                    </excludes>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>

配置配置文件bootstrap.yml配置
一般来说,spring boot的配置将在application.yml(也可以是application.properties)文件中编写, 由于使用外部配置中心,必须将原先的application.yml重命名为bootstrap.yml,bootstrap.yml如下所示:
spring.cloud.nacos.config.server-addr 指定了Nacos Server的网络地址和端口号。以上配置文件说明该应用将从地址为127.0.0.1:8848配置中心获取配置,通过以下信息定位配置集:

server:
  port: 9005 #启动端口 命令行注入
spring:
  application:
    name: test
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848 # 注册中心地址
      config:
        file-extension: yaml # 文件扩建类型
        namespace: 8b69db17-ccbc-44a8-b376-c87f22d507b7 # 开发环境
        group: DEFAULT_GROUP # 组名
        server-addr: 127.0.0.1:8848 # 配置中心地址

Note:spring-cloud-starter-alibaba-nacos-config 在加载配置的时候,加载了以 dataid 为 s p r i n g . a p p l i c a t i o n . n a m e . {spring.application.name}. spring.application.name.{file-extension:properties}的基础配置。对应以上的配置,它会去 nacos server中加载data id为test.yaml的配置集。

namespace:8b69db17-ccbc-44a8-b376-c87f22d507b7 # 开发环境
group:DEFAULT_GROUP # 测试组
Data Id:test.yaml

Note: 若没有指定spring.cloud.nacos.config.group配置,则默认为DEFAULT_GROUP。

在这里插入图片描述
此处,如何获取配置,可以通过@Value的方式,如果想使用@ConfigurationProperties的方式获取实体配置的话,请点此处转至笔者的另一篇文章详细查看:@ConfigurationProperties详解

@RestController
public class TestController {
    
    @Value("${common.config}")
    private String configInfo;
    
    @GetMapping("/getConfig")
    public ResponseEntity<String> getConfig() {
        return ResponseEntity.status(HttpStatus.OK).body(configInfo);
    }
}

在这里插入图片描述
支持配置的动态更新
基于上面快速上手的例子,若要实现配置的动态更新,只需要进行如下改造:

// 注入配置文件上下文
	@Autowired
	private ConfigurableApplicationContext applicationContext;
    @GetMapping("/getConfig")
    public ResponseEntity<String> getConfig() {
        return ResponseEntity.status(HttpStatus.OK).body(applicationContext.getEnvironment().getProperty("common.config"););
    }

当然这种方式肯定是很繁琐的,因此,我们需要加入一个SpringCloud的注解---->@RefreshScope

@RefreshScope
@RestController
public class TestController {
    
    @Value("${common.config}")
    private String configInfo;
    
    @GetMapping("/getConfig")
    public ResponseEntity<String> getConfig() {
        return ResponseEntity.status(HttpStatus.OK).body(configInfo);
    }
}

我们通过nacos控制台更新common.config的配置值,再次访问web端点/getConfig,发现应用程序能够获取到最新的配置值,说明spring-cloud-starter-alibaba-nacos-config 支持配置的动态更新。

Note 可以通过配置spring.cloud.nacos.config.refresh.enabled=false来关闭动态刷新

自定义扩展的Data Id配置
Spring Cloud Alibaba Nacos Config可支持自定义 Data Id 的配置。 一个完整的配置案例如下所示:

server:
  port: 9005 #启动端口 命令行注入
spring:
  application:
    name: test
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848 # 配置中心地址
      config:
        server-addr: 127.0.0.1:8848
        ext-config[0]:
          data‐id: config1.yaml
          group: REFRESH_GROUP
          refresh: false
        ext-config[1]:
          data‐id: ext‐config‐common03.properties
          group: DEFAULT_GROUP
          refresh: true

通过 spring.cloud.nacos.config.ext-config[n].data-id的配置方式来支持多个 Data Id 的配置。
通过 spring.cloud.nacos.config.ext-config[n].group的配置方式自定义 Data Id 所在的组,不明确配置的话,默认是 DEFAULT_GROUP。
通过 spring.cloud.nacos.config.ext-config[n].refresh的配置方式来控制该 Data Id 在配置变更时,是否支持应用中可动态刷新, 感知到最新的配置值。默认是不支持的。

Note : spring.cloud.nacos.config.ext-config[n].data-id的值必须带文件扩展名,文件扩展名既可支持properties,又可以支持 yaml/yml。 此时spring.cloud.nacos.config.file-extension 的配置对自定义扩展配置的 Data Id 文件扩展名没有影响。通过自定义扩展的 Data Id 配置,既可以解决多个应用间配置共享的问题,又可以支持一个应用有多个配置文件。

自定义共享 Data Id 配置
为了更加清晰的在多个应用间配置共享的 Data Id ,你可以通过以下的方式来配置:

server:
  port: 9005 #启动端口 命令行注入
spring:
  application:
    name: test
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848 # 配置中心地址
      config:
        shared-dataids: config1.yaml,config2.yaml

通过 spring.cloud.nacos.config.shared-dataids来支持多个共享 Data Id 的配置,多个之间用逗号隔开。通过 spring.cloud.nacos.config.refreshable-dataids来支持哪些共享配置的 Data Id 在配置变化时,应用中是否可动态刷新, 感知到最新的配置值,多个 Data Id 之间用逗号隔开。如果没有明确配置,默认情况下所有共享配置的 Data Id 都不支持动态刷新。

Note:通过 spring.cloud.nacos.config.shared-dataids 来支持多个共享配置的 Data Id 时,多个共享配 置间的一个优先级的关系我们约定:按照配置出现的先后顺序,即后面的优先级要高于前面。

Note:通过 spring.cloud.nacos.config.shared-dataids 来配置时,Data Id
必须带文件扩展名,文件扩 展名既可支持 properties,也可以支持 yaml/yml。 此时
spring.cloud.nacos.config.file-extension 的配置对自定义扩展配置的 Data Id
文件扩展名没有影响。

Note: spring.cloud.nacos.config.refreshable-dataids 给出哪些需要支持动态刷新时,Data Id 的值也 必须明确给出文件扩展名

注意:以上的配置已经被弃用
在这里插入图片描述

建议使用以下配置:

server:
  port: 9005 #启动端口 命令行注入
spring:
  application:
    name: test
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848 # 配置中心地址
      config:
        server-addr: 127.0.0.1:8848
        extension-configs[0]:
          dataId: config1.yaml
          group: DEFAULT_GROUP
          refresh: true
        extension-configs[1]:
          dataId: config2.yaml
          group: DEFAULT_GROUP
          refresh: true

5.完全关闭配置

通过设置 spring.cloud.nacos.config.enabled = false来完全关闭 Spring Cloud Nacos Config

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值