[随笔]Movable Type 的数据库瓶颈与多说评论反向同步

随笔 而已,能力所限,只是个人看法。

就向大家看见的这样,由于多说服务器的原因,路杨正在逐步把 多说的评论写回本地 Movable Type 的数据库。

过程不算难。通过 API 获得 JSON 数据,分析后写入MT评论数据库罢了。 为了区分和拓展,给 MT 数据库的评论表增加了3行,分别记录 远程服务器名(remote_service),远程服务器ID (remote_id) 和 UA ( agent)。

mt_db_table_comment.png

开始直接 Perl 脚本。核心代码为

code   sub check_ds_comments {
       my $list_ref;
       my $log_id;
       foreach my $a(@{$response} ) {
        my $act = $a->{'action'}; 
        if ( $act eq 'create') {   my $ds = $a->{'meta'};  my  $ds_id= $ds->{"post_id"};    $list_ref->{$ds_id} = $ds;  } 
       else
       {  
         foreach my $var ( @{$a->{'meta'}} ) { $list_ref->{$var}->{'status'} = ($act eq 'approve') ? 'approved' : $act ;  }
        }
         $log_id = $a->{'log_id'};  
       }

    my $str;
    my $nos = 0;        
    my @ids =sort {$a <=> $b} keys %$list_ref;        
    foreach my $ids ( @ids )
    {
      my $dscs = $list_ref->{$ids};
      print "<br>\t$ids => $dscs->{'status'} \n<br>";
      next if ( ($dscs->{'status'} eq 'delete-forever') || ($dscs->{'status'} eq 'delete') || ($dscs->{'status'} eq 'spam'));
      next if  ($dscs->{"thread_key"}  !~ /^\d+$/);
      &import_comments($dscs);
      $nos ++;
   }                   
    $str .= "<p>信息:</p>"  ;
    $str .= "<br>last_log_id=" .$log_id;        
    $str .= "<br>共有有效评论 " .$nos ." 条。OK!!!!<br>";       
    print $str; 

} 

