nacos只支持mysql的原因分析

什么是Nacos

英文全称Dynamic Naming and Configuration Service,Na为naming/nameServer即注册中心,co为configuration即注册中心,service是指该注册/配置中心都是以服务为核心。服务在nacos是一等公民

没看源码之前,觉得很离谱,为啥只能限制数据库为mysql,按道理来说,nacos用了JdbcTemplate,可以适配很多数据库才是

最近看了nacos的源码,发现其中有很多硬编码,才明白原因

nacos的数据源获取都是通过com.alibaba.nacos.config.server.service.datasource.DynamicDataSource来获取的

在获取数据源时,根据配置判断你到底是使用内置的本地数据库还是外部的数据库(mysql)

public synchronized DataSourceService getDataSource() {
    try {

        // Embedded storage is used by default in stand-alone mode
        // In cluster mode, external databases are used by default
        // 根据System.getProperty("nacos.standalone")来判断你到底是不是standalone模式
        // standalone模式,使用内置数据库
        if (PropertyUtil.isEmbeddedStorage()) {
            if (localDataSourceService == null) {
                localDataSourceService = new LocalDataSourceServiceImpl();
                localDataSourceService.init();
            }
            return localDataSourceService;
        } else {
            // 如果不是standalone,直接创建外部的数据源
            if (basicDataSourceService == null) {
                basicDataSourceService = new ExternalDataSourceServiceImpl();
                basicDataSourceService.init();
            }
            return basicDataSourceService;
        }
    } catch (Exception e) {
        throw new RuntimeException(e);
    }
}

外部数据源com.alibaba.nacos.config.server.service.datasource.ExternalDataSourceServiceImpl.init()

@Override
public void init() {
    queryTimeout = ConvertUtils.toInt(System.getProperty("QUERYTIMEOUT"), 3);
    jt = new JdbcTemplate();
    // Set the maximum number of records to prevent memory expansion
    jt.setMaxRows(50000);
    jt.setQueryTimeout(queryTimeout);
    testMasterJT = new JdbcTemplate();
    testMasterJT.setQueryTimeout(queryTimeout);
    testMasterWritableJT = new JdbcTemplate();
    // Prevent the login interface from being too long because the main library is not available
    testMasterWritableJT.setQueryTimeout(1);
    //  Database health check
    testJtList = new ArrayList<JdbcTemplate>();
    isHealthList = new ArrayList<Boolean>();
    tm = new DataSourceTransactionManager();
    tjt = new TransactionTemplate(tm);
    // Transaction timeout needs to be distinguished from ordinary operations.
    tjt.setTimeout(TRANSACTION_QUERY_TIMEOUT);
    // 判断到底是是不是用外部数据库
    // 这个可以在com.alibaba.nacos.config.server.utils.PropertyUtil#loadSetting中看到
    // setUseExternalDB("mysql".equalsIgnoreCase(getString("spring.datasource.platform", "")));
    // 好家伙,直接判断配置的是不是mysql,是mysql那就是外部数据库,进行reload,不是,那就不管了
    if (PropertyUtil.isUseExternalDB()) {
        try {
            reload();
        } catch (IOException e) {
            e.printStackTrace();
            throw new RuntimeException(DB_LOAD_ERROR_MSG);
        }
        if (this.dataSourceList.size() > DB_MASTER_SELECT_THRESHOLD) {
            ConfigExecutor.scheduleConfigTask(new SelectMasterTask(), 10, 10, TimeUnit.SECONDS);
        }
        ConfigExecutor.scheduleConfigTask(new CheckDbHealthTask(), 10, 10, TimeUnit.SECONDS);
    }
}

在com.alibaba.nacos.config.server.service.datasource.ExternalDataSourceServiceImpl#reload中,我们可以看到

