nacos适配国产数据库-人大金仓

2,890 阅读9分钟

背景描述

公司项目最近要做信创,要适配国产化操作系统以及数据库。选定操作系统为中标麒麟V7,数据库为人大金仓(Kingbase8)V8。除了公司自己项目要需要做适配,项目中用到nacos也要做适配,nacos默认使用derby数据库,外部数据库可以使用MySQL,正式版只支持这两种数据库,feature_multiple_datasource_support分支支持oracle数据库不过很久没更新了。

本次教程基于人大金仓数据库,其他数据库实现原理一致,主要是修改数据库驱动以及不兼容的SQL语句。

此次使用的nacos版本为2.0.3,GitHub地址如下:

https://github.com/alibaba/nacos/tree/2.0.3

人大金仓官网如下,数据库安装、迁移工具、数据库连接驱动等可以在官网下载:

https://www.kingbase.com.cn/

2022年9月20日更新: 人大金仓数据库配置注意:字符串判空问题,如果下列设置为false,那么就会导致配置文件查询不到,sql中的''判断就会出问题,需要改成false,后重启数据库服务。

ora_input_emptystr_isnull = on

nacos中的查询语句,就会导致查不出来
SELECT count(*) FROM config_info WHERE data_id=? AND tenant_id=''

环境准备

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

  1. 64 bit JDK 1.8+;
  2. Maven 3.2.x+;
  3. docker(如需打nacos镜像)。

拉取代码

git clone --branch 2.0.3 https://github.com/alibaba/nacos.git

初始化数据库

在nacos-distribution项目的conf下有MySQL的数据库初始化语句,需要在数据库里执行。本次使用kingbase8,需要在kingbase8中初始化数据库。有两种办法:

  1. 正常导入到MySql中,在迁移到kingbase中,迁移工具金仓官网有提供;
  2. 在金仓官网将Mysql语句转换成kingbase语句。

image.png

kingbase的nacos初始化脚本如下:

CREATE TABLE "public"."config_info" (
    "id" int8 NOT NULL DEFAULT nextval('config_info_id_seq'::regclass),
    "data_id" varchar(255) NOT NULL,
    "group_id" varchar(255) NULL,
    "content" text NOT NULL,
    "md5" varchar(32) NULL,
    "gmt_create" timestamp(6) NOT NULL DEFAULT CURRENT_TIMESTAMP,
    "gmt_modified" timestamp(6) NOT NULL DEFAULT CURRENT_TIMESTAMP,
    "src_user" text NULL,
    "src_ip" varchar(50) NULL,
    "app_name" varchar(128) NULL,
    "tenant_id" varchar(128) NULL DEFAULT ''::varchar,
    "c_desc" varchar(256) NULL,
    "c_use" varchar(64) NULL,
    "effect" varchar(64) NULL,
    "type" varchar(64) NULL,
    "c_schema" text NULL,
    CONSTRAINT "config_info_PRIMARY" PRIMARY KEY ("id"),
    CONSTRAINT "config_info_uk_configinfo_datagrouptenant" UNIQUE ("data_id","group_id","tenant_id")
);
​
COMMENT ON COLUMN "public"."config_info"."tenant_id" IS '租户字段';
COMMENT ON COLUMN "public"."config_info"."src_ip" IS 'source ip';
COMMENT ON COLUMN "public"."config_info"."src_user" IS 'source user';
COMMENT ON COLUMN "public"."config_info"."gmt_modified" IS '修改时间';
COMMENT ON COLUMN "public"."config_info"."gmt_create" IS '创建时间';
COMMENT ON COLUMN "public"."config_info"."md5" IS 'md5';
COMMENT ON COLUMN "public"."config_info"."content" IS 'content';
COMMENT ON COLUMN "public"."config_info"."data_id" IS 'data_id';
COMMENT ON COLUMN "public"."config_info"."id" IS 'id';
COMMENT ON TABLE "public"."config_info" IS 'config_info';
​
CREATE TABLE "public"."config_info_aggr" (
    "id" int8 NOT NULL DEFAULT nextval('config_info_aggr_id_seq'::regclass),
    "data_id" varchar(255) NOT NULL,
    "group_id" varchar(255) NOT NULL,
    "datum_id" varchar(255) NOT NULL,
    "content" text NOT NULL,
    "gmt_modified" timestamp(6) NOT NULL,
    "app_name" varchar(128) NULL,
    "tenant_id" varchar(128) NULL DEFAULT ''::varchar,
    CONSTRAINT "config_info_aggr_PRIMARY" PRIMARY KEY ("id"),
    CONSTRAINT "config_info_aggr_uk_configinfoaggr_datagrouptenantdatum" UNIQUE ("data_id","group_id","tenant_id","datum_id")
);
​
COMMENT ON COLUMN "public"."config_info_aggr"."tenant_id" IS '租户字段';
COMMENT ON COLUMN "public"."config_info_aggr"."gmt_modified" IS '修改时间';
COMMENT ON COLUMN "public"."config_info_aggr"."content" IS '内容';
COMMENT ON COLUMN "public"."config_info_aggr"."datum_id" IS 'datum_id';
COMMENT ON COLUMN "public"."config_info_aggr"."group_id" IS 'group_id';
COMMENT ON COLUMN "public"."config_info_aggr"."data_id" IS 'data_id';
COMMENT ON COLUMN "public"."config_info_aggr"."id" IS 'id';
COMMENT ON TABLE "public"."config_info_aggr" IS '增加租户字段';
​
CREATE TABLE "public"."config_info_beta" (
    "id" int8 NOT NULL DEFAULT nextval('config_info_beta_id_seq'::regclass),
    "data_id" varchar(255) NOT NULL,
    "group_id" varchar(128) NOT NULL,
    "app_name" varchar(128) NULL,
    "content" text NOT NULL,
    "beta_ips" varchar(1024) NULL,
    "md5" varchar(32) NULL,
    "gmt_create" timestamp(6) NOT NULL DEFAULT CURRENT_TIMESTAMP,
    "gmt_modified" timestamp(6) NOT NULL DEFAULT CURRENT_TIMESTAMP,
    "src_user" text NULL,
    "src_ip" varchar(50) NULL,
    "tenant_id" varchar(128) NULL DEFAULT ''::varchar,
    CONSTRAINT "config_info_beta_PRIMARY" PRIMARY KEY ("id"),
    CONSTRAINT "config_info_beta_uk_configinfobeta_datagrouptenant" UNIQUE ("data_id","group_id","tenant_id")
);
​
COMMENT ON COLUMN "public"."config_info_beta"."tenant_id" IS '租户字段';
COMMENT ON COLUMN "public"."config_info_beta"."src_ip" IS 'source ip';
COMMENT ON COLUMN "public"."config_info_beta"."src_user" IS 'source user';
COMMENT ON COLUMN "public"."config_info_beta"."gmt_modified" IS '修改时间';
COMMENT ON COLUMN "public"."config_info_beta"."gmt_create" IS '创建时间';
COMMENT ON COLUMN "public"."config_info_beta"."md5" IS 'md5';
COMMENT ON COLUMN "public"."config_info_beta"."beta_ips" IS 'betaIps';
COMMENT ON COLUMN "public"."config_info_beta"."content" IS 'content';
COMMENT ON COLUMN "public"."config_info_beta"."app_name" IS 'app_name';
COMMENT ON COLUMN "public"."config_info_beta"."group_id" IS 'group_id';
COMMENT ON COLUMN "public"."config_info_beta"."data_id" IS 'data_id';
COMMENT ON COLUMN "public"."config_info_beta"."id" IS 'id';
COMMENT ON TABLE "public"."config_info_beta" IS 'config_info_beta';
​
CREATE TABLE "public"."config_info_tag" (
    "id" int8 NOT NULL DEFAULT nextval('config_info_tag_id_seq'::regclass),
    "data_id" varchar(255) NOT NULL,
    "group_id" varchar(128) NOT NULL,
    "tenant_id" varchar(128) NULL DEFAULT ''::varchar,
    "tag_id" varchar(128) NOT NULL,
    "app_name" varchar(128) NULL,
    "content" text NOT NULL,
    "md5" varchar(32) NULL,
    "gmt_create" timestamp(6) NOT NULL DEFAULT CURRENT_TIMESTAMP,
    "gmt_modified" timestamp(6) NOT NULL DEFAULT CURRENT_TIMESTAMP,
    "src_user" text NULL,
    "src_ip" varchar(50) NULL,
    CONSTRAINT "config_info_tag_PRIMARY" PRIMARY KEY ("id"),
    CONSTRAINT "config_info_tag_uk_configinfotag_datagrouptenanttag" UNIQUE ("data_id","group_id","tenant_id","tag_id")
);
COMMENT ON COLUMN "public"."config_info_tag"."src_ip" IS 'source ip';
COMMENT ON COLUMN "public"."config_info_tag"."src_user" IS 'source user';
COMMENT ON COLUMN "public"."config_info_tag"."gmt_modified" IS '修改时间';
COMMENT ON COLUMN "public"."config_info_tag"."gmt_create" IS '创建时间';
COMMENT ON COLUMN "public"."config_info_tag"."md5" IS 'md5';
COMMENT ON COLUMN "public"."config_info_tag"."content" IS 'content';
COMMENT ON COLUMN "public"."config_info_tag"."app_name" IS 'app_name';
COMMENT ON COLUMN "public"."config_info_tag"."tag_id" IS 'tag_id';
COMMENT ON COLUMN "public"."config_info_tag"."tenant_id" IS 'tenant_id';
COMMENT ON COLUMN "public"."config_info_tag"."group_id" IS 'group_id';
COMMENT ON COLUMN "public"."config_info_tag"."data_id" IS 'data_id';
COMMENT ON COLUMN "public"."config_info_tag"."id" IS 'id';
COMMENT ON TABLE "public"."config_info_tag" IS 'config_info_tag';
​
CREATE TABLE "public"."config_tags_relation" (
    "id" int8 NOT NULL,
    "tag_name" varchar(128) NOT NULL,
    "tag_type" varchar(64) NULL,
    "data_id" varchar(255) NOT NULL,
    "group_id" varchar(128) NOT NULL,
    "tenant_id" varchar(128) NULL DEFAULT ''::varchar,
    "nid" int8 NOT NULL DEFAULT nextval('config_tags_relation_nid_seq'::regclass),
    CONSTRAINT "config_tags_relation_PRIMARY" PRIMARY KEY ("nid"),
    CONSTRAINT "config_tags_relation_uk_configtagrelation_configidtag" UNIQUE ("id","tag_name","tag_type")
);
COMMENT ON COLUMN "public"."config_tags_relation"."tenant_id" IS 'tenant_id';
COMMENT ON COLUMN "public"."config_tags_relation"."group_id" IS 'group_id';
COMMENT ON COLUMN "public"."config_tags_relation"."data_id" IS 'data_id';
COMMENT ON COLUMN "public"."config_tags_relation"."tag_type" IS 'tag_type';
COMMENT ON COLUMN "public"."config_tags_relation"."tag_name" IS 'tag_name';
COMMENT ON COLUMN "public"."config_tags_relation"."id" IS 'id';
COMMENT ON TABLE "public"."config_tags_relation" IS 'config_tag_relation';
CREATE INDEX config_tags_relation_idx_tenant_id ON config_tags_relation USING btree (tenant_id);
​
CREATE TABLE "public"."group_capacity" (
    "id" int8 NOT NULL DEFAULT nextval('group_capacity_id_seq'::regclass),
    "group_id" varchar(128) NOT NULL DEFAULT ''::varchar,
    "quota" int8 NOT NULL DEFAULT 0,
    "usage" int8 NOT NULL DEFAULT 0,
    "max_size" int8 NOT NULL DEFAULT 0,
    "max_aggr_count" int8 NOT NULL DEFAULT 0,
    "max_aggr_size" int8 NOT NULL DEFAULT 0,
    "max_history_count" int8 NOT NULL DEFAULT 0,
    "gmt_create" timestamp(6) NOT NULL DEFAULT CURRENT_TIMESTAMP,
    "gmt_modified" timestamp(6) NOT NULL DEFAULT CURRENT_TIMESTAMP,
    CONSTRAINT "group_capacity_PRIMARY" PRIMARY KEY ("id"),
    CONSTRAINT "group_capacity_uk_group_id" UNIQUE ("group_id")
);
COMMENT ON COLUMN "public"."group_capacity"."gmt_modified" IS '修改时间';
COMMENT ON COLUMN "public"."group_capacity"."gmt_create" IS '创建时间';
COMMENT ON COLUMN "public"."group_capacity"."max_history_count" IS '最大变更历史数量';
COMMENT ON COLUMN "public"."group_capacity"."max_aggr_size" IS '单个聚合数据的子配置大小上限,单位为字节,0表示使用默认值';
COMMENT ON COLUMN "public"."group_capacity"."max_aggr_count" IS '聚合子配置最大个数,,0表示使用默认值';
COMMENT ON COLUMN "public"."group_capacity"."max_size" IS '单个配置大小上限,单位为字节,0表示使用默认值';
COMMENT ON COLUMN "public"."group_capacity"."usage" IS '使用量';
COMMENT ON COLUMN "public"."group_capacity"."quota" IS '配额,0表示使用默认值';
COMMENT ON COLUMN "public"."group_capacity"."group_id" IS 'Group ID,空字符表示整个集群';
COMMENT ON COLUMN "public"."group_capacity"."id" IS '主键ID';
COMMENT ON TABLE "public"."group_capacity" IS '集群、各Group容量信息表';
​
CREATE TABLE "public"."his_config_info" (
    "id" numeric NOT NULL,
    "nid" int8 NOT NULL DEFAULT nextval('his_config_info_nid_seq'::regclass),
    "data_id" varchar(255) NOT NULL,
    "group_id" varchar(128) NOT NULL,
    "app_name" varchar(128) NULL,
    "content" text NOT NULL,
    "md5" varchar(32) NULL,
    "gmt_create" timestamp(6) NOT NULL DEFAULT CURRENT_TIMESTAMP,
    "gmt_modified" timestamp(6) NOT NULL DEFAULT CURRENT_TIMESTAMP,
    "src_user" text NULL,
    "src_ip" varchar(50) NULL,
    "op_type" character(10) NULL,
    "tenant_id" varchar(128) NULL DEFAULT ''::varchar,
    CONSTRAINT "his_config_info_PRIMARY" PRIMARY KEY ("nid")
);
COMMENT ON COLUMN "public"."his_config_info"."tenant_id" IS '租户字段';
COMMENT ON COLUMN "public"."his_config_info"."app_name" IS 'app_name';
COMMENT ON TABLE "public"."his_config_info" IS '多租户改造';
CREATE INDEX his_config_info_idx_did ON his_config_info USING btree (data_id);
CREATE INDEX his_config_info_idx_gmt_create ON his_config_info USING btree (gmt_create);
CREATE INDEX his_config_info_idx_gmt_modified ON his_config_info USING btree (gmt_modified);
​
CREATE TABLE "public"."permissions" (
    "role" varchar(50) NOT NULL,
    "resource" varchar(255) NOT NULL,
    "action" varchar(8) NOT NULL,
    CONSTRAINT "permissions_uk_role_permission" UNIQUE ("role","resource","action")
);
​
CREATE TABLE "public"."roles" (
    "username" varchar(50) NOT NULL,
    "role" varchar(50) NOT NULL,
    CONSTRAINT "roles_idx_user_role" UNIQUE ("username","role")
);
​
CREATE TABLE "public"."tenant_capacity" (
    "id" int8 NOT NULL DEFAULT nextval('tenant_capacity_id_seq'::regclass),
    "tenant_id" varchar(128) NOT NULL DEFAULT ''::varchar,
    "quota" int8 NOT NULL DEFAULT 0,
    "usage" int8 NOT NULL DEFAULT 0,
    "max_size" int8 NOT NULL DEFAULT 0,
    "max_aggr_count" int8 NOT NULL DEFAULT 0,
    "max_aggr_size" int8 NOT NULL DEFAULT 0,
    "max_history_count" int8 NOT NULL DEFAULT 0,
    "gmt_create" timestamp(6) NOT NULL DEFAULT CURRENT_TIMESTAMP,
    "gmt_modified" timestamp(6) NOT NULL DEFAULT CURRENT_TIMESTAMP,
    CONSTRAINT "tenant_capacity_PRIMARY" PRIMARY KEY ("id"),
    CONSTRAINT "tenant_capacity_uk_tenant_id" UNIQUE ("tenant_id")
);
COMMENT ON COLUMN "public"."tenant_capacity"."gmt_modified" IS '修改时间';
COMMENT ON COLUMN "public"."tenant_capacity"."gmt_create" IS '创建时间';
COMMENT ON COLUMN "public"."tenant_capacity"."max_history_count" IS '最大变更历史数量';
COMMENT ON COLUMN "public"."tenant_capacity"."max_aggr_size" IS '单个聚合数据的子配置大小上限,单位为字节,0表示使用默认值';
COMMENT ON COLUMN "public"."tenant_capacity"."max_aggr_count" IS '聚合子配置最大个数';
COMMENT ON COLUMN "public"."tenant_capacity"."max_size" IS '单个配置大小上限,单位为字节,0表示使用默认值';
COMMENT ON COLUMN "public"."tenant_capacity"."usage" IS '使用量';
COMMENT ON COLUMN "public"."tenant_capacity"."quota" IS '配额,0表示使用默认值';
COMMENT ON COLUMN "public"."tenant_capacity"."tenant_id" IS 'Tenant ID';
COMMENT ON COLUMN "public"."tenant_capacity"."id" IS '主键ID';
COMMENT ON TABLE "public"."tenant_capacity" IS '租户容量信息表';
​
CREATE TABLE "public"."tenant_info" (
    "id" int8 NOT NULL DEFAULT nextval('tenant_info_id_seq'::regclass),
    "kp" varchar(128) NOT NULL,
    "tenant_id" varchar(128) NULL DEFAULT ''::varchar,
    "tenant_name" varchar(128) NULL DEFAULT ''::varchar,
    "tenant_desc" varchar(256) NULL,
    "create_source" varchar(32) NULL,
    "gmt_create" int8 NOT NULL,
    "gmt_modified" int8 NOT NULL,
    CONSTRAINT "tenant_info_PRIMARY" PRIMARY KEY ("id"),
    CONSTRAINT "tenant_info_uk_tenant_info_kptenantid" UNIQUE ("kp","tenant_id")
);
COMMENT ON COLUMN "public"."tenant_info"."gmt_modified" IS '修改时间';
COMMENT ON COLUMN "public"."tenant_info"."gmt_create" IS '创建时间';
COMMENT ON COLUMN "public"."tenant_info"."create_source" IS 'create_source';
COMMENT ON COLUMN "public"."tenant_info"."tenant_desc" IS 'tenant_desc';
COMMENT ON COLUMN "public"."tenant_info"."tenant_name" IS 'tenant_name';
COMMENT ON COLUMN "public"."tenant_info"."tenant_id" IS 'tenant_id';
COMMENT ON COLUMN "public"."tenant_info"."kp" IS 'kp';
COMMENT ON COLUMN "public"."tenant_info"."id" IS 'id';
COMMENT ON TABLE "public"."tenant_info" IS 'tenant_info';
CREATE INDEX tenant_info_idx_tenant_id ON tenant_info USING btree (tenant_id);
​
CREATE TABLE "public"."users" (
    "username" varchar(50) NOT NULL,
    "password" varchar(500) NOT NULL,
    "enabled" bool NOT NULL,
    CONSTRAINT "users_PRIMARY" PRIMARY KEY ("username")
);
​
INSERT INTO "public"."users" ("username","password","enabled") VALUES (
'nacos','$2a$10$EuWPZHzz32dJN7jexM34MOeYirDdFAZm2kuWj7VEOJhhZkDrxfvUu',true);
​

下载数据库驱动:

https://www.kingbase.com.cn/qd/index.htm

下载后数据库驱动为jar包需要引入到pom依赖中,可以直接添加到本地maven仓库中。

mvn install:install-file -DgroupId=com.kingbase -DartifactId=kingbase8 -Dversion=8.6.0 -Dpackaging=jar -Dfile=kingbase8-8.6.0.jar

源码更改

添加驱动

  1. 根目录nacos-all的pom.xml中,在配置版本号,${kingbase.version},在下添加依赖:

                <dependency>
                    <groupId>com.kingbase</groupId>
                    <artifactId>kingbase8</artifactId>
                    <version>${kingbase.version}</version>
                </dependency>
    
  2. config项目下的pom.xml

                <dependency>
                    <groupId>com.kingbase</groupId>
                    <artifactId>kingbase8</artifactId>
               </dependency>
    
  3. naming项目/pom.xml

                <dependency>
                    <groupId>com.kingbase</groupId>
                    <artifactId>kingbase8</artifactId>
               </dependency>
    

kingbase支持代码

  1. PropertiesConstant.java

    添加 public static final String KINGBASE = "kingbase";

    /*
     * Copyright 1999-2018 Alibaba Group Holding Ltd.
     *
     * Licensed under the Apache License, Version 2.0 (the "License");
     * you may not use this file except in compliance with the License.
     * You may obtain a copy of the License at
     *
     *      http://www.apache.org/licenses/LICENSE-2.0
     *
     * Unless required by applicable law or agreed to in writing, software
     * distributed under the License is distributed on an "AS IS" BASIS,
     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     * See the License for the specific language governing permissions and
     * limitations under the License.
     */
    ​
    package com.alibaba.nacos.config.server.constant;
    ​
    /**
     * PropertiesConstant.
     *
     * @author lixiaoshuang
     */
    public class PropertiesConstant {
    ​
        public static final String NOTIFY_CONNECT_TIMEOUT = "notifyConnectTimeout";
    ​
        public static final String NOTIFY_SOCKET_TIMEOUT = "notifySocketTimeout";
    ​
        public static final String IS_HEALTH_CHECK = "isHealthCheck";
    ​
        public static final String MAX_HEALTH_CHECK_FAIL_COUNT = "maxHealthCheckFailCount";
    ​
        public static final String MAX_CONTENT = "maxContent";
    ​
        public static final String IS_MANAGE_CAPACITY = "isManageCapacity";
    ​
        public static final String IS_CAPACITY_LIMIT_CHECK = "isCapacityLimitCheck";
    ​
        public static final String DEFAULT_CLUSTER_QUOTA = "defaultClusterQuota";
    ​
        public static final String DEFAULT_GROUP_QUOTA = "defaultGroupQuota";
    ​
        public static final String DEFAULT_TENANT_QUOTA = "defaultTenantQuota";
    ​
        public static final String DEFAULT_MAX_SIZE = "defaultMaxSize";
    ​
        public static final String DEFAULT_MAX_AGGR_COUNT = "defaultMaxAggrCount";
    ​
        public static final String DEFAULT_MAX_AGGR_SIZE = "defaultMaxAggrSize";
    ​
        public static final String CORRECT_USAGE_DELAY = "correctUsageDelay";
    ​
        public static final String INITIAL_EXPANSION_PERCENT = "initialExpansionPercent";
    ​
        public static final String SPRING_DATASOURCE_PLATFORM = "spring.datasource.platform";
    ​
        public static final String MYSQL = "mysql";
    ​
        public static final String KINGBASE = "kingbase";
    ​
        public static final String EMBEDDED_STORAGE = "embeddedStorage";
    ​
    }
    ​
    
  2. PropertyUtil.java#loadSetting

    修改:final String platform = getString(PropertiesConstant.SPRING_DATASOURCE_PLATFORM, ""); setUseExternalDB(PropertiesConstant.MYSQL.equalsIgnoreCase(platform) || PropertiesConstant.KINGBASE.equalsIgnoreCase(platform));

     private void loadSetting() {
            try {
                setNotifyConnectTimeout(Integer.parseInt(EnvUtil.getProperty(PropertiesConstant.NOTIFY_CONNECT_TIMEOUT,
                        String.valueOf(notifyConnectTimeout))));
                LOGGER.info("notifyConnectTimeout:{}", notifyConnectTimeout);
                setNotifySocketTimeout(Integer.parseInt(EnvUtil.getProperty(PropertiesConstant.NOTIFY_SOCKET_TIMEOUT,
                        String.valueOf(notifySocketTimeout))));
                LOGGER.info("notifySocketTimeout:{}", notifySocketTimeout);
                setHealthCheck(Boolean.parseBoolean(
                        EnvUtil.getProperty(PropertiesConstant.IS_HEALTH_CHECK, String.valueOf(isHealthCheck))));
                LOGGER.info("isHealthCheck:{}", isHealthCheck);
                setMaxHealthCheckFailCount(Integer.parseInt(
                        EnvUtil.getProperty(PropertiesConstant.MAX_HEALTH_CHECK_FAIL_COUNT,
                                String.valueOf(maxHealthCheckFailCount))));
                LOGGER.info("maxHealthCheckFailCount:{}", maxHealthCheckFailCount);
                setMaxContent(
                        Integer.parseInt(EnvUtil.getProperty(PropertiesConstant.MAX_CONTENT, String.valueOf(maxContent))));
                LOGGER.info("maxContent:{}", maxContent);
                // capacity management
                setManageCapacity(getBoolean(PropertiesConstant.IS_MANAGE_CAPACITY, isManageCapacity));
                setCapacityLimitCheck(getBoolean(PropertiesConstant.IS_CAPACITY_LIMIT_CHECK, isCapacityLimitCheck));
                setDefaultClusterQuota(getInt(PropertiesConstant.DEFAULT_CLUSTER_QUOTA, defaultClusterQuota));
                setDefaultGroupQuota(getInt(PropertiesConstant.DEFAULT_GROUP_QUOTA, defaultGroupQuota));
                setDefaultTenantQuota(getInt(PropertiesConstant.DEFAULT_TENANT_QUOTA, defaultTenantQuota));
                setDefaultMaxSize(getInt(PropertiesConstant.DEFAULT_MAX_SIZE, defaultMaxSize));
                setDefaultMaxAggrCount(getInt(PropertiesConstant.DEFAULT_MAX_AGGR_COUNT, defaultMaxAggrCount));
                setDefaultMaxAggrSize(getInt(PropertiesConstant.DEFAULT_MAX_AGGR_SIZE, defaultMaxAggrSize));
                setCorrectUsageDelay(getInt(PropertiesConstant.CORRECT_USAGE_DELAY, correctUsageDelay));
                setInitialExpansionPercent(getInt(PropertiesConstant.INITIAL_EXPANSION_PERCENT, initialExpansionPercent));
                // External data sources are used by default in cluster mode
                final String platform = getString(PropertiesConstant.SPRING_DATASOURCE_PLATFORM, "");
                setUseExternalDB(PropertiesConstant.MYSQL.equalsIgnoreCase(platform)
                        || PropertiesConstant.KINGBASE.equalsIgnoreCase(platform));
    ​
                // must initialize after setUseExternalDB
                // This value is true in stand-alone mode and false in cluster mode
                // If this value is set to true in cluster mode, nacos's distributed storage engine is turned on
                // default value is depend on ${nacos.standalone}
    ​
                if (isUseExternalDB()) {
                    setEmbeddedStorage(false);
                } else {
                    boolean embeddedStorage =
                            PropertyUtil.embeddedStorage || Boolean.getBoolean(PropertiesConstant.EMBEDDED_STORAGE);
                    setEmbeddedStorage(embeddedStorage);
    ​
                    // If the embedded data source storage is not turned on, it is automatically
                    // upgraded to the external data source storage, as before
                    if (!embeddedStorage) {
                        setUseExternalDB(true);
                    }
                }
            } catch (Exception e) {
                LOGGER.error("read application.properties failed", e);
                throw e;
            }
        }
    
  3. ExternalDataSourceProperties.java

    private static final String JDBC_DRIVER_NAME_KINGBASE = "com.kingbase8.Driver";

    String jdbcDriverName = JDBC_DRIVER_NAME_MYSQL; if (PropertiesConstant.KINGBASE.equalsIgnoreCase( EnvUtil.getProperty(PropertiesConstant.SPRING_DATASOURCE_PLATFORM))) { jdbcDriverName = JDBC_DRIVER_NAME_KINGBASE; }

    /*
     * Copyright 1999-2018 Alibaba Group Holding Ltd.
     *
     * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
     * the License. You may obtain a copy of the License at
     *
     * http://www.apache.org/licenses/LICENSE-2.0
     *
     * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
     * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
     * specific language governing permissions and limitations under the License.
     */package com.alibaba.nacos.config.server.service.datasource;
    ​
    import com.alibaba.nacos.config.server.constant.PropertiesConstant;
    import com.alibaba.nacos.sys.env.EnvUtil;
    import com.google.common.base.Preconditions;
    import com.zaxxer.hikari.HikariDataSource;
    import org.apache.commons.collections.CollectionUtils;
    import org.springframework.boot.context.properties.bind.Bindable;
    import org.springframework.boot.context.properties.bind.Binder;
    import org.springframework.core.env.Environment;
    ​
    import java.util.ArrayList;
    import java.util.List;
    import java.util.Objects;
    import java.util.concurrent.TimeUnit;
    ​
    import static com.alibaba.nacos.common.utils.CollectionUtils.getOrDefault;
    ​
    /**
     * Properties of external DataSource.
     *
     * @author Nacos
     */
    public class ExternalDataSourceProperties {
    ​
        private static final String JDBC_DRIVER_NAME_MYSQL = "com.mysql.cj.jdbc.Driver";
    ​
        private static final String JDBC_DRIVER_NAME_KINGBASE = "com.kingbase8.Driver";
    ​
        private static final String TEST_QUERY = "SELECT 1";
    ​
        private Integer num;
    ​
        private List<String> url = new ArrayList<>();
    ​
        private List<String> user = new ArrayList<>();
    ​
        private List<String> password = new ArrayList<>();
    ​
        public void setNum(Integer num) {
            this.num = num;
        }
    ​
        public void setUrl(List<String> url) {
            this.url = url;
        }
    ​
        public void setUser(List<String> user) {
            this.user = user;
        }
    ​
        public void setPassword(List<String> password) {
            this.password = password;
        }
    ​
        /**
         * Build serveral HikariDataSource.
         *
         * @param environment {@link Environment}
         * @param callback    Callback function when constructing data source
         * @return List of {@link HikariDataSource}
         */
        List<HikariDataSource> build(Environment environment, Callback<HikariDataSource> callback) {
            String jdbcDriverName = JDBC_DRIVER_NAME_MYSQL;
            if (PropertiesConstant.KINGBASE.equalsIgnoreCase(
                    EnvUtil.getProperty(PropertiesConstant.SPRING_DATASOURCE_PLATFORM))) {
                jdbcDriverName = JDBC_DRIVER_NAME_KINGBASE;
            }
            List<HikariDataSource> dataSources = new ArrayList<>();
            Binder.get(environment).bind("db", Bindable.ofInstance(this));
            Preconditions.checkArgument(Objects.nonNull(num), "db.num is null");
            Preconditions.checkArgument(CollectionUtils.isNotEmpty(user), "db.user or db.user.[index] is null");
            Preconditions.checkArgument(CollectionUtils.isNotEmpty(password), "db.password or db.password.[index] is null");
            for (int index = 0; index < num; index++) {
                int currentSize = index + 1;
                Preconditions.checkArgument(url.size() >= currentSize, "db.url.%s is null", index);
                DataSourcePoolProperties poolProperties = DataSourcePoolProperties.build(environment);
                poolProperties.setDriverClassName(jdbcDriverName);
                poolProperties.setJdbcUrl(url.get(index).trim());
                poolProperties.setUsername(getOrDefault(user, index, user.get(0)).trim());
                poolProperties.setPassword(getOrDefault(password, index, password.get(0)).trim());
                HikariDataSource ds = poolProperties.getDataSource();
                ds.setConnectionTestQuery(TEST_QUERY);
                ds.setIdleTimeout(TimeUnit.MINUTES.toMillis(10L));
                ds.setConnectionTimeout(TimeUnit.SECONDS.toMillis(3L));
                dataSources.add(ds);
                callback.accept(ds);
            }
            Preconditions.checkArgument(CollectionUtils.isNotEmpty(dataSources), "no datasource available");
            return dataSources;
        }
    ​
        interface Callback<D> {
    ​
            /**
             * Perform custom logic.
             *
             * @param datasource dataSource.
             */
            void accept(D datasource);
        }
    }
    ​
    
  4. StartingApplicationListener.java

    private static final String DATABASE_KINGBASE = "kingbase"; final String platform = env.getProperty(DATASOURCE_PLATFORM_PROPERTY, DEFAULT_DATASOURCE_PLATFORM); boolean useExternalStorage = (DATABASE_MYSQL.equalsIgnoreCase(platform) || DATABASE_KINGBASE.equalsIgnoreCase(platform));

    /*
     * Copyright 1999-2018 Alibaba Group Holding Ltd.
     *
     * Licensed under the Apache License, Version 2.0 (the "License");
     * you may not use this file except in compliance with the License.
     * You may obtain a copy of the License at
     *
     *      http://www.apache.org/licenses/LICENSE-2.0
     *
     * Unless required by applicable law or agreed to in writing, software
     * distributed under the License is distributed on an "AS IS" BASIS,
     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     * See the License for the specific language governing permissions and
     * limitations under the License.
     */package com.alibaba.nacos.core.listener;
    ​
    import com.alibaba.nacos.api.exception.NacosException;
    import com.alibaba.nacos.api.exception.runtime.NacosRuntimeException;
    import com.alibaba.nacos.common.executor.ExecutorFactory;
    import com.alibaba.nacos.common.executor.NameThreadFactory;
    import com.alibaba.nacos.common.executor.ThreadPoolManager;
    import com.alibaba.nacos.common.notify.NotifyCenter;
    import com.alibaba.nacos.common.event.ServerConfigChangeEvent;
    import com.alibaba.nacos.sys.env.EnvUtil;
    import com.alibaba.nacos.sys.file.FileChangeEvent;
    import com.alibaba.nacos.sys.file.FileWatcher;
    import com.alibaba.nacos.sys.file.WatchFileCenter;
    import com.alibaba.nacos.sys.utils.ApplicationUtils;
    import com.alibaba.nacos.sys.utils.DiskUtils;
    import com.alibaba.nacos.sys.utils.InetUtils;
    import com.alibaba.nacos.common.utils.StringUtils;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.boot.env.OriginTrackedMapPropertySource;
    import org.springframework.context.ConfigurableApplicationContext;
    import org.springframework.core.env.ConfigurableEnvironment;
    ​
    import java.io.File;
    import java.io.IOException;
    import java.nio.file.Paths;
    import java.util.List;
    import java.util.Map;
    import java.util.concurrent.ConcurrentHashMap;
    import java.util.concurrent.ScheduledExecutorService;
    import java.util.concurrent.TimeUnit;
    ​
    /**
     * init environment config.
     *
     * @author <a href="mailto:huangxiaoyu1018@gmail.com">hxy1991</a>
     * @since 0.5.0
     */
    public class StartingApplicationListener implements NacosApplicationListener {
    ​
        private static final Logger LOGGER = LoggerFactory.getLogger(StartingApplicationListener.class);
    ​
        private static final String MODE_PROPERTY_KEY_STAND_MODE = "nacos.mode";
    ​
        private static final String MODE_PROPERTY_KEY_FUNCTION_MODE = "nacos.function.mode";
    ​
        private static final String LOCAL_IP_PROPERTY_KEY = "nacos.local.ip";
    ​
        private static final String NACOS_APPLICATION_CONF = "nacos_application_conf";
    ​
        private static final String NACOS_MODE_STAND_ALONE = "stand alone";
    ​
        private static final String NACOS_MODE_CLUSTER = "cluster";
    ​
        private static final String DEFAULT_FUNCTION_MODE = "All";
    ​
        private static final String DATABASE_MYSQL = "mysql";
    ​
        private static final String DATABASE_KINGBASE = "kingbase";
    ​
        private static final String DATASOURCE_PLATFORM_PROPERTY = "spring.datasource.platform";
    ​
        private static final String DEFAULT_DATASOURCE_PLATFORM = "";
    ​
        private static final String DATASOURCE_MODE_EXTERNAL = "external";
    ​
        private static final String DATASOURCE_MODE_EMBEDDED = "embedded";
    ​
        private static final Map<String, Object> SOURCES = new ConcurrentHashMap<>();
    ​
        private ScheduledExecutorService scheduledExecutorService;
    ​
        private volatile boolean starting;
    ​
        @Override
        public void starting() {
            starting = true;
        }
    ​
        @Override
        public void environmentPrepared(ConfigurableEnvironment environment) {
            makeWorkDir();
    ​
            injectEnvironment(environment);
    ​
            loadPreProperties(environment);
    ​
            initSystemProperty();
        }
    ​
        @Override
        public void contextPrepared(ConfigurableApplicationContext context) {
            logClusterConf();
    ​
            logStarting();
        }
    ​
        @Override
        public void contextLoaded(ConfigurableApplicationContext context) {
    ​
        }
    ​
        @Override
        public void started(ConfigurableApplicationContext context) {
            starting = false;
    ​
            closeExecutor();
    ​
            ApplicationUtils.setStarted(true);
            judgeStorageMode(context.getEnvironment());
        }
    ​
        @Override
        public void running(ConfigurableApplicationContext context) {
        }
    ​
        @Override
        public void failed(ConfigurableApplicationContext context, Throwable exception) {
            starting = false;
    ​
            makeWorkDir();
    ​
            LOGGER.error("Startup errors : ", exception);
            ThreadPoolManager.shutdown();
            WatchFileCenter.shutdown();
            NotifyCenter.shutdown();
    ​
            closeExecutor();
    ​
            context.close();
    ​
            LOGGER.error("Nacos failed to start, please see {} for more details.",
                    Paths.get(EnvUtil.getNacosHome(), "logs/nacos.log"));
        }
    ​
        private void injectEnvironment(ConfigurableEnvironment environment) {
            EnvUtil.setEnvironment(environment);
        }
    ​
        private void loadPreProperties(ConfigurableEnvironment environment) {
            try {
                SOURCES.putAll(EnvUtil.loadProperties(EnvUtil.getApplicationConfFileResource()));
                environment.getPropertySources()
                        .addLast(new OriginTrackedMapPropertySource(NACOS_APPLICATION_CONF, SOURCES));
                registerWatcher();
            } catch (Exception e) {
                throw new NacosRuntimeException(NacosException.SERVER_ERROR, e);
            }
        }
    ​
        private void registerWatcher() throws NacosException {
    ​
            WatchFileCenter.registerWatcher(EnvUtil.getConfPath(), new FileWatcher() {
                @Override
                public void onChange(FileChangeEvent event) {
                    try {
                        Map<String, ?> tmp = EnvUtil.loadProperties(EnvUtil.getApplicationConfFileResource());
                        SOURCES.putAll(tmp);
                        NotifyCenter.publishEvent(ServerConfigChangeEvent.newEvent());
                    } catch (IOException ignore) {
                        LOGGER.warn("Failed to monitor file ", ignore);
                    }
                }
    ​
                @Override
                public boolean interest(String context) {
                    return StringUtils.contains(context, "application.properties");
                }
            });
    ​
        }
    ​
        private void initSystemProperty() {
            if (EnvUtil.getStandaloneMode()) {
                System.setProperty(MODE_PROPERTY_KEY_STAND_MODE, NACOS_MODE_STAND_ALONE);
            } else {
                System.setProperty(MODE_PROPERTY_KEY_STAND_MODE, NACOS_MODE_CLUSTER);
            }
            if (EnvUtil.getFunctionMode() == null) {
                System.setProperty(MODE_PROPERTY_KEY_FUNCTION_MODE, DEFAULT_FUNCTION_MODE);
            } else if (EnvUtil.FUNCTION_MODE_CONFIG.equals(EnvUtil.getFunctionMode())) {
                System.setProperty(MODE_PROPERTY_KEY_FUNCTION_MODE, EnvUtil.FUNCTION_MODE_CONFIG);
            } else if (EnvUtil.FUNCTION_MODE_NAMING.equals(EnvUtil.getFunctionMode())) {
                System.setProperty(MODE_PROPERTY_KEY_FUNCTION_MODE, EnvUtil.FUNCTION_MODE_NAMING);
            }
    ​
            System.setProperty(LOCAL_IP_PROPERTY_KEY, InetUtils.getSelfIP());
        }
    ​
        private void logClusterConf() {
            if (!EnvUtil.getStandaloneMode()) {
                try {
                    List<String> clusterConf = EnvUtil.readClusterConf();
                    LOGGER.info("The server IP list of Nacos is {}", clusterConf);
                } catch (IOException e) {
                    LOGGER.error("read cluster conf fail", e);
                }
            }
        }
    ​
        private void closeExecutor() {
            if (scheduledExecutorService != null) {
                scheduledExecutorService.shutdownNow();
            }
        }
    ​
        private void makeWorkDir() {
            String[] dirNames = new String[] {"logs", "conf", "data"};
            for (String dirName : dirNames) {
                LOGGER.info("Nacos Log files: {}", Paths.get(EnvUtil.getNacosHome(), dirName).toString());
                try {
                    DiskUtils.forceMkdir(new File(Paths.get(EnvUtil.getNacosHome(), dirName).toUri()));
                } catch (Exception e) {
                    throw new RuntimeException(e);
                }
            }
        }
    ​
        private void logStarting() {
            if (!EnvUtil.getStandaloneMode()) {
    ​
                scheduledExecutorService = ExecutorFactory
                        .newSingleScheduledExecutorService(new NameThreadFactory("com.alibaba.nacos.core.nacos-starting"));
    ​
                scheduledExecutorService.scheduleWithFixedDelay(() -> {
                    if (starting) {
                        LOGGER.info("Nacos is starting...");
                    }
                }, 1, 1, TimeUnit.SECONDS);
            }
        }
    ​
        private void judgeStorageMode(ConfigurableEnvironment env) {
    ​
            // External data sources are used by default in cluster mode
            final String platform = env.getProperty(DATASOURCE_PLATFORM_PROPERTY, DEFAULT_DATASOURCE_PLATFORM);
            boolean useExternalStorage = (DATABASE_MYSQL.equalsIgnoreCase(platform)
                    || DATABASE_KINGBASE.equalsIgnoreCase(platform));
    ​
            // must initialize after setUseExternalDB
            // This value is true in stand-alone mode and false in cluster mode
            // If this value is set to true in cluster mode, nacos's distributed storage engine is turned on
            // default value is depend on ${nacos.standalone}
    ​
            if (!useExternalStorage) {
                boolean embeddedStorage = EnvUtil.getStandaloneMode() || Boolean.getBoolean("embeddedStorage");
                // If the embedded data source storage is not turned on, it is automatically
                // upgraded to the external data source storage, as before
                if (!embeddedStorage) {
                    useExternalStorage = true;
                }
            }
    ​
            LOGGER.info("Nacos started successfully in {} mode. use {} storage",
                    System.getProperty(MODE_PROPERTY_KEY_STAND_MODE), useExternalStorage ? DATASOURCE_MODE_EXTERNAL : DATASOURCE_MODE_EMBEDDED);
        }
    }
    

兼容kingbase

1.主键
# 全局替换:
Statement.RETURN_GENERATED_KEYS 替换为 new String[]{"id"}
2.LIKE

com.alibaba.nacos.config.server.auth.ExternalRolePersistServiceImpl#findRolesLikeRoleName

com.alibaba.nacos.config.server.auth.ExternalUserPersistServiceImpl#findUserLikeUsername

原有

 String sql = "SELECT role FROM roles WHERE role LIKE '%' ? '%'";
        List<String> users = this.jt.queryForList(sql, new String[] {role}, String.class);

替换后

#如上图类似替换
LIKE '%' ? '%' 替换成 LIKE ?
new String[] {username} 替换成 new String[] {String.format("%%%s%%", username)}
​
# LIKE '%' '%AD' '%'的方式,mysql可用,但并非标准sql
# LIKE '%%AD%'的方式,是标准sql
3.LIMIT

全局替换

LIMIT ?,?     替换为 OFFSET ? LIMIT ?

这个方法这块需要手动改修改一下

com.alibaba.nacos.config.server.service.repository.extrnal.ExternalStoragePaginationHelperImpl#fetchPage(java.lang.String, java.lang.String, java.lang.Object[], int, int, java.lang.Long, org.springframework.jdbc.core.RowMapper)

