一、引言
由于业务需求的井喷,对于系统服务的要求越来越高,为了满足系统稳定性、可靠性和可扩展上的发展,以及大量录音数据的分布式存储,为此开发Recdumper录音文件转储服务,将本地磁盘文件上传至MongoDB文件系统GridFS进行存储。Recdumper可配置工作目录,也就是存放录音的根目录。Recdumper会定时扫描该目录,将指定后缀的录音文件上传至MongoDB数据库。另外Recdumper提供Restful接口供系统使用,目的是告知Recdumper需要上传数据库的本地录音文件绝对路径。
注: 所有需要保留的数据均以ObjectID生成规则生成uuid作为文件名保存。
二、写入
-
定义restful接口,給其他服务调用。支持POST请求,body中包含文件所在路径。实现上采用libevent作为http服务端。
-
可支持配置文件的方式,定时遍历录音根目录,将数据存入MongoDB以年月日命名的db表中。
-
商定了由外部服务生成ObjectID,使用mongo-c-driver时,定制化修改了ObjectID的生成方式,使用外部传入字符串作为ObjectID,并将ObjectID作为GridFS文件名。
-
要留意配置的账号,需要 赋予用户所有数据库的读写权限( readWriteAnyDatabase)。如果新建账户,可以这样写:
db.createUser( {user: "admin",pwd: "123456",roles: [ { role: "readWriteAnyDatabase", db: "admin" } ]})
三、读取
Openresty作为多媒体文件的服务端,以开源lua-resty-moongoo模块和lua脚本处理请求消息,从MongoDB读取录音文件并应答。openresty配置参考:
location ~ ^/records/([-_a-zA-Z0-9/]+).mp3{set $audio_name $1;content_by_lua_file /usr/local/openresty/record_http_srv.lua;}
四、集群(Replica Sets)
-
概览
所有数据库都无法摆脱环境故障的影响。复制提供了一种防备故障的方式。除了避免故障,复制对于数据持久化功能也非常重要。但是要注意的是,虽然可复制集群是冗余的,但是它也无法取代备份机制。备份是过去某个时间点上数据库数据的快照,可复制集群通常是最新的。在通常的规则下,备份是必须的,即使运行了复制机制也需要启动备份。换句话说,备份是为了预防逻辑故障,比如突发性数据丢失或者数据冲突。生产环境下,一定要启用复制和日志功能,除非可以接受数据丢失。
-
可复制集(Replica sets)
可复制节点推荐使用的最小配置包含3个节点,在最小配置下,3个节点中的2个节点为第一级别,作为mongod实例。每个都可以作为可复制集的主节点,而且都有数据的完整拷贝。集合中的第3个节点,作为裁判,它不需要复制数据,仅仅作为观察者服务器,裁判是轻量级的MongoDB服务器,它只参与主节点选举,但是不会复制任何数据。
通过mongo shell进入主节点的数据库,执行复制集初始化命令
rs.initiate( {_id : "COOL",members: [{ _id: 0, host: "192.168.71.137:27017" },{ _id: 1, host: "192.168.71.137:27018" },{ _id: 2, host: "192.168.71.137:27019" }]})
查看复制集状态:
COOL:SECONDARY> rs.status(){"set" : "COOL","date" : ISODate("2020-06-15T09:10:10.347Z"),"myState" : 2,"term" : NumberLong(1),"syncingTo" : "192.168.71.137:27018","syncSourceHost" : "192.168.71.137:27018","syncSourceId" : 1,"heartbeatIntervalMillis" : NumberLong(2000),"majorityVoteCount" : 2,"writeMajorityCount" : 2,"optimes" : {"lastCommittedOpTime" : {"ts" : Timestamp(1592212192, 1),"t" : NumberLong(1)},"lastCommittedWallTime" : ISODate("2020-06-15T09:09:52.622Z"),"readConcernMajorityOpTime" : {"ts" : Timestamp(1592212192, 1),"t" : NumberLong(1)},"readConcernMajorityWallTime" : ISODate("2020-06-15T09:09:52.622Z"),"appliedOpTime" : {"ts" : Timestamp(1592212192, 1),"t" : NumberLong(1)},"durableOpTime" : {"ts" : Timestamp(1592212192, 1),"t" : NumberLong(1)},"lastAppliedWallTime" : ISODate("2020-06-15T09:09:52.622Z"),"lastDurableWallTime" : ISODate("2020-06-15T09:09:52.622Z")},"lastStableRecoveryTimestamp" : Timestamp(1592212191, 5),"lastStableCheckpointTimestamp" : Timestamp(1592212191, 5),"electionParticipantMetrics" : {"votedForCandidate" : true,"electionTerm" : NumberLong(1),"lastVoteDate" : ISODate("2020-06-15T09:09:51.633Z"),"electionCandidateMemberId" : 1,"voteReason" : "","lastAppliedOpTimeAtElection" : {"ts" : Timestamp(1592212180, 1),"t" : NumberLong(-1)},"maxAppliedOpTimeInSet" : {"ts" : Timestamp(1592212180, 1),"t" : NumberLong(-1)},"priorityAtElection" : 1,"newTermStartDate" : ISODate("2020-06-15T09:09:51.637Z"),"newTermAppliedDate" : ISODate("2020-06-15T09:09:52.610Z")},"members" : [{"_id" : 0,"name" : "192.168.71.137:27017","health" : 1,"state" : 2,"stateStr" : "SECONDARY","uptime" : 572,"optime" : {"ts" : Timestamp(1592212192, 1),"t" : NumberLong(1)},"optimeDate" : ISODate("2020-06-15T09:09:52Z"),"syncingTo" : "192.168.71.137:27018","syncSourceHost" : "192.168.71.137:27018","syncSourceId" : 1,"infoMessage" : "","configVersion" : 1,"self" : true,"lastHeartbeatMessage" : ""},{"_id" : 1,"name" : "192.168.71.137:27018","health" : 1,"state" : 1,"stateStr" : "PRIMARY","uptime" : 28,"optime" : {"ts" : Timestamp(1592212192, 1),"t" : NumberLong(1)},"optimeDurable" : {"ts" : Timestamp(1592212192, 1),"t" : NumberLong(1)},"optimeDate" : ISODate("2020-06-15T09:09:52Z"),"optimeDurableDate" : ISODate("2020-06-15T09:09:52Z"),"lastHeartbeat" : ISODate("2020-06-15T09:10:08.604Z"),"lastHeartbeatRecv" : ISODate("2020-06-15T09:10:09.637Z"),"pingMs" : NumberLong(0),"lastHeartbeatMessage" : "","syncingTo" : "","syncSourceHost" : "","syncSourceId" : -1,"infoMessage" : "","electionTime" : Timestamp(1592212191, 1),"electionDate" : ISODate("2020-06-15T09:09:51Z"),"configVersion" : 1},{"_id" : 2,"name" : "192.168.71.137:27019","health" : 1,"state" : 2,"stateStr" : "SECONDARY","uptime" : 28,"optime" : {"ts" : Timestamp(1592212192, 1),"t" : NumberLong(1)},"optimeDurable" : {"ts" : Timestamp(1592212192, 1),"t" : NumberLong(1)},"optimeDate" : ISODate("2020-06-15T09:09:52Z"),"optimeDurableDate" : ISODate("2020-06-15T09:09:52Z"),"lastHeartbeat" : ISODate("2020-06-15T09:10:08.604Z"),"lastHeartbeatRecv" : ISODate("2020-06-15T09:10:08.604Z"),"pingMs" : NumberLong(0),"lastHeartbeatMessage" : "","syncingTo" : "192.168.71.137:27018","syncSourceHost" : "192.168.71.137:27018","syncSourceId" : 1,"infoMessage" : "","configVersion" : 1}],"ok" : 1,"$clusterTime" : {"clusterTime" : Timestamp(1592212192, 1),"signature" : {"hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),"keyId" : NumberLong(0)}},"operationTime" : Timestamp(1592212192, 1)}
五、备份
复制提供了数据的冗余备份,并在多个服务器上存储数据副本,提高了数据的可用性,
并可以保证数据的安全性。备份则是为了应对灾难,当出现故障时,良好的备份可以节约大量的恢复时间。
对于备份
MongoDB
数据库,有一些通用的规则,如下所示:
-
使用 mongodump 和 mongorestore
-
复制原始数据文件
-
使用 MMS 备份