一、简洁搭建mognodb副本集
- 环境说明
我都是在云服务器上搭建的,CentOS7
,Docker环境,版本忘记了。我就直接在同一台服务器上搭建三个mongodb
即可。
1、基本信息如下
服务器地址 www.it307.top
副本集名称 rs
容器节点及端口映射
m0 37017:27017
m1 47017:27017
m2 57017:27017
注:机器环境安装docker
2、部署步骤
2.1、下载mongo镜像
docker pull mongo
其实不用下载也可以,创建容器的时候会自动下载的。
2.2、启动三个节点
docker run --name m0 -p 37017:27017 -d mongo --replSet "rs"
docker run --name m1 -p 47017:27017 -d mongo --replSet "rs"
docker run --name m2 -p 57017:27017 -d mongo --replSet "rs"
--replSet "rs"
如果不集群,就不要添加这个,添加了访问不了。
2.3、连接任意一个节点,进行副本集配置
- 进入其中一个容器
随便进入一个即可,没有要求。也并不是说进入这个就是住节点。
docker exec -it m0 /bin/bash
- 连接三个节点中的任意一个,注意
ip
地址为宿主机ip
,我当前的为www.it307.top
。我有域名绑定的。
好像链接的这个节点就是主节点了
mongo --host www.it307.top --port 37017
此时已连接到m0
节点,进行副本集配置。我们值在m0
这台mongodb
中配置就可以了,其他两个副本不需要配置的。
var config={_id:"rs",members:[{_id:0,host:"www.it307.top:37017"},{_id:1,host:"www.it307.top:47017"},{_id:2,host:"www.it307.top:57017"}
]};
rs.initiate(config)
响应应该类似下面,注意此时命令提示符已经发生变化,由原来的 >
变成了rs:SECONDARY>
2.4、查看副本集配置信息
rs.conf()
2.5、查看副本集状态
rs.status()
这样就搭建完成了。
3、测试
将这个太mongodb
链接上,并且这三台mongodb
是没有user
这个数据库的。
我在37017
这个mongodb
里面添加一条数据,因为这台mongodb
是主mongodb
不是副本。副本是不能添加数据的,我们在上面配置的时候,只配置37017
这台mongodb
的。
在37017
这台mognodb
中插入数据后,数据在三台mongodb
都会存在。这样副本就搭建成功。
二、详细搭建mongodb
副本集
需要将配置文件和数据保存目录都挂在到宿主机上,部署副本、集群和分片这种事情应该是在不同的机器上操作的,但是条件有限。我就全部在同一台机器上操作,利用不同的端口区分和不同的保存目录区分即可。
注意:本来是想将数据挂载到宿主机,然后开启认证。但是最终效果没有实现,所以只是为了做副本,能够使用事务的话。暂时我的解决思路是:启动两个mongodb,设置一个主机节点和副本节点,副本节点不要认证,但是端口号不要开放,Linux不要开放副本节点的端口号,然后将主节点设置为认证的方式,这样就可以使用副本了。
2.1、创建文件
2.1.1、创建配置文件保存目录和数据保存目录
mkdir -p /opt/mongodb/{/m0/data,/m0/conf,/m0/logs,/m1/conf,/m1/logs,/m2/data,/m2/data,/m2/conf,/m2/logs}
2.1.2、创建秘钥文件
因为使用集群以后,mongodb
就会使用一个秘钥文件开做校验。集群之间使用的这个秘钥文件必须一样,可以生成一份出来复制到不同机器,我的是同一台机器,我就复制到不同文件里面。必须是同一份,同一份。
- 生成和设置权限
openssl rand -base64 756 > /opt/mongodb/mongodbKeyfile.key
chmod 400 /opt/mongodb/mongodbKeyfile.key
- 分别复制到三个节点下面
cp /opt/mongodb/mongodbKeyfile.key /opt/mongodb/m0/conf
cp /opt/mongodb/mongodbKeyfile.key /opt/mongodb/m1/conf
cp /opt/mongodb/mongodbKeyfile.key /opt/mongodb/m2/conf
分别将这个秘钥文件复制到这三个节点的
conf
目录下面。
2.2、配置文件
注意:我们的配置文件目录分别是 /opt/mongodb/m0/conf
,/opt/mongodb/m1/conf
,/opt/mongodb/m2/conf
,上面这个命令已经创建建了。
在 /opt/mongodb/m0,m1,m2/conf
目录下面分别创建 mongod.conf
配置文件,分别写入一下内容。三个节点都需要写。注意:当前文件使用的是yml语法,如果格式不好确定,可以在idea
开发工具里面写好在复制过来。
直接创建一个直接复制的,下面有现成命令。
net:port: 27017 # 这是启动端口bindIp: 0.0.0.0 # 允许哪些ip连接(好像和下面的命令参数 --bind_ip_all 相类似的作用)
systemLog:logAppend: true # 重新启动的mongodb的时候日志拼接在以前的日志文件上,不用新建
security:keyFile: /data/configdb/mongodbKeyfile.key # 这个路径很重要(注意这个路径是下面容器映射过的路径),不要写宿主机的绝对路径,容器是访问不到宿主机的,应该写容器中的路径。authorization: "enabled" # 开启客户端连接验证
replication:replSetName: "rs" # 副本集的名称
特别强调这个问题,容器是不能访问宿主机的。所以配置文件里面配置的路径,一定要写容器对应的路径,千万不能写宿主机上的路径。因为容器是不能访问宿主机的,切记!!!切记!!!
- 直接创建一个,复制到其他两个节点下
vim /opt/mongodb/m0/conf/mongod.conf
写入上面的配置文件,然后直接复制到其他节点下面。
cp /opt/mongodb/m0/conf/mongod.conf /opt/mongodb/m1/conf/
cp /opt/mongodb/m0/conf/mongod.conf /opt/mongodb/m2/conf/
2.3、创建启动命令
首先不要增加任何认证,先启动不用认证的mongodb
,在里面将需要的集合、副本和用户名密码权限等信息创建好,在使用需要认证的命令重新创建容器。因为配置的信息已经映射到宿主机上了,只要不把宿主机上的数据删除,所有配置都是存在的。
mkdir -p /opt/docker
vim /opt/docker/mongodb.sh
- 不用认证的命令
MONGODB_DIR=/opt/mongodb/m0
NAME=mognoM0
PORT=37017
docker stop ${NAME}
docker rm ${NAME}
docker run -p ${PORT}:27017 \--name ${NAME} \-v ${MONGODB_DIR}/data:/data/db \-v ${MONGODB_DIR}/conf:/data/configdb \-v ${MONGODB_DIR}/logs:/data/log \-d mongo:4.2.5 \--replSet "rs"MONGODB_DIR=/opt/mongodb/m1
NAME=mognoM1
PORT=47017
docker stop ${NAME}
docker rm ${NAME}
docker run -p ${PORT}:27017 \--name ${NAME} \-v ${MONGODB_DIR}/data:/data/db \-v ${MONGODB_DIR}/conf:/data/configdb \-v ${MONGODB_DIR}/logs:/data/log \-d mongo:4.2.5 \--replSet "rs"MONGODB_DIR=/opt/mongodb/m2
NAME=mognoM2
PORT=57017
docker stop ${NAME}
docker rm ${NAME}
docker run -p ${PORT}:27017 \--name ${NAME} \-v ${MONGODB_DIR}/data:/data/db \-v ${MONGODB_DIR}/conf:/data/configdb \-v ${MONGODB_DIR}/logs:/data/log \-d mongo:4.2.5 \--replSet "rs"
--replSet "rs"
表示集群的名称,我在配置文件里面写了,但是这里写上是没问题的,不写我不知道可不可以,应该是可以的吧。增加这个配置就说明是集群了,所以单体的还是不要增加。
- 启动
sh /opt/docker/mongodb.sh
使用 docker ps
查询是否全部启动成功。
2.4、配置副本
- 连接任意一个节点,进行副本集配置
随便进入,进入哪个都行。注意:我这里起的容器名和第一章的容器名不同。
docker exec -it mognoM0 /bin/bash
连接三个节点中的任意一个,链接这个就是主节点。
mongo --host www.it307.top --port 37017
注意:如果安全组这些设置的很严格,这里填写内网地址。
- 配置副本
这里在一个
mongodb
里面配置就行了,会自动同步到其他mognodb中的。
var config={_id:"rs",members:[{_id:0,host:"www.it307.top:37017",priority:3},{_id:1,host:"www.it307.top:47017",priority:2},{_id:2,host:"www.it307.top:57017",priority:1}
]};
rs.initiate(config)
这样副本就配置完成了,但是当前是不需要认证的。在线上环境我们需要用户名和密码认证。
- 查看副本集配置信息
rs.conf()
- 查看副本集状态
rs.status()
2.5、创建用户名和密码
- 创建用户
随意进入一个MongoDB
容器,然后在这台容器里面分别登录其他MongoDB
。直接使用MongoDB
连接工具连接,然后直接在工具里面创建用户也可以。现在都是么有用户名和密码的,可以随便登录访问的。
如果当前是在mongodb里面,进入容器和登录
mongodb
这两个命令就可以不用了。如果不喜欢Linux界面操作,可以直接使用mongodb
链接工具,直接链接上mongodb
操作也可以的。
docker exec -it mognoM0 /bin/bash
- 登录第一个
MongoDB
mongo --host www.it307.top --port 37017
- 切换数据库和创建用户
use user_db
db.createUser({ user: 'admin', pwd: 'admin123', roles: [ { role: "root", db: "admin" } ] });
db.auth('admin', 'admin123');
这里需要注意权限的问题,权限给
root
似乎太大了,但是也不能给太小,给太小会发现干啥都没权限。
一下为说明
use user_db # 切换到user_db 这个数据,给user_db这个数据创建用户
db.createUser({user: 'userDb', # 用户名pwd: '123456', # 密码roles:[{role: 'root', # 角色db: 'admin' # 数据库,这个是不用改变的,意思是角色保存的数据库吧。}]
})db.auth('userDb', '123456') # 如果返回 1 说明用户名和密码没有问题,返回 0 说明用户名和密码不正确吧
show users # 查看当前库下的用户db.dropUser('testadmin') # 删除用户db.updateUser('admin', {pwd: '654321'}) # 修改用户密码db.auth('admin', '654321') # 密码认证
2.6、创建需要认证的启动命令
- 先退出mongodb和容器,回到宿主机
exit
exit
把原来的启动命令删除,重新编辑启动命令。
rm -rf /opt/docker/mongodb.sh
vim /opt/docker/mongodb.sh
- 写入启动命令
MONGODB_DIR=/opt/mongodb/m0
NAME=mognoM0
PORT=37017
docker stop ${NAME}
docker rm ${NAME}
docker run -p ${PORT}:27017 \--name ${NAME} \-v ${MONGODB_DIR}/data:/data/db \-v ${MONGODB_DIR}/conf:/data/configdb \-v ${MONGODB_DIR}/logs:/data/log \-v /data/mongod/conf/keyfile/:/data/keyfile/ \--privileged \--restart=always \-d mongo:4.2.5 \--replSet "rs" --bind_ip_all -f "/data/configdb/mongod.conf"MONGODB_DIR=/opt/mongodb/m1
NAME=mognoM1
PORT=47017
docker stop ${NAME}
docker rm ${NAME}
docker run -p ${PORT}:27017 \--name ${NAME} \-v ${MONGODB_DIR}/data:/data/db \-v ${MONGODB_DIR}/conf:/data/configdb \-v ${MONGODB_DIR}/logs:/data/log \--privileged \--restart=always \-d mongo:4.2.5 \--replSet "rs" --bind_ip_all -f "/data/configdb/mongod.conf"MONGODB_DIR=/opt/mongodb/m2
NAME=mognoM2
PORT=57017
docker stop ${NAME}
docker rm ${NAME}
docker run -p ${PORT}:27017 \--name ${NAME} \-v ${MONGODB_DIR}/data:/data/db \-v ${MONGODB_DIR}/conf:/data/configdb \-v ${MONGODB_DIR}/logs:/data/log \--privileged \--restart=always \-d mongo:4.2.5 \--replSet "rs" --bind_ip_all -f "/data/configdb/mongod.conf"
--restart=always
:容器自动启动,docker启动后容器就自动启动了。
--replSet "rs"
:容器名称,配置文件里面指定过了,但是这里指定也没有问题。这里不指定可不可以不太清楚。
-f
:配置文件地址,虽然配置文件我们做了挂在,但是这里必须指定,否则配置文件没有效果的。并且这里路径一定是容器的地址,绝对不能写宿主机上的地址。因为容器是不能访问宿主机的。
特别注意:这里的 -f
表示指定配置文件的地址,一定要写容器里面的路径。容器是访问不了宿主机的。
- 重启mongodb
sh /opt/docker/mongodb.sh
2.7、测试
在主节点37017
机器下面创建一条数据,然后再副本中查询,三台机器上都可以查询到,说明副本搭建成功。
2.8、关闭容器删除容器和删除本地数据
- 关闭所有容器
docker stop mognoM0 mognoM1 mognoM2
- 删除容器
docker rm mognoM0 mognoM1 mognoM2
- 删除本地数据
rm -rf /opt/mongodb
rm -rf /opt/docker/mongodb.sh
三、springdataMongoDB实现事务处理
- 引入依赖
<!---mongodb相关依赖-->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>
yml
配置
server:port: 8083spring:data:mongodb:# mongodb://用户名:密码@链接地址1:端口号,链接地址2:端口号,……/连接的数据库uri: mongodb://admin:admin123@www.it307.top:37017,www.it307.top:47017,www.it307.top:57017/user_dblogging:level:org.springframework.data.mongodb.core: debug # 显示操作的mognodb语句,开发环境使用
切记,一定不能再路径后面增加以下语句,不能增加,不能增加,不能增加。
?slaveOk=true&replicaSet=repl&write=1&readPreference=secondaryPreferred&connectTimeoutMS=300000
- 配置事务
package com.mongodb.config;import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.mongodb.MongoDatabaseFactory;
import org.springframework.data.mongodb.MongoTransactionManager;@Configuration
public class MongoTransactionConfig {@Beanpublic MongoTransactionManager transactionManager(MongoDatabaseFactory dbFactory) {return new MongoTransactionManager(dbFactory);}
}
- 使用事务
@Transactional(rollbackFor = Exception.class)
直接在需要使用事务的上面打上这个注解即可。但是
@Test
下面测试好像事务不生效的。
- 案例
控制层
@PostMapping("/transactional")
public void mongodbTransactional(){userService.mongodbTransactional();
}
@Transactional(rollbackFor = Exception.class)
@Override
public void mongodbTransactional() {UserDO user = new UserDO();user.setId(ObjectId.get());user.setMobile("18580759247");user.setNickname("我靠你大爷的");mongoTemplate.save(user);user.setId(ObjectId.get());mongoTemplate.insert(user);System.out.println(1/0); // 这里会报错误,如果这里不报错误,那么上面就会增加成功,这里报错误了,上面增加的数据都会被回滚。
}
四、常见的问题
Cannot create namespace user_db.user in multi-document transaction.; nested exception is com.mongodb.MongoWriteException: Cannot create namespace user_db.user in multi-document transaction.
不能再分布式事务中创建文档XXX
,使用MongoDB
连接工具连接上集群的所有MongoDB
,在里面插入一条数据,这是相应的集合就创建了,在使用java
程序就不会出现这个问题。
这里面只能有一台主节点才能插入数据的,所以要试着看,总有一台式能插入的。
注意:创建容器的时候,配置的路径一定是容器里面的路径,访问不到宿主机的。
command insert requires authentication
这个问题是权限不够,创建用户的时候我们给那个用户的角色太小,没有权限操作。给这个用户附一个大一点的权限,有相关操作的权限即可。
Error reading file /opt/mongodb/m1/conf/mongodbKeyfile.key: No such file or directory
这个问题就是配置文件里面配置的路径或者启动命令上指定配置文件的路径写错了。注意:这个路径一定要写容器的路径,千万不能写宿主机的路径。虽然容器的路径已经挂在到宿主机相应的文件里面,但是容器是不能访问宿主机的,所以必须写容器里面的路径。写容器的路径,容器路径,切记!!!切记!!!切记!!!
Timed out after 30000 ms while waiting for a server that matches com.mongodb.client.internal.MongoClientDelegate$1@71bfc42c. Client view of cluster state is {type=REPLICA_SET, servers=[]; nested exception is com.mongodb.MongoTimeoutException: Timed out after 30000 ms while waiting for a server that matches com.mongodb.client.internal.MongoClientDelegate$1@71bfc42c. Client view of cluster state is {type=REPLICA_SET, servers=[]
这种情况就是主节点挂了,最后只有一个从节点,没有办法实现一个主节点一个从节点的最低要求了。使用一下命令查看一下
rs.status()
一定要用命令查看状态,可能docker容器是运行的,但是MongoDB已经不再正常的运行了。
docket
启动容器报一下错误
docker: Error response from daemon: driver failed programming external connectivity on endpoint mognoM0 (68acee746dba74982afecf366dc5e18dd2fa8cc2298816482ad1f550a0589363): (iptables failed: iptables --wait -t nat -A DOCKER -p tcp -d 0/0 --dport 37017 -j DNAT --to-destination 172.18.0.5:27017 ! -i docker0: iptables: No chain/target/match by that name.
五、快速卸载当前配置的容器和数据
除了安装mongodb操作意外,千万不能使用。这个命令下去,所有的数据和容器都全毁了。
docker stop mognoM0 mognoM1 mognoM2
docker rm mognoM0 mognoM1 mognoM2
rm -rf /opt/mongodb /opt/docker
解决方法,直接重启docker
systemctl restart docker
六、注意事项
- 凡是出现什么问题,第一就是看日志。容器查看日志方式。
docker logs 容器名称 | 容器id
-
集群配置,配置文件配置的地址和启动命令上指定的地址,一定要写容器地址,千万不能写宿主机地址。容器不能访问宿主机,如果直接写宿主机的地址,会提示么有这样的文件或者目录等等。错误信息里面有相关说明。
-
如果配置文件指定集群名称
replSetName: "rs"
或者启动命令上指定集群名称--replSet "rs"
。如果不配置集群,那么会提示不是主节点的错误信息。所以必须先配置集群信息,也就是副本信息,然后再做操作。所以:上面我都是先创建副本了在创建用户信息的。
七、部署的时候注意
有些使用我将庞大的后端你系统部署上去以后,MongoDB
挂了。虽然docker容器是启动的,但是实际上已经没有工作了,可以使用 rs.status()
命令查看到。这个时候我们在将docker
容器关闭,重新启动。或者直接执行docker容器创建命令也可以,但是每次都会出现启动失败,直接将docker
重启在去执行MongoDB
创建命令,最后查看状态,运行正常。