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