MongoDB副本集使用
MongoDB副本集使用
前言
mongodb副本集是个好东西,本文主要说明mongodb副本集的原理、功能以及如何与后端程序对接。
概述
为了适应越来越多的访问量,单台服务器已经不太顶了,即使这台服务器的性能很强。因此,就出现了集群的概念。所谓集群,说的是有好几个相同功能的服务器,一起提供服务,通过前端的负载均衡服务器或者是后端程序的操作,将访问量分流,压力分发之后,每台服务器都能平稳工作。更重要的是,当其中一台服务器崩溃之后,另外的服务器能正常提供服务,不至于整个业务崩溃。
在mongodb中,也有解决类似问题的方案——副本集
所谓mongodb副本集,指的是N个mongodb服务器工作在同一个组的内时该组的叫法,与上图的集群有点不一样的是,mongodb副本集里的节点有主从之分,虽然都是对外提供服务,但是主节点能提供读写功能,而从节点只能提供读的功能。
你可能会注意到,primary和secondary节点也有talk,并且你可能会有疑问,主从节点是按照什么来指定的?这里就涉及到了mongo副本集的基本算法——Bully算法,这里读者可以自行百度,就不过分赘述,简单说一下过程(参考raft算法):
最初的时候,大家都是从节点,但是突然有一天,其中有个节点发起了选举,鉴于友谊,每个节点都必须选择赞成。此时发起投票的节点就检查票数,当它发现赞成大于1/2时,它就升级为主节点并且告诉定时告诉其它节点,我是boss,你们不能选举,都听我的。为啥票数不是100%,因为可能由于网络故障等种种问题,主节点没收到某些票。当票数小于1/2时,那么大家就当无事发生,等待下一个节点提出选举。
raft算法
此时,对主节点的操作,主节点会及时同步到从节点(参考mysql的两阶段提交),这样就实现了多服务器的数据一致了。
总的来说,集群还是副本集都是为了提高服务器的可用性
说了这么多的原理,那么接下来讲讲当有了一个副本集之后,我们的项目去使用它。
对接流程
准备
系统环境:ubuntu 16.04
可用节点:192.168.1.100:8003,192.168.2.101:8004
仲裁者:192.168.1.100:8005
后端环境:spring-framework,IDEA
mongodb搭建:mongodb副本集搭建
需求
当一个项目需要接入mongodb服务器时,需要考虑下列问题
(1)后端程序如何连接mongodb
(2)后端程序与mongodb的传输的数据格式
(3)后端如何接入mongodb集群
单节点连接
对于第一个问题,spring框架已经为我们提供了方法,只需通过配置application.yml文件,将需要连接的mongodb服务器节点加入即可。
参考配置如下
server:
# 通用配置
port: 9000
servlet:
context-path: /mongo
spring:
profiles:
#执行模块
active: mongo
jackson:
time-zone: GMT+8
date-format: yyyy-MM-dd HH:mm:ss
---
# mongoDB模块
spring:
profiles: mongo
data:
mongodb:
# 未设置账号密码
uri: mongodb://192.168.1.100:8003/Test
# 有账号密码
uri: mongodb://user:aaa@qq.com:8003/Test
(2)对于第二个问题,spring中也有具体的类去实现对mongodb数据库的增删改查操作,我写了三个层来做测试
实现如图
1)controller层与前端交互
public class MongoController {
@Autowired
private MongoService mongoService;
@PostMapping
public void setUser(@RequestBody UserPo user) {
mongoService.addUser(user);
}
@GetMapping
public Object getUser() {
return mongoService.getUser();
}
}
2)service层
@Service
public interface MongoService {
/**
* 添加用户
* @param user
*/
void addUser(UserPo user);
/**
* 查看所有用户
* @return
*/
Object getUser();
}
3)impl层具体实现service及与数据库交互
@Service
@Slf4j
public class MongoServiceImpl implements MongoService {
@Autowired
private MongoTemplate mongoTemplate;
@Override
public void addUser(UserPo user) {
mongoTemplate.insert(user, "test");
}
@Override
public Object getUser() {
List<Object> objects = new ArrayList<>();
FindIterable findIterable = mongoTemplate.getCollection("test").find();
MongoCursor<UserPo> cursor = findIterable.iterator();
while(cursor.hasNext()) {
objects.add(cursor.next());
}
return objects;
}
}
4)用于接收前端发送数据的userPo类
@Data
public class UserPo {
/**
* 姓名
*/
private String name;
/**
* 年龄
*/
private Integer age;
/**
*id
*/
private Object id;
}
通过前端发送的读写api请求可以发现,后端与数据库之间的数据是按照Json格式传递的。
副本集连接
后端程序如何接入mongodb集群
在接入集群前,务必确保以下几点
(1)确保所有的mongodb节点都开着
(2)进入主节点查看所有节点状态
(3)若指定读节点中只有从节点,确保开启从节点的读权限
(1)思考
(1)若后端程序需要读写功能,那么必须确保连接在主节点上
(2)因为主节点可能会发生故障转移,转移后的主节点ip就变了,那么后端的连接地址就不能固定
(3)改变连接节点的工作必须由后端完成
(2)实现
搭建结构
在mongodb的副本集中,不仅仅主从节点之间存在心跳机制,主节点和连接它的客户端之间也存在心跳机制,即定时发送一个心跳来确保对方还在。
spring框架中有负责监听、连接mongodb主节点的类以及定义的方法
在impl中的两个方法中添加节点池
{
List<ServerAddress> serverAddresses = new ArrayList<>();
ServerAddress address1 = new ServerAddress("192.168.1.100", 8003);
ServerAddress address2 = new ServerAddress("192.168.2.101", 8004);
serverAddresses.add(address1);
serverAddresses.add(address2);
MongoClient client = new MongoClient(serverAddresses);
}
(3)测试
(1)关闭主节点
当读写都确定没有问题之后,可以尝试关闭主节点,此时后端程序的会发出报告,并在一定时间(secondary节点变成主节点之后)连接上新的主节点。
(2)重新将关闭节点打开
重新打开新的节点之后,往主节点中进行写操作,并查看从节点数据是否同步成功。
test:SECONDARY> use Test
switched to db Test
test:SECONDARY> db.test.find()
到这里,mongodb的副本集与项目的对接基本完成,但是还可以进行下一步优化,将集群的读写进行分离。在Mongodb类中有一个ReadPreference方法可以实现读操作只在从节点上进行,这样处理之后,可以降低主节点的压力,减少主节点挂掉的风险。
参考资料
推荐阅读