SpringCloud中Config分布式配置--狂神说

工具:idea
详细了解博客连接:https://blog.csdn.net/weixin_43591980/article/details/106255122
视频链接:https://www.bilibili.com/video/BV1jJ411S7xr

概述
分布式系统面临的—配置文件的问题
微服务意味着要将单体应用中的业务拆分成一个个子服务,每个服务的粒度相对较小,因此系统中会出现大量的服务,由于每个服务都需要必要的配置信息才能运行,所以一套集中式的、动态的配置管理设施是必不可少的。SpringCloud提供了ConfigServer来解决这个问题,我们每一个微服务自己带着一个application.yml,那上百的配置文件要修改起来,岂不是要疯。
什么是SpringCloud config分布式配置中心
在这里插入图片描述

SpringCloud Config为微服务架构中的微服务提供集中化的外部配置支持,配置服务器为各个不同微服务应用的所有环节提供了一个中心化的外部配置。
SpringCloud Config分为服务端和客户端两部分;
服务端也称为 分布式配置中心,它是一个独立的微服务应用,用来连接配置服务器并为客户端提供获取配置信息,加密,解密信息等访问接口。
客户端则是通过制定的配置中心来管理应用资源以及与业务相关的配置内容, 并在启动的时候从配置中心获取和加载配置信息,配置服务器默认采用git来存储配置信息,这样有助于对环境配置进行版本管理,并且可以通过git客户端工具来方便的管理和访问配置内容。

SpringCloud Config分布式配置中心能干什么

  • 集中管理配置文件
  • 不同环境,不同配置,动态化的配置更新,分环境部署,比如/dev /test/ /prod /beta /release通过—三个横杠隔开
  • 运行期间动态调整配置,不再需要在每个服务部署的机器上编写配置文件,服务会向配置中心统一拉取配置自己的信息
  • 当配置发生变动是,服务不需要重启即可感知到配置的变化,并应用新的配置。这个得需要热部署插件才可以做到。
  • 将配置信息以REST借口的形式暴露

SpringCloud Config分布式配置中心与GitHub整合
由于SpringCloud Config默认使用Git来存储配置文件(也有其他方式,比如支持SVN和本地文件),但是最推荐的还是Git,而且使用的是http/https访问的形式;

Git环境搭建

首先得安装Git,安装完毕后,电脑右键会出现两个git的新建项
在这里插入图片描述

搜索码云,注册或者登陆账号,然后创建一个仓库

在这里插入图片描述
点击创建后页面跳转填写信息
在这里插入图片描述

完成后会看到这样的一个界面
在这里插入图片描述

点击克隆/下载按钮,选择ssh,点击复制
在这里插入图片描述

在电脑上随便找个位置新建一个文件夹,名称随便取,然后电脑右键打开git bash
在这里插入图片描述

回车
在这里插入图片描述

上面说没有权限公开,在gitee刚刚创建仓库的界面上点击git大全
在这里插入图片描述

往下翻,找到配置
在这里插入图片描述

输入命令查看配置:git config –list
在这里插入图片描述

没有用户配置

配置用户信息

# 设置提交代码时的用户信息
$ git config [--global] user.name "[name]"
$ git config [--global] user.email "[email address]"

在这里插入图片描述

再次输入git config –list
在这里插入图片描述

生成秘钥。首先找到.ssh文件,然后删掉
在这里插入图片描述

在你新建放置springcloud-config仓库的文件夹里面右键打开git bash。
输入这个命令来生成秘钥ssh-keygen -t rsa -C “847475198@qq.com”。当然,QQ号就写成自己的即可。
在这里插入图片描述

回到.ssh文件那边看,可以看到生成了两个文件
在这里插入图片描述

需要添加秘钥到GitHub上面,上图的第二个文件打开,复制
在这里插入图片描述

复制到公钥栏里面,输入密码验证
在这里插入图片描述

打开个人主页,选择新建的仓库名称,再次复制地址进行克隆。打开当前要放置的项目—》右键—》git bash
在这里插入图片描述

可以打开看到,跟gitee上面的一模一样
在这里插入图片描述

在这个目录下新建一个application.yml文件,在里面进行编辑
在这里插入图片描述

把application.yml提交到Gitee码云上
1、把application.yml添加到暂存区。git add .
在这里插入图片描述

2、提交到本地git commit -m “”
在这里插入图片描述

3、push到远程
在这里插入图片描述

刷新gitee,可以看到已经上传成功
在这里插入图片描述

服务端配置gitee配置

目录结构
在这里插入图片描述

新建一个module,**springcloud-config-server-6666
**,后面的端口号可以自已定义。
Pom文件

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <!--eureka-->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-eureka</artifactId>
        <version>1.4.6.RELEASE</version>
    </dependency>
    <!--config-->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-config-server</artifactId>
        <version>2.2.7.RELEASE</version>
    </dependency>
</dependencies>

Application.yml文件

server:
  port: 6666
spring:
  application:
    name: springcloud-config-server
    #连接远程仓库
  cloud:
    config:
      server:
        git:
          uri: https://gitee.com/angelordevil/springcloud-config.git  #连接gitee上的HTTPS的路径

也就是这个HTTPS远程地址。点击复制
在这里插入图片描述

Config_Server_6666 启动类

package com.springcloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.config.server.EnableConfigServer;
@SpringBootApplication
@EnableConfigServer 
public class Config_Server_6666 {
    public static void main(String[] args) {
        SpringApplication.run(Config_Server_6666.class,args);
    }
}

然后启动,输入地址http://localhost:6666
如果启动失败,就去pom文件中把eureka依赖删除,目前暂时不用这个功能。不然读取的时候会一直失败然后一直去读取。接着添加一个actuator监控依赖

<!--actuator 完善监控信息-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

访问成功后
在这里插入图片描述

想访问谁就在地址栏上写上谁,比如访问application.yml文件。就输入地址: http://localhost:6666/application-dev.yml
在这里插入图片描述
这个可以通过java程序去读取到GitHub的所有东西

官方文档中介绍HTTP服务具有一下格式资源。也就是可以通过下面这些路径方式去加载资源
/{application}/{profile}[/{label}] //http://localhost:6666/application/dev/master意思:读取git上的master分支上的application配置中的dev属性

/{application}-{profile}.yml
/{label}/{application}-{profile}.yml
/{application}-{profile}.properties
/{label}/{application}-{profile}.properties

客户端连接服务端访问远程

首先在项目路径下新建一个config-client.yml文件,然后打开编辑,写上配置
在这里插入图片描述

右键打开git bash
在这里插入图片描述

Push到gitee上
在这里插入图片描述

可以到gitee上查看
在这里插入图片描述

新建一个module,springcloud-config-client-7777
目录结构
在这里插入图片描述

Pom文件

<dependencies>
    <!--config-->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-config</artifactId>
        <version>2.2.7.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-config-server</artifactId>
        <version>2.2.7.RELEASE</version>
    </dependency>
    <!--actuator 完善监控信息-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>
</dependencies>

Bootstrap.yml。bootstrap.yml是系统级别的配置,而application.yml是用户级别的配置。所以bootstrap.yml的优先级高

# 系统级别的配置
#客户端连接远程项目。先去连接6666中的服务,再通过6666这个服务去连接GitHub上的获取
spring:
  cloud:
    config:
      name: config-client #需要从git上获取的资源名称,无需后缀
      profile: dev
      label: master
      uri: http://localhost:6666

Application.yml

# 用户级别的配置
spring:
  application:
    name: springcloud-config-client-7777

ConfigClient_7777

package com.springcloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
//客户端去拿服务端的东西
@SpringBootApplication
public class ConfigClient_7777 {
    public static void main(String[] args) {
        SpringApplication.run(ConfigClient_7777.class,args);
    }
}

ConfigClientController

package com.springcloud.controller;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class ConfigClientController {
    /*参照gitee上的config-client.yml文件注入进去*/
    @Value("${spring.application.name}")
    private String applicationName;
    @Value("${eureka.client.service-url.defualtZone}")
    private String eurekaServer;
    @Value("${server.port}")
    private String port;
    //如果能打印出来这段话证明成功
    @RequestMapping("/config")
    public String getConfig(){
        return "applicationName:" + applicationName+
                "eurekaServer:"+ eurekaServer+
                "port:" + port;
    }
}

