博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
MongoDB3.6 一键化自动部署方案
阅读量:4089 次
发布时间:2019-05-25

本文共 37763 字,大约阅读时间需要 125 分钟。

1.系统基础配置

下面的命令默认都使用root用户进行操作,操作系统为Centos7,mongodb3.6.x以上版本

1.1 修改系统配置文件/etc/security/limits.conf和/etc/profile

 修改/etc/security/limits.conf持久化设置允许用户/进程打开文件句柄数,这一步需要重启系统,不然不起作用

* soft nofile 1048576

* hard nofile 1048576

* soft nproc 524288

* hard nproc 524288

修改/etc/profile,在最后添加ulimit -s 1024,然后保存并source /etc/profile

1.2 关闭每台机器的防火墙

Centos7以下命令:

chkconfig iptables off && service iptables stop

 

使用命令查看chkconfig --list 是否设置自动启动为关闭

Centos7以上命令:

systemctl stop firewalld

systemctl is-enabled firewalld  

1.3 每台机器修改/etc/hosts和hostname

在/etc/hosts添加集群所有的ip及对应的hostname

注意:配置的ip数与选择的模板相关联

1.4 机器之间配置ssh免密登录

每台机器分别执行下面命令,这里以5台机器为准:

ssh-keygen -t rsa -P "" -f /root/.ssh/id_rsa

ssh-copy-id node1

ssh-copy-id node2

ssh-copy-id node3

ssh-copy-id node4

ssh-copy-id node5

1.5 检测自动化部署所需的命令是否可用

ifconfig、ssh需要可用

需要安装numactl,使用rpm -qa | grep numactl查看numactl是否已安装

注意:完成上面5步操作后重启每台机器。重启完毕,使用命令ulimit -a 查看

要保证 open files 对应的值为 1048576

Stack size 对应的值为1024

Max user processes对应的值为 524288

1.6 检查mongodb安装下bin目录和shell目录执行权限

进入到shell目录,执行chmod +x auto*加执行权限

