簡單副本集測試
這一節主要對上一節搭建的副本集做一些簡單的測試。
我們首先進入primary節點(37017),並向test.test集合里插入10W條數據:
1. rs0:PRIMARY> for(var i=0;i<100000;i++){ 2. db.test.insert({"name":"zhanjindong"+i,"age":23}) 3. }
等數據插入完畢我們登入到兩個secondary節點,發現數據已經同步過來了:
./bin/mongo -port 37018 2. rs0:SECONDARY> db.getMongo().setSlaveOk(); 3. rs0:SECONDARY> db.test.count() 4. 100000
注意:在secondary節點上執行操作之前需要執行db.getMongo().setSlaveOk()命令,該設置允許連接從非master端讀取數據。
secondary節點宕機:
模擬副本集中一個secondary節點宕機的情況,直接kill -9掉37018這個節點:
1. kill -9 9508
然后再登錄之前primary節點,再插入1W條數據:
1. rs0:PRIMARY> for(var i=0;i<10000;i++){ 2. db.test.insert({"name":"zhanjindong"+i,"age":23}) 3. }
然后再將37018這個節點啟起來,過一會就發現宕機的節點數據已經同步過來了:
1. rs0:SECONDARY> db.getMongo().setSlaveOk() 2. rs0:SECONDARY> db.test.count() 3. 110000
primary節點宕機:
再模擬primary節點宕機的情況:kill -9掉37017端口,這時查看監控頁面:http://192.168.129.129:38018/_replSet/
可以看到37018已經成了primary節點。主界面宕機導致了整個集群發生一次election,實現了failover。等37017恢復了會自動成為secondary節點:
所有secondary節點宕機:
當所有secondary宕機,或者副本集中只剩下一台機器的時候,那么剩下的機器只能成為secondary節點,也就意味着整個集群智能進行讀操作而不能進行寫操作:
當集群從故障中恢復過來后,仍然的primary節點仍然是primary節點。
注意:當某個節點宕機后重新啟動該節點會有一段的時間(時間長短視集群的數據量和宕機時間而定)導致整個集群中所有節點都成為secondary而無法進行寫操作(如果應用程序沒有設置相應的ReadReference也可能不能進行讀取操作)。
因此官方推薦的最小的副本集也應該具備一個primary節點和兩個secondary節點。兩個節點的副本集不具備真正的故障轉移能力。
應用程序訪問副本集
副本集搭建好了,基本的故障轉移能力也驗證過了,那么應用程序如何訪問呢?下面分別介紹下C#和Java驅動如何訪問mongodb副本集。
C#:
C#驅動下載地址http://driver-downloads.mongodb.org/dotnet/index.html,我使用的最新版本1.8.2。
下面是訪問副本集基本的代碼:
MongoClientSettings set = new MongoClientSettings(); List<MongoServerAddress> servers = new List<MongoServerAddress>(); servers.Add(new MongoServerAddress("192.168.129.129", 37017)); servers.Add(new MongoServerAddress("192.168.129.129", 37018)); servers.Add(new MongoServerAddress("192.168.129.129", 37019)); set.Servers = servers; //設置副本集名稱 set.ReplicaSetName = "rs0"; //設置超時時間為3秒 set.ConnectTimeout = new TimeSpan(0, 0, 0, 3, 0); MongoClient client = new MongoClient(set); MongoServer server = client.GetServer(); MongoDatabase db = server.GetDatabase("test"); MongoCollection coll = db.GetCollection("test"); //插入 BsonDocument bd = new BsonDocument(); bd.Add("name", "zhanjindong"); bd.Add("age", 23); bd.Add("sex", "男D"); coll.Insert(bd); //讀取 QueryDocument qd = new QueryDocument(); qd.Add("name", "zhanjindong"); qd.Add("age", 23); qd.Add("sex", "男D"); BsonDocument rd = coll.FindOneAs<BsonDocument>(qd); Console.WriteLine(rd.ToString());
當kill掉一個節點(secondary或primary)的時候,讀取和寫入的操作都是正常的,說明副本集的failover起作用了。但kill模擬兩個節點宕機的時候,發現上面的代碼在讀取的時候會報下面的錯誤:
這是因為默認的C#驅動的ReadPreference是Primary,也就是說讀寫操作都是在主節點上的,那么當集群中只剩下一個節點的時候,按照前面所說該節點一定是secondary節點,這樣讀取操作也是沒法進行的(通過mongo shell當然是可以的)。解決方法就是設置驅動的ReadPreferenceMode:
set.ReadPreference = new ReadPreference(ReadPreferenceMode.PrimaryPreferred);
將ReadPreferenceMode設置成Secondary或SecondaryPreferred。后面讀寫分離一節對ReadPreference會有詳細的說明。
Java:
Java驅動下載地址:http://central.maven.org/maven2/org/mongodb/mongo-java-driver/
我用的是最新版本mongo-java-driver-2.11.2.jar。
下面是Java訪問MongoDB副本集的基本代碼:
List<ServerAddress> addresses = new ArrayList<ServerAddress>(); ServerAddress address1 = new ServerAddress("192.168.129.129", 37017); ServerAddress address2 = new ServerAddress("192.168.129.129", 37018); ServerAddress address3 = new ServerAddress("192.168.129.129", 37018); addresses.add(address1); addresses.add(address2); addresses.add(address3); MongoClient client = new MongoClient(addresses); DB db = client.getDB("test"); DBCollection coll = db.getCollection("test"); // 插入 BasicDBObject object = new BasicDBObject(); object.append("name", "zhanjindong"); object.append("age", 23); object.append("sex", "男"); coll.insert(object); BasicDBObject qobj = new BasicDBObject(); qobj.append("name", "zhanjindong"); qobj.append("age", 23); qobj.append("sex", "男"); DBObject robj = coll.findOne(qobj); System.out.println(robj.toString());
和C#一樣如果想從secondary節點進行讀取操作也是需要設置ReadPreference:
ReadPreference preference = ReadPreference.secondary();
DBObject robj = coll.findOne(qobj, qobj, preference);
不同的是Java驅動發現副本集中某個成員連接不了會發出警告信息:
而且發現如果讀取不到的話Java驅動會進行不停的重試。
注:設置驅動的ReadReference也可以通過MongoDB連接字符串配置:mongodb://example1.com,example2.com,example3.com/?readPreference=secondary。通過連接字符串指定的read preference是針對整個連接。