gyn

Win32下的Perl,无用的select,停滞的Tk,结束吧....

单服务器升级至双机热备实例

摘要:介绍了基于网络的双机热备系统的设计思路以及通过在主备服务器中配置脚本程序,将阅文站服务器升级至双机热备系统的过程。
关键词:双机热备,心跳监测,同步
Abstract: This paper introduce a design method of network based hot standby service system as well as the procedure of upgrade from single document server to hot standby service system by configuring script files on both active and standby server.
Keyword: hot standby service, heartbeat monitor, synchronization


1 :升级方案建立的起因
    目前的内部文件阅读服务器由于使用年代较长,经常出现无响应甚至死机的状况。由于内部文件阅读服务器是我台员工了解并获取规章制度内容和近期活动消息的一个重要途径,所以该服务器的稳定性显得十分重要。就目前的管理情况而言,除了定期的检查之外,一般都是通过阅读者的通报获知服务器处于无响应的状况,很难做到及时发现并处理。
    单纯通过增加检查次数的方法很难收到理想的效果。一来即便达到一天十次检查的量,对于服务器而言仍然是小时单位的数量级,而且只能在工作时间内施行;再者,由于没有配套的同步备份方案,即便发现服务器崩溃,恢复重建所消耗的时间依然相当可观。长时间停机导致信息传达的滞后将直接影响到工作的开展,试想如果这种情况发生在关键性服务器如新闻文稿系统上的话,将造成不可估量的严重后果。
 

2
:网络双机热备的思路
    所要采用的采用双机热备的办法,双机热备即是目前通常所说的 active/standby 方式。当 active 服务器出现故障的时候,通过软件诊测(一般是通过心跳诊断)将 standby 机器激活,保证应用在短时间内完全恢复正常使用。数据同步的方案一般在设计双机热备服务器时制定,比方说使用共享存储器就是个简单方便的解决办法;更加有效的方法的是在编写配置与数据输入输出相关的服务程序,使数据同时写入主备服务器中相关的数据库。但是在本文的例子中,由于文件服务器在建立时是一个单机的形式,所以其中的程序和数据都是针对单机进行设置的,如果要实现传统意义上双机热备的数据同步,需要对原先的网站程序和存储方案做较大的的修改,除开 standby 服务器的费用外,可能还需要另外购买存储设备。在对源文件无法进行修改或者预算有限的情况下,这种代价的方案并不是完全适用。
    为了克服以上的几个局限,我们利用网络代替传统心跳线和传输数据来同步数据,不但利用了现有的网络资源来节省投入的成本,而且在必要的情况下通过建立在不同端口的监听,
standby 服务器甚至可以同时为多台提供不同服务的 active 服务器的备用支持。
    首先,
standby 服务器中配置监控脚本,通过循环心跳检测 active 服务器的生存状况,达到实时监控的目的,一旦发现出错状况就可以采取预先设置的方案及时接管 active 服务器服务。通常情况下,一旦服务器崩溃,其网络功能也将停止,所以可以简单采用 ping 的方法监测 active 服务器;但是更一般的情况是,服务器仅仅是停止了某一项服务,比如网站发布服务或者数据库服务,而其网络联通性依然良好,针对这种情况需要在主备双方配置监测特定服务的信息问答脚本。比方说, active 服务器提供数据库查询服务,在每次接受到来自备服务器的心跳包后, active 服务器执行一次特定的 sql 查询并将查询所得与预期的比对,得到的结果以布尔值的形式传回备服务器,最后由备服务器决定是否接管服务。
    接着,为了保持
active 服务器与 standby 服务器中数据的一致性,需要将 active 服务器上的文件或数据库的更新内容,定期地通过一定的途径同步到备服务器中。对于数据处理不是很频繁的服务器,可以采用定期备份的方式。如需要采取更加细致的做法的话,在被服务器定期备份的基础上, active 服务器可以配置数据更新检测脚本,一旦检测到有数据更新就将其写入同步数据包中,当数据包达到规定大小之后就可用它来同步 standby 服务器。同步数据包大小的设置视乎网络的通畅度和服务器的响应能力而定。在两个条件都较好的情况下,可以采用小数据包的形式,这样做的好处在于主备的同步度较高, standby 服务器在接管服务时可以最大限度再现 active 服务器内容。而在有任何一个条件或者两者都不理想的情况下,需要适当增加数据包的大小。另外,由于目前一些大型的数据库普遍支持触发器,在同步这类数据库时我们可以利用触发器来保存添加、删除和更新的记录操作,很大程度上简化了同步的过程。  

