【仓颉三方库】分布式——config-server

39 篇文章 0 订阅
39 篇文章 0 订阅

介绍

Config是一个分布式配置管理系统,它提供了一个中心化的配置服务器来管理应用程序的配置信息。它允许开发人员将应用程序的配置信息存储在一个集中的位置,并将这些配置信息分发给多个应用程序实例。Config支持多种后端存储,包括Git、Redis、高斯数据库、本地文件系统等。它还提供了一组REST API,可以用于动态获取配置信息,以便应用程序能够及时更新自己的配置。通过使用Config,开发人员可以轻松地管理和更新应用程序的配置信息,从而提高应用程序的可维护性和可扩展性。

Config Server是一个可横向扩展、集中式的配置服务器,它用于集中管理应用程序各个环境下的配置。

特性

  • 🚀 支持Git、Redis、AWS S3、本地文件系统、高斯数据库存储配置信息。
  • 🚀 配置文件支持json、yaml格式。
  • 🚀 存储配置文件支持json、yaml、properties格式。
  • 🚀 多种配置存储方式同时生效并可设置优先级。
  • 🚀 一次获取多版本配置文件信息。
  • 🚀 客户端从服务端拉取配置可开启安全校验。
  • 🚀 提供获取配置的纯文本格式和二进制流格式,其中yaml、json、properties格式支持配置信息解密。
  • 🚀 提供获取服务加解密功能运行状态接口。
  • 🚀 提供加解密接口配置文件内容支持3DES和RSA加解密,RSA加解密通过密钥库文件可使用多对密钥。
  • 🚀 提供自定义加解密。
  • 🚀git存储配置信息可设置对配置文件名称的模式匹配
  • 🚀 配置的EnvironmentRepository健康检测。
  • 🚀 兼容Spring Cloud Config Client。

架构图

源码目录

    .
    ├── README.md
    ├── access.log
    ├── build.cj
    ├── cjpm.lock
    ├── cjpm.toml
    ├── doc
    ├── generate
    │   └── silo_woods_generator
    ├── profile
    │   ├── client.properties
    │   └── master
    │       ├── client-dev.properties
    │       ├── client-prod.json
    │       └── client.yml
    ├── rebuild.sh
    ├── resources
    │   ├── applicationContext.json
    │   ├── config.cks
    │   ├── config_server
    │   │   ├── application.json
    │   │   └── application.yml
    │   ├── db-init
    │   │   └── openguass
    │   │       └── schema-jdbc.sql
    │   ├── private.pem
    │   ├── rsa_public.pem
    │   ├── session.json
    │   ├── spirit.xml
    │   └── sqls
    │       └── config_server_sql_config.xml
    ├── src
    │   ├── config_server
    │   │   ├── common
    │   │   │   ├── config_utils
    │   │   │   │   ├── encrypt_util.cj
    │   │   │   │   ├── import.cj
    │   │   │   │   ├── read_profiles.cj
    │   │   │   │   ├── to_string.cj
    │   │   │   │   └── yaml_to_proerties.cj
    │   │   │   └── model
    │   │   │       ├── config_case.cj
    │   │   │       ├── config_properties.cj
    │   │   │       ├── environment.cj
    │   │   │       ├── health.cj
    │   │   │       ├── import.cj
    │   │   │       ├── key_store.cj
    │   │   │       └── property_sources.cj
    │   │   ├── connection
    │   │   │   ├── db_connection_proxy.cj
    │   │   │   ├── redis_connection.cj
    │   │   │   └── s3_connection.cj
    │   │   ├── controller
    │   │   │   ├── encryption_controller.cj
    │   │   │   ├── environment_controller.cj
    │   │   │   ├── import.cj
    │   │   │   └── resource_controller.cj
    │   │   ├── encryption
    │   │   │   ├── cipher_environment_encryptor.cj
    │   │   │   ├── default_encryptor_locator.cj
    │   │   │   ├── default_secret_encryptor.cj
    │   │   │   ├── environment_prefix_helper.cj
    │   │   │   ├── import.cj
    │   │   │   ├── text_encryptor.cj
    │   │   │   └── text_encryptor_locator.cj
    │   │   ├── environment
    │   │   │   ├── awsS3_environment_repository.cj
    │   │   │   ├── cdbc_environment_repository.cj
    │   │   │   ├── environment_repository.cj
    │   │   │   ├── import.cj
    │   │   │   ├── native_environment_repository.cj
    │   │   │   └── redis_environment_repository.cj
    │   │   ├── initialization
    │   │   │   └── profiles_initialization.cj
    │   │   └── interceptor
    │   │       └── securit_interceptor.cj
    │   ├── config_server_test
    │   │   ├── confiig_file
    │   │   │   ├── application.json
    │   │   │   ├── application.yml
    │   │   │   ├── applicationContext.json
    │   │   │   ├── copy
    │   │   │   └── test_yml_to_properties.json
    │   │   └── resource_controller_test.cj
    │   ├── generated
    │   │   ├── ioc
    │   │   │   └── initialization
    │   │   │       ├── AppContextInit.cj
    │   │   │       ├── IOCImport.cj
    │   │   │       └── seed_bag_pack_0.cj
    │   │   └── plugin
    │   │       └── initialization
    │   │           └── ext_plugin_file.cj
    │   └── main.cj
    └── start.sh
  • doc 存放库的设计文档、提案、库的使用文档、LLT 覆盖率报告
  • profile 配置文件存储在本地系统示例
  • resources config_server的配置文件、数据库初始化脚本、密钥、数据库配置
  • src/config_server 存放config核心代码
  • src/config_server_test 存放测试用例,包括 HLT 用例、LLT 用例和 UT 用例