进入mongodb的bin目录,执行chmod +x ./*加执行权限

2.MongoDB 一键化部署

2.1 一键化部署shell文件介绍

shell脚本文件目前放置在mongodb的安装目录下:

⑪config.properties,配置文件,包括mongodb安装目录、mongoCluster集群安装目录、所需主机、数据库等等。如图:

 

上图几个参数可能需要随环境不同而修改。

说明:

ips=192.168.187.201, 192.168.187.202, 192.168.187.203, 192.168.187.204, 192.168.187.205

这里默认192.168.187.201为mongdb集群的节点node1,192.168.187.202为mongdb集群的节点node2,192.168.187.203为mongdb集群的节点node3,192.168.187.204为mongdb集群的节点node4,,192.168.187.205为mongdb集群的节点node5。

template与ips的ip数量对应,ips的ip数为5,则意味着采用5台机器的模板,这里默认为5。

⑬template 目录包括3.conf、4.conf和5.conf,为mongodb3台机器、4台机器和5台机器安装模板,有需要者可以自行修改模板。

3.conf 3台机器分配的mongodb节点模板为

#所有mongo节点nodes=config,shard1,shard2,shard3,mongos#机器分配到的mongo节点node1=mongos,config,shard1,shard2,shard3node2=mongos,config,shard1,shard2,shard3node3=mongos,config,shard1,shard2,shard3#mongo节点分配到的机器,注意shard部分排在第一的为master,第二为secondary,第三位arbiteryconfig=node1,node2,node3mongos=node1,node2,node3shard1=node1,node2,node3shard2=node2,node3,node1shard3=node3,node2,node1#mongo节点端口config_port=29040mongos_port=29050shard1_port=29010shard2_port=29020shard3_port=29030

注意:这里的node1、node2、node3不是机器hostname(当然也可以以这种node1、node2方式去命名hostname),只是机器ip的代名称,因为不同开发环境中的ip总是不同的,所以使用node1、node2...去代替变化的ip。这里的node1、node2、node3默认与config.properties中的ips属性对应,即ips中的第一个ip默认为node1,第二个ip默认为node2,第三个ip默认为node3,4.conf和5.conf类同。查看下图,可以清晰看到mongodb分布的节点:

4.conf 4台机器分配mongodb节点的模版为

#所有mongo节点nodes=config,shard1,shard2,shard3,shard4,mongos#机器分配到的mongo节点node1=mongos,shard1,shard3,shard4node2=mongos,config,shard1,shard2,shard4node3=mongos,config,shard1,shard2,shard3node4=config,shard2,shard3,shard4#mongo节点分配到的机器,注意shard部分排在第一的为master,第二为secondary,第三位arbiteryconfig=node2,node3,node4mongos=node1,node2,node3shard1=node1,node2,node3shard2=node2,node3,node4shard3=node3,node4,node1shard4=node4,node1,node2#mongo节点端口config_port=29040mongos_port=29050shard1_port=29000shard2_port=29010shard3_port=29020shard4_port=29030

5.conf 5台机器分配mongodb节点的模版为

#所有mongo节点nodes=config,shard1,shard2,shard3,shard4,shard5,mongos#机器分配到的mongo节点node1=mongos,shard1,shard4,shard5node2=mongos,shard1,shard2,shard5node3=config,shard1,shard2,shard3node4=config,shard2,shard3,shard4node5=config,shard3,shard4,shard5#mongo节点分配到的机器, 注意shard部分排在第一的为master,第二为secondary,第三位arbiteryconfig=node3,node4,node5mongos=node1,node2shard1=node1,node2,node3shard2=node2,node3,node4shard3=node3,node4,node5shard4=node4,node5,node1shard5=node5,node1,node2#mongo节点端口config_port=29040mongos_port=29050shard1_port=29060shard2_port=29070shard3_port=29080shard4_port=29090shard5_port=29100

模板需要配置的几项内容:

  • mongodb所有节点
  • 每台机器分配的mongodb节点
  • 每个mongodb节点分配的机器
  • mongo节点的端口

如有需要,可自行配置6、7或者更多的机器分配mongo模板

①autoCheckLive.sh脚本为创建所在服务器对应的mongodb的进程存活监控,不存在则自动拉起。

#! /bin/bash#shell目录的绝对路径shellPath=$1configPath=$shellPath/config.properties#从config.properties文件读取数据出来clusterPath=`awk -F= -v k=clusterPath '{ if ( $1 == k ) print $2; }' $configPath`template=`awk -F= -v k=template '{ if ( $1 == k ) print $2; }' $configPath`templatePath=$shellPath/template/$template.confips=`awk -F= -v k=ips '{ if ( $1 == k ) print $2; }' $configPath`#ip数组eval $(echo $ips | awk '{split($0, arr, ","); for(i in arr) print "ipArray["i"]="arr[i]}')#本地iplocalIp=`/sbin/ifconfig -a|grep inet|grep -v 127.0.0.1|grep -v inet6 | awk '{print $2}' | tr -d "addr:"`suffix=""#判断linux系统cpu是否为numa架构,如果是则传回_numa,禁用numactlnumaCount=`grep -i numa /var/log/dmesg | wc -l`nodeCount=`grep -i numa /var/log/dmesg | grep -wi "node" | wc -l`offCount=`grep -i numa /var/log/dmesg | grep -wi "numa=off" | wc -l`if [[ ($numaCount -gt 1) && ($nodeCount -gt 1) && ($offCount -eq 0) ]]then    suffix="_numa"fi#遍历ip数组for((i=1; i<=${#ipArray[@]}; i++))do  #获取本地ip在模板中的代称号,根据本地ip所在ip数组中的下标数字获取  if [[ $localIp = ${ipArray[i]} ]]  then       #获取模板中本地ip对应的代称号所拥有的mongo节点       mongodbNodes=`awk -F= -v k=node$i '{ if ( $1 == k ) print $2; }' $templatePath`       eval $(echo $mongodbNodes | awk '{split($0, mongodbArr, ","); for(y in mongodbArr) print "mongodbArray["y"]="mongodbArr[y]}')       for n in ${mongodbArray[*]}       do         pid=`cat $clusterPath/$n/pid/$n.pid`         #判断是否存活         count=`ps -ef | grep $pid | grep -v grep | wc -l`         #程序挂掉啦,启动         if [ $count -eq 0 ];then            cd $shellPath && $shellPath/autoStartUp.sh $n$suffix         fi       done  fidone

②autoClusterClose.sh脚本为mongodb集群关闭脚本,用法:./autoClusterClose.sh shutdown或者./autoClusterClose.sh kill,这里建议使用shutdown方式关闭。

#! /bin/bashconfigPath=config.properties#mongodb的安装目录mongodb_home=`awk -F= -v k=mongodbHome '{ if ( $1 == k ) print $2; }' $configPath`#mongodb集群安装目录clusterPath=`awk -F= -v k=clusterPath '{ if ( $1 == k ) print $2; }' $configPath`#所采用的安装模板template=`awk -F= -v k=template '{ if ( $1 == k ) print $2; }' $configPath`#模板所在的位置templatePath=template/$template.confuser=`awk -F= -v k=user '{ if ( $1 == k ) print $2; }' $configPath`localIp=`/sbin/ifconfig -a|grep inet|grep -v 127.0.0.1|grep -v inet6 | awk '{print $2}' | tr -d "addr:"`ips=`awk -F= -v k=ips '{ if ( $1 == k ) print $2; }' $configPath`eval $(echo $ips | awk '{split($0, arr, ","); for(i in arr) print "ipArray["i"]="arr[i]}')#获取所有的配置、路由、分片nodes=`awk -F= -v k=nodes '{ if ( $1 == k ) print $2; }' $templatePath`eval $(echo $nodes | awk '{split($0, nodeArr, ","); for(i in nodeArr) print "nodeArray["i"]="nodeArr[i]}')#查看/etc/crontab是否开启定时任务,有则关闭for((i=1; i<=${#ipArray[@]}; i++))do   if [[ $localIp = ${ipArray[i]} ]]   then       #kill本地的mongod、mongos       echo "*****close ${ipArray[i]} 定时任务:日志切割和mongo进程存活监控*****"       $mongodb_home/shell/autoConfig.sh removeCronTask   else       echo "*****close ${ipArray[i]} 定时任务:日志切割和mongo进程存活监控*****"       ssh $user@${ipArray[i]} "cd $mongodb_home/shell && $mongodb_home/shell/autoConfig.sh removeCronTask"    fidoneif [[ $1 = "kill" ]]then  for((i=1; i<=${#ipArray[@]}; i++))  do    if [[ $localIp = ${ipArray[i]} ]]    then       #kill本地的mongod、mongos       echo "*****close ${ipArray[i]} mongodb*****"       ps -ef | grep $clusterPath/conf | grep -v grep | cut -c 9-15 | xargs kill -2    else       echo "*****close ${ipArray[i]} mongodb*****"       ssh $user@${ipArray[i]} "ps -ef | grep $clusterPath/conf | grep -v grep | cut -c 9-15 | xargs kill -2"    fi  donefiif [[ $1 = "shutdown" ]]then  #1.先关闭mongos  #找到mongos对应的机器节点  mongoss=`awk -F= -v k=mongos '{ if ( $1 == k ) print $2; }' $templatePath`  eval $(echo $mongoss | awk '{split($0, mongosArr, ","); for(i in mongosArr) print "mongosArray["i"]="mongosArr[i]}')  for mongosNode in ${mongosArray[*]}  do    #删除node,保留右边字符。下面两句是根据机器节点的代名称数字找到其对应的ip    mongosIpNum=${mongosNode#*node}    mongosIp=${ipArray[$mongosIpNum]}    if [[ $localIp = $mongosIp ]]    then       echo "****************close $mongosIp mongos****************"       ps -ef | grep $clusterPath/conf/mongos.conf | grep -v grep | cut -c 9-15 | xargs kill -2    else       echo "****************close $mongosIp mongos****************"       ssh $user@$mongosIp "ps -ef | grep $clusterPath/conf/mongos.conf | grep -v grep | cut -c 9-15 | xargs kill -2"    fi  done    #2.再关闭configs  configs=`awk -F= -v k=config '{ if ( $1 == k ) print $2; }' $templatePath`  eval $(echo $configs | awk '{split($0, configArr, ","); for(i in configArr) print "configArray["i"]="configArr[i]}')  #副本集,先关闭仲裁节点、从节点,最后关闭主节点  for((i=${#configArray[@]}; i>=1; i--))  do    configNode=${configArray[i]}    #删除node,保留右边字符    configIpNum=${configNode#*node}    configIp=${ipArray[$configIpNum]}    if [[ $localIp = $configIp ]]    then       echo "****************close $configIp config****************"       $mongodb_home/bin/mongod -f $clusterPath/conf/config.conf --shutdown    else       echo "****************close $configIp config****************"       ssh $user@$configIp "$mongodb_home/bin/mongod -f $clusterPath/conf/config.conf --shutdown"    fi  done  #3.最后关闭shards  for node in ${nodeArray[*]}    do       if [[ $node =~ "shard"  ]]                then           shards=`awk -F= -v k=$node '{ if ( $1 == k ) print $2; }' $templatePath`           eval $(echo $shards | awk '{split($0, shardArr, ","); for(i in shardArr) print "shardArray["i"]="shardArr[i]}')           #副本集,先关闭仲裁节点、从节点,最后关闭主节点           for((i=${#shardArray[@]}; i>=1; i--))           do              shardNode=${shardArray[i]}              #删除node,保留右边字符              shardIpNum=${shardNode#*node}              shardIp=${ipArray[$shardIpNum]}              if [[ $localIp = $shardIp ]]              then                 echo "****************close $shardIp $node****************"                 $mongodb_home/bin/mongod -f $clusterPath/conf/$node.conf --shutdown              else                 echo "****************close $shardIp $node****************"                 ssh $user@$shardIp "$mongodb_home/bin/mongod -f $clusterPath/conf/$node.conf --shutdown"              fi           done       fi    donefi

③autoClusterIndex.sh脚本为mongodb集群初始化配置文件和修改系统配置、config和shard副本集以及mongos路由启动并初始化、mongodb集群数据库表分片和基础数据初始化的脚本、添加定时任务。用法:./ autoClusterIndex.sh。使用该脚本,可以全自动化安装mongodb

#! /bin/bashconfigPath=config.properties#从config.properties文件读取数据出来clusterPath=`awk -F= -v k=clusterPath '{ if ( $1 == k ) print $2; }' $configPath`mongodb_home=`awk -F= -v k=mongodbHome '{ if ( $1 == k ) print $2; }' $configPath`ips=`awk -F= -v k=ips '{ if ( $1 == k ) print $2; }' $configPath`localIp=`/sbin/ifconfig -a|grep inet|grep -v 127.0.0.1|grep -v inet6 | awk '{print $2}' | tr -d "addr:"`user=`awk -F= -v k=user '{ if ( $1 == k ) print $2; }' $configPath`template=`awk -F= -v k=template '{ if ( $1 == k ) print $2; }' $configPath`templatePath=template/$template.conf#获取所有的配置、路由、分片nodes=`awk -F= -v k=nodes '{ if ( $1 == k ) print $2; }' $templatePath`eval $(echo $nodes | awk '{split($0, nodeArr, ","); for(i in nodeArr) print "nodeArray["i"]="nodeArr[i]}')eval $(echo $ips | awk '{split($0, arr, ","); for(i in arr) print "ipArray["i"]="arr[i]}')#遍历ip数组for((i=1; i<=${#ipArray[@]}; i++))do  if [[ $localIp = ${ipArray[i]} ]]  then     #1.创建mongodbCluster并修改系统配置     echo "********************************start ${ipArray[i]} autoConfig && autoSystemProperties********************************"     $mongodb_home/shell/autoConfig.sh && $mongodb_home/shell/autoSystemProperties.sh  else     #把远程服务器旧的mongodb安装包删除     ssh $user@${ipArray[i]} "rm -rf $mongodb_home"     #把mongodb安装包拷贝到远程服务器     scp -r $mongodb_home $user@${ipArray[i]}:$mongodb_home     echo "********************************start ${ipArray[i]} autoConfig && autoSystemProperties********************************"     ssh $user@${ipArray[i]} "cd $mongodb_home/shell && $mongodb_home/shell/autoConfig.sh && $mongodb_home/shell/autoSystemProperties.sh"  fidone#2.mongodb集群启动config、shard1、shard2、shard3echo "********************************start mongodb集群启动config、shard1、shard2、shard3********************************"#不启动mongos,只启动config,shards$mongodb_home/shell/autoClusterStartUp.sh notmongos#3.mongodb集群配置、分片的副本集初始化echo "********************************start mongodb集群分片和副本集初始化********************************"$mongodb_home/shell/autoClusterInitSvr.sh cs#4.mongodb集群启动mongosecho "********************************start mongodb集群mongos********************************"mongoss=`awk -F= -v k=mongos '{ if ( $1 == k ) print $2; }' $templatePath`eval $(echo $mongoss | awk '{split($0, mongosArr, ","); for(i in mongosArr) print "mongosArray["i"]="mongosArr[i]}')for mongosNode in ${mongosArray[*]}do  #删除node,保留右边字符  mongosIpNum=${mongosNode#*node}  mongosIp=${ipArray[$mongosIpNum]}  if [[ $localIp = $mongosIp ]]  then      echo "****************start $mongosIp mongos****************"      $mongodb_home/shell/autoStartUp.sh mongos  else      echo "****************start $mongosIp mongos****************"      ssh $user@$mongosIp "cd $mongodb_home/shell && ./autoStartUp.sh mongos"  fidone#5.mongodb集群mongos初始化echo "********************************start mongodb集群mongos初始化********************************"$mongodb_home/shell/autoClusterInitSvr.sh mongos#6.mongodb集群数据库表分片和初始化echo "********************************start mongodb集群数据库表分片和初始化********************************"$mongodb_home/shell/autoClusterShardedAndInitDB.sh#7.mongodb集群配置定时任务:日志每天切割,保留7天日志/每隔10分钟监控mongo进程是否存活,不存活则自动拉起for((i=1; i<=${#ipArray[@]}; i++))do  if [[ $localIp = ${ipArray[i]} ]]  then     echo "******************************** ${ipArray[i]} 添加定时任务:日志切割和mongo存活监控 ********************************"     $mongodb_home/shell/autoConfig.sh addCronTask      else     echo "******************************** ${ipArray[i]} 添加定时任务:日志切割和mongo存活监控 ********************************"     ssh $user@${ipArray[i]} "cd $mongodb_home/shell && $mongodb_home/shell/autoConfig.sh addCronTask"  fidone

④autoClusterInitSvr.sh脚本为mongodb集群config、shard和mongos初始化脚本。用法:./autoClusterInitSvr.sh cs,初始化config和shard副本集配置;./autoClusterInitSvr.sh mongos,初始化mongos路由配置。

#! /bin/bashconfigPath=config.propertiesmongodb_home=`awk -F= -v k=mongodbHome '{ if ( $1 == k ) print $2; }' $configPath`template=`awk -F= -v k=template '{ if ( $1 == k ) print $2; }' $configPath`templatePath=template/$template.confips=`awk -F= -v k=ips '{ if ( $1 == k ) print $2; }' $configPath`eval $(echo $ips | awk '{split($0, arr, ","); for(i in arr) print "ipArray["i"]="arr[i]}')#获取所有的配置、路由、分片nodes=`awk -F= -v k=nodes '{ if ( $1 == k ) print $2; }' $templatePath`eval $(echo $nodes | awk '{split($0, nodeArr, ","); for(i in nodeArr) print "nodeArray["i"]="nodeArr[i]}')param=$1if [[ $param = "cs" ]]then        #自动化设置配置副本集    config_numbers=""    ccount=0    config_port=`awk -F= -v k=config_port '{ if ( $1 == k ) print $2; }' $templatePath`    configs=`awk -F= -v k=config '{ if ( $1 == k ) print $2; }' $templatePath`    eval $(echo $configs | awk '{split($0, configArr, ","); for(i in configArr) print "configArray["i"]="configArr[i]}')    configMasterNode=${configArray[1]}    echo "configMasterNode: $configMasterNode"    #删除node,保留右边字符    configMasterIpNum=${configMasterNode#*node}    #echo "configMasterIpNum: $configMasterIpNum"    configMasterIp=${ipArray[$configMasterIpNum]}    #echo "configMasterIp: $configMasterIp"    for((i=1; i<=${#configArray[@]}; i++))    do       configNode=${configArray[i]}       #删除node,保留右边字符       configIpNum=${configNode#*node}       config_numbers=$config_numbers"{_id : $ccount, host : '${ipArray[$configIpNum]}:$config_port'},"       ccount=`expr $ccount + 1`    done    #删除最后一个,保留左边字符    echo "********************************设置config副本集********************************"    config_numbers=${config_numbers%,*}    echo $config_numbers    $mongodb_home/bin/mongo $configMasterIp:$config_port/admin << EOFconfig = {_id : "configs", members : [ $config_numbers ] };rs.initiate(config);EOF    #自动化设置分片shard副本集    for node in ${nodeArray[*]}    do       if [[ $node =~ "shard"  ]]                then           shard_numbers=""           shardMasterIp=""           scount=0           shard_port=`awk -F= -v k=$node"_port" '{ if ( $1 == k ) print $2; }' $templatePath`           shards=`awk -F= -v k=$node '{ if ( $1 == k ) print $2; }' $templatePath`           eval $(echo $shards | awk '{split($0, shardArr, ","); for(i in shardArr) print "shardArray["i"]="shardArr[i]}')           for((i=1; i<=${#shardArray[@]}; i++))           do              shardNode=${shardArray[i]}              #echo "shardNode: $shardNode"              #删除node,保留右边字符              shardIpNum=${shardNode#*node}              #echo "shardIpNum: $shardIpNum"              if [[ $scount = 0 ]]              then                  shardMasterIp=${ipArray[$shardIpNum]}                  shard_numbers=$shard_numbers"{_id : $scount, host : '${ipArray[$shardIpNum]}:$shard_port', priority : 2},"              fi              if [[ $scount = 1 ]]              then                  shard_numbers=$shard_numbers"{_id : $scount, host : '${ipArray[$shardIpNum]}:$shard_port', priority : 1},"              fi              if [[ $scount = 2 ]]              then                  shard_numbers=$shard_numbers"{_id : $scount, host : '${ipArray[$shardIpNum]}:$shard_port', arbiterOnly : true}"              fi              scount=`expr $scount + 1`           done              #echo "shard_numbers: $shard_numbers"              echo "********************************设置$node副本集********************************"              $mongodb_home/bin/mongo $shardMasterIp:$shard_port/admin << EOFconfig = {_id : "$node", members : [ $shard_numbers ] };rs.initiate(config);EOF       fi    donefiif [[ $param = "mongos" ]]then      #自动化设置路由分片    mongos_numbers=""    mongos_port=`awk -F= -v k=mongos_port '{ if ( $1 == k ) print $2; }' $templatePath`    mongoss=`awk -F= -v k=mongos '{ if ( $1 == k ) print $2; }' $templatePath`    eval $(echo $mongoss | awk '{split($0, mongosArr, ","); for(i in mongosArr) print "mongosArray["i"]="mongosArr[i]}')    mongosNode=${mongosArray[1]}    #删除node,保留右边字符    mongosIpNum=${mongosNode#*node}    mongosIp=${ipArray[$mongosIpNum]}    for node in ${nodeArray[*]}    do       if [[ $node =~ "shard" ]]           then           shard_numbers=""           shard_port=`awk -F= -v k=$node"_port" '{ if ( $1 == k ) print $2; }' $templatePath`           shards=`awk -F= -v k=$node '{ if ( $1 == k ) print $2; }' $templatePath`           eval $(echo $shards | awk '{split($0, shardArr, ","); for(i in shardArr) print "shardArray["i"]="shardArr[i]}')           for((i=1; i<=${#shardArray[@]}; i++))           do              shardNode=${shardArray[i]}              #删除node,保留右边字符              shardIpNum=${shardNode#*node}              shardIp=${ipArray[$shardIpNum]}              shard_numbers=$shard_numbers$shardIp:$shard_port","           done           shard_numbers=${shard_numbers%,*}           echo "mongos add $node shard_numbers:"$shard_numbers           echo "********************************添加mongos分片$node********************************"           $mongodb_home/bin/mongo $mongosIp:$mongos_port/admin << EOFsh.addShard("$node/$shard_numbers");EOF       fi      done    $mongodb_home/bin/mongo $mongosIp:$mongos_port/admin << EOFsh.status();EOFfi

上述代码实现的config、shards以及mongos配置内容如下:

config副本集配置,参考示例:

> config = {

...      _id : "configs",

...      members : [

...         {_id : 0, host : "192.168.187.203:29040"},

...         {_id : 1, host : "192.168.187.204:29040"},

...         {_id : 2, host : "192.168.187.205:29040"}

...      ]

... }

> rs.initiate(config)

shards副本集配置,参考示例:

> config = {

...      _id : "shard1",

...      members : [

...         {_id : 0, host : "192.168.187.201:29060",priority:2},

...         {_id : 1, host : "192.168.187.202:29060",priority:1},

...         {_id : 2, host : "192.168.187.203:29060", arbiterOnly : true}

...      ]

... }

> rs.initiate(config)

这里默认,第一个ip的优先级为2,第二个ip的优先级为1,第三个ip为仲裁节点,所以第一个ip为primary主节点,第二位secondary从节点,第三个只参与投票,shard1根据分片的数据变化为shard2、shard3、shard4、shard5等等。

mongos配置内容为添加shards,参考示例为:

sh.addShard("shard1/192.168.187.201:29060,192.168.187.202:29060,192.168.187.203:29060")

sh.addShard("shard2/192.168.187.202:29070,192.168.187.203:29070,192.168.187.204:29070")

sh.addShard("shard3/192.168.187.203:29080,192.168.187.204:29080,192.168.187.205:29080")

sh.addShard("shard4/192.168.187.204:29090,192.168.187.205:29090,192.168.187.201:29090")

sh.addShard("shard5/192.168.187.205:29100,192.168.187.201:29100,192.168.187.202:29100")

sh.status() #参考分片的情况是否添加上

⑤autoClusterShardedAndInitDB.sh 脚本为指定数据库testDB1、testDB2、testDB3及其数据库表分片。

#! /bin/bashconfigPath=config.propertiesmongodb_home=`awk -F= -v k=mongodbHome '{ if ( $1 == k ) print $2; }' $configPath`ips=`awk -F= -v k=ips '{ if ( $1 == k ) print $2; }' $configPath`template=`awk -F= -v k=template '{ if ( $1 == k ) print $2; }' $configPath`templatePath=template/$template.confmongoss=`awk -F= -v k=mongos '{ if ( $1 == k ) print $2; }' $templatePath`testDB1=`awk -F= -v k=testDB1 '{ if ( $1 == k ) print $2; }' $configPath`testDB2=`awk -F= -v k=testDB2 '{ if ( $1 == k ) print $2; }' $configPath`testDB3=`awk -F= -v k=testDB3 '{ if ( $1 == k ) print $2; }' $configPath`eval $(echo $ips | awk '{split($0, arr, ","); for(i in arr) print "ipArray["i"]="arr[i]}')eval $(echo $mongoss | awk '{split($0, mongosArr, ","); for(i in mongosArr) print "mongosArray["i"]="mongosArr[i]}')for mongosNode in ${mongosArray[*]}do  #删除node,保留右边字符  mongosIpNum=${mongosNode#*node}  mongosIp=${ipArray[$mongosIpNum]}  #指定数据库分片生效  $mongodb_home/bin/mongo $mongosIp:29050/admin << EOFdb.runCommand({enablesharding : "$testDB1"});db.runCommand({shardcollection : "$testDB1.account", key : {_id : "hashed"}});db.runCommand({shardcollection : "$testDB1.alarm", key : {_id : "hashed"}});db.runCommand({shardcollection : "$testDB1.blackClass", key : {_id : "hashed"}});db.runCommand({enablesharding : "$testDB2"});db.runCommand({shardcollection : "$testDB2.device", key : {_id : "hashed"}});db.runCommand({shardcollection : "$testDB2.deviceParam", key : {_id : "hashed"}});db.runCommand({enablesharding : "$testDB3"});db.runCommand({shardcollection : "$testDB3.blackImsiFace", key : {_id : "hashed"}});db.runCommand({shardcollection : "$testDB3.face", key : {_id : "hashed"}});EOFdonemongosInitNode=${mongosArray[1]}#删除node,保留右边字符mongosInitIpNum=${mongosInitNode#*node}mongosInitIp=${ipArray[$mongosInitIpNum]}port=`awk -F= -v k=mongos_port '{ if ( $1 == k ) print $2; }' $templatePath`#初始化数据库initDBPath=$mongodb_home/shell#解压initConfig.zip包 -o:不提示的情况下覆盖文件,-d /opt:指明将文件解压缩到/opt目录unzip -o -d $initDBPath/initConfig $initDBPath/initConfig.zip$mongodb_home/bin/mongorestore --host $mongosInitIp:$port --authenticationDatabase admin -d $testDB1 $initDBPath/initConfig/testDB1$mongodb_home/bin/mongorestore --host $mongosInitIp:$port --authenticationDatabase admin -d $testDB2 $initDBPath/initConfig/testDB2$mongodb_home/bin/mongorestore --host $mongosInitIp:$port --authenticationDatabase admin -d $testDB3 $initDBPath/initConfig/testDB3

⑥autoClusterStartUp.sh 脚本为config和shard副本集、mongos路由启动脚本,用法:./ autoClusterStartUp.sh,全部启动,包括config和shard副本集、mongos路由;./ autoClusterStartUp.sh notmongos,只启动config和shard副本集。

#! /bin/bashconfigPath=config.properties#从config.properties文件读取数据出来clusterPath=`awk -F= -v k=clusterPath '{ if ( $1 == k ) print $2; }' $configPath`mongodb_home=`awk -F= -v k=mongodbHome '{ if ( $1 == k ) print $2; }' $configPath`template=`awk -F= -v k=template '{ if ( $1 == k ) print $2; }' $configPath`ips=`awk -F= -v k=ips '{ if ( $1 == k ) print $2; }' $configPath`user=`awk -F= -v k=user '{ if ( $1 == k ) print $2; }' $configPath`localIp=`/sbin/ifconfig -a|grep inet|grep -v 127.0.0.1|grep -v inet6 | awk '{print $2}' | tr -d "addr:"`templatePath=template/$template.conf#获取所有的配置、路由、分片nodes=`awk -F= -v k=nodes '{ if ( $1 == k ) print $2; }' $templatePath`eval $(echo $nodes | awk '{split($0, nodeArr, ","); for(i in nodeArr) print "nodeArray["i"]="nodeArr[i]}')#ips切割为数组arrayeval $(echo $ips | awk '{split($0, arr, ","); for(i in arr) print "ipArray["i"]="arr[i]}')suffix=""#判断linux系统cpu是否为numa架构numaCount=`grep -i numa /var/log/dmesg | wc -l`nodeCount=`grep -i numa /var/log/dmesg | grep -wi "node" | wc -l`offCount=`grep -i numa /var/log/dmesg | grep -wi "numa=off" | wc -l`if [[ ($numaCount -gt 1) && ($nodeCount -gt 1) && ($offCount -eq 0) ]]then    suffix="_numa"fi#轮询启动mongodb的节点,先config,后shard,再mongosfor n in ${nodeArray[*]}do   if [[ $n = "mongos" ]]   then      #传参为空,则mongos也执行,不为空则不执行      if [[ $1 = "" ]]      then         mongoss=`awk -F= -v k=mongos '{ if ( $1 == k ) print $2; }' $templatePath`         eval $(echo $mongoss | awk '{split($0, mongosArr, ","); for(i in mongosArr) print "mongosArray["i"]="mongosArr[i]}')         for((i=1; i<=${#mongosArray[@]}; i++))         do            mongosNode=${mongosArray[i]}            #删除node,保留右边字符            mongosIpNum=${mongosNode#*node}            mongosIp=${ipArray[$mongosIpNum]}            echo "********************************start $mongosIp $n********************************"            #启动本地服务或ssh远程调用            if [[ $localIp = $mongosIp ]]            then                $mongodb_home/shell/autoStartUp.sh $n            else                ssh $user@$mongosIp "cd $mongodb_home/shell && ./autoStartUp.sh $n"            fi         done                  #mongos也启动,即mongodb集群全启动         #查看/etc/crontab是否开启定时任务,没有则开启         for((i=1; i<=${#ipArray[@]}; i++))         do           if [[ $localIp = ${ipArray[i]} ]]           then              echo "*****start ${ipArray[i]} 定时任务:日志切割和mongo进程存活监控*****"              $mongodb_home/shell/autoConfig.sh addCronTask           else              echo "*****start ${ipArray[i]} 定时任务:日志切割和mongo进程存活监控*****"              ssh $user@${ipArray[i]} "cd $mongodb_home/shell && $mongodb_home/shell/autoConfig.sh addCronTask"           fi         done      fi   else         shards=`awk -F= -v k=$n '{ if ( $1 == k ) print $2; }' $templatePath`      eval $(echo $shards | awk '{split($0, shardArr, ","); for(i in shardArr) print "shardArray["i"]="shardArr[i]}')      for((i=1; i<=${#shardArray[@]}; i++))      do         shardNode=${shardArray[i]}         #删除node,保留右边字符         shardIpNum=${shardNode#*node}         shardIp=${ipArray[$shardIpNum]}         echo "********************************start $shardIp $n********************************"         #启动本地服务或ssh远程调用         if [[ $localIp = $shardIp ]]         then             $mongodb_home/shell/autoStartUp.sh $n$suffix         else             ssh $user@$shardIp "cd $mongodb_home/shell && ./autoStartUp.sh $n$suffix"         fi      done   fidone

⑦autoConfig.sh脚本为创建所在服务器对应的mongodb的配置文件如config.conf、shard1.conf、mongos.conf,以及添加日志切割和mongo进程存活监控定时任务,用法:./autoConfig.sh addCronTask。

#! /bin/bash#适用于3台或5台机器搭建15个或20个节点的mongodb高可用集群,3个或5个分片,每个分片(1主+1副+1仲裁)、3个配置、3个或2个路由configPath=config.properties#从config.properties文件读取数据出来template=`awk -F= -v k=template '{ if ( $1 == k ) print $2; }' $configPath`clusterDataPath=`awk -F= -v k=clusterDataPath '{ if ( $1 == k ) print $2; }' $configPath`clusterLogPath=`awk -F= -v k=clusterLogPath '{ if ( $1 == k ) print $2; }' $configPath`mongodb_home=`awk -F= -v k=mongodbHome '{ if ( $1 == k ) print $2; }' $configPath`function createConfig(){   ips=`awk -F= -v k=ips '{ if ( $1 == k ) print $2; }' $configPath`   hostname=`hostname`   localIp=`cat /etc/hosts | grep $hostname | awk -F " " '{print $1}'`   #localIp=`/sbin/ifconfig -a|grep inet|grep -v 127.0.0.1|grep -v inet6 | awk '{print $2}' | tr -d "addr:"`    #配置mongodb_home   #echo "配置mongodb_home"   #editMongodbHome   #创建conf文件夹   echo "********************************创建conf文件夹********************************"   createConfFolders   eval $(echo $ips | awk '{split($0, arr, ","); for(i in arr) print "ipArray["i"]="arr[i]}')   templatePath=template/$template.conf   #遍历ip数组   for((i=1; i<=${#ipArray[@]}; i++))   do      if [[ $localIp = ${ipArray[i]} ]]      then          nodes=`awk -F= -v k=node$i '{ if ( $1 == k ) print $2; }' $templatePath`          echo "********************************${ipArray[i]}:$nodes********************************"          eval $(echo $nodes | awk '{split($0, nodeArr, ","); for(i in nodeArr) print "nodeArray["i"]="nodeArr[i]}')          for node in ${nodeArray[*]}          do            port=`awk -F= -v k=$node"_port" '{ if ( $1 == k ) print $2; }' $templatePath`            case "$node" in                 "config")                         #创建config配置文件                         echo "***************创建config配置文件***************"                         createConfigFoldersConf $port                         ;;                 "mongos")                         #创建mongos路由配置文件                         echo "***************创建mongos路由配置文件***************"                         configPort=`awk -F= -v k=config_port '{ if ( $1 == k ) print $2; }' $templatePath`                         getConfigsIpsPort $configPort                          createMongosFoldersConf $port $ipsAndPorts                         ;;            esac                      if [[ $node =~ "shard"  ]]            then                #创建shard分片配置文件                echo "***************创建$node分片配置文件***************"                createShardFoldersConf $node $port            fi          done      fi   done}function getConfigsIpsPort(){ ipsAndPorts="" for((i=1; i<=${#ipArray[@]}; i++)) do   nodes=`awk -F= -v k=node$i '{ if ( $1 == k ) print $2; }' $templatePath`   if [[ $nodes =~ "config" ]]   then       ipsAndPorts=$ipsAndPorts"${ipArray[i]}:$1,"   fi done ipsAndPorts=${ipsAndPorts%,*}}#为每台机器创建6个目录,shard1、shard2、shard3、config、mongos、conffunction createConfFolders(){   if [[ -d $clusterDataPath/conf ]]  then      rm -rf $clusterDataPath/conf  fi    #创建配置文件夹  mkdir -p $clusterDataPath/conf  }function createMongosFoldersConf(){ #创建mongos路由服务器的日志文件夹log、进程文件夹pid  mkdir -p $clusterLogPath/mongos/log  mkdir -p $clusterDataPath/mongos/pid    #设置路由服务器  cat >> $clusterDataPath/conf/mongos.conf << EOFsystemLog:  destination: file  path: $clusterLogPath/mongos/log/mongos.log  logAppend: trueprocessManagement:  fork: true  pidFilePath: $clusterDataPath/mongos/pid/mongos.pidnet:  bindIp: $localIp  port: $1  maxIncomingConnections: 20000sharding:  configDB: configs/$2EOF}function createConfigFoldersConf(){  #创建config配置服务器的数据文件夹data、日志文件夹log、进程文件夹pid  mkdir -p $clusterDataPath/config/data  mkdir -p $clusterLogPath/config/journal  mkdir -p $clusterLogPath/config/log  mkdir -p $clusterDataPath/config/pid  #把数据目录的journal日志映射到日志目录里面  ln -s $clusterLogPath/config/journal $clusterDataPath/config/data/journal   #设置配置服务器副本集  cat >> $clusterDataPath/conf/config.conf << EOFsystemLog:  destination: file  path: $clusterLogPath/config/log/config.log  logAppend: true processManagement:  fork: true  pidFilePath: $clusterDataPath/config/pid/config.pidnet:  bindIp: $localIp  port: $1  maxIncomingConnections: 20000storage:  dbPath: $clusterDataPath/config/data  journal:    enabled: true    commitIntervalMs: 500  directoryPerDB: true  syncPeriodSecs: 300  engine: wiredTigerreplication:  oplogSizeMB: 10000  replSetName: configssharding:  clusterRole: configsvrEOF}function createShardFoldersConf(){  #创建shard1分片服务器的数据文件夹data、日志文件夹log、进程文件夹pid  mkdir -p $clusterDataPath/$1/data  mkdir -p $clusterLogPath/$1/journal  mkdir -p $clusterLogPath/$1/log  mkdir -p $clusterDataPath/$1/pid  #把数据目录的journal日志映射到日志目录里面  ln -s $clusterLogPath/$1/journal $clusterDataPath/$1/data/journal  #设置第一个分片副本集  cat >> $clusterDataPath/conf/$1.conf << EOFsystemLog:  destination: file  path: $clusterLogPath/$1/log/$1.log  logAppend: trueprocessManagement:  fork: true  pidFilePath: $clusterDataPath/$1/pid/$1.pidnet:  bindIp: $localIp  port: $2  maxIncomingConnections: 20000storage:  dbPath: $clusterDataPath/$1/data  journal:     enabled: true    commitIntervalMs: 500  directoryPerDB: true  syncPeriodSecs: 300  engine: wiredTiger  wiredTiger:    engineConfig:      cacheSizeGB: 103      statisticsLogDelaySecs: 0      journalCompressor: snappy      directoryForIndexes: false    collectionConfig:      blockCompressor: snappy    indexConfig:      prefixCompression: truereplication:  oplogSizeMB: 10000  replSetName: $1sharding:  clusterRole: shardsvrEOF}#配置mongodb环境变量function editMongodbHome(){  profile=/etc/profile  #配置MONGODB_HOME  sed -i "/^export MONGODB_HOME/d" $profile  echo "export MONGODB_HOME=$mongodb_home" >> $profile    #配置PATH  sed -i "/^export PATH=\$PATH:\$MONGODB_HOME\/bin/d" $profile  echo "export PATH=\$PATH:\$MONGODB_HOME/bin" >> $profile  #使/etc/profile文件生效  source /etc/profile}if [[ $1 = "" ]]then   createConfigfiif [[ $1 = "addCronTask" ]]then   cronfile=/etc/crontab   shellPath=${mongodb_home//\//\\/}\\/shell   logRotateCronTaskNum=`sed -n -e "/\0 \0 \* \* \* root $shellPath\/autoLogRotate.sh $shellPath/=" $cronfile`   if [[ $logRotateCronTaskNum = "" ]]   then       #没有则追加       echo "0 0 * * * root $mongodb_home/shell/autoLogRotate.sh $mongodb_home/shell > /dev/null 2>&1 &" >> $cronfile   fi   checkLiveCronTaskNum=`sed -n -e "/\*\/10 \* \* \* \* root $shellPath\/autoCheckLive.sh $shellPath/=" $cronfile`   if [[ $checkLiveCronTaskNum = "" ]]   then       #没有则追加       echo "*/10 * * * * root $mongodb_home/shell/autoCheckLive.sh $mongodb_home/shell > /dev/null 2>&1 &" >> $cronfile   fi   fiif [[ $1 = "removeCronTask" ]]then   cronfile=/etc/crontab   shellPath=${mongodb_home//\//\\/}\\/shell   sed -i "/^\*\/10 \* \* \* \* root $shellPath\/autoCheckLive.sh $shellPath/d" $cronfile   sed -i "/^\0 \0 \* \* \* root $shellPath\/autoLogRotate.sh $shellPath/d" $cronfilefi

⑧autoLogRotate.sh脚本为创建所在服务器对应的mongodb的日志切割脚本,并添加定时任务,默认为每天凌晨0点0分,对mongodb数据库日志进行切割并删除7天前的日志。

#! /bin/bash#Rotate the MongoDB logs to prevent a single logfile from consuming too much disk space. shellPath=$1configPath=$shellPath/config.properties#从config.properties文件读取数据出来clusterPath=`awk -F= -v k=clusterPath '{ if ( $1 == k ) print $2; }' $configPath`template=`awk -F= -v k=template '{ if ( $1 == k ) print $2; }' $configPath`templatePath=$shellPath/template/$template.confips=`awk -F= -v k=ips '{ if ( $1 == k ) print $2; }' $configPath`eval $(echo $ips | awk '{split($0, arr, ","); for(i in arr) print "ipArray["i"]="arr[i]}')localIp=`/sbin/ifconfig -a|grep inet|grep -v 127.0.0.1|grep -v inet6 | awk '{print $2}' | tr -d "addr:"`#代表删除7天前的备份,即只保留最近7天的备份days=7#遍历ip数组for((i=1; i<=${#ipArray[@]}; i++))do  if [[ $localIp = ${ipArray[i]} ]]  then       mongodbNodes=`awk -F= -v k=node$i '{ if ( $1 == k ) print $2; }' $templatePath`       eval $(echo $mongodbNodes | awk '{split($0, mongodbArr, ","); for(i in mongodbArr) print "mongodbArray["i"]="mongodbArr[i]}')       for((n=1; n<=${#mongodbArray[@]}; n++))       do         pid=`cat $clusterPath/${mongodbArray[n]}/pid/${mongodbArray[n]}.pid`         logdir=$clusterPath/${mongodbArray[n]}/log         #切割日志         /bin/kill -SIGUSR1 $pid         find $logdir/ -mtime +$days -delete       done  fidone