保证远程是能连接上的
在这里插入图片描述

启动7777这个服务,因为没有配置端口号,默认走的是8201这个分支。输入http://localhost:8201/
在这里插入图片描述

然后输入http://localhost:8201/config
在这里插入图片描述

然后改bootstrap.yml文件
在这里插入图片描述

再访问就只能访问8202这个分支了。
总结:
可以写上多个客户端,把配置统一放在GitHub上,通过server去连接GitHub。有问题可以在GitHub去调节,然后代码还是代码,server还是server,远程的变化对于这两个没有影响。

远程配置实战测试

新建两个application.yml
在这里插入图片描述

config-eureka.yml

spring:
  profiles:
    active: dev #激活dev
#---
server:
  port: 7001
#spring配置
spring:
  profiles: dev
  application:
    name: springcloud-config-eureka
# eureka配置
eureka:
  instance:
    hostname: eureka7001.com  #eureka服务端的实例名称
  client:
    register-with-eureka: false  # 表示是否向eureka注册中心注册自己
    fetch-registry: false #fetch-registry如果为false,则表示自己为注册中心
    service-url:
      # 单机
      #defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/   #监控页面
      # 集群(关联)在7001中挂载7002
      defultZone: http://eureka7002.com:7002/eureka/,http://eureka7003:com:7003/eureka/
#----
server:
  port: 7001
#spring配置
spring:
  profiles: test
  application:
    name: springcloud-config-eureka
# eureka配置
eureka:
  instance:
    hostname: eureka7001.com  #eureka服务端的实例名称
  client:
    register-with-eureka: false  # 表示是否向eureka注册中心注册自己
    fetch-registry: false #fetch-registry如果为false,则表示自己为注册中心
    service-url:
      # 单机
      #defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/   #监控页面
      # 集群(关联)在7001中挂载7002
      defultZone: http://eureka7002.com:7002/eureka/,http://eureka7003:com:7003/eureka/

application.yml
spring:
  profiles:
    active: dev #激活dev
#----
server:
  port: 8001
