BAE公共MongoDB重连机制

Mon Jun 22 12:42:34 CST 2015 4228 Java

文章摘要BAE上的公共MongoDB数据库服务不支持长连接,闲置30s后会被断开。为解决这一问题,很多大牛在node.js,python等提出了不少解决方案,而在java语言中,却难找到方便的解决方法。

很多开发者为节约成本,便于维护,往往会使用SAE、BAE等云平台应用引擎部署自己的应用;目前,在数据库方面,SAE只提供MySQL数据库,而BAE提供的云数据库服务有MySQL、MongoDB、Redis等。BAE提供免费的云数据库服务,但不支持长连接,一个连接闲置30s便会断开;这样就对一些建立在长连接基础上的框架造成麻烦,譬如在关系型数据方面Hibernate就没有很大的用武之地了。网上也有很多大牛针对公共数据库不支持长连接提出了解决方案。


嗯,进入正题。一个我最近部署的应用,使用了公共的MongoDB数据库,开发的时候使用的mongodb-java-driver版本是2.13.0,结果部署到BAE上出现连接错误,后咨询技术客服,得知最佳的driver驱动版本是2.11.1.

同样地,因为使用的MongoDB是公共数据库,BAE不支持长连接,一个连接闲置30s会断开,所以在这点上我也遇到了问题,折腾了几个小时。


在说我的解决方案前,先了解一下mongodb-java-driver自带的重连机制。

mongodb-java-driver里的MongoClient内置连接池,API里建议一个JVM里最好只创建一个MongoClient实例。通常可以按照如下方法创建它:

MongoClientOptions.Builder build = new MongoClientOptions.Builder().cursorFinalizerEnabled(false);
		
//每个主机允许最大的连接数
build.connectionsPerHost(connectionsPerHost);
//每个线程等待连接的最长等待时间,不宜太短
build.maxWaitTime(maxWaitTime);
//当连接池全部在忙时,每个连接后面允许最大的等待线程数
build.threadsAllowedToBlockForConnectionMultiplier(maxWaitThead);
//连接超时时间
build.connectTimeout(connectTimeout);

build.cursorFinalizerEnabled(cursorFinalizerEnabled);
//是否自动重连
build.autoConnectRetry(autoConnectRetry);
		
		
//MongoClientOptions options = new MongoClientOptions.Builder().build();
MongoClientOptions options = build.build();
		
try {
	//创建线程池
	mongoClient = new MongoClient(
			new ServerAddress(serverName),
			Arrays.asList
					(
						MongoCredential.createMongoCRCredential(
								username, 
								dbname, 
								password.toCharArray()
					) 
			),
			options
	);
} catch (Exception e) {
	e.printStackTrace();
}


MongoClient创建后,可通过

DB mongoDB = mongoClient.getDB();

来获取一个数据库连接,我们不需担心MongoClient里内置的连接池的情况。

但MongoClient不会主动发现内置线程池的连接是否断开,它是被动发现的,比如当使用getDB()方法获得一个数据库对象后,用数据库对象进行文档操作,如果抛出了connection reset错误,mongoClient才知道连接池中有一条连接断开了,就会清空连接池里所有的连接,然后重新一次性创建事先设置好数目个连接。这将导致应用在BAE上部署后,会丢失个别请求数据,但因为mongoClient发现有连接断开后会自动重新创建连接,如果客户端短时间内刷新页面(或者重新提交请求),又能正常访问了。即使刷新(重新提交)可以正常访问,但这对于应用的健壮性来说,是不可容忍的。

我们可以这样来避免发生这样的情况。我们在调用DB mongoDB = mongoClient.getDB()后,使用mongoDB进行一次操作,主动出击,若连接被端口,则捕捉到异常后重新再getDB()一次,这样得到的DB对象连接是好的。先看代码

	public static DB getDB(){
		DB mongoDB = mongoClient.getDB(dbname);
		
		//能及时BAE公共mongoDB连接是否被断开,如果断开就会抛异常,这样就激活mongoClient的重连机制
		try{
			mongoDB.collectionExists("AAAA");	//这里的数据库名随意	
		} catch(Exception e) {
			mongoDB = mongoClient.getDB(dbname);
		}
		return mongoDB;
	
	}

修改代码后,重新部署到百度应用引擎BAE上,好了,应用正常了。

这种方法要点在于从mongoClient连接池中取得数据库连接时,主动去检测连接状态,及时发现连接断开与否,进而激发mongoClient的重连机制。


在这里抛砖引玉,希望有人有更好更方便的方法。



打赏
打赏

分享到: