下面的实现方式比较死板,而且不优雅,还要主动的区分读写操作,使用主库还是从库,建议阅读新博客,SpringBoot对MongoDB副本集(读写分离)实现
1.上篇文章简单的介绍了Mongodb,和做了一个简单的SpringBoot - Mongodb单机实现,本文将介绍 SpringBoot mongodb replica set复制集读写分离的实现,主库:Primary , 从库:Secondary
复制主要用于备份、灾难恢复和读写分离,可以应对更多的读操作,当主库挂了可以从副本集中选一个主节点,更多作用请自行百度,怎么搭建复制集请参考我之前的 Mongodb安装与设置副本集 二 教程
2.spring boot 项目 Pom.xml中添加 spring-boot-starter-data-mongodb 包引用
org.springframework.boot
spring-boot-starter-data-mongodb
3.在application.properties中配置连接信息(使用yaml配置比较优雅,但我配置后未能实现连接)
spring.data.mongodb.primary.host=192.168.68.128
spring.data.mongodb.primary.port=27017
spring.data.mongodb.primary.database=test
spring.data.mongodb.primary.username=admin
spring.data.mongodb.primary.password=admin
spring.data.mongodb.secondary.host=192.168.68.129
spring.data.mongodb.secondary.port=27017
spring.data.mongodb.secondary.database=test
spring.data.mongodb.secondary.username=admin
spring.data.mongodb.secondary.password=admin
4.创建一个实体 User ,重写toString()方法,并在类上注解 @Document(collection = “user”) ,表名
@Document(collection = "user")
public class User {
private Long mid;
private String userName;
private String passWord;
@Override
public String toString() {
return "User{" +
"mid=" + mid +
", userName='" + userName + '\'' +
", passWord='" + passWord + '\'' +
'}';
}
//Getter Setter....
}
5.重点–创建一个抽象类 AbstractMongoConfig.class , 创建一个主库配置类 PrimaryMongoConfig.class 和一个从库配置类 SecondaryMongoConfig.class 继承抽象类,重写 getMongoTemplate() 方法,用户返回 MongoTemplate 对象
public abstract class AbstractMongoConfig {
// Mongo DB Properties
private String host, database, username, password;
private int port;
/*
* Method that creates MongoDbFactory Common to both of the MongoDb
* connections
*/
public MongoDbFactory mongoDbFactory() throws Exception {
String url = "mongodb://"+username+":"+password+"@"+host+":"+port+"/"+database;
MongoClientURI uri = new MongoClientURI(url);
return new SimpleMongoDbFactory(new MongoClient(uri),database);
}
/*
* Factory method to create the MongoTemplate
*/
abstract public MongoTemplate getMongoTemplate() throws Exception;
//host, database, username, password Getter Setter....
}
@Configuration
//在application.properties中查找 spring.data.mongodb.primary 开头的配置信息
@ConfigurationProperties(prefix = "spring.data.mongodb.primary")
public class PrimaryMongoConfig extends AbstractMongoConfig {
/**
* Implementation of the MongoTemplate factory method
* @Bean gives a name (primaryMongoTemplate) to the created MongoTemplate instance
* @Primary declares that if MongoTemplate is autowired without providing a specific name,
* this is the instance which will be mapped by default
*/
@Primary
@Override
public @Bean(name = "primaryMongoTemplate") MongoTemplate getMongoTemplate() throws Exception {
return new MongoTemplate(mongoDbFactory());
}
}
@Configuration
//在application.properties中查找 spring.data.mongodb.secondary开头的配置信息
@ConfigurationProperties(prefix = "spring.data.mongodb.secondary")
public class SecondaryMongoConfig extends AbstractMongoConfig {
/**
* Implementation of the MongoTemplate factory method
* @Bean gives a name (primaryMongoTemplate) to the created MongoTemplate instance
* Note that this method doesn't have @Primary
*/
@Override
public @Bean(name = "secondaryMongoTemplate") MongoTemplate getMongoTemplate() throws Exception {
return new MongoTemplate(mongoDbFactory());
}
}
6.UserDaoImpl类(省略了UserDao接口类),使用配置类,获取 MongoTemplate
@Repository
public class UserDaoImpl implements UserDao{
// Using MongoTemplate for primary database
@Autowired
@Qualifier(value = "primaryMongoTemplate") //和上面配置类中的Bean(name)的名字对应
protected MongoTemplate primaryMongoTemplate;
// Using mongoTemplate for secondary database
@Autowired
@Qualifier(value = "secondaryMongoTemplate")//和上面配置类中的Bean(name)的名字对应
protected MongoTemplate secondaryMongoTemplate;
//主库存
@Override
public void savePrimary(User user) {
primaryMongoTemplate.save(user);
}
//从库存
@Override
public void saveSecondary(User user) {
secondaryMongoTemplate.save(user);
}
//从库读
@Override
public User findUserByUserName(String userName) {
Query query = new Query();
query.addCriteria(Criteria.where("userName").is(userName));
return secondaryMongoTemplate.findOne(query,User.class);
}
}
7.测试,在测试类中运行如下代码:
//1.测试主库存
@Test
public void testSavePrimary() throws Exception {
User pUser = new User();
pUser.setMid(11L);
pUser.setUserName("primary");
pUser.setPassWord("123456");
userDao.savePrimary(pUser);
}
//2.测试从库存
@Test
public void testSaveSecondary() throws Exception {
User pSecond = new User();
pSecond.setMid(12L);
pSecond.setUserName("secondary");
pSecond.setPassWord("123456");
userDao.savePrimary(pSecond);
}
//3.从库读
@Test
public void testFindUser() throws Exception {
User pSecond = userDao.findUserByUserName("secondary");
System.out.println(pSecond.toString());
}
8.测试结果正确的保存和读取出来了,主库-从库保存的数据,从库读取的数据直接用toString()方法打印到控制台了:
实现读写分离还有另外一个方式:使用 lombok和spring-boot-autoconfigure 包,但我未实现出来,供大家参考和百度。
最后总结,此方式还不是太优雅,只能算是一个简单实现,遗留问题:要是有多个从库该怎么办,在配置文件中配置多个数据源?然后写多个从库配置类?调用的时候再写一个轮询方法,用来获取MongoTemplate?这些都是应该考虑的问题,现在的方式还不够优雅。。。有待研究与改进。希望那位大大指教指教