mysql 数据同步方案

jopen 10年前

mysql 有主从服务器可以进行数据同步。这个是最好的解决方法之一了。但是前提是服务器可以互联。加入服务器之间不能直接互联。比如我现在的业务,内外网之间只可以通过邮件进行通讯,如何做数据同步?

服务器:两台windows,mysql数据库,数据量不大。

网络:不能互联,只能通过邮件(单向)进行通讯。

需求,A数据库每天同步到B数据库,实时性要求不高,每天即可。

解决方案:mysql 有 dump 命令,方便得进行数据库创建以及数据移植。考虑每天程序将相关表的 dump脚本 自动邮件到 B服务器,然后B 服务器执行 dump 脚本入库。

A 服务器现在要做2件事,1.生成 dump 脚本,2.邮件到B服务器。

python 有发邮件的库,但是公司的exchange邮箱的登录验证方法是自带库没有的

   server = smtplib.SMTP(mail_host,587)          #server.connect(mail_host)     print server.ehlo(mail_host)#打印出邮件服务器的验证方法

,无奈之下,选择用c#库去做发邮件这件事情,代码如下, Email.cs:

using System;  using System.IO;  using System.Net.Mail;  using System.Text;  using System.Threading;  using System.Configuration;    namespace EmailRpc  {      /// <summary>      /// 邮件报警      /// </summary>      public class Email      {          private static string Sender = ConfigurationManager.AppSettings["Sender"];          private static string Password = Encoding.Default.GetString(Convert.FromBase64String(ConfigurationManager.AppSettings["Password"]));          private static string SenderName = ConfigurationManager.AppSettings["SenderName"];          private static int Port = int.Parse(ConfigurationManager.AppSettings["Port"]);          private static string EmailServer = ConfigurationManager.AppSettings["EmailServer"];          private static bool EnableSSL = Boolean.Parse(ConfigurationManager.AppSettings["EnableSSL"]);                    //附件目录          private static string AttachmentsDir = ConfigurationManager.AppSettings["AttachmentsDir"];          //附件后缀          private static string FilePostfix = ConfigurationManager.AppSettings["FilePostfix"];            /// <summary>          /// 邮件配置          /// </summary>          /// <param name="emailServer"></param>          /// <param name="port"></param>          /// <param name="sender"></param>          /// <param name="password"></param>          /// <param name="senderName"></param>          public static void ConfigEmail(string emailServer, int port, string sender, string password, string senderName, bool enableSSL)          {              Sender = sender;              Password = password;              SenderName = senderName;              Port = port;              EmailServer = emailServer;              EnableSSL = enableSSL;          }            /// <summary>          /// 发送邮件实现了IAlarm接口的Alarm()方法          /// </summary>          /// <param name="sendTo"></param>          /// <param name="ccTo">抄送 2014-4-10 wangxinxin</param>          /// <param name="title"></param>          /// <param name="content"></param>          /// <param name="attachment"></param>          public static bool Send(Message message)          {              try              {                  string sendTo = message.SendTo;                  string ccTo = message.CcTo;                  string title = message.Title;                  string content = message.Content;                  //ParameterizedThreadStart parmThread = new ParameterizedThreadStart(SendThread);                  //Thread thread = new Thread(parmThread);                  string[] parm = { sendTo, ccTo, title, content };                  //thread.Start(parm);                  SendThread(parm);                  return true;              }              catch (Exception)              {                  return false;              }              // 因为发送邮件可能耗时较长,所以放到单独的线程中去执行            }            private static void SendThread(string[] parms)          {              //string[] p = parms as string[];              if (parms != null && parms.Length > 2)              {                  string sendTo = parms[0];                  string ccTo = parms[1];                  string title = parms[2];                  string content = parms[3];                    MailMessage msg = new MailMessage();                  foreach (var to in sendTo.Split('|'))                  {                      // 添加收件人                      if (!string.IsNullOrEmpty(to))                      {                          msg.To.Add(to);                      }                    }                  //添加密抄                  if (!string.IsNullOrEmpty(ccTo))                  {                      foreach (var to in ccTo.Split('|'))                      {                          if (!string.IsNullOrEmpty(to))                          {                              msg.Bcc.Add(new MailAddress(to));                          }                      }                  }                    Encoding encoding = Encoding.UTF8;                  //发件人信息                  msg.From = new MailAddress(Sender, SenderName, encoding);                                    //获取邮件附件,根据目录,以及附件后缀。                  string[] filenames = Directory.GetFiles(AttachmentsDir, FilePostfix);                    try                  {                      msg.Subject = title;   //邮件标题                      msg.SubjectEncoding = encoding;    //标题编码                      msg.Body = content; //邮件主体                      msg.BodyEncoding = encoding;                      msg.IsBodyHtml = true;  //是否HTML                      msg.Priority = MailPriority.Normal;   //优先级                      foreach (string file in filenames)                      {                          msg.Attachments.Add(new Attachment(file));//将附件附加到邮件消息对象中                        }                          SmtpClient client = new SmtpClient();                      client.Credentials = new System.Net.NetworkCredential(Sender, Password);                      //client.DeliveryMethod = SmtpDeliveryMethod.Network;                       client.Port = Port;                      client.Host = EmailServer;                      client.EnableSsl = EnableSSL;                      client.Send(msg);                  }                  catch (Exception e)                  {                      Console.WriteLine(e);                  }              }          }        }        public class Message      {          public string SendTo { get; set; }            public string CcTo { get; set; }            public string Title { get; set; }            public string Content { get; set; }      }  }

一个console程序,program.cs:

using System;  using System.Collections.Generic;  using System.Configuration;  using System.Linq;  using System.Text;  using System.Threading.Tasks;    namespace EmailRpc  {      class Program      {          static void Main(string[] args)          {              //加密密码              //byte[] bytes = Encoding.Default.GetBytes("123456");              //Console.WriteLine(Convert.ToBase64String(bytes));                //解密              //byte[] outputb = Convert.FromBase64String("Zmx5aW5nXzIyMg==");              //string orgStr = Encoding.Default.GetString(outputb);              //Console.WriteLine(orgStr);                  string sendto = ConfigurationManager.AppSettings["SendTo"];              string title = ConfigurationManager.AppSettings["Title"];                           Message m = new Message()              {                  SendTo = sendto,                  Title = title,                  Content = DateTime.Now.ToString()              };                Email.Send(m);              Console.WriteLine("Email sent!");              //Console.ReadKey();          }        }  }

编译后生成 sendMail.exe , 这个程序运行的结果,就是将指定目录下的 某后缀的所有文件作为附件发送到指定收件人。我配置的目录是当前目录,后缀是sql.  然后编写bat文件,生成 dump sql  ,调用sendMail.exe , start.bat 文件:

d:  cd D:\dump  mysqldump -h [host] -u root --password=[password] emt umeng1 > umeng1.sql  mysqldump -h [host] -u root --password=[password] emt umeng2 > umeng2.sql  sendEmail.exe

好啦。添加windows定时任务,定时自动执行上述 start.bat 文件。A 服务器结束。

======================================================================

B 服务器要做的就是读取邮件,下载附件,并且执行附件的sql,getEmailAttach.py:

#coding=utf-8  import poplib  import base64  import os  import re  import imp  import datetime  import email_config  from email import parser    #这个字典,key是正则表达式,值是相应的解析模块名,匹配不同文件调用相应的解析模块  #比如当 123.sql 时,调用 dump.py 模块  parse_map = {   '^test.xlsx$':'test',   '.*\.sql$':'dump'  }    def getMailAttach():   pop_conn = poplib.POP3("10.1.1.1","110")   pop_conn.user('yanggaofei@happy.com')   pop_conn.pass_('123456')     length = len(pop_conn.list()[1])   if length>20:    point = length -20   else:    point = 0     #每次获得前20封邮件即可,看自己的业务需要了,邮件不那么频繁   messages = [pop_conn.retr(i) for i in range(point, length+1)]   messages = ["\n".join(mssg[1]) for mssg in messages]   messages = [parser.Parser().parsestr(mssg) for mssg in messages]   i=0   for message in messages:    i = i+1    m_from = message["From"]    m_to = message["To"]    m_subject = message["Subject"]      #解码。邮件中有中文时候    if(message["From"].find('?B?') != -1):     m_from = base64.decodestring(message["From"].split('?')[3])    if(message["To"].find('?B?') != -1):     m_to = base64.decodestring(message["To"].split('?')[3])    if(message["Subject"].find('?B?') != -1):     m_subject = base64.decodestring(message["Subject"].split('?')[3])      mailName = "s%" % (m_subject)      #邮件的标题以[DATA]开头的邮件,认为是需要程序解析的邮件,其它邮件则不用管    if(not m_subject.startswith('[DATA]')):     continue      #邮件日期不比最后解析日期新的邮件,不执行,避免同一份邮件多次解析    try:     last_date = datetime.datetime.strptime(email_config.last_date,'%Y/%m/%d %H:%M:%S')    except:     last_date = datetime.datetime.strptime('2014/11/11 00:00:00','%Y/%m/%d %H:%M:%S')    m_date = datetime.datetime.strptime(message["Date"][:-6],'%a, %d %b %Y %H:%M:%S')      if(not m_date > last_date):     continue      print m_from    print m_to    print message["Date"]    print m_subject      email_config.last_date = m_date.strftime('%Y/%m/%d %H:%M:%S')    for part in message.walk():     fileName = part.get_filename()     contentType = part.get_content_type()     #save attachment       if fileName:      fileName = fileName.decode('utf-8')      if(fileName.find('?B?') != -1):       fileName = base64.decodestring(fileName.split('?')[3])      data = part.get_payload(decode=True)      fEx = open(fileName,'wb')      fEx.write(data)      fEx.close()        moduleName = ''      for key in parse_map.keys():       if(re.match(key,fileName)):        moduleName = parse_map[key]        break      print moduleName        if(moduleName != ''):       try:        #动态加载module,在scripts 目录下找同名module        parseModule = imp.load_module(moduleName,*imp.find_module(moduleName,['./scripts']))        print '  load_module: ' + moduleName        parseModule.parse(fileName)       except Exception,e:        print e     #删除附件中的图片等多余文件   for f in os.listdir('./'):    if os.path.isfile(f):     if(f.endswith('.png') or f.endswith('.jpg') or f.endswith('wmz')):      os.remove(f)     pop_conn.quit()   #write last_date   config_file = open('email_config.py','w')   config_file.write('last_date = "' + email_config.last_date + '"')   config_file.close()    getMailAttach()

上述文件会在登录邮件服务器后,自动获取最新20封邮件,找出其中以'[DATA]' 打头的邮件,并且邮件日期比上次程序执行日期新的文件,取其附件。然后调用 ./scripts 目录下的解析文件,我将解析文件放在scripts目录下,会根据文件名动态加载,下面我们看 scripts 下 dump.py 文件(scripts 目录下放一个空的 __init__.py , 这样python 就会将此路径加入到 寻找模块的path):

# coding=utf8  import xlrd  import sys  import MySQLdb  import datetime  import os  import stat  import shutil    def parse(file):      values = []      if(file.split('.')[-1] != 'sql'):          print '---skip' + file          return      file_object = open(file)      sql = file_object.read()      try:          conn = MySQLdb.connect(host=config.mysql_host,user=config.mysql_user,passwd=config.mysql_passwd,db='emt',port=config.mysql_port,charset='utf8')          cur = conn.cursor()          #执行dump 的 sql ,数据建表入库          cur.execute(sql)          print '  dump table success: ' + file      except MySQLdb.Error,e:          print "Mysql Error %d: %s" % (e.args[0], e.args[1])        cur.close()      conn.close()      file_object.close()        if(os.path.exists('tmp/'+file)):          os.chmod('tmp/'+file,stat.S_IWRITE)#去掉只读属性          os.remove('tmp/'+file)        shutil.move(file,'tmp/')      print '  move filt to temp: ' + file      print '  success!'    if __name__ == '__main__':      reload(sys)      sys.setdefaultencoding('utf-8')      exec "import CONFIG as config"      print '=='*10      #os.remove('../tmp/test.xlsx')      #shutil.move('../test.xlsx','tmp/')      uipath = unicode('../ss.sql' , "utf8")      parse(uipath)  else:      exec "import scripts.CONFIG as config"

在B服务器上添加定时任务,10分钟执行一下 获取邮件附件的脚本。

Ok了,测试成功 :)。

 

 来自:http://my.oschina.net/u/867090/blog/346933