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内容请搜索www.887551.com以前的文章或继续浏览下面的相关文章希望大家以后多多支持www.887551.com!

(0)
上一篇 2022年3月21日
下一篇 2022年3月21日

相关推荐