背景描述
公司项目最近要做信创,要适配国产化操作系统以及数据库。选定操作系统为中标麒麟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环境,请确保是在以下版本环境中安装使用:
- 64 bit JDK 1.8+;
- Maven 3.2.x+;
- docker(如需打nacos镜像)。
拉取代码
git clone --branch 2.0.3 https://github.com/alibaba/nacos.git
初始化数据库
在nacos-distribution项目的conf下有MySQL的数据库初始化语句,需要在数据库里执行。本次使用kingbase8,需要在kingbase8中初始化数据库。有两种办法:
- 正常导入到MySql中,在迁移到kingbase中,迁移工具金仓官网有提供;
- 在金仓官网将Mysql语句转换成kingbase语句。
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
源码更改
添加驱动
-
根目录nacos-all的pom.xml中,在配置版本号,${kingbase.version},在下添加依赖:
<dependency> <groupId>com.kingbase</groupId> <artifactId>kingbase8</artifactId> <version>${kingbase.version}</version> </dependency> -
config项目下的pom.xml
<dependency> <groupId>com.kingbase</groupId> <artifactId>kingbase8</artifactId> </dependency> -
naming项目/pom.xml
<dependency> <groupId>com.kingbase</groupId> <artifactId>kingbase8</artifactId> </dependency>
kingbase支持代码
-
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"; } -
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; } } -
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); } } -
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编译中添加去除代码格式检查。
编译后的包如下:
在linux系统下可以使用startup.sh运行,运行要注意文件换行符是否是unix,使用vi编辑器:
set ff
set ff=unix
在windows系统下使用startup.cmd运行。
默认使用集群运行,可以在上述两个文件中,修改为standalone模式运行。
docker镜像运行
-
解压nacos-server-2.0.3.tar.gz,文件夹名修改为nacos。
-
修改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" -
编写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
如有问题可留言。