本文属于个人原创作品、个人总结,谢绝转载、抄袭。如果您有疑问或者希望沟通交流,可以联系QQ:865562060。
现阶段服务器、数据库上云的选择越来越多,但不是说服务器和数据库上云管理之后就可以安心睡觉了。做好云服务器和云数据库的备份管理也尤为重要。
最近用C#做阿里云数据库RDS的自动备份,感觉受到了很深的歧视。因为.Net模块里阿里云提供的demo是Java的,.Net SDK文档里的调用示例居然也是Java的。真是不让人省心的调用,经过几个小时的努力总算是摸索着dll弄好了。
我这里实现的思路是这样的:云数据库RDS我们是设置的云上保存7天的备份,每隔一天全量备份、隔一天增量备份,这些备份都有外链和内链支持下载(外网连接阿里云下载使用外链,但速度较慢;阿里云内网下载使用内链,速度非常快)。我要做一个定时运行的程序(我选择控制台应用程序,并在一台windows服务器上添加任务计划,定时运行这个控制台应用程序),自动下载云数据库备份的文件,并转储到指定地点。这里我转储两个地方,一个是公司磁盘(使用外链),另外一个是阿里云对象存储服务OSS(使用内链)。这样即使RDS和OSS都崩了,我本地也还有备份(虽然完全不可能出现这种情况)。
1、创建控制台应用程序AliyunRDS(可以根据业务选择不同的程序),添加以下三个服务引用:aliyun-net-sdk-Core、aliyun-net-sdk-rds、Aliyun.OSS。
下载地址:https://develop.aliyun.com/tools/sdk?spm=a2c4g.11186623.2.10.7eea5234QGc6PP#/dotnet
主要下载以下三个:
2、 Program.cs中引用
using Aliyun.Acs.Rds;
using Aliyun.Acs.Rds.Model.V20140815;
using Aliyun.Acs.Rds.Transform.V20140815;
using Aliyun.Acs.Core.Profile;
using Aliyun.Acs.Core;
using Aliyun.Acs.Core.Http;
using Aliyun.OSS;
using Aliyun.OSS.Common;
3、主要代码实现
namespace AliyunRDS
{
class Program
{
static void Main(string[] args)
{
// 创建DefaultAcsClient实例并初始化
DefaultProfile profile = DefaultProfile.GetProfile(
ConfigurationManager.AppSettings["RegionID"],// 您的可用区ID
ConfigurationManager.AppSettings["AccessKey_ID"],// 您的AccessKey ID
ConfigurationManager.AppSettings["AccessKey_Secret"]);// 您的AccessKey Secret
IAcsClient client = new DefaultAcsClient(profile);
// 创建API请求并设置参数
CommonRequest request = new CommonRequest();
request.Method = MethodType.POST;
request.Domain = "rds.aliyuncs.com";
request.Version = "2014-08-15";
request.Action = "DescribeBackups";//查询RDS实例的日志备份文件,包括文件的下载链接。
request.AddQueryParameters("DBInstanceId", ConfigurationManager.AppSettings["DBInstanceId"]);//实例ID
request.AddQueryParameters("BackupStatus", "Success");//实例ID
request.AddQueryParameters("StartTime", DateTime.Now.AddDays(-7).ToString("yyyy-MM-dd") + "T12:00Z");//查询开始时间。格式:yyyy-MM-ddTHH:mmZ,例如2018-10-31T08:40Z
request.AddQueryParameters("EndTime", DateTime.Now.ToString("yyyy-MM-dd") + "T12:00Z");//查询结束时间。格式:yyyy-MM-ddTHH:mmZ,例如2018-10-31T08:40Z 必须晚于查询开始时间。
request.AddQueryParameters("PageSize", "30");//每页最多显示的日志文件个数。取值:30,50,100默认值:30
request.AddQueryParameters("PageNumber", "1");//页码。大于0且不超过Integer的最大值。默认值:1。
// 发起请求并处理应答或异常
CommonResponse response = new CommonResponse();
try
{
response = client.GetCommonResponse(request);
Result model = JsonConvert.DeserializeObject(response.Data);
if (model != null)
{
List list = model.Items.Backup;
if (list.Count > 0)
{
//阿里云接口返回的数据库备份文件为按时间倒序排序。我们每天执行就取第一条下载
LogHelper.WriteProgramLog(DateTime.Now.ToString() + " 备份文件内网下载地址为:" + list[0].BackupIntranetDownloadURL);
//1、下载、设置参数
HttpWebRequest requestFile = WebRequest.Create(list[0].BackupIntranetDownloadURL) as HttpWebRequest;
//发送请求并获取相应回应数据
HttpWebResponse responseFile = requestFile.GetResponse() as HttpWebResponse;
//直到requestFile.GetResponse()程序才开始向目标网页发送Post请求
Stream responseStream = responseFile.GetResponseStream();
//2、上传文件目录
string uploadPath = ConfigurationManager.AppSettings["uploadPath"];
if (!Directory.Exists(uploadPath))
{
Directory.CreateDirectory(uploadPath);
}
string[] strArray = list[0].BackupIntranetDownloadURL.Split(new string[] { "?" }, StringSplitOptions.RemoveEmptyEntries);
string fileName = "";
if (strArray.Length > 0)
{
string[] files = strArray[0].Split('/');
fileName = files[files.Length - 1];
}
else
fileName = DateTime.Now.ToString("yyyyMMddHHssmm") + new Random().Next(10000) + ".zip";
string filePath = uploadPath + "\\" + fileName;
//3、创建写入流 数据库备份文件可能非常大 所以这里使用分块下载
FileStream fs = new FileStream(filePath, FileMode.Create, FileAccess.Write);
byte[] bytes = new byte[1024 * 512];
int readCount = 0;
while (true)
{
readCount = responseStream.Read(bytes, 0, bytes.Length);
if (readCount <= 0)
break;
fs.Write(bytes, 0, readCount);
fs.Flush();
}
fs.Close();
responseStream.Close();
responseFile.Close();
string sResult = PutObject(ConfigurationManager.AppSettings["bucketName"], fileName, filePath);
if (!string.IsNullOrEmpty(sResult)) //Put Object请求处理成功后,OSS会将收到文件的MD5值放在返回结果的ETag中。用户可以根据ETag检验上传的文件与本地的是否一致。使用下面的方法GetMD5HashFromFile(),这里未做检验。
{
LogHelper.WriteProgramLog(DateTime.Now.ToString() + " 文件上传文件到OSS成功,删除本地文件!");
if (File.Exists(filePath))
File.Delete(filePath);
LogHelper.WriteProgramLog(DateTime.Now.ToString() + " 备份完成!");
}
}
else
LogHelper.WriteProgramLog(DateTime.Now.ToString() + " 查询到的备份文件列表为空!");
}
else
Console.WriteLine(DateTime.Now.ToString() + " 发生错误!");
}
catch (Exception ex)
{
LogHelper.WriteProgramLog(DateTime.Now.ToString() + " 发生错误:" + ex.Message);
throw ex;
}
}
///
/// 获取随机字符串
///
/// 长度
/// 是否使用数字
/// 是否使用小写字母
/// 是否使用大写字母
/// 是否使用特殊字符
/// 固定字符串
///
public static string GetRandomString(int length, bool useNum, bool useLow, bool useUpp, bool useSpe, string custom)
{
byte[] b = new byte[4];
new System.Security.Cryptography.RNGCryptoServiceProvider().GetBytes(b);
Random r = new Random(BitConverter.ToInt32(b, 0));
string s = null, str = custom;
if (useNum == true) { str += "0123456789"; }
if (useLow == true) { str += "abcdefghijklmnopqrstuvwxyz"; }
if (useUpp == true) { str += "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; }
if (useSpe == true) { str += "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~"; }
for (int i = 0; i < length; i++)
{
s += str.Substring(r.Next(0, str.Length - 1), 1);
}
return s;
}
///
/// 上传本地的文件到指定的OSS的存储空间
///
/// 指定的存储空间名称
/// 文件的在OSS上保存的名称
/// 指定上传文件的本地完整路径
public static string PutObject(string bucketName, string key, string fileToUpload)
{
try
{
ClientConfiguration ossConfig = new ClientConfiguration();
OssClient ossClient = new OssClient(
ConfigurationManager.AppSettings["endpoint"],
ConfigurationManager.AppSettings["OSSaccessKeyId"],
ConfigurationManager.AppSettings["OSSaccessKeySecret"],
ossConfig);
var result = ossClient.PutObject(bucketName, key, fileToUpload);
return result.ETag;
}
catch (Exception ex)
{
LogHelper.WriteProgramLog(DateTime.Now.ToString() + " 文件上传OSS失败!");
LogHelper.WriteProgramLog(DateTime.Now.ToString() + ex.Message);
throw ex;
}
}
///
/// 获取文件的MD5码
///
/// 传入的文件名(含路径及后缀名)
///
public static string GetMD5HashFromFile(string fileName)
{
try
{
FileStream file = new FileStream(fileName, System.IO.FileMode.Open);
MD5 md5 = new MD5CryptoServiceProvider();
byte[] retVal = md5.ComputeHash(file);
file.Close();
StringBuilder sb = new StringBuilder();
for (int i = 0; i < retVal.Length; i++)
{
sb.Append(retVal[i].ToString("x2"));
}
return sb.ToString();
}
catch (Exception ex)
{
LogHelper.WriteProgramLog(DateTime.Now.ToString() + " 获取文件的MD5码失败!");
throw new Exception("GetMD5HashFromFile() fail,error:" + ex.Message);
}
}
}
}