3
:具体的升级方案和主要脚本分析
3.1
:心跳监视
    现在将以上的思路与具体的情况想结合,实现文稿阅读服务器升级至双机热备。由于该服务器提供的是
http 的网页浏览服务,所以在 standby 服务器中只需要配置定期具备获取网页功能的脚本即可以达到判断 active 服务器服务是否正常,换一句换说在做这个特例中 active 服务器的心跳应答是建立在已有的 http 服务上的,无需再建立额外的监听端口。
    以下是备份服务器上的心跳脚本
heartbeat.bat ,心跳间隔为一个小时即 3600 秒, active 服务器域名是 file.zsgd.com standby 服务器为 file-bak.zsgd.com 脚本首先建立了一个无穷循环,在该循环中每隔一个小时使用 LWP::Simple[1] 下载一次阅文站首页的内容,如果下载失败则表示 active 服务器 http 服务失效, standby 服务器将调用 iisweb.vbs[2] 来启动本身的 iis 网站服务。然后用 Net::FTP[3] 登录 zsgd.com 主站的 ftp 下载默认页面文件 default.asp ,修改其中的阅文站链接,使其指向 standby 服务器。最后将修改完的 default.asp 上传至 zsgd.com 主站 ftp ,覆盖原先的文件。

@rem = '--*-Perl-*--
@echo off
if "%OS%" == "Windows_NT" goto WinNT
perl -x -S "%0" %1 %2 %3 %4 %5 %6 %7 %8 %9
goto endofperl
:WinNT
perl -x -S %0 %*
if NOT "%COMSPEC%" == "%SystemRoot%\system32\cmd.exe" goto endofperl
if %errorlevel% == 9009 echo You do not have Perl in your PATH.
if errorlevel 1 goto script_failed_so_exit_with_non_zero_val 2>nul
goto endofperl
@rem ';
#!perl
#line 15
use strict;
use LWP::Simple;
use Net::FTP;
my ( $none, $interval, $ftp, $backserver) = ( '', 3600, undef, 'file-bak.zsgd' );
my $content = $none;
whil(1){
       $content = get 'http://file.zsgd.com/';
       if( $content eq $none ){
              system ' cscript.exe %systemroot%/system32/iisweb.vbs /stop file ';
              system 'cscript.exe %systemroot%/system32/iisweb.vbs /start file';
              $ftp = Net::FTP->new('file.zsgd.com', debug => 0);
              $ftp->login('***', '***');
              $ftp->get('default.asp');
              open FL, '<default.asp';
              open TMP, '>tmp';
              while(<FL>){
                     chomp;
                     $_ =~ s/(.*)(file\.zsgd)(.*)/$1$backserver$3/;
                     print TMP $_."\n";
              }
              close FL; close TMP;
              system 'del default.asp';
              system 'ren tmp default.asp';
              $ftp->put('default.asp');
              $ftp->quit;
       }
       $content = $none;
       sleep $interval;
}
__END__
:endofperl

heatbeat.bat

3.2
:数据同步
    在数据同步方面, msdn 上有关于基于 Microsoft Jet 数据库引擎的 internet 数据库同步的具体方案 [4] ,为 windows 平台上大型数据库的同步提供了良好的途径。但是这次升级的服务器中配置的是小型 access 数据库,而且数据上载周期较长,没有 频繁的删除或者更新数据项的操作。所以出于实验和学习的目的,这里通过配置脚本来实现 internet access 数据库的监视与同步。
    在