#mybatis配置
mybatis:
  type-aliases-package: com.springcloud.pojo
  config-location: classpath:mybatis/mybatis-config.xml    #mybatis的核心配置
  mapper-locations: classpath:mybatis/mapper/*.xml
#spring配置
spring:
  profiles: dev
  application:
    name: springcloud-config-dept
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    driver-class-name: com.mysql.jdbc.Driver      #org.gjt.mm.mysql.Driver
    url: jdbc:mysql://localhost:3306/cloud01?useUnicode=true&characterEncoding=utf-8
    username: root
    password: sa123
#eureka的配置,defualtZone  服务注册到7001中的yml的路径上
eureka:
  client:
    service-url:
      defualtZone: http://eureka7001.com :7001/eureka/,http://eureka7002.com :7002/eureka/,http://eureka7003.com :7003/eureka/   #http://localhost:7001/eureka/
  instance:
    instance-id: spring-cloud-provider-dept8001   #修改eureka上的默认描述信息
#    prefer-ip-address: true   #可以显示服务ip地址,可以定位到远程的IP地址
#info配置
info:
  app.name: study-springcloud
  company.name: gd.familychen.com
#----
server:
  port: 8001
#mybatis配置
mybatis:
  type-aliases-package: com.springcloud.pojo
  config-location: classpath:mybatis/mybatis-config.xml    #mybatis的核心配置
  mapper-locations: classpath:mybatis/mapper/*.xml
#spring配置
spring:
  profiles: dev
  application:
    name: springcloud-config-dept
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    driver-class-name: com.mysql.jdbc.Driver      #org.gjt.mm.mysql.Driver
    url: jdbc:mysql://localhost:3306/cloud02?useUnicode=true&characterEncoding=utf-8
    username: root
    password: sa123
#eureka的配置,defualtZone  服务注册到7001中的yml的路径上
eureka:
  client:
    service-url:
      defualtZone: http://eureka7001.com :7001/eureka/,http://eureka7002.com :7002/eureka/,http://eureka7003.com :7003/eureka/   #http://localhost:7001/eureka/
  instance:
    instance-id: spring-cloud-provider-dept8001   #修改eureka上的默认描述信息
#    prefer-ip-address: true   #可以显示服务ip地址,可以定位到远程的IP地址
#info配置
info:
  app.name: study-springcloud
  company.name: gd.familychen.com

然后push到远程。跟上面一样的操作

  1. git add .
  2. git status
  3. git commit -m “这里填第几次提交的信息”
  4. git push origin master

新建一个module,springcloud-config-eureka-7001。把7001的内容复制过来改。
目录结构:
在这里插入图片描述

Pom文件:

<dependencies>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-eureka-server</artifactId>
        <version>1.4.6.RELEASE</version>
    </dependency>
    <!--热部署-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-devtools</artifactId>
    </dependency>
    <!--config-->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-config</artifactId>
        <version>2.2.7.RELEASE</version>
    </dependency>
</dependencies>

ConfigEurekaServer_7001

package com.springcloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
@SpringBootApplication
@EnableEurekaServer//开启eureka服务  EnableEurekaServer服务端的启动类,可以接受别人注册进来
public class ConfigEurekaServer_7001 {
    public static void main(String[] args) {
        SpringApplication.run(ConfigEurekaServer_7001.class,args);
    }
}

bootstrap.yml

spring:
  cloud:
    config:
      name: config-eureka
      label: master
      profile: dev
      uri: http://localhost:6666

application.yml

#配置信息,代表是哪个(客户端)module的配置
spring:
  application:
    name: springcloud-config-eureka-7001

启动6666,让它连接远程的config-eureka的dev,访问路径:localhost:6666/master/config-eureka-dev.yml。测试成功后,启动springcloud-config-eureka-7001,访问路径:localhost:7001
在这里插入图片描述

新建一个module,springcloud-config-dept-8001,复制8001修改一下配置。
目录结构:
在这里插入图片描述
Pom文件

<dependencies>
    <!--要拿实体类,所以要配置api module-->
    <dependency>
        <groupId>com.cloud</groupId>
        <artifactId>springcloud-api</artifactId>
        <version>1.0-SNAPSHOT</version>
    </dependency>
    <!--junit-->
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
    </dependency>
    <!--数据库-->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
    </dependency>
    <!--druid数据源-->
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>druid</artifactId>
    </dependency>
    <!--logback-->
    <dependency>
        <groupId>ch.qos.logback</groupId>
        <artifactId>logback-core</artifactId>
    </dependency>
    <dependency>
        <groupId>org.mybatis.spring.boot</groupId>
        <artifactId>mybatis-spring-boot-starter</artifactId>
    </dependency>
    <!--test-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-test</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <!--jetty-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-jetty</artifactId>
    </dependency>
    <!--热部署-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-devtools</artifactId>
    </dependency>
    <!--eureka依赖-->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-eureka</artifactId>
        <version>1.4.6.RELEASE</version>
    </dependency>
    <!--actuator 完善监控信息-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>
    <!--hystrix依赖-->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
        <version>2.2.9.RELEASE</version>
    </dependency>
<!--config-->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-config</artifactId>
    <version>2.2.7.RELEASE</version>
</dependency>
</dependencies>

DeptController

package springcloud.controller;
import com.springcloud.pojo.Dept;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;
import springcloud.service.DeptService;
import java.util.List;
//提供restful服务
@RestController
public class DeptController {
    @Autowired
    private DeptService deptService;
    //获取一些配置的信息,得到具体的微服务
    @Autowired
    private DiscoveryClient client;
    @PostMapping("/dept/add")
    public boolean addDept(Dept dept){
        return deptService.addDept(dept);
    }
    @GetMapping("/dept/get/{id}")
    public Dept get(@PathVariable("id") Long id){
        Dept dept = deptService.queryById(id);
        if (dept==null){
            throw new RuntimeException("not find resource");
        }
        return dept;
    }
    @GetMapping("/dept/list")
    public List<Dept> queryAll(){
        return deptService.queryAll();
    }
    //注册进来的微服务,获取一些消息
    @GetMapping("/dept/discovery")
    public Object discovery(){
        //获取微服务列表的清单
        List<String> services = client.getServices();
        System.out.println("discovery=>services:"+services);
        //得到一个具体的微服务信息,通过具体的微服务id,applicationName:
        List<ServiceInstance> instances = client.getInstances("SPRINGCLOUD-PROVIDER-DEPT");
        for (ServiceInstance instance: instances) {
            System.out.println(
                    instance.getHost()+"\t"+
                    instance.getPort()+"\t"+
                    instance.getUri()+"\t"+
                    instance.getInstanceId()
            );
        }
        return this.client;
    }
}

DeptDao

package springcloud.dao;
import com.springcloud.pojo.Dept;
import org.apache.ibatis.annotations.Mapper;
import org.springframework.stereotype.Repository;
import java.util.List;
@Mapper
@Repository
public interface DeptDao {
    //增加
    public boolean addDept(Dept dept);
    //查询
    public Dept queryById(Long id);
    //查询所有
    public List<Dept> queryAll();
}

DeptService 
package springcloud.service;
import com.springcloud.pojo.Dept;
import java.util.List;
public interface DeptService {
    //增加
    public boolean addDept(Dept dept);
    //查询
    public Dept queryById(Long id);
    //查询所有
    public List<Dept> queryAll();
}

DeptServiceImp
package springcloud.service;
import com.springcloud.pojo.Dept;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import springcloud.dao.DeptDao;
import java.util.List;
@Service
public class DeptServiceImp implements DeptService {
    @Autowired
    private DeptDao deptDao;
    @Override
    public boolean addDept(Dept dept) {
        return deptDao.addDept(dept);
    }
    @Override
    public Dept queryById(Long id) {
        return deptDao.queryById(id);
    }
    @Override
    public List<Dept> queryAll() {
        return deptDao.queryAll();
    }
}

DeptProvider_8001

package springcloud;
import com.netflix.hystrix.contrib.metrics.eventstream.HystrixMetricsStreamServlet;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.context.annotation.Bean;
//启动类
@SpringBootApplication
@EnableEurekaClient  //开启注解,在服务启动后自动配置到7001中的EurekaServer_7001去
@EnableDiscoveryClient  //服务发现
public class DeptProvider_8001 {
    public static void main(String[] args) {
        SpringApplication.run(DeptProvider_8001.class,args);
    }
    //添加一个servlet
    //固定的代码
    @Bean
    public ServletRegistrationBean hystrixMetricsStreamServlet(){
        ServletRegistrationBean registrationBean = new ServletRegistrationBean(new HystrixMetricsStreamServlet());
        registrationBean.addUrlMappings("/actuator/hystrix.stream");//访问这个页面就可以被监控了
        return registrationBean;
    }
}

application.yml

spring:
  application:
    name: springcloud-config-dept-8001

bootstrap.yml

spring:
  cloud:
    config:
      name: config-dept
      label: master
      profile: dev
      uri: http://localhost:6666

mybatis-config.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <settings>
        <!--开启二级缓存-->
        <setting name="cacheEnabled" value="true"/>
    </settings>
</configuration>

DeptMapper.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.DeptDao">
    <insert id="addDept" parameterType="Dept">
        insert into dept(dname,db_source)
        values (#{dname},DATABASE())
    </insert>
    <select id="queryById" resultType="Dept" parameterType="Long">
        select * from dept where deptno = #{deptno};
    </select>
    <select id="queryAll" resultType="Dept">
        select * from dept;
    </select>
</mapper>

最主要是改配置,代码不需要动。
启动springcloud-config-dept-8001,能放到eureka注册中心证明成功。访问路径:localhost:8001/dept/get/1。http://localhost:6666/master/config-dept-dev.yml
在这里插入图片描述

假设在gitee上改一些配置信息,改config-dept.yml文件,下面那个spring配置改成test。然后在最上面把数据库cloud01改成cloud03,如果查询数据的话就会走cloud03这个数据库的数据
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

提交信息
在这里插入图片描述

远程改了信息,在项目中热部署一下,这样才能更改信息。访问地址:localhost:8001/dept/get/1
这个可以看到数据库变成了cloud03了。然后可以自行把所有项目中的所有配置都丢到远程上。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值