String selectSql = "";
        if (isDerby()) {
            selectSql = sqlFetchRows + " OFFSET " + startRow + " ROWS FETCH NEXT " + pageSize + " ROWS ONLY";
        } else if (lastMaxId != null) {
            selectSql = sqlFetchRows + " AND id > " + lastMaxId + " ORDER BY id ASC" + " LIMIT " + pageSize + " OFFSET " + 0;
        } else {
            selectSql = sqlFetchRows + " LIMIT " + pageSize + " OFFSET " + startRow;
        }

4.Bug处理

# 1. ExternalStoragePersistServiceImpl#findAllConfigInfoForDumpAll
params.toArray() 替换为 new Object[] {(pageNo - 1) * pageSize, pageSize}
​
# 2. EmbeddedStoragePersistServiceImpl#findAllConfigInfoForDumpAll
EMPTY_ARRAY 替换为 new Object[] {(pageNo - 1) * pageSize, pageSize}

以上参考;blog.csdn.net/dph5199278/…

配置文件修改

添加kingbase数据库驱动

console/src/main/resources/application.properties

spring.datasource.platform=kingbase
db.num=1
db.jdbcDriverName= com.kingbase8.Driver
## Connect URL of DB:
db.url.0=jdbc:kingbase8://111.111.111.913:54321/nacos_config
db.user.0=system
db.password.0=system