使用说明

编译

cjpm build
配置

在服务端下中的resources/config_server添加application.json或application.yml

{
    "config": {
        "server": {
            "repository": {
                "active": "native",
                "native": {
                    "path": "./profile",
                    "order": 3
                },
                "cdbc": {
                    "type": "opengauss",
                    "url": "192.168.10.100:5432/config?sslmode=disable",
                    "username": "gaussdb",
                    "password": "Enmo@123",
                    "order": 2
                },
                "redis": {
                    "host": "192.168.10.100",
                    "username": null,
                    "port": 6379,
                    "password": "root",
                    "order": 3
                },
                "awss3": {
                    "region": "cn-north-1",
                    "bucket": "config-test11111",
                    "accessKeyId": "AKIAQAJXPICNHJEHNUE6",
                    "secretAccessKey": "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",
                    "order": 1
                }
            },
            "security": {
                "enable": true,
                "username": "admin",
                "password": "admin"
            },
            "encrypt": {
                "enable": true,
                "type": "rsa",
                "key": "abcdefghijklmnopqrstuvwx",
                "keyStoreLocation": "./resources/config.cks",
                "keyStorepassword": "123456"
            },
            "health": {
                "down-health-status": "testdown",
                "repositories": {
                    "myservice": {
                        "name": "myservice",
                        "label": "mylabel",
                        "profiles": "dev"
                    }
                }
            }
        }
    }
}

config:
  server:
    repository:
      active: native                                                                # 配置文件获取方式(现在仅支持awss3、redis、native和cdbc),支持多种配置文件获取,用[,]分割
      native:                                                                       # 本地文件配置项
        path: ./profile                                                             # 默认应用配置文件存放路径
        order: 3                                                                    # 配置项加载顺序
      cdbc:                                                                         # 数据库配置项
        type: opengauss                                                             # 数据库类型(目前只支持OpenGauss数据库)
        url: 192.168.10.100:5432/config?sslmode=disable                             # 数据库连接字符串
        username: gaussdb                                                           # 数据库用户名
        password: Enmo@123                                                          # 数据库密码
        order: 2                                                                    # 配置项加载顺序
      redis:                                                                        # Redis配置项
        host: 192.168.10.100                                                        # Redis 地址配置
        username:                                                                   # Redis 用户名配置
        port: 6379                                                                  # Redis 端口号配置
        password: 'root'                                                            # Redis 密码配置(纯数字密码请用['']包裹,例:'123123')
        order: 3                                                                    # 配置项加载顺序
      awss3:                                                                        # s3配置项
        region: cn-north-1                                                          # s3 时区
        bucket: config-test11111                                                    # s3 桶名称
        accessKeyId: 'AKIAQAJXPICNHJEHNUE6'                                         # s3 用户id
        secretAccessKey: 'gErd+MzvvVllEJx8ARBai8KG15a6+JNjDfm4yZMx'                 # s3 凭证
        order: 1                                                                    # 配置项加载顺序
    security:                                                                       # 安全校验
      enable: true                                                                  # 是否开启安全校验
      username: admin                                                               # 安全校验用户名
      password: admin                                                               # 安全校验密码
    encrypt:                                                                        # 配置文件加密
      enable: true                                                                  # 是否开启加密
      type: rsa                                                                     # 加密类型(现在仅支持3DES和RSA)
      key: abcdefghijklmnopqrstuvwx                                                 # 3DES秘钥Key 必须24位
      keyStoreLocation: ./resources/config.cks
      keyStorepassword: '123456'
    health:                                                                         # 配置中心健康检测
      down-health-status: testdown                                                  # 自定义失败状态,默认DOWN
      repositories:                                                                 # 检测项
        myservice:                                                                  # 检测项服务名
          name: myservice                                                           # 检测项服务名,优先级高
          label: mylabel                                                            # 检测项label
          profiles: dev                                                             # 检测项profiles

启动服务端

sh start.sh

功能示例

获取配置信息

服务端规定了配置文件访问规则

/{application}/{profile}[/{label}]

通过application、profile、label来定位获取哪个配置文件。

application为服务名称。profile为服务的不同版本配置文件,若要获取多个版本,用’,'分隔,若不区分版本,用default填充。label表示配置文件的分支,本地文件系统存储配置文件时label表示配置文件所在的文件夹名称。

在本地文件系统存储配置文件:

地址访问示例
/profile/master/client-dev.properties/client/dev/master
/profile/master/client-prod.properties/client/prod/master
/profile/client-prod.properties/client/prod
/profile/client.properties/client/default

配置项存储在高斯数据库中:

-- 如要修改表名,需要一同修改config_server_sql_config.xml中对应的查询表名
CREATE TABLE PROPERTIES (
  "key" VARCHAR(2048),
  "value" VARCHAR(4096),
  APPLICATION VARCHAR(128),
  PROFILE VARCHAR(128),
  LABEL VARCHAR(128)
);
-- 测试数据 start
INSERT into PROPERTIES(APPLICATION, PROFILE, LABEL, "key", "value") values ('foo', 'bar', 'master', 'a.b.c', 'foo-bar1');
INSERT into PROPERTIES(APPLICATION, PROFILE, LABEL, "key", "value") values ('foo', 'bar', 'master', 'd.e.f', 'foo-bar2');
INSERT into PROPERTIES(APPLICATION, PROFILE, LABEL, "key", "value") values ('foo', null, 'master', 'a.b.c', 'foo-default');
INSERT into PROPERTIES(APPLICATION, PROFILE, LABEL, "key", "value") values ('foo', null, null, 'a.b.c', 'foo');
-- 测试数据 end

访问/foo/bar/master,会返回a.b.c=foo-bar1和d.e.f=foo-bar2两条配置项信息。

访问/foo/default/master,会返回a.b.c=foo-default一条配置项信息。

访问/foo/default,会返回a.b.c=foo一条配置项信息。

多种配置存储方式

开启多种配置存储方式配置如下

config:
  server:
    active: cdbc,native

不同配置存储方式用[,]隔开 。此时请求服务端获取配置会返回数据库和本地文件系统存储的配置信息。

例如:GAT请求服务端/client/dev/master返回值如下

{
    "name": "client",
    "profiles": [
        "dev"
    ],
    "label": "master",
    "propertySources": [
        {
            "name": "client-dev-master",
            "source": {
                "database": "vvv"
            }
        },
        {
            "name": "./profile/master/client-dev.properties",
            "source": {
                "user.username": "111",
                "user.password": "222"
            }
        }
    ]
}

database=vvv为存储在数据库中的配置信息,user.username= 111,user.password=222为存储在本地文件中的配置信息。

存储方式设置优先级

在各配置存储方式添加order配置项便可设置优先级,数值越小,优先级越高。

config:
  server:
    profile:                                            # 本地文件配置项
      path: ./profile                                   # 默认应用配置文件存放路径
      order: 1                                          # 配置项加载顺序
    datasource:                                         # 数据库配置项
      type: opengauss                                   # 数据库类型(目前只支持OpenGauss数据库)
      url: 127.0.0.1:15400/config?sslmode=disable       # 数据库连接字符串
      username: root                                    # 数据库用户名
      password: root@123                                # 数据库密码
      order: 2                                          # 配置项加载顺序