@Override
public synchronized void reload() throws IOException {
    try {
        // 根据配置文件,构建数据源集合
        dataSourceList = new ExternalDataSourceProperties()
            .build(EnvUtil.getEnvironment(), (dataSource) -> {
                JdbcTemplate jdbcTemplate = new JdbcTemplate();
                jdbcTemplate.setQueryTimeout(queryTimeout);
                jdbcTemplate.setDataSource(dataSource);
                testJtList.add(jdbcTemplate);
                isHealthList.add(Boolean.TRUE);
            });
        new SelectMasterTask().run();
        new CheckDbHealthTask().run();
    } catch (RuntimeException e) {
        FATAL_LOG.error(DB_LOAD_ERROR_MSG, e);
        throw new IOException(e);
    }
}

在com.alibaba.nacos.config.server.service.datasource.ExternalDataSourceProperties#build中

List<HikariDataSource> build(Environment environment, Callback<HikariDataSource> callback) {
    List<HikariDataSource> dataSources = new ArrayList<>();
    // 把胚子信息绑定到当前的ExternalDataSourceProperties对象,赋值操作
    // 因为外面是直接new出来的,需要对属性根据文件进行赋值
    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);
        // 拿到spring.datasource.xxx一堆,这个针对所有的数据源都适用
        DataSourcePoolProperties poolProperties = DataSourcePoolProperties.build(environment);
        // 为每一个数据源进行单独的url,user,password进行替换
        poolProperties.setDriverClassName(JDBC_DRIVER_NAME);
        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);
        dataSources.add(ds);
        callback.accept(ds);
    }
    Preconditions.checkArgument(CollectionUtils.isNotEmpty(dataSources), "no datasource available");
    return dataSources;
}

这个整体还行,但是为啥JDBC_DRIVER_NAME是硬编码呢,代码中清晰看到

private static final String JDBC_DRIVER_NAME = "com.mysql.cj.jdbc.Driver";

到这已经一目了然,代码中硬编码了mysql,driver也没法改,所以根本没法更换数据库驱动,有点骚,而且com.mysql.cj.jdbc.Driver是mysql8的驱动,对mysql版本是有要求的

再看其他部分,也可以发现大量的硬编码,例如com.alibaba.nacos.config.server.auth.ExternalUserPersistServiceImpl

public class ExternalUserPersistServiceImpl implements UserPersistService {

    @Autowired
    private ExternalStoragePersistServiceImpl persistService;

    private JdbcTemplate jt;

    @PostConstruct
    protected void init() {
        jt = persistService.getJdbcTemplate();
    }

    /**
     * Execute create user operation.
     *
     * @param username username string value.
     * @param password password string value.
     */
    public void createUser(String username, String password) {
        String sql = "INSERT into users (username, password, enabled) VALUES (?, ?, ?)";

        try {
            jt.update(sql, username, password, true);
        } catch (CannotGetJdbcConnectionException e) {
            LogUtil.FATAL_LOG.error("[db-error] " + e.toString(), e);
            throw e;
        }
    }

    /**
     * Execute delete user operation.
     *
     * @param username username string value.
     */
    public void deleteUser(String username) {
        String sql = "DELETE from users WHERE username=?";
        try {
            jt.update(sql, username);
        } catch (CannotGetJdbcConnectionException e) {
            LogUtil.FATAL_LOG.error("[db-error] " + e.toString(), e);
            throw e;
        }
    }

    /**
     * Execute update user password operation.
     *
     * @param username username string value.
     * @param password password string value.
     */
    public void updateUserPassword(String username, String password) {
        try {
            jt.update("UPDATE users SET password = ? WHERE username=?", password, username);
        } catch (CannotGetJdbcConnectionException e) {
            LogUtil.FATAL_LOG.error("[db-error] " + e.toString(), e);
            throw e;
        }
    }

    /**
     * Execute find user by username operation.
     *
     * @param username username string value.
     * @return User model.
     */
    public User findUserByUsername(String username) {
        String sql = "SELECT username,password FROM users WHERE username=? ";
        try {
            return this.jt.queryForObject(sql, new Object[] {username}, USER_ROW_MAPPER);
        } catch (CannotGetJdbcConnectionException e) {
            LogUtil.FATAL_LOG.error("[db-error] " + e.toString(), e);
            throw e;
        } catch (EmptyResultDataAccessException e) {
            return null;
        } catch (Exception e) {
            LogUtil.FATAL_LOG.error("[db-other-error]" + e.getMessage(), e);
            throw new RuntimeException(e);
        }
    }

    public Page<User> getUsers(int pageNo, int pageSize) {

        PaginationHelper<User> helper = persistService.createPaginationHelper();

        String sqlCountRows = "select count(*) from users where ";
        String sqlFetchRows = "select username,password from users where ";

        String where = " 1=1 ";

        try {
            Page<User> pageInfo = helper
                    .fetchPage(sqlCountRows + where, sqlFetchRows + where, new ArrayList<String>().toArray(), pageNo,
                            pageSize, USER_ROW_MAPPER);
            if (pageInfo == null) {
                pageInfo = new Page<>();
                pageInfo.setTotalCount(0);
                pageInfo.setPageItems(new ArrayList<>());
            }
            return pageInfo;
        } catch (CannotGetJdbcConnectionException e) {
            LogUtil.FATAL_LOG.error("[db-error] " + e.toString(), e);
            throw e;
        }
    }
    @Override
    public List<String> findUserLikeUsername(String username) {
        String sql = "SELECT username FROM users WHERE username like '%' ? '%'";
        List<String> users = this.jt.queryForList(sql, new String[]{username}, String.class);
        return users;
    }
}

几乎所有的sql都是硬编码....所以要改造成其他数据库工作量还是非常大的

到此这篇关于为什么nacos只支持mysql的文章就介绍到这了,更多相关nacos只支持mysql内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • nacos无法连接mysql的解决方法

    原因 nacos的pom依赖的mysql版本与mysql版本不一致导致 步骤1: 修改pom文件中mysql为对应版本依赖 <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.19</version> </dependency> 步骤2: building项目解决编译报错

  • Nacos配置MySQL8的方法

    1.MySQL创建数据库nacos_config 2.选中数据库nacos_config,执行Nacos自带的创建表脚本https://github.com/alibaba/nacos/blob/master/distribution/conf/nacos-mysql.sql或nacos安装目录下conf/nacos-mysql.sql 3.下载源码 https://github.com/alibaba/nacos/tree/1.2.0 4.修改根pom.xml 5.修改com.alibaba.

  • nacos只支持mysql的原因分析

    什么是Nacos 英文全称Dynamic Naming and Configuration Service,Na为naming/nameServer即注册中心,co为configuration即注册中心,service是指该注册/配置中心都是以服务为核心.服务在nacos是一等公民 没看源码之前,觉得很离谱,为啥只能限制数据库为mysql,按道理来说,nacos用了JdbcTemplate,可以适配很多数据库才是 最近看了nacos的源码,发现其中有很多硬编码,才明白原因 nacos的数据源获取

  • Vscode中不再支持JDK8的原因分析及解决方案

    昨天还用得好好的,今天突然给我说仅支持JDK11以上了,也不能进行正常的代码补全了. 看了一下,是Language Support for Java™ by Red Hat这个拓展更新到0.65.0的原因. 因为Eclipse平台决定将JDK11作为9月发布的最低要求,而vscode是依赖eclipsejdt.ls服务器的,所以需要更新到JDK11. 解决方案1 如果不是必须使用JDK8,可以选择更换为JDK11或更新 解决方案2 官方说可以在settings.json中配置java.confi

  • 总结12个MySQL慢查询的原因分析

    目录 1. SQL 没加索引 2. SQL 索引不生效 2.1 隐式的类型转换,索引失效 2.2 查询条件包含 or,可能导致索引失效 2.3. like 通配符可能导致索引失效 2.5 在索引列上使用 mysql 的内置函数 2.6 对索引进行列运算(如,+.-.*./), 索引不生效 2.7 索引字段上使用(!= 或者 < >),索引可能失效 2.8 索引字段上使用 is null, is not null,索引可能失效 2.9 左右连接,关联的字段编码格式不一样 2.10 优化器选错了索

  • MySQL产生死锁原因分析讲解

    目录 锁类型介绍 死锁产生原因和示例 1.产生原因 2.产生示例 案例一 案例二 案例三 案例四 案例五 案例六 锁类型介绍 MySQL 有三种锁的级别:页级.表级.行级 1 表级锁:开销小,加锁快:不会出现死锁:锁定粒度大,发生锁冲突的概率最高, 并发度最低. 2 行级锁:开销大,加锁慢:会出现死锁:锁定粒度最小,发生锁冲突的概率最低, 并发度也最高. 3 页面锁:开销和加锁时间界于表锁和行锁之间:会出现死锁:锁定粒度界于表锁和行锁之间,并发度一般. 死锁产生原因和示例 1.产生原因 所谓死锁

  • MySQL 出现错误1418 的原因分析及解决方法

    MySQL 出现错误1418 的原因分析及解决方法 具体错误: 使用mysql创建.调用存储过程,函数以及触发器的时候会有错误符号为1418错误. ERROR 1418 (HY000): This function has none of DETERMINISTIC, NO SQL,or READS SQL DATA in its declaration and binary logging is enabled(you *might* want to use the less safe log

  • MYSQL数据表损坏的原因分析和修复方法小结(推荐)

    1.表损坏的原因分析 以下原因是导致mysql 表毁坏的常见原因: 1. 服务器突然断电导致数据文件损坏. 2. 强制关机,没有先关闭mysql 服务. 3. mysqld 进程在写表时被杀掉. 4. 使用myisamchk 的同时,mysqld 也在操作表. 5. 磁盘故障. 6. 服务器死机. 7. mysql 本身的bug . 2.表损坏的症状 一个损坏的表的典型症状如下: 1 .当在从表中选择数据之时,你得到如下错误: Incorrect key file for table: '...

  • MySql范围查找时索引不生效问题的原因分析

    1 问题描述 本文对建立好的复合索引进行排序,并取记录中非索引字段,发现索引不生效,例如,有如下表,DDL语句为: CREATE TABLE `employees` ( `emp_no` int(11) NOT NULL, `birth_date` date NOT NULL, `first_name` varchar(14) NOT NULL, `last_name` varchar(16) NOT NULL, `gender` enum('M','F') NOT NULL, `hire_da

  • MySQL定时任务不能正常执行的原因分析及解决方法

    目录 前言 原因分析及解决方法 让定时任务快速执行 总结 前言 在使用数据库定时任务时,常常会出现定时任务不执行的问题,现对该问题出现的原因及解决方案做一些分析和整理. 原因分析及解决方法 当我们发现MySQL的定时任务没有执行时,首先去定时任务中查看[上次运行]时间是否正确,判断其是否正常执行,如果正常执行则需要考虑是否是事件逻辑不正确. 如果上次运行时间为空或者不正确,则需要查看数据库是否打开了定时任务,通过执行下列查询语句进行查询 show VARIABLES like '%event_s

  • mysql 无法联接常见故障及原因分析

    ===================================================================================================== 故障现象 : 无法连接 mysql 错误信息 :ERROR 2003 (HY000): Can't connect to MySQL server on 'hostxxxxx' (10061) 原因 : mysqld数据库服务没有启动. 检查 :在windows 的任务管理器,或者 unix/l

  • 浅谈MySQL索引优化分析

    为什么你写的sql查询慢?为什么你建的索引常失效?通过本章内容,你将学会MySQL性能下降的原因,索引的简介,索引创建的原则,explain命令的使用,以及explain输出字段的意义.助你了解索引,分析索引,使用索引,从而写出更高性能的sql语句.还在等啥子?撸起袖子就是干! 案例分析 我们先简单了解一下非关系型数据库和关系型数据库的区别. MongoDB是NoSQL中的一种.NoSQL的全称是Not only SQL,非关系型数据库.它的特点是性能高,扩张性强,模式灵活,在高并发场景表现得尤

随机推荐