⑨autoStartUp.sh脚本为启动该脚本所在服务器的config、shard、mongos节点,启动分正常启动和禁止numactl启动两种。

#! /bin/bashconfigPath=config.propertiesmongodb_home=`awk -F= -v k=mongodbHome '{ if ( $1 == k ) print $2; }' $configPath`clusterPath=`awk -F= -v k=clusterPath '{ if ( $1 == k ) print $2; }' $configPath`param=$1#configif [[ $param = "config" ]]then   $mongodb_home/bin/mongod -f $clusterPath/conf/config.conffiif [[ $param = "config_numa" ]]then   numactl --interleave=all $mongodb_home/bin/mongod -f $clusterPath/conf/config.conffi#shard1paramLegth=`echo "$param" |wc -L`if [[ ($param =~ "shard") && ($paramLegth -lt 11) && ($paramLegth -gt 5)]]then   $mongodb_home/bin/mongod -f $clusterPath/conf/$param.conffiif [[ ($param =~ "shard") && $param =~ "numa" ]]then   shardName=${param%_*}   numactl --interleave=all $mongodb_home/bin/mongod -f $clusterPath/conf/$shardName.conffi#mongosif [[ $param = "mongos" ]]then   $mongodb_home/bin/mongos -f $clusterPath/conf/mongos.conffi