sub import_comments {
  my $ds = shift ;
  print "<ul><li>--正常评论,开始处理----------------------------</li>";
  print "<li>信息如下:<br />";
  print "thread_key =". $ds->{"thread_key"}. "<br />";
  print "post_id =". $ds->{"post_id"}. "<br />";
  print "author_name =". $ds->{"author_name"}. "<br />";
  print "message =". $ds->{"message"};
  print "</li>";

 print "<li>检查 id=". $ds->{"post_id"} ." 的多说评论是否存在于本地数据库</li>" ;
  my $comment_id = &get_id_form_ds_id( $ds->{"post_id"},'no_output'); # \comment_parent_id# \,
  if ( $comment_id ne 'NULL' )
  { 
   print "<li>这条多说评论已经存在。 本地数据库中 id=". $comment_id ."</li>" ;
   print "</ul>" ;
   return; 
  }
  else {  print "<li>没有在本地数据库中检查到这条评论,准备写入本地数据库..........</li>"; }


my $blog_id = 2;
my $mt_id =  ($ds->{'author_id'} =='11415448' )?  1 : 'NULL';
&c_to_c(\$ds->{"message"});
&c_to_c(\$ds->{"agent"});

my $str ='INSERT INTO `mt_comment` (`comment_id`, `comment_author`, `comment_blog_id`, `comment_commenter_id`, `comment_created_by`, `comment_created_on`, `comment_email`, `comment_entry_id`, `comment_ip`, `comment_junk_log`, `comment_junk_score`, `comment_junk_status`, `comment_last_moved_on`, `comment_modified_by`, `comment_modified_on`, `comment_parent_id`, `comment_text`, `comment_url`, `comment_visible`, `comment_remote_id`, `comment_remote_service`, `comment_agent`) VALUES
(';

$str .=$comment_id .',';# \comment_id# \,
$str .='"'. $ds->{"author_name"} .'",'; # \comment_author# \,
$str .='2,' ;  # \comment_blog_id# \,
$str .= $mt_id .','; #$ds->{"mt_author_id"}.',';  # \comment_commenter_id# \,
$str .='NULL,';  # \comment_created_by# \,
$str .='"'. $ds->{"created_at"} .'",'; # \comment_created_on# \,
$str .='"'. $ds->{"author_email"} .'",';  # \comment_email# \,
$str .=int($ds->{"thread_key"}) .','; # \comment_entry_id# \,
$str .='"'. $ds->{"ip"} .'",'; # \comment_ip# \,
$str .='NULL,';  # \comment_junk_log# \,
$str .='NULL,'; # \comment_junk_score# \,
$str .='1,';  # \comment_junk_status# \,
$str .="'2000-01-01 00:00:00',"; # \comment_last_moved_on# \,
$str .='NULL,'; # \comment_modified_by# \,
$str .='NULL,'; # \comment_modified_on# \,
$str .= &get_id_form_ds_id( $ds->{"parent_id"} ).','; # \comment_parent_id# \,
$str .="'". $ds->{"message"} ."',";  # \comment_text# \,
$str .="'". $ds->{"author_url"} ."',";  # \comment_url# \,
$str .='0,';  # \comment_visible# \,   # 0 代表 等待发布。 1代表直接发布
$str .=$ds->{"post_id"}.','; # \comment_remote_id# \,
$str .='"DUOSHUO",' ;  # \comment_remote_service# \
$str .='"'. $ds->{"agent"}. '"'; # \comment_created_on# \,
$str .=');' ;
 print "<li>";
 print "str: \n<br />";
 print $str." \n</li>";
my $rows = $dbh->do($str) or die "Can't execute sql: ".$dbh->errstr."\n";
print "</ul>" ;


}
# 转码符号
sub c_to_c
{
my $str = shift ;
$$str =~ s/\'/\\\'/gs;
$$str =~ s/\"/\\\"/gs;
$$str =~ s/\,/\\\,/gs;
$$str =~ s/\;/\\\;/gs;

}
# 检查是否存在此条评论
sub get_id_form_ds_id
{
 my $dsparent_id = shift ;
 my $no_output = shift ;
 unless ($no_output) { print "<br />dsparent_id -> comment_id \n<br />dsparent_id = ". $dsparent_id ."\n<br />"; }
  my $delim = 'NULL';     

  my $sql= "select `comment_id` from `mt_comment` where `comment_remote_id` ='". $dsparent_id ."'";

  my $sth = $dbh->prepare($sql);   #准备  
  $sth->execute() or die "Cannot execute: " . $sth->errstr();  #执行
  while (my @result = $sth->fetchrow_array) {
      $delim = @result[0];        

 }

  $sth->finish;#结束句柄
  unless ($no_output) { print '<br>检查到id=' . $delim. "\n<br />"; }
  $delim;
}

代码很随意,设置读取 200 条评论, 几乎瞬间写入 MySQL 数据库。 界面代码为: http://mt.easun.org/cgi-bin/Util/ds.cgi

登录 MT 后台,查看评论,发现所有的多说远程评论都乖乖的显示在了本地评论列表里面。

ds_to_mt.png

本来一切都 OK 了。 但是突想:既为什么不写成 MT 的插件呢, 这样可以利用 MT 自身封装的函数操作数据库,也不用硬编码进 MySQL 的用户名和密码。 说干就干。 其实有了 Perl脚本,改起来也很容易: 核心代码改写如下:

code sub check_ds_comments {
    my $app = shift ;     
    my $response = shift ;     
    my $list_ref;
    my $log_id;
    foreach my $a(@{$response} ) {
        my $act = $a->{'action'}; 
        if ( $act eq 'create') {   my $ds = $a->{'meta'};  my  $ds_id= $ds->{"post_id"};    $list_ref->{$ds_id} = $ds;  } 
       else
       {  
         foreach my $var ( @{$a->{'meta'}} ) { $list_ref->{$var}->{'status'} = ($act eq 'approve') ? 'approved' : $act ;  }
       }
       $log_id = $a->{'log_id'};  
    }

    my $str;
    my $nos = 0;        
    my @ids =sort {$a <=> $b} keys %$list_ref;        
    foreach my $ids ( @ids )
    {
       # print "<br>$ids:\n<br>";
       my $dscs = $list_ref->{$ids};
      $str .= "<br>\t$ids => $dscs->{'status'} \n<br>";
      next if ( ($dscs->{'status'} eq 'delete-forever') || ($dscs->{'status'} eq 'delete') || ($dscs->{'status'} eq 'spam'));
      next if  ($dscs->{"thread_key"}  !~ /^\d+$/);
      &import_comments($dscs);
      $nos ++;
   }                   
    $str .= "<p>信息:</p>"  ;
    $str .= "<br>last_log_id=" .$log_id;        
    $str .= "<br>共有有效评论 " .$nos ." 条。OK!!!!<br>";       
    return $app->error($str); 

 }
sub import_comments {
    my $ds = shift ;
    require MT::Comment;
    my @comments = MT::Comment->load( 
     { remote_service =>'DUOSHUO', remote_id=>$ds->{"post_id"},}, 
     { sort => 'created_on', direction => 'ascend',}  
    );
  my $comment =  @comments[0] ||  MT::Comment->new;

  my $str ; 
  if ( $comment->id )
  {
    $str=  "<li>这个多说评论已经存在。 数据库中 id=". $comment->id ."</li>" ;      
    return; 
  }
  else {  $str .= "<li>没有检查到这个评论,准备写入...........</li>"; }


    $comment->author($ds->{"author_name"} );
    $comment->blog_id(2); 
    $comment->commenter_id(1)  if ($ds->{'author_id'} =='11415448' ) ; 
    $comment->email($ds->{"author_email"} ); 
    $comment->entry_id( int($ds->{"thread_key"}) ); 
    $comment->ip( $ds->{"ip"} ); 
    $comment->parent_id( &get_id_form_ds_id( $ds->{"parent_id"} ) ); 
    $comment->text( $ds->{"message"} );  
    $comment->url( $ds->{"author_url"} );  
    $comment->visible(0) unless ($comment->id) ;    # 0 代表 等待发布。 1代表直接发布
    $comment->remote_id($ds->{"post_id"}); 
    $comment->remote_service('DUOSHUO' );  
    $comment->agent($ds->{"agent"}) ; 
    $comment->save  or die $comment->errstr;
}

代码的确简洁多了~~~~

在后台启用此插件。运行。。。、 浏览器漫长的等待,结果返回 504 。。。。。

My GOD ..... 修改获取多说数据为 20 条,这次正常了。但是也几乎耗时5分钟。。。

MT 的代码太复杂了,几乎可以判断资源浪费在了 $comment->save 上,但是为什么不得而知。。

按理来说,同样是Perl脚本,同样是连接 MySQL 。为什么差别这么大呢? Movable Type 看来真的该减肥了。。

懒惰了。 就这样使用独立的 Perl 脚本了。

这次算是把 多说 的所有评论都反向同步到了本地,同时 将 http://mt.easun.org/cgi-bin/Util/ds.cgi 作为了多说系统的本地回调地址,这样,每次评论,都可以即时写进本地数据库。

PS: 顺手改了一下多说的 JS ,让它不*重复*显示已经同步并发表出来的多说评论, 仅仅显示没有发布出来的多说评论。