动态连接数据库的疑问

去年做的一个spring小项目,现在打算转为jfinal,看了下jfinal的sql模板和Template Engine,觉得可能会更适合我的这个项目。

在俱乐部群里看到 @杜福忠 有发过相关讨论,但还有些疑问,故发此帖,希望获得帮助,也许这样能帮助更多有次疑问的人。

项目情况:

本公司有两款分销软件,分布在不同服务器,本项目就是从不同服务器读取数据,然后呈现报表给相应的客户(一个客户对应一个数据库)。

目前项目建了一个maindb数据库,里面存放了客户各个数据库连接地址。本项目配置文件中配置了maindb的数据库地址。

客户登录时,先到maindb中获取相应的客户数据库连接地址,登录成功后,从相应的客户数据库中获取数据。

如下图:

11.png

本人JFinal动态连数据库主要Demo代码如下:

public class IndexController extends Controller {

    public void index() {
	String ztnum = "old"; //String ztnum = getPara("ztnum");
		
	// 根据JFinal configPlugin中配置的主数据库, 连接到主数据库maindb,然后获取客户数据库连接信息
	List<Record> recList = Db.find("select * from yzn_sys_zt where ztnum=? limit 0,1",ztnum);
		
	if (recList.size()>0) {
	    Record rec = recList.get(0); 
			
	    DruidPlugin dp = null;
	    ActiveRecordPlugin arp = null;
			
	    // 获取configname,如果抛空指针异常,则配置数据库连接池及ActiveRecord插件,否则可以直接使用configname进行数据查询
	    try {
	        DbKit.getConfig(ztnum).getName();
		
	    } catch (NullPointerException e) {
	        // 根据参数拼接数据库连接地址
    	        String dburl = "jdbc:sqlserver://"+rec.getStr("dbhost")+":"+rec.getStr("dbport")+";DatabaseName="+rec.getStr("dbname");				
    	        // 配置DruidPlugin数据库连接池
	        dp = new DruidPlugin(dburl, rec.getStr("dbusername"), rec.getStr("dbpassword"));
	        // 配置ActiveRecord数据库连接池插件
	        arp = new ActiveRecordPlugin(rec.getStr("ztnum"),dp);
				
	        dp.start();
	        arp.start();
	    }
			
	    List<Record> recList2 = Db.use(ztnum).find("select top 2 * from sys_user"); 			
	    for(Record r:recList2){
	        System.out.println("测试信息 查询客户数据库中数据信息:"+r.toJson());
	    }
			
	    // 这里是否需要stop,测试时发现不stop,数据库中会保留很多连接,如果不stop还会有什么问题?
	    // 而且网上说创建一个数据库连接池比较耗资源,这样的话是不是没必要 每次客户查询 就创建一次数据库连接池,这个利弊如何平衡?
	    // arp.stop(); 
	    // dp.stop();
			
	    render("index.html"); 
	    
	} else {
	    // 获取不到连接信息,跳转到错误页。
	    render("error.html"); 
	    
	}
		
    }
	
}


疑惑:

1、如上代码写法是否有会有问题? 或者有没有更好的方案?

2、关于stop,测试时发现不stop,数据库中会保留数据库连接,如果不stop还会有什么问题?

而且网上说创建一个数据库连接池比较耗资源,这样的话是不是没必要  每次客户查询  就创建一次数据库连接池?

这个利弊如何平衡?


@杜福忠 @JFinal

评论区

JFinal

2017-03-22 12:24

1:代码大致是可以的,但不够简洁,例如第七行的 find 可以改为 findFirst。更好的方案后面说
2:如果动态创建 ActiveRecordPlugin 与其依赖的 DuirdPlugin,那么都需要动态回收资源,需要调用 ActiveRecordPlugin 以及 DruidPlugin 的 stop() 方法

创建数据库连接池会有一定的延迟,所以通常是系统初始化的时候创建,下面给出新的方案:
1:在系统启动的时候,读出所有客户有关数据库的信息,一次性统一创建好 ActiveRecordPlugin
2:如果后期有新客户加入,那么在加入的同时就创建好 ActiveRecordPlugin
3:为了避免多个 ActiveRecordPlugin 在启动时就耗尽数据库允许的最大连接数,所以要需要控制 DruidPlugin 的初始连接个数
4:创建一个名为 ConfigNameInterceptor 的全局拦截器,里面放一个 ThreadLocal属性来存放当前请求客户的 configName,通过查询主数据库设定好合适的 configName
5:用户请求某个业务时,业务从 ConfigNameInterceptor 的 ThreadLocal 中获取 configName
6:代码中统一: Db.use(configName).find(...) ,这样就实现了业务层对所有客户都是完全一样代码的目标

liugz

2017-03-22 15:59

@JFinal 非常感谢詹总的热心的解答,您给的新方案非常好。

再请教下: 像我们公司有几百个客户,公司的服务器一台里面就有几十个客户的数据库,按照新方案,系统启动的时候就创建几百个ActiveRecordPlugin,对本程序性能等方面会有什么影响不? 另外对公司服务器会形成较大压力不?

帖子demo写的try catch 不stop方式,客户第一次访问稍慢点,但不会每次创建连接池;如果stop,则客户的每次操作都要创建下连接池,这个是我纠结的。

(之前这个项目的方式就是jdbc直接动态连的,没用连接池,运行将近1年没发现有什么问题。因为一般的一个客户数据库就1 2个人使用,只是客户量多点而已)

JFinal

2017-03-22 16:18

@liugz 几百个 ActiveRecordPlugin 估计就占内存 几K 而已,可以忽略不计,对性能毫无影响,对服务器的压力无从谈起

ActiveRecordPlugin 建好以后,不要去 stop() 它,让它一直运行着就好,根据具体的使用情况,调整好连接池的参数就好

liugz

2017-03-22 16:29

@JFinal 太赞了。 我就担心突然在公司某台数据库服务器上创建几十个数据库连接池会有影响。 看来我的担心是多余的了。

杜福忠

2017-03-22 20:53

我这儿有个业务和你的很相似, 也是程序都一样,只是数据库不同,域名不同.
业务程序代码基本都已修改完毕, 现正做项目控制中心(我们叫运营系统), 可以通过控制中心去动态部署项目, 动态增减数据库以及连接和域名,监控项目状态等等一系列操作, 会在项目小结后 抽出demo 分享给大家

liugz

2017-03-22 21:14

@杜福忠 那就太感谢了,在俱乐部群里有看过你们聊这方面。
之前项目都是dbutils直接动态连的, jfinal也是对jdbc做了简单封装的话,那就应该也是可以实现的。

杜福忠

2017-03-24 21:58

分享了一文章, 请多多支持: http://www.jfinal.com/share/236

pp

2017-04-23 07:27

这个分享不错,有收获!

热门反馈

扫码入社