编译打包

官网命令如下:

mvn -Prelease-nacos -Dmaven.test.skip=true clean install -U

上诉命令可能报错,多数原因就是因为修改代码代码格式不规范造成的,可以在mvn编译中添加去除代码格式检查。

编译后的包如下:

image.png

在linux系统下可以使用startup.sh运行,运行要注意文件换行符是否是unix,使用vi编辑器:

set ff
set ff=unix

在windows系统下使用startup.cmd运行。

默认使用集群运行,可以在上述两个文件中,修改为standalone模式运行。

docker镜像运行

  1. 解压nacos-server-2.0.3.tar.gz,文件夹名修改为nacos。

  2. 修改startup.sh,121行后添加启动时数据库连接参数

    JAVA_OPT="${JAVA_OPT} -Dspring.datasource.platform=kingbase -Ddb.num=1 -Ddb.url.0=$KINGBASE_URL -Ddb.jdbcDriverName=com.kingbase8.Driver -Ddb.user.0=$KINGBASE_USERNAME -Ddb.password.0=$KINGBASE_PASSWORD"
    

    根据需求也可以修改启动是单机启动还是集群启动,在55行

    export MODE="standalone"
    
  3. 编写DockFile

FROM java:8
​
#指定工作目录
WORKDIR /nacos
​
#拷贝文件
COPY ./nacos $WORKDIR/nacos
​
#检查是否拷贝成功
RUN sh -c 'ls $WORKDIR/nacos'#设置环境变量
ENV JAVA_OPTS=$JAVA_OPTS#设置Kingbase 环境变量 在启动springBoot时可直接使用如:-Dserver.port=$PORT
ENV KINGBASE_URL=$KINGBASE_URL
ENV KINGBASE_USERNAME=$MKINGBASE_USERNAME
ENV KINGBASE_PASSWORD=$KINGBASE_PASSWORD#添加执行目录
RUN chmod +x $WORKDIR/nacos/bin/startup.sh
​
#运行 nacos是通过 startup.sh 允许的 如果是普通的jar包我们可以替换为 ["nohup","java","-jar","-Dserver.port=${port} -Dspring.profiles.active=${spring_env} /data/app.jar","&"]
ENTRYPOINT [ "bash", "-c", "$WORKDIR/nacos/bin/startup.sh && tail -f $WORKDIR/nacos/logs/start.out" ]
​

启动命令

docker  run -p 18848:8848 --name nacos-kingbase  -e JAVA_OPTS="-Xmx128m -Xss512k" -e KINGBASE_URL="jdbc:kingbase8://191.191.191.193:54321/nacos_config" -e KINGBASE_USERNAME="system" -e KINGBASE_PASSWORD="11111" nacos


如有问题可留言。