active 服务器中配置 datamonitor.bat 脚本监视数据库的更新情况。首先读入参数文件中的数据,该参数文件保存的是前一次脚本运行得到的 access 数据库文件的最后修改时间和其中各个表的数据项 id 值,通过比对本次脚本运行得到的数据库修改时间与最后一次修改时间即可判断数据库在两次脚本运行期间是否更新了数据。一旦发现有更新,脚本将使用 DBD::ADO 连接 access 数据库。首先,检测各个表中 id 值大于前次查询最大 id 的数据记录,这些就是新插入的数据项;再在小于最大 id 值的 id 中检索不重复项,这些是被删除的数据项;接着用本次查询所得的 id 值替换参数文件中的内容;然后将这些数据分类打包,发送给 standby 服务器的数据同步监听脚本;最后做一些必要清理和初始化工作,一个小时后进入下一个周期重复以上的步骤。
    这么做的一个缺点是很难检测更新数据项,虽然理论上可以通过在表中设置更新标记的方法提示数据更新,但是更新标记的修改程序必须嵌入网站程序,这在加密的网页程序中将很难实现,退一步讲如果服务是基于编译了的程序的话,这样的修改将无从谈起。所以从某种程度上表明,重要的数据服务不要建立在文本型的数据库上,对于有同步要求的服务,选择的数据库至少要支持触发过程。在资金允许的前提下,最好使用如
oracle db2 等大型的数据库系统,另外免费的 mysql postgresql 也是不错的选择。

@rem = '--*-Perl-*--
@echo off
if "%OS%" == "Windows_NT" goto WinNT
perl -x -S "%0" %1 %2 %3 %4 %5 %6 %7 %8 %9
goto endofperl
:WinNT
perl -x -S %0 %*
if NOT "%COMSPEC%" == "%SystemRoot%\system32\cmd.exe" goto endofperl
if %errorlevel% == 9009 echo You do not have Perl in your PATH.
if errorlevel 1 goto script_failed_so_exit_with_non_zero_val 2>nul
goto endofperl
@rem ';
#!perl
#line 15
use strict;
use DBI;
my ($PF_INET, $port, $SOCK_DGRAM) = (2, 2345, 2);
my $remote_addr = pack('SnC4x8',$PF_INET,$port,192,168,138,228);
socket(CLIENT, $PF_INET, $SOCK_DGRAM, getprotobyname('udp'));
my $dsn = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=c:/temp/tp.mdb";
my $dbh = DBI->connect("dbi:ADO:$dsn", {PrintError => 0});
my $tbh = $dbh->prepare('select * from pic'); $tbh->execute;
my (@data, @tbl_id, $max_tbl_id, $del_id, @insert_data_ref)= undef;
my ($index, $id, $index_id) = (0, undef, undef);
while(1){
       open TBL, '<tbl-pic';
       open TMP, '>tmp';
       while(<TBL>){ chomp; push @tbl_id, $_ ; } #$_ = atoi $_;
       close TBL;
       $max_tbl_id = $tbl_id[(scalar @tbl_id) - 1];
       while(@data = $tbh->fetchrow_array()){
              $id = $data[0];
              print TMP $id."\n"
              if($id <= $max_tbl_id){
                     $index_id = $tbl_id[$index];
                     while($index_id <= $id){
                            if($index_id < $id){ $del_id .= $index_id."\t"; }
                            $index += 1;
                            if($index < (scalar @tbl_id)){ $index_id = $tbl_id[$index]; }else{ last; }
                     }
              }else{ my @temp = @data; push @insert_data_ref, \@temp; }
       }
       close TMP; system 'del tbl-pic'; system 'ren tmp tbl-pic';
       undef $tbh;
       $dbh->disconnect;
       send(CLIENT,$del_id, 0, $remote_addr);
       foreach my $ref(@insert_data_ref){
              my $item = undef;
              foreach my $field(@$ref){ $item .= $field."\t"; }
              send(UDP_CLIENT, $item, 0, $remote_addr);
       }
       close CLIENT;     
       sleep 3600;
       $dbh = DBI->connect("dbi:ADO:$dsn", {PrintError => 0});
       socket(CLIENT, $PF_INET, $SOCK_DGRAM, getprotobyname('udp'));
       (@tbl_id, $del_id, @insert_data_ref) = undef;
       $index = 0;
}
__END__
:endofperl

tbl-pic-monitor.bat
    在
