mysql 数据同步方案
mysql 数据同步方案
BryanYang 发表于3年前
mysql 数据同步方案
  • 发表于 3年前
  • 阅读 1282
  • 收藏 72
  • 点赞 1
  • 评论 7

腾讯云 十分钟定制你的第一个小程序>>>   

摘要: 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了,测试成功 :)。



标签: python email
共有 人打赏支持
粉丝 16
博文 90
码字总数 48637
评论 (7)
沙画
78就是来看看你的、52
BryanYang

引用来自“沙画”的评论

78就是来看看你的、52
哈哈,爱你
kslr
邮件我记得是base64编码(?)这样搞不会很累么
羊驼君
既然不能互联只能通邮件,那说明25端口是可以通信的,直接写个客户和服务端进行同步操作不就OK...发邮件还要解析多么蛋疼...
BryanYang

引用来自“KB325688”的评论

既然不能互联只能通邮件,那说明25端口是可以通信的,直接写个客户和服务端进行同步操作不就OK...发邮件还要解析多么蛋疼...
邮件服务器不是我负责的,公司的。我们内外网物理上就是隔绝的。
BryanYang

引用来自“kslr”的评论

邮件我记得是base64编码(?)这样搞不会很累么
没错,base64编码。
陈雄飞
创新啊
×
BryanYang
如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!
* 金额(元)
¥1 ¥5 ¥10 ¥20 其他金额
打赏人
留言
* 支付类型
微信扫码支付
打赏金额:
已支付成功
打赏金额: