前言
业务场景
用户在拉勾网投递简历时,我们会为每次投递的简历生成一份快照,将快照信息存储到 MongoDB中。 功能需求:搭建MongoDB分片集群,模拟简历快照数据进行操作,具体要求如下:
(1) 如图搭建一个分片集群,要求每个分片节点中的复制集含有一个仲裁节点
(2) 使用权限控制,建立要访问的数据库lg_resume,这个账号名字是lagou_gx、密码是abc321 这个账号对数据库有读写权限
(3) 使用SpringBoot 进行访问分片集群,对lg_resume 库中的lg_resume_datas 进行增加和查询操作
网络拓扑如下:
思路分析
1 搭建复制集群的环境
由网络拓扑结构可知,该MongoDB分片集群中总共3类角色
1 路由节点(1个);
2 配置节点集群(3个);
3 复制节点集群,每个集群中4个MongoDB实例(4个)
共1+3+4*4=20个MongDB实例.
2 集群添加权限控制
路由节点生成testKeyFile.file并发送到集群中所有的节点。并创建一个账户授予读写权限。
3 springboot访问
编写 Repository 接口 继承 MongoRepository
一 搭建MongoDB复制分片集群
1.1 搭建单机版monogoDB
tar -xvf mongodb-linux-x86_64-4.1.3.tgz
mv mongodb-linux-x86_64-4.1.3 mongodb
mv mongodb /
cd /mongodb
mkdir single
我们在single文件下创建配置文件mongo.conf,内容如下:
dbpath=/mongodb/single/data/
port=27017
bind_ip=0.0.0.0
fork=true
logpath = /mongodb/single/data/MongoDB.log
logappend = true
auth=false
启动
/mongodb/bin/mongod -f /mongodb/single/mongo.conf
我们使用自带客户端进行登录
/mongodb/bin/mongo
mongo的标准格式为
mongo --host=主机IP --port=端口
1.2 搭建mongoDB复制集群
1.2.1 安装psmisc
因为mongoDB集群可以使用一台机器实现,或者对等数量的虚拟机,我们采用第一种方式方便快捷(主要是因为穷,囧!)。故我们安装psmisc工具帮助我们批量的杀程序;
如杀掉所有的java进程
killall java
我们使用yum进行安装
yum install -y psmisc
1.2.2 搭建配置节点集群
我们创建一个目录用于存放配置节点集群
mkdir -p /mongodb/config/config1
参照单机的搭建方式,创建配置文件,配置如下:
# 数据库文件位置
dbpath=/mongodb/config/config1
#日志文件位置
logpath=/mongodb/config/config1/config1.log
# 以追加方式写入日志
logappend=true
# 是否以守护进程方式运行
fork = true
bind_ip=0.0.0.0
port = 17011
# 表示是一个配置服务器
configsvr=true
#配置服务器副本集名称
replSet=configsvr
重复1.2.2的步骤,创建复制集群另外两个节点17013和17015
1.2.3 搭建配置分片集群
创建4个分片集群文件夹
mkdir -p /mongodb/shard/shard1/shard1-37011
mkdir -p /mongodb/shard/shard1/shard1-37013
mkdir -p /mongodb/shard/shard1/shard1-37015
mkdir -p /mongodb/shard/shard1/shard1-37017
mkdir -p /mongodb/shard/shard2/shard2-47011
mkdir -p /mongodb/shard/shard2/shard2-47013
mkdir -p /mongodb/shard/shard2/shard2-47015
mkdir -p /mongodb/shard/shard2/shard2-47017
mkdir -p /mongodb/shard/shard3/shard3-57011
mkdir -p /mongodb/shard/shard3/shard3-57013
mkdir -p /mongodb/shard/shard3/shard3-57015
mkdir -p /mongodb/shard/shard3/shard3-57017
mkdir -p /mongodb/shard/shard4/shard4-58011
mkdir -p /mongodb/shard/shard4/shard4-58013
mkdir -p /mongodb/shard/shard4/shard4-58015
mkdir -p /mongodb/shard/shard4/shard4-58017
以第一个分片集群shard1为例
其配置文件如下
dbpath=/mongodb/shard/shard1/shard1-37011
bind_ip=0.0.0.0
port=37011
fork=true
logpath=shard/shard1/shard1-37011.log
replSet=shard1
shardsvr=true
dbpath=/mongodb/shard/shard1/shard1-37013
bind_ip=0.0.0.0
port=37013
fork=true
logpath=shard/shard1/logs/shard1-37013.log
replSet=shard1
shardsvr=true
dbpath=/mongodb/shard/shard1/shard1-37015
bind_ip=0.0.0.0
port=37015
fork=true
logpath=shard/shard1/logs/shard1-37015.log
replSet=shard1
shardsvr=true
dbpath=/mongodb/shard/shard1/shard1-37017
bind_ip=0.0.0.0
port=37017
fork=true
logpath=shard/shard1/logs/shard1-37017.log
replSet=shard1
shardsvr=true
shard2配置文件
dbpath=/mongodb/shard/shard2/shard2-47011
bind_ip=0.0.0.0
port=47011
fork=true
logpath=shard/shard2/shard2-47011.log
replSet=shard2
shardsvr=true
dbpath=/mongodb/shard/shard2/shard2-47013
bind_ip=0.0.0.0
port=47013
fork=true
logpath=shard/shard2/logs/shard2-47013.log
replSet=shard2
shardsvr=true
dbpath=/mongodb/shard/shard2/shard2-47015
bind_ip=0.0.0.0
port=47015
fork=true
logpath=shard/shard2/logs/shard2-47015.log
replSet=shard2
shardsvr=true
dbpath=/mongodb/shard/shard2/shard2-47017
bind_ip=0.0.0.0
port=47017
fork=true
logpath=shard/shard2/logs/shard2-47017.log
replSet=shard2
shardsvr=true
shard3配置文件
dbpath=/mongodb/shard/shard3/shard3-57011
bind_ip=0.0.0.0
port=57011
fork=true
logpath=shard/shard3/shard3-57011.log
replSet=shard3
shardsvr=true
dbpath=/mongodb/shard/shard3/shard3-57013
bind_ip=0.0.0.0
port=57013
fork=true
logpath=shard/shard3/logs/shard3-57013.log
replSet=shard3
shardsvr=true
dbpath=/mongodb/shard/shard3/shard3-57015
bind_ip=0.0.0.0
port=57015
fork=true
logpath=shard/shard3/logs/shard3-57015.log
replSet=shard3
shardsvr=true
dbpath=/mongodb/shard/shard3/shard3-57017
bind_ip=0.0.0.0
port=57017
fork=true
logpath=shard/shard3/logs/shard3-57017.log
replSet=shard3
shardsvr=true
shard4配置文件
dbpath=/mongodb/shard/shard4/shard4-58011
bind_ip=0.0.0.0
port=58011
fork=true
logpath=shard/shard4/shard4-58011.log
replSet=shard4
shardsvr=true
dbpath=/mongodb/shard/shard4/shard4-58013
bind_ip=0.0.0.0
port=58013
fork=true
logpath=shard/shard4/logs/shard4-58013.log
replSet=shard4
shardsvr=true
dbpath=/mongodb/shard/shard4/shard4-58015
bind_ip=0.0.0.0
port=58015
fork=true
logpath=shard/shard4/logs/shard4-58015.log
replSet=shard4
shardsvr=true
dbpath=/mongodb/shard/shard4/shard4-58017
bind_ip=0.0.0.0
port=58017
fork=true
logpath=shard/shard4/logs/shard4-58017.log
replSet=shard4
shardsvr=true
1.2.4 搭建mongoDB路由节点
创建路由文件夹router
mkdir -p /mongodb/router
创建路由配置文件,内容如下
vim /mongodb/router/router27017.conf
# 数据库文件位置
dbpath=/mongodb/router
#日志文件位置
logpath=/mongodb/router/config1.log
# 以追加方式写入日志
logappend=true
# 是否以守护进程方式运行
fork = true
bind_ip=0.0.0.0
port = 27017
# 表示是一个配置服务器
configsvr=true
#配置服务器副本集名称
replSet=configsvr
1.2.5 启动集群
启动配置集群
进入任意配置节点,添加配置集群
/mongodb/bin/mongo --port 17017
use admin
var cfg ={"_id":"configsvr",
"members":[
{"_id":1,"host":"192.168.126.128:17011"},
{"_id":2,"host":"192.168.126.128:17013"},
{"_id":3,"host":"192.168.126.128:17015"}]
};
rs.initiate(cfg)
启动分片集群1
进入shard1中任意节点,添加分片集群shard1
var cfg ={"_id":"shard1",
"protocolVersion" : 1,
"members":[
{"_id":1,"host":"192.168.126.128:37011","priority":10},
{"_id":2,"host":"192.168.126.128:37013","priority":5},
{"_id":3,"host":"192.168.126.128:37015","priority":0},
{"_id":4,"host":"192.168.126.128:37017","arbiterOnly":true}
]
};
rs.initiate(cfg)
rs.status()
启动分片集群2
进入shard2中任意节点,添加分片集群shard2
var cfg ={"_id":"shard2",
"protocolVersion" : 1,
"members":[
{"_id":1,"host":"192.168.126.128:47011","priority":10},
{"_id":2,"host":"192.168.126.128:47013","priority":5},
{"_id":3,"host":"192.168.126.128:47015","priority":0},
{"_id":4,"host":"192.168.126.128:47017","arbiterOnly":true}
]
};
rs.initiate(cfg)
rs.status()
启动分片集群3
进入shard3中任意节点,添加分片集群shard3
var cfg ={"_id":"shard3",
"protocolVersion" : 1,
"members":[
{"_id":1,"host":"192.168.126.128:57011","priority":10},
{"_id":2,"host":"192.168.126.128:57013","priority":5},
{"_id":3,"host":"192.168.126.128:57015","priority":0},
{"_id":4,"host":"192.168.126.128:57017","arbiterOnly":true}
]
};
rs.initiate(cfg)
rs.status()
启动分片集群4
进入shard4中任意节点,添加分片集群shard4
var cfg ={"_id":"shard4",
"protocolVersion" : 1,
"members":[
{"_id":1,"host":"192.168.126.128:58011","priority":10},
{"_id":2,"host":"192.168.126.128:58013","priority":5},
{"_id":3,"host":"192.168.126.128:58015","priority":0},
{"_id":4,"host":"192.168.126.128:58017","arbiterOnly":true}
]
};
rs.initiate(cfg)
rs.status()
启动路由节点
进入路由节点,添加分片集群
/mongodb/bin/mongo --port 27017
sh.status()
sh.addShard("shard1/192.168.126.128:37011,192.168.126.128:37013,192.168.126.128:37015,192.168.126.128:37017");
sh.addShard("shard2/192.168.126.128:47011,192.168.126.128:47013,192.168.126.128:47015,192.168.126.128:47017");
sh.addShard("shard3/192.168.126.128:57011,192.168.126.128:57013,192.168.126.128:57015,192.168.126.128:57017");
sh.addShard("shard4/192.168.126.128:58011,192.168.126.128:58013,192.168.126.128:58015,192.168.126.128:58017");
sh.status()
1.2.6 编写一键启动脚本
vim /mongodb/bin/startAll.sh
echo " ============================================= 开始启动mongoDB集群 ================================================"
echo "=================================================== 开始启动配置节点 ==========================================================="
/mongodb/bin/mongod -f /mongodb/config/config1/17011conf.conf
/mongodb/bin/mongod -f /mongodb/config/config2/17013conf.conf
/mongodb/bin/mongod -f /mongodb/config/config3/17015conf.conf
echo "======================================================== 配置节点启动完毕 ============================================================================="
echo "======================================================== shard1集群开始启动 ============================================================================="
/mongodb/bin/mongod -f /mongodb/shard/shard1/shard1-37011/37011.conf
/mongodb/bin/mongod -f /mongodb/shard/shard1/shard1-37013/37013.conf
/mongodb/bin/mongod -f /mongodb/shard/shard1/shard1-37015/37015.conf
/mongodb/bin/mongod -f /mongodb/shard/shard1/shard1-37017/37017.conf
echo "======================================================== shard1集群启动完毕 ============================================================================="
echo "======================================================== shard2集群开始启动 ============================================================================="
/mongodb/bin/mongod -f /mongodb/shard/shard1/shard1-37011/37011.conf
/mongodb/bin/mongod -f /mongodb/shard/shard2/shard2-47011/47011.conf
/mongodb/bin/mongod -f /mongodb/shard/shard2/shard2-47013/47013.conf
/mongodb/bin/mongod -f /mongodb/shard/shard2/shard2-47015/47015.conf
/mongodb/bin/mongod -f /mongodb/shard/shard2/shard2-47017/47017.conf
echo "======================================================== shard2集群启动完毕 ============================================================================="
echo "======================================================== shard3集群开始启动 ============================================================================="
/mongodb/bin/mongod -f /mongodb/shard/shard3/shard3-57011/57011.conf
/mongodb/bin/mongod -f /mongodb/shard/shard3/shard3-57013/57013.conf
/mongodb/bin/mongod -f /mongodb/shard/shard3/shard3-57015/57015.conf
/mongodb/bin/mongod -f /mongodb/shard/shard3/shard3-57017/57017.conf
echo "======================================================== shard3集群启动完毕 ============================================================================="
echo "======================================================== shard4集群开始启动 ============================================================================="
/mongodb/bin/mongod -f /mongodb/shard/shard4/shard4-58011/58011.conf
/mongodb/bin/mongod -f /mongodb/shard/shard4/shard4-58013/58013.conf
/mongodb/bin/mongod -f /mongodb/shard/shard4/shard4-58015/58015.conf
/mongodb/bin/mongod -f /mongodb/shard/shard4/shard4-58017/58017.conf
echo "======================================================== shard4集群启动完毕 ============================================================================="
echo "======================================================== 路由节点开始启动 ============================================================================="
/mongodb/bin/mongos -f /mongodb/router/router27017.conf
echo "======================================================== 路由节点启动完毕 ============================================================================="
echo "======================================================== mongodb集群启动完毕 ============================================================================="
授予执行权限
chmod +x /mongodb/bin/startAll.sh
1.2.7 集群开启数据库和集合分片
登录路由节点
/mongodb/bin/mongo --port 27017
sh.enableSharding("lg_resume")
sh.shardCollection("lg_resume.lagou_resume_datas",{"name":"hashed"})
1.2.8 创建集群用户
/mongodb/bin/momgo --port
use admin
db.createUser(
... {
... user:"root",
... pwd:"123456",
... roles:[{role:"root",db:"admin"}]
... })
use lg_resume;
db.createUser({
... user:"lagou_gx",
... pwd:"123456",
... roles:[{role:"readWrite",db:"lg_resume"}]
... })
db.createUser({
... user:"lagou_gx",
... pwd:"123456",
... roles:[{role:"read",db:"lg_resume"}]
... })
1.2.9 分片集群安全认证
首先我们需要将所有的mongo关闭
killall mongod
killall mongos
生成密钥文件 并修改权限
openssl rand -base64 756 > /mongodb/bin/testKeyFile.file
chmod 600 /mongodb/bin/testKeyFile.file
配置节点集群和分片节点集群开启安全认证和指定密钥文件
auth=true
keyFile=/mongodb/bin/testKeyFile.file
在路由配置文件中 设置密钥文件
keyFile/mongodb/bin/testKeyFile.file
1.3 代码实例
新建springboot,pom.xml文件内容如下:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.lagou</groupId>
<artifactId>mongo_springboot_repository</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
<version>2.2.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<version>2.2.2.RELEASE</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>11</source>
<target>11</target>
<testSource>11</testSource>
<testTarget>11</testTarget>
</configuration>
<version>3.8.1</version>
</plugin>
</plugins>
</build>
</project>
application.properties
spring.data.mongodb.host=192.168.126.128
spring.data.mongodb.port=27017
spring.data.mongodb.database=lg_resume
spring.data.mongodb.username=lagou_gx
spring.data.mongodb.password=123456
建立resume实体类
package com.lagou.bean;
import org.springframework.data.mongodb.core.mapping.Document;
import java.util.Date;
@Document("lg_resume_datas")
public class Resume {
@Override
public String toString() {
return "Resume{" +
"id='" + id + '\'' +
", name='" + name + '\'' +
", city='" + city + '\'' +
", birthday=" + birthday +
", expectSalary=" + expectSalary +
'}';
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
public double getExpectSalary() {
return expectSalary;
}
public void setExpectSalary(double expectSalary) {
this.expectSalary = expectSalary;
}
public Resume() {
}
public Resume(String id, String name, String city, Date birthday, double expectSalary) {
this.id = id;
this.name = name;
this.city = city;
this.birthday = birthday;
this.expectSalary = expectSalary;
}
private String id;
private String name;
private String city;
private Date birthday;
private double expectSalary;
}
建立ResumeRepository
package com.lagou.repository;
import com.lagou.bean.Resume;
import org.springframework.data.mongodb.repository.MongoRepository;
import java.util.List;
public interface ResumeRepository extends MongoRepository<Resume,String> {
List<Resume> findByNameEquals(String name);
List<Resume> findByNameAndExpectSalary(String name,double expectSalary);
}
建立测试类
package com.lagou;
import com.lagou.bean.Resume;
import com.lagou.repository.ResumeRepository;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import java.util.List;
/**
* <p>Title: mongo Repository测试</p>
* <p>Description: </p>
* <p>Copyright: Copyright (c) 2020</p>
* <p>Company: http://www.ubisys.com.cn/</p>
*
* @Auther: cw
* @Date: 2020/8/29 00:29
*/
@RunWith(SpringRunner.class)
@SpringBootTest(classes = MongoRepositoryMain.class)
public class TestMongoDB {
@Autowired
private ResumeRepository resumeRepository;
/**
* 测试 写
*/
@Test
public void testMongoWrite() {
Resume resume = new Resume();
resume.setName("chengdaotest");
resume.setExpectSalary(1);
resume.setCity("bj");
resumeRepository.save(resume);
}
/**
* 测试读
*/
@Test
public void testMongoRead() {
List<Resume> list = resumeRepository.findByNameEquals("chengdaotest");
list.forEach(resume -> {
System.out.println(resume.toString());
});
}
/**
* 测试分片
*/
@Test
public void testMongoShard() {
for (int i = 1; i < 1000; i++) {
Resume resume = new Resume();
resume.setName("chengdaotest1" + i);
resume.setExpectSalary(1);
resume.setCity("bj");
resumeRepository.save(resume);
}
}
}