反应在响应体中就是,在propertySources数组中位置越往后,优先级越高。

{
    "name": "client",
    "profiles": [
        "prod"
    ],
    "label": "master",
    "propertySources": [
        {
            "name": "client-prod-master",
            "source": {
                "user.password": "666",
                "user.username": "333"
            }
        },
        {
            "name": "./profile/master/client-prod.json",
            "source": {
                "user.password": "\"222\"",
                "user.username": "\"111\""
            }
        }
    ]
}

在客户端接收到propertySources数组后,相同的属性名,在数组位置靠后的会把靠前的覆盖掉。这里本地文件存储方式优先级高。

当我们把本地文件存储order改为3

config:
  server:
    profile:                                            # 本地文件配置项
      path: ./profile                                   # 默认应用配置文件存放路径
      order: 3                                          # 配置项加载顺序
    datasource:                                         # 数据库配置项
      type: opengauss                                   # 数据库类型(目前只支持OpenGauss数据库)
      url: 127.0.0.1:15400/config?sslmode=disable       # 数据库连接字符串
      username: root                                    # 数据库用户名
      password: root@123                                # 数据库密码
      order: 2                                          # 配置项加载顺序

响应体为

{
    "name": "client",
    "profiles": [
        "prod"
    ],
    "label": "master",
    "propertySources": [
        {
            "name": "./profile/master/client-prod.json",
            "source": {
                "user.password": "\"222\"",
                "user.username": "\"111\""
            }
        },
        {
            "name": "client-prod-master",
            "source": {
                "user.password": "666",
                "user.username": "333"
            }
        }
    ]
}
拉取配置可开启安全校验

开启安全校验配置如下

config:
  server:    
    security:                                           # 安全校验
      enable: true                                      # 是否开启安全校验
      username: admin                                   # 安全校验用户名
      password: admin                                   # 安全校验密码

服务端会从请求头中获取验证项,校验不通过如下。

配置项加解密

在配置文件中可配置是否开启配置项的加解密功能,目前支持3DES对称加密和RSA非对称加密。

通过GET请求/encrypt/status查看服务端加解密功能是否开启。

3DES对称加密
config:
  server:    
    encrypt:                                            # 配置文件加密
      enable: true                                      # 是否开启加密
      type: 3des                                        # 加密类型
      key: abcdefghijklmnopqrstuvwx                     # 3DES秘钥Key 必须24位

通过POST请求/encrypt,对body中的配置项进行加密,返回加密后的值。

配置项为加密内容时要加’{cipher}'前缀,这里以3des加密作为演示。

配置文件内容未加密前。

user.username=dev
user.password=123

调用/encrypt获取加密后的值,并手动修改配置文件内容。

user.username={cipher}78bc5ac10701cc67
user.password={cipher}58300a727be80ca3

请求服务端获取配置信息会自动解密。

RSA非对称加密
config:
  server:
    encrypt:                                                                      # 配置文件加密
      enable: true                                                                # 是否开启加密
      type: rsa                                                                   # 加密类型(现在仅支持3DES和RSA)
      keyStoreLocation: ./resources/config.cks                                    #密钥库文件路径
      keyStorepassword: '123456'                                                  #密钥库文件访问密码
      seedName: myEncryptorLocator                                                #自定义加解密bean名称
获取密钥库文件

通过请求服务端来生成密钥库文件。服务端提供get请求的/key-store端点生成只包含一个密钥对的密钥库文件,服务端配置文件中不需要添加keyStorepassword配置项。服务端也提供了自定义密钥库文件访问密码和密钥库密钥对别名端点,get请求/key-store/{alias}/{password},例如,要生成一个密钥库文件的访问密码是123456,包含三个密钥对,密钥对别名是key1、key2、key3,请求为/key-store/key1,key2,key3/123456。请求后以二进制流形式返回密钥库文件。

使用多个密钥对加解密

通过/key-store端点生成的默认密钥库文件来进行加解密和上诉3des相同。下面演示通过请求/key-store/{alias}/{password}获取的密钥库文件的使用方法。本地启动配置中心服务端,浏览器访问http://127.0.0.1:8081/key-store/key1,key2,key3/123456,浏览器会下载到一个config.cks文件。这里我们把config.cks放到服务端根目录resources文件夹下。配置示例如下