⑩autoSystemProperties.sh 脚本为修改服务器的系统配置,TCP配置等。

#! /bin/bash#系统全局允许分配的最大文件句柄数sysctl -w fs.file-max=2097152sysctl -w fs.nr_open=2097152echo 2097152 > /proc/sys/fs/nr_open#允许当前会话/进程打开文件句柄数ulimit -n 1048576#修改fs.file-max设置到 /etc/sysctl.conf 文件:#fs.file-max = 1048576#修改/etc/security/limits.conf 持久化设置允许用户/进程打开文件句柄数,手动添加#* soft nofile 1048576#* hard nofile 1048576#* soft nproc 524288#* hard nproc 524288#并发连接backlog设置sysctl -w net.core.somaxconn=32768sysctl -w net.ipv4.tcp_max_syn_backlog=16384sysctl -w net.core.netdev_max_backlog=16384#可用知名端口范围:sysctl -w net.ipv4.ip_local_port_range='80 65535'#sysctl -w net.core.rmem_default=262144sysctl -w net.core.wmem_default=262144sysctl -w net.core.rmem_max=16777216sysctl -w net.core.wmem_max=16777216sysctl -w net.core.optmem_max=16777216#TCP Socket 读写 Buffer 设置:sysctl -w net.core.rmem_default=262144sysctl -w net.core.wmem_default=262144sysctl -w net.core.rmem_max=16777216sysctl -w net.core.wmem_max=16777216sysctl -w net.core.optmem_max=16777216sysctl -w net.ipv4.tcp_rmem='1024 4096 16777216'sysctl -w net.ipv4.tcp_wmem='1024 4096 16777216'#修改系统内核参数:echo "never" > /sys/kernel/mm/transparent_hugepage/enabledecho "never" >  /sys/kernel/mm/transparent_hugepage/defrag#TCP 连接追踪设置:sysctl -w net.nf_conntrack_max=1000000sysctl -w net.netfilter.nf_conntrack_max=1000000sysctl -w net.netfilter.nf_conntrack_tcp_timeout_time_wait=30#修改stack size为1024,不启作用,只能手动source#if [ `grep -c "ulimit -s 1024" /etc/profile` -eq '0' ];then#  echo "ulimit -s 1024" >> /etc/profile#  source /etc/profile#fi

