参考资料:
http://www.imooc.com/article/details/id/258885
https://blog.csdn.net/weixin_34117522/article/details/94609770
https://blog.csdn.net/wangshuang1631/article/details/53857319
前序–聊聊数据库升级方案
#主节点master
docker run --name mongo-master --restart=always -d --net="bridge" -p 27017:27017 \
-v /root/fct/mongocluster/master/data/db:/data/db \
-v /root/fct/mongocluster/master/logs/mongodb:/var/log/mongodb \
mongo:3.4 \
/bin/sh -c 'mongod --dbpath /data/db --replSet annosys'
#从节点slave1
docker run --name mongo-slave1 --restart=always -d --net="bridge" -p 27027:27017 \
-v /root/fct/mongocluster/slave1/data/db:/data/db \
-v /root/fct/mongocluster/slave1/logs/mongodb:/var/log/mongodb \
mongo:3.4 \
/bin/sh -c 'mongod --dbpath /data/db --replSet annosys'
#仲裁节点arbiter
docker run --name mongo-arbiter -d --net="bridge" -p 27037:27017 \
mongo:3.4 \
/bin/sh -c 'mongod --dbpath /data/db --replSet annosys --smallfiles'
将各个单个的mongdb通过配置构建成给一个mongodb集群
参考:https://blog.csdn.net/wangshuang1631/article/details/53857319
2.1在三台机器上任意一台机器登陆mongodb
进入任意一mongodb的容器中,然后
mongo
切换到admin 库
use admin
定义副本集配置变量,这里的 _id:” annosys” 和上面配置文件中replSet=annosys要保持一样。
config={ _id:"annosys", members:[
{_id:0,host:'172.19.32.142:27017',priority:5},
{_id:1,host:'172.19.32.142:27027',priority:3},
{_id:2,host:'172.19.32.142:27037',arbiterOnly:true}]
}
初始化副本集配置
rs.initiate(config)
查看集群节点的状态
rs.status()
annosys:SECONDARY> rs.status()
{
"set" : "annosys",
"date" : ISODate("2019-10-23T09:43:42.784Z"),
"myState" : 2,
"term" : NumberLong(1),
"syncingTo" : "172.19.32.142:27017",
"syncSourceHost" : "172.19.32.142:27017",
"syncSourceId" : 0,
"heartbeatIntervalMillis" : NumberLong(2000),
"optimes" : {
"lastCommittedOpTime" : {
"ts" : Timestamp(1571823820, 1),
"t" : NumberLong(1)
},
"appliedOpTime" : {
"ts" : Timestamp(1571823820, 1),
"t" : NumberLong(1)
},
"durableOpTime" : {
"ts" : Timestamp(1571823820, 1),
"t" : NumberLong(1)
}
},
"members" : [
{
"_id" : 0,
"name" : "172.19.32.142:27017",
"health" : 1,
"state" : 1,
"stateStr" : "PRIMARY",
"uptime" : 3032,
"optime" : {
"ts" : Timestamp(1571823820, 1),
"t" : NumberLong(1)
},
"optimeDurable" : {
"ts" : Timestamp(1571823820, 1),
"t" : NumberLong(1)
},
"optimeDate" : ISODate("2019-10-23T09:43:40Z"),
"optimeDurableDate" : ISODate("2019-10-23T09:43:40Z"),
"lastHeartbeat" : ISODate("2019-10-23T09:43:42.362Z"),
"lastHeartbeatRecv" : ISODate("2019-10-23T09:43:42.036Z"),
"pingMs" : NumberLong(0),
"lastHeartbeatMessage" : "",
"syncingTo" : "",
"syncSourceHost" : "",
"syncSourceId" : -1,
"infoMessage" : "",
"electionTime" : Timestamp(1571820799, 1),
"electionDate" : ISODate("2019-10-23T08:53:19Z"),
"configVersion" : 1
},
{
"_id" : 1,
"name" : "172.19.32.142:27027",
"health" : 1,
"state" : 2,
"stateStr" : "SECONDARY",
"uptime" : 4498,
"optime" : {
"ts" : Timestamp(1571823820, 1),
"t" : NumberLong(1)
},
"optimeDate" : ISODate("2019-10-23T09:43:40Z"),
"syncingTo" : "172.19.32.142:27017",
"syncSourceHost" : "172.19.32.142:27017",
"syncSourceId" : 0,
"infoMessage" : "",
"configVersion" : 1,
"self" : true,
"lastHeartbeatMessage" : ""
},
{
"_id" : 2,
"name" : "172.19.32.142:27037",
"health" : 1,
"state" : 7,
"stateStr" : "ARBITER",
"uptime" : 3032,
"lastHeartbeat" : ISODate("2019-10-23T09:43:42.362Z"),
"lastHeartbeatRecv" : ISODate("2019-10-23T09:43:40.850Z"),
"pingMs" : NumberLong(0),
"lastHeartbeatMessage" : "",
"syncingTo" : "",
"syncSourceHost" : "",
"syncSourceId" : -1,
"infoMessage" : "",
"configVersion" : 1
}
],
"ok" : 1
}
各个节点的健康状态正常。
如果在检查的过程中出现如下错误:not master and slaveOk=false
annosys:SECONDARY> use test;
switched to db test
annosys:SECONDARY> show tables;
2019-10-23T09:44:28.838+0000 E QUERY [thread1] Error: listCollections failed: {
"ok" : 0,
"errmsg" : "not master and slaveOk=false",
"code" : 13435,
"codeName" : "NotMasterNoSlaveOk"
}:
_getErrorWithCode@src/mongo/shell/utils.js:25:13
DB.prototype._getCollectionInfosCommand@src/mongo/shell/db.js:807:1
DB.prototype.getCollectionInfos@src/mongo/shell/db.js:819:19
DB.prototype.getCollectionNames@src/mongo/shell/db.js:830:16
shellHelper.show@src/mongo/shell/utils.js:807:9
shellHelper@src/mongo/shell/utils.js:704:15
此时需要执行rs.slaveOk()
命令即可
annosys:SECONDARY> rs.slaveOk()
annosys:SECONDARY> use test
switched to db test
annosys:SECONDARY> db.testdb.find()
{ "_id" : ObjectId("5db01956f6d1094b8e5c0a16"), "test1" : "testval1" }
{ "_id" : ObjectId("5db01f167be4feac94c9f596"), "name" : "fct----1" }
{ "_id" : ObjectId("5db01f167be4feac94c9f597"), "name" : "fct----2" }
{ "_id" : ObjectId("5db01f167be4feac94c9f599"), "name" : "fct----4" }
{ "_id" : ObjectId("5db01f167be4feac94c9f598"), "name" : "fct----3" }
如果发现数据同步,证明设置正常。
当前的集群是,只能在master中写数据,然后在salve中读数据,
如果在slave中执行写数据,则报如下错误
annosys:SECONDARY> db.testdb.insert({msg: 'this is from primary change yyy', ts: new Date()})
WriteResult({ "writeError" : { "code" : 10107, "errmsg" : "not master" } })
在master节点则没问题
annosys:PRIMARY> db.testdb.insert({msg: 'this is from primary change xxx', ts: new Date()})
WriteResult({ "nInserted" : 1 })
说明节点做了读写分离。
尝试docker停止master主节点服务时候,值slave节点容器中执行插入动作时候,
annosys:SECONDARY> db.testdb.insert({msg: 'this is from primary change yyy', ts: new Date()})
WriteResult({ "writeError" : { "code" : 10107, "errmsg" : "not master" } })
annosys:SECONDARY> db.testdb.insert({msg: 'this is from primary change yyy', ts: new Date()})
WriteResult({ "nInserted" : 1 })
annosys:PRIMARY> db.testdb.insert({msg: 'this is from primary change yyy', ts: new Date()})
WriteResult({ "nInserted" : 1 })
结果表明:
①此时原来只能从节点slave1只能读的节点,也可以写数据了;
②注意查看原来的annosys:SECONDARY
的从节点升级为annosys:PRIMARY
主节点了。
说明成功完成了故障转移。
至此,测试mongodb集群搭建和高可用性测试完成!
(后期如果需要添加从节点,可以直接修改配置文件后,更新配置即可!用时候需要测试)
package mongotest;
import com.mongodb.MongoClient;
import com.mongodb.ServerAddress;
import com.mongodb.client.FindIterable;
import com.mongodb.client.MongoCollection;
import com.mongodb.client.MongoDatabase;
import org.bson.Document;
import java.util.ArrayList;
import java.util.List;
/**
* 描述: java连接mongdb集群;测试高可用性
* @author: fangchangtan
* @version 创建时间:2018年11月26日 下午7:45:29
*/
public class TestMongoDBReplSet {
public static void main(String[] args) {
ArrayList<ServerAddress> arrayList = new ArrayList<>();
try {
//mongpdb的java连接配置为一个集群模式
List<ServerAddress> addresses = new ArrayList<ServerAddress>();
ServerAddress address1 = new ServerAddress("172.19.32.142" , 27017);
ServerAddress address2 = new ServerAddress("172.19.32.142" , 27027);
ServerAddress address3 = new ServerAddress("172.19.32.142" , 27037);
addresses.add(address1);
addresses.add(address2);
addresses.add(address3);
MongoClient mongoClient = new MongoClient(addresses);
MongoDatabase database = mongoClient.getDatabase("test");
MongoCollection<Document> collection = database.getCollection("testdb");
for (int i = 0; i < 1000; i++) {
System.out.println("==========================");
Thread.sleep(2000);
//向集群中插入文档数据
collection.insertOne(new Document("name","dog"+i));
//查询集群中的数据记录
FindIterable<Document> find = collection.find();
for(Document doc : find){
System.out.println(doc);
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
mongodb集群可以正常的插入和删除数据;
但是当集群发生slave与master节点切换的时候,会发生异常
2019-10-23 20:21:05,972 WARN [org.mongodb.driver.connection] - Got socket exception on connection [connectionId{localValue:4, serverValue:15}] to 172.19.32.142:27017. All connections to 172.19.32.142:27017 will be closed.
com.mongodb.MongoSocketReadException: Prematurely reached end of stream
at com.mongodb.connection.SocketStream.read(SocketStream.java:88)
at com.mongodb.connection.InternalStreamConnection.receiveResponseBuffers(InternalStreamConnection.java:494)
at com.mongodb.connection.InternalStreamConnection.receiveMessage(InternalStreamConnection.java:224)
at com.mongodb.connection.UsageTrackingInternalConnection.receiveMessage(UsageTrackingInternalConnection.java:96)
at com.mongodb.connection.DefaultConnectionPool$PooledConnection.receiveMessage(DefaultConnectionPool.java:440)
at com.mongodb.connection.WriteCommandProtocol.receiveMessage(WriteCommandProtocol.java:262)
at com.mongodb.connection.WriteCommandProtocol.execute(WriteCommandProtocol.java:104)
at com.mongodb.connection.InsertCommandProtocol.execute(InsertCommandProtocol.java:67)
at com.mongodb.connection.InsertCommandProtocol.execute(InsertCommandProtocol.java:37)
at com.mongodb.connection.DefaultServer$DefaultServerProtocolExecutor.execute(DefaultServer.java:168)
at com.mongodb.connection.DefaultServerConnection.executeProtocol(DefaultServerConnection.java:289)
at com.mongodb.connection.DefaultServerConnection.insertCommand(DefaultServerConnection.java:118)
at com.mongodb.operation.MixedBulkWriteOperation$Run$2.executeWriteCommandProtocol(MixedBulkWriteOperation.java:465)
at com.mongodb.operation.MixedBulkWriteOperation$Run$RunExecutor.execute(MixedBulkWriteOperation.java:656)
at com.mongodb.operation.MixedBulkWriteOperation$Run.execute(MixedBulkWriteOperation.java:411)
at com.mongodb.operation.MixedBulkWriteOperation$1.call(MixedBulkWriteOperation.java:177)
at com.mongodb.operation.MixedBulkWriteOperation$1.call(MixedBulkWriteOperation.java:168)
at com.mongodb.operation.OperationHelper.withConnectionSource(OperationHelper.java:426)
at com.mongodb.operation.OperationHelper.withConnection(OperationHelper.java:417)
at com.mongodb.operation.MixedBulkWriteOperation.execute(MixedBulkWriteOperation.java:168)
at com.mongodb.operation.MixedBulkWriteOperation.execute(MixedBulkWriteOperation.java:74)
at com.mongodb.Mongo.execute(Mongo.java:845)
at com.mongodb.Mongo$2.execute(Mongo.java:828)
at com.mongodb.MongoCollectionImpl.executeSingleWriteRequest(MongoCollectionImpl.java:550)
at com.mongodb.MongoCollectionImpl.insertOne(MongoCollectionImpl.java:317)
at com.mongodb.MongoCollectionImpl.insertOne(MongoCollectionImpl.java:307)
at mongotest.TestMongoDBReplSet.main(TestMongoDBReplSet.java:39)
报错:com.mongodb.MongoSocketReadException: Prematurely reached end of stream
这时候要使用try-catch{}捕获异常,并重连mongodb集群即可。(具体代码不再给出,参照redis集群自己体会!)
1.常见警告WARNING提示,可以忽略
搭建完成之后,进入任意mongo容器之中执行mongo
root@a10c7a769a4a:/# mongo
MongoDB shell version v3.4.23
connecting to: mongodb://127.0.0.1:27017
MongoDB server version: 3.4.23
Server has startup warnings:
2019-10-23T08:25:41.590+0000 I CONTROL [initandlisten]
2019-10-23T08:25:41.591+0000 I CONTROL [initandlisten] ** WARNING: Access control is not enabled for the database.
2019-10-23T08:25:41.591+0000 I CONTROL [initandlisten] ** Read and write access to data and configuration is unrestricted.
2019-10-23T08:25:41.591+0000 I CONTROL [initandlisten] ** WARNING: You are running this process as the root user, which is not recommended.
2019-10-23T08:25:41.591+0000 I CONTROL [initandlisten]
2019-10-23T08:25:41.591+0000 I CONTROL [initandlisten]
2019-10-23T08:25:41.591+0000 I CONTROL [initandlisten] ** WARNING: /sys/kernel/mm/transparent_hugepage/enabled is 'always'.
2019-10-23T08:25:41.591+0000 I CONTROL [initandlisten] ** We suggest setting it to 'never'
2019-10-23T08:25:41.591+0000 I CONTROL [initandlisten]
2019-10-23T08:25:41.591+0000 I CONTROL [initandlisten] ** WARNING: /sys/kernel/mm/transparent_hugepage/defrag is 'always'.
2019-10-23T08:25:41.591+0000 I CONTROL [initandlisten] ** We suggest setting it to 'never'
2019-10-23T08:25:41.591+0000 I CONTROL [initandlisten]
可以看到4个WARNING信息,基于安全的警告,对使用mongo基本没啥影响。
2.出现No host described in new configuration 1 for replica set annosys maps to this node
> use admin
switched to db admin
> config={ _id:"annosys", members:[
... {_id:0,host:'mongo-master:27017',priority:5},
... {_id:1,host:'mongo-slave1:27017',priority:3},
... {_id:3,host:'mongo-arbiter:27017',arbiterOnly:true}]
... }
{
"_id" : "annosys",
"members" : [
{
"_id" : 0,
"host" : "mongo-master:27017",
"priority" : 5
},
{
"_id" : 1,
"host" : "mongo-slave1:27017",
"priority" : 3
},
{
"_id" : 3,
"host" : "mongo-arbiter:27017",
"arbiterOnly" : true
}
]
}
> rs.initiate(config)
{
"ok" : 0,
"errmsg" : "No host described in new configuration 1 for replica set annosys maps to this node",
"code" : 93,
"codeName" : "InvalidReplicaSetConfig"
}
初始化集群失败,集群状态为失败。此时提示是配置项中host无法找到,副本集服务映射到该节点
需要将mongo-master名称修改为真实的ip:172.19.32.142。
config={ _id:"annosys", members:[
{_id:0,host:'172.19.32.142:27017',priority:5},
{_id:1,host:'172.19.32.142:27027',priority:3},
{_id:2,host:'172.19.32.142:27037',arbiterOnly:true}]
}
> rs.initiate(config)
{ "ok" : 1 }