config.server.encrypt.keyStoreLocation = ./resources/config.cks							
config.server.encrypt.keyStorepassword = '123456'	

下面我们通过使用不同密钥对加解密配置项。

配置文件内容未加密前。

user.username=dev
user.password=123

调用/encrypt获取加密后的值,并手动修改配置文件内容。

使用密钥库中别名为key1的公钥加密user.username配置项值,请求体为{key:key1}dev。

加密后配置项值为{key:key1}383c6a5d91303b8ba41948c69cd0408c0cf5ff8477c4ec9a19c17c5713a421f8c812e3b2d73a4b89c0507d79d032de93b688277ec9993ef6e87989e9309f1546960f8fd30e160b2d3e393be48352663a25f28a9e20d4af81ad3ebda39d9febe30f2787fe8a37ba881ab1cc6b5125bbc5de2d0038f93d70260b8e3be06e5f2845

同样使用密钥库中别名为key2的公钥加密user.password配置项值,请求体为{key:key2}123。

手动修改配置文件内容

user.username={cipher}{key:key1}383c6a5d91303b8ba41948c69cd0408c0cf5ff8477c4ec9a19c17c5713a421f8c812e3b2d73a4b89c0507d79d032de93b688277ec9993ef6e87989e9309f1546960f8fd30e160b2d3e393be48352663a25f28a9e20d4af81ad3ebda39d9febe30f2787fe8a37ba881ab1cc6b5125bbc5de2d0038f93d70260b8e3be06e5f2845
user.password={cipher}{key:key2}78df3c7752cf8af4cdda0817ba292a71137f5bb112006b578d036c8445e15a4258fcc64c04ae8e5e1756b12130aca42b143d2785d435ae480f58946bd13b75bab3f60ab19d84f1e6bab9ef6d7502ce4fd7e071b4261168fac79b1cd87a33141e1e093c08bb4a72979e382d9c9558fd9821d9e6a865a23a69ad974fd8eecb06de

请求服务端获取配置信息会自动解密。

自定义加解密逻辑

你可以提供一个 TextEncryptorLocator 类型的 Bean来覆盖默认的加解密加载器,以提供自定义加解密逻辑。

类如我们创建TestEncryptorLocator加载器和TestEncryptor加解密,代码如下:

package config_server.encryption

@Component
public open class TestEncryptorLocator <: TextEncryptorLocator {
    public func locate(keys: Map<String, String>): TextEncryptor {
        return TestEncryptor()
    }
}

public open class TestEncryptor <: TextEncryptor {
    public func encrypt(text: String): String {
        return "myEncrypt"
    }
    public func decrypt(encryptedText: String): String {
        return "myDecrypt"
    }
}

服务端配置文件添加配置项,指定自定TextEncryptorLocator的名称

config.server.encrypt.seedName = testEncryptorLocator			

项目重新编译,运行后执行加解密如下:

获取配置文件纯文本格式

你的应用程序可能需要为其环境量身定制的通用纯文本配置文件,配置服务器通过/origin/{application}/{profiles}[/{label}]/{path}的额外端点提供这些文件,其中 application、profiles和labe的含义与常规环境端点相同,但path 是一个文件名的路径(如log.xml)。目前只支持本地和AWS S3存储配置文件方式可提供纯文本服务。存储的配置文件为yaml、json、properties格式支持配置信息解密。

get请求/text/{application}/{profiles}[/{label}]/{path}获取配置文件纯文本格式。

例如我们要读取client-dev.properties这个配置文件,文件内容为

user.username=111
user.password=222

get请求/origin/client/dev/master/client-dev.properties

获取配置文件二进制流格式

为了从配置服务器获取二进制文件,你将需要发送一个 application/octet-stream 的 Accept header。请求地址与获取配置文件纯文本格式的地址相同。

健康指标

配置服务器带有一个健康指示器,用于检查配置的 EnvironmentRepository 是否在工作。默认情况下,它要求 EnvironmentRepository 提供应用程序name、profile 和 label。

