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

113 阅读14分钟

介绍

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"
            }
        }
    ]
}

DD一下:欢迎大家关注工粽号<程序猿百晓生>,可以了解到以下知识点。

`欢迎大家关注工粽号<程序猿百晓生>,可以了解到以下知识点。`
1.OpenHarmony开发基础
2.OpenHarmony北向开发环境搭建
3.鸿蒙南向开发环境的搭建
4.鸿蒙生态应用开发白皮书V2.0 & V3.0
5.鸿蒙开发面试真题(含参考答案) 
6.TypeScript入门学习手册
7.OpenHarmony 经典面试题(含参考答案)
8.OpenHarmony设备开发入门【最新版】
9.沉浸式剖析OpenHarmony源代码
10.系统定制指南
11.【OpenHarmony】Uboot 驱动加载流程
12.OpenHarmony构建系统--GN与子系统、部件、模块详解
13.ohos开机init启动流程
14.鸿蒙版性能优化指南
.......

拉取配置可开启安全校验

开启安全校验配置如下

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"
    }
}