standby 服务器中配置监听服务脚本用以检查并接受来自 active 服务器更新数据包,接着将接收到的数据中的前缀指示判断该数据是用于插入或是删除,从而再运行下一步的数据库更新操作,最后脚本进入下一个周期的等待监听。

@rem = '--*-Perl-*--
@echo off
if "%OS%" == "Windows_NT" goto WinNT
perl -x -S "%0" %1 %2 %3 %4 %5 %6 %7 %8 %9
goto endofperl
:WinNT
perl -x -S %0 %*
if NOT "%COMSPEC%" == "%SystemRoot%\system32\cmd.exe" goto endofperl
if %errorlevel% == 9009 echo You do not have Perl in your PATH.
if errorlevel 1 goto script_failed_so_exit_with_non_zero_val 2>nul
goto endofperl
@rem ';
#!perl
#line 15
my $dsn = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=c:/temp/tp.mdb";
my ($dbh, $sign, $content) = undef;
my ($buffer, $PF_INET, $port, $SOCK_DGRAM) = (undef, 2, 2345, 2);
my $local_addr = pack('SnC4x8', $PF_INET, $port, 192, 168, 138, 228);
socket(SERVER, $PF_INET,$SOCK_DGRAM, getprotobyname('udp')) or die("$!");
bind(SERVER, $local_addr) or die("$!");
listen(SERVER, 10);
while(1){
       next unless my $remote_addr = recv(UDP_SERVER, $buffer, 100, 0);
       chomp($buffer);
       $dbh = DBI->connect("dbi:ADO:$dsn", {PrintError => 0});
     
($sign, $content) = split $buffer, '::';
       if($sign eq 'add'){
              $dbh->prepare('insert into pic values(?, ?, ?, ?)');
              $dbh->execute(split($content, "\t"));
              $dbh->close;
       }else{
              $dbh->prepare('delete from pic where id = ?');
              foreach my $tmp(split $content, "\t"){ $dbh->execute($tmp); }
       }
       $dbh->disconnect;
}
__END__
:endofperl

tbl-pic-recv.bat 

4
:建立跟踪系统运行的系统日志
    一个成功服务器解决方案的产生,需要后期长时间的跟踪和调试。在建立了基于网络的双机热备系统之后,为了方便了解和检查 active standby 服务器中各个脚本的执行情况,需要对脚本做一些修改,使其能将执行的结果输出并存储到特定的日志文件中,方便管理员通过查询其中的内容来判断系统运行的状况。对于严重的状况,比如 active 服务器 down 机,系统还可以通过发送 email 的方式通知管理员,使其能及时做出反应。
    在心跳监测脚本中,当每个周期运行结束后,向日志文件里记录本周期检测启动成功与否、启动或失效的日期时间和检测得到的
http 反馈内容,如果没有得到内容则发送 email 给指定的管理员。对于数据同步, active 服务器中的脚本将记录本周期同步启动日期时间、数据库文件的最后一次修改时间、数据库连接状况、 socket 客户端启动成功与否及数据发送的结果; standby 服务器中的脚本将记录收到更新数据的时间和所有的数据内容以便于在必要的时候恢复数据库。

参考文献:

[1] perldoc5.8.8 www.perl.org 2006


[1] libwww-perl 的简称,提供一组用以访问 www 资源的简单而持久的 API
[2] IIS 网站管理脚本,在运行 IIS 6.0 的服务器上创建、删除、列出、启动、停止和暂停网站。
[3] 一个提供 ftp 客户端简单功能的类
[4] http://msdn2.microsoft.com/en-us/library/aa140025(office.10).aspx#intrjet4_distribrepset

posted on 2007-07-08 22:35 gyn_tadao 阅读(1279) 评论(1)  编辑 收藏 引用 所属分类: perl

评论

# re: 单服务器升级至双机热备实例[未登录] 2008-08-05 10:07 winter

太棒了,顶啊  回复  更多评论   

只有注册用户登录后才能发表评论。
<2007年7月>
24252627282930
1234567
891011121314
15161718192021
22232425262728
2930311234

导航

统计

常用链接

留言簿(15)

随笔分类(126)

随笔档案(108)

相册

搜索

最新评论

阅读排行榜

评论排行榜