config:
  server:
    health:                                                                       # 配置中心健康检测
      down-health-status: testdown                                                # 自定义失败状态,默认DOWN
      repositories:                                                               # 检测项
        myservice:                                                                # 检测项服务名
          name: myservice                                                         # 检测项服务名,优先级高
          label: mylabel                                                          # 检测项label
          profiles: dev                                                           # 检测项profiles  	

get请求服务端/actuator/health,服务端会以name=myservice,label=mylabel,profiles=dev来定位获取配置信息,当获取配置信息过程中未出现异常,响应体如下:

{
    "status": "UP",
    "details": {
        "repositories": [
            {
                "name": "myservice",
                "profiles": [
                    "dev"
                ],
                "label": "mylabel",
                "sources": [
                    "mylabel:myservice-dev"
                ]
            }
        ],
        "repository": null,
        "error": null
    }
}

当获取配置信息过程中出现异常,例如配置信息存储在redis,但配置中心服务端无法与redis建立连接,响应体如下

{
    "status": "testdown",
    "details": {
        "repositories": null,
        "repository": {
            "application": "myservice",
            "profiles": "dev",
            "label": "mylabel"
        },
        "error": "Exception: Connection closed"
    }
}

鸿蒙全栈开发全新学习指南

总是有很多小伙伴反馈说:鸿蒙开发不知道学习哪些技术?不知道需要重点掌握哪些鸿蒙应用开发知识点? 为了解决大家这些学习烦恼。在这准备了一份很实用的鸿蒙(HarmonyOS NEXT)学习路线与学习文档给大家用来跟着学习。

针对一些列因素,整理了一套纯血版鸿蒙(HarmonyOS Next)全栈开发技术的学习路线,包含了鸿蒙开发必掌握的核心知识要点,内容有(ArkTS、ArkUI开发组件、Stage模型、多端部署、分布式应用开发、WebGL、元服务、OpenHarmony多媒体技术、Napi组件、OpenHarmony内核、OpenHarmony驱动开发、系统定制移植等等)鸿蒙(HarmonyOS NEXT)技术知识点。

本路线共分为四个阶段

第一阶段:鸿蒙初中级开发必备技能

请添加图片描述

第二阶段:鸿蒙南北双向高工技能基础:gitee.com/MNxiaona/733GH

请添加图片描述

第三阶段:应用开发中高级就业技术

在这里插入图片描述

第四阶段:全网首发-工业级南向设备开发就业技术:gitee.com/MNxiaona/733GH

《鸿蒙 (Harmony OS)开发学习手册》(共计892页)

如何快速入门?

1.基本概念
2.构建第一个ArkTS应用
3.……

在这里插入图片描述

开发基础知识:gitee.com/MNxiaona/733GH

1.应用基础知识
2.配置文件
3.应用数据管理
4.应用安全管理
5.应用隐私保护
6.三方应用调用管控机制
7.资源分类与访问
8.学习ArkTS语言
9.……

在这里插入图片描述

基于ArkTS 开发

1.Ability开发
2.UI开发
3.公共事件与通知
4.窗口管理
5.媒体
6.安全
7.网络与链接
8.电话服务
9.数据管理
10.后台任务(Background Task)管理
11.设备管理
12.设备使用信息统计
13.DFX
14.国际化开发
15.折叠屏系列
16.……

鸿蒙开发面试真题(含参考答案):gitee.com/MNxiaona/733GH

在这里插入图片描述

OpenHarmony 开发环境搭建

图片

《OpenHarmony源码解析》:gitee.com/MNxiaona/733GH

搭建开发环境
系统架构分析

  • 构建子系统
  • 启动流程
  • 子系统
  • 分布式任务调度子系统
  • 分布式通信子系统
  • 驱动子系统
  • ……
    图片

OpenHarmony 设备开发学习手册

图片

项目实战开发教学:gitee.com/MNxiaona/733GH

在这里插入图片描述

写在最后

如果你觉得这篇内容对你还蛮有帮助,我想邀请你帮我三个小忙

  • 点赞,转发,有你们的 『点赞和评论』,才是我创造的动力。
  • 关注小编,同时可以期待后续文章ing🚀,不定期分享原创知识。
  • 想要获取更多完整鸿蒙最新学习资源,请移步前往小编:gitee.com/MNxiaona/733GH

在这里插入图片描述

  • 8
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值