⑫initConfig.zip 为数据库test及其数据库表的初始化文件,有需要的可以自行配置。

2.2 全自动化部署

1. 把mongodb安装目录放置在5台机器中的任意一台上面即可,选定好安装的路径。

2.进入mongodb/shell目录。

根据实际环境需要修改config.properties文件的clusterPath、mongodbHome、ips、template、数据库5个属性

3.autoClusterIndex.sh脚本所在的目录执行如下命令,否则脚本会不起作用,执行命令:

./autoClusterIndex.sh

执行上面命令,会一键自动化部署。

至此,完成部署。

2.3 部署验证

5台机器的验证:

服务器node1(192.168.187.201):

 

服务器node2(192.168.187.202):

 

服务器node3(192.168.187.203):

 

服务器node4(192.168.187.204):

 

服务器node5(192.168.187.205):

 

4台机器的验证:

服务器node1(192.168.187.201):

 

服务器node2(192.168.187.202):

 

服务器node3(192.168.187.203):

 

服务器node4(192.168.187.204):

 

3台机器的验证:

服务器node1(192.168.187.201)、服务器node2(192.168.187.202)、服务器node3(192.168.187.203)都会启动如下服务:

 

一键化部署的shell命令github地址为:

https://github.com/SwordfallYeung/MongodbCluster_autoDeploy

转载地址:http://nycii.baihongyu.com/

你可能感兴趣的文章
图形学 图形渲染管线
查看>>
DirectX11 计时和动画
查看>>
DirectX11 光照与材质的相互作用
查看>>
DirectX11 法线向量
查看>>
DirectX11 兰伯特余弦定理(Lambert)
查看>>
DirectX11 漫反射光
查看>>
DirectX11 环境光
查看>>
DirectX11 镜面光
查看>>
DirectX11 三种光照组成对比
查看>>
DirectX11 指定材质
查看>>
DirectX11 平行光
查看>>
DirectX11 点光
查看>>
DirectX11 聚光灯
查看>>
DirectX11 HLSL打包(packing)格式和“pad”变量的必要性
查看>>
DirectX11 光照演示示例Demo
查看>>
漫谈一下前端的可视化技术
查看>>
VUe+webpack构建单页router应用(一)
查看>>
Vue+webpack构建单页router应用(二)
查看>>
从头开始讲Node.js——异步与事件驱动
查看>>
Node.js-模块和包
查看>>