mysql 数据同步方案
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