开篇介绍
大多数情况下我们的 SSIS 包都会配置在 SQL Agent Job 中周期性的按计划执行,比如每天晚上调用 SSIS 包刷新数据,处理 Cube 等。一旦 SSIS 包中出现任何异常,报错,那么配置在 SQL Agent Job 中的通知,邮件提醒就会把这些错误信息发邮件到指定的用户或者系统维护者,这样就起到了一个错误监控的作用。
但是在有的情况下,有一些自定义的 SSIS 调度框架的计划调度都不是通过 SQL Agent Job 配置来完成的。比如我以前在一个小项目中设计过一个 SSIS 调度框架,只有一个主包配置在 SQL Agent Job 中,所有子包的上线不由 SQL Agent Job 支配。每次新增的子包要上线,只需要将相应的信息以及调度计划注册到相应的表中即可,不需要打开 SQL Agent Job 调整各个子包的执行顺序等等。那么在这种情况下,就只能在 SSIS 内部使用发送邮件的功能来发送错误信息,因为所有的子包和 SQL Agent Job 都是没有直接关系的。
在 SSIS 中我们可以通过 Send Mail Task 或者 Script Task 来发送邮件,当然还有第三种方式就是写存储过程调用发送邮件存储过程.
Send Mail Task 发送邮件
Send Mail Task 的使用其实非常的简单,配置的步骤也并不多,但是在使用它的时候有几个限制:
- 只能发送普通的文本格式的邮件,也就是说不支持 HTML 格式的邮件。
- 连接 SMTP Server 时不支持用户填写用户名和密码,也就是说用户要么在域环境下 SMTP Server 对用户进行 Windows 方式验证,要么就是访问的 SMTP Server 支持匿名访问,不需要提供用户名和密码。
- SMTP Server 的端口号使用默认的,也就是说如果在域中的 SMTP Server 端口号不是默认的话 Send Mail Task 就无法使用了。
这里是我常用的一个测试服务器地址 http://www.yopmail.com/en/ ,免费的并支持匿名访问,可以通过它来进行邮件发送的测试。关于 Send Mail Task 的使用方式就不在这里演示了,在非域和非 Windows 验证方式下用我提到的可匿名访问的 Yop Mail 一试便知,主要是讲 Script Task 发送邮件的方式。
Script Task 发送邮件
新建一个包并首先创建一个 SMTP 连接 - New Connection。
选择 SMTP 连接管理器。
以我的邮箱为例子 [email protected] 发送邮箱服务器是 smtp.126.com,这个可以在网易邮箱配置页面查到。但是非常遗憾!在这里仍然是没有地方可以配置用户名和密码的,默认的情况下仍然是只支持 Windows 验证或者匿名访问。
所以说,如果想要在 SMTP Connection 里自己动手配置用户名和密码是不可能的,那么像这种 163,126等等很多很多邮箱直接通过 SMTP Connection 是肯定无法完成发送邮件操作的。
看下图时一定要理解一点:几乎每一个 SSIS 控件的属性都是可配置的,也是可获取的,这是我们在 SSIS ETL 的学习过程中解决很多很多问题的最关键的地方!
新建一些常用的变量,发件人,收件人,抄送,邮件标题,内容,附件地址,还有用户名和密码等。
PS:关于这个密码有一个故事,以前在公司里有一个管机房管电脑的同事,他的网络下载速度最快也没人管,他的电脑里有各种游戏,高清蓝光和 1024。每次大家伙要点东西像挤牙膏似的,大家都在想办法搞他的密码,直到后来大家有意无意记了并试出了密码 - 大概就是 lowformat1MBD!! 类似于这样的!看了密码之后我们都一致认为这个密码确实很符合他的职业,不过貌似苦大仇深啊!
其中 PV_CONTENT 变量描述的是邮件发送的 HTML 格式的内容,注意是字符串类型的因此需要双引号。
"<h1>Hi BIWORK!</h1> <p> This is a test email from the test SSIS package of BIWORK! </p> <p>Server Name - "+ @[System::MachineName] +"</p> <p>Package Name - "+ @[System::PackageName] +"</p> <p>Package Start Time - "+ (DT_WSTR, 12) ( DT_DBDATE) @[System::StartTime] +"</p> <p></br>Thanks and Regards!</p> <p>Simon</p>"
系统变量的信息也通过表达式嵌入到邮件正文中。
编辑 Script Task 并引入这些变量,Script Task 中的代码 -
using System.Net.Mail; // BIWORK Added using System.Text.RegularExpressions; // BIWORK Added
Main 中的代码包括处理优先级,多个收件人的处理,用户名和密码,添加附件的方式等等。
public void Main() { // Default Priority and SMTP Server Port int iPriority = 1; int smtpPort = 25; //User::PV_ATTACHED_FILE,User::PV_CONTENT,User::PV_MAIL_FROM, //User::PV_MAIL_TO,User::PV_MAIL_TO_CC,User::PV_TITLE String mailFrom = Dts.Variables["User::PV_MAIL_FROM"].Value.ToString(); String mailTo = Dts.Variables["User::PV_MAIL_TO"].Value.ToString(); String mailToCC = Dts.Variables["User::PV_MAIL_TO_CC"].Value.ToString(); String mailTitle = Dts.Variables["User::PV_TITLE"].Value.ToString(); String mailContent = Dts.Variables["User::PV_CONTENT"].Value.ToString(); String mailAttachedFilePath = Dts.Variables["User::PV_ATTACHED_FILE"].Value.ToString(); // User Information String loginUser = Dts.Variables["User::PV_LOGIN_USER"].Value.ToString(); String loginPwd = Dts.Variables["User::PV_LOGIN_PWD"].Value.ToString(); // Get SMTP Server Information from Connection Manager String smtpServer = Dts.Connections["CM_SMTP_126"].Properties["SmtpServer"].GetValue(Dts.Connections["CM_SMTP_126"]).ToString(); try { SmtpClient smtpClient = new SmtpClient(); MailMessage message = new MailMessage(); MailAddress fromAddress = new MailAddress(mailFrom, "BIWORKTEST"); string[] sEmailTo = Regex.Split(mailTo, ";"); string[] sEmailToCC = Regex.Split(mailToCC, ";"); smtpClient.Host = smtpServer; smtpClient.Port = smtpPort; // Login information System.Net.NetworkCredential myCredentials = new System.Net.NetworkCredential(loginUser, loginPwd); smtpClient.Credentials = myCredentials; // Attachment System.Net.Mail.Attachment attachment; attachment = new System.Net.Mail.Attachment(mailAttachedFilePath); message.Attachments.Add(attachment); message.From = fromAddress; // Multiple email to address if (sEmailTo != null) { for (int i = 0; i < sEmailTo.Length; ++i) { if (sEmailTo[i] != null && sEmailTo[i] != "") { message.To.Add(sEmailTo[i]); } } } // Multiple cc address if (sEmailToCC != null) { for (int i = 0; i < sEmailToCC.Length; ++i) { if (sEmailToCC[i] != null && sEmailToCC[i] != "") { message.To.Add(sEmailToCC[i]); } } } // Email priority switch (iPriority) { case 1: message.Priority = MailPriority.High; break; case 3: message.Priority = MailPriority.Low; break; default: message.Priority = MailPriority.Normal; break; } message.Subject = mailTitle; message.IsBodyHtml = true; message.Body = mailContent; smtpClient.Send(message); }catch (Exception ex) { Dts.TaskResult = (int)ScriptResults.Failure; } // Close Script Task with success Dts.TaskResult = (int)ScriptResults.Success; }
保存并执行 SSIS 包,执行成功!
优先级别,标题,发送人等信息 -
附件信息和内容都可以看的到,包括在抄送栏中抄送给了自己,Package Start Time 的时间是来自虚机的时间,我虚机的时间还是 28日,这个是正确的 -
总结
从中可以看出,虽然在 SSIS 中提供了 SMTP 的连接方式,但是实际上它还是默认只允许通过 Windows 验证或者匿名访问的方式来访问。但是我们通过 Script Task 在 Script 中关联到了 SMTP 服务器,并同时写入用户名和密码,这样就实现了在 SSIS 中访问非域环境下第三方的 SMTP 服务器并通过身份验证且发送邮件的效果。
当然,这种方式在实际项目中可能用的并不是很多,因为项目中的邮件肯定是通过域验证的方式去访问更安全一些,毕竟系统信息的传递只能限制在域中。
在项目中使用邮件通知如是 Windows 验证的方式,有这几个点需要注意:
- 在开发与测试过程中,手动执行 SSIS Package 的这个用户要有域中发送邮件的权限,能够通过 Windows 验证。
- 当包部署到 SQL Agent Job 中后,执行 SQL Agent Job 的账号要有域发送邮件的权限,也必须能通过 Windows 验证。
- 如果不需要部署到 SQL Agent Job,那么就直接使用 Send Mail Task 通过 Windows 验证也可以直接发送错误消息提醒。
疑问
在这个例子中的代码是不是一定要像这样去访问 SMTP Server 的连接信息,感觉是不是多此一举? - 可以不用这样访问,其实很多 SMTP 的配置信息最为方便的是配置成变量,通过变量传递给 Script Task 就可以了。这里通过这样的一种方式来表示在 Script Task 中如何获取连接管理器的属性信息的,因为很多时候我们只知道如何去获取变量信息,这一点体现了 C# Script 的灵活性,我们在 Script 中可以做非常多的事情。包括这些访问 SMTP Server 如何使用 smtpClient 发送邮件的代码可以在网上找到现成的很多很多示例,而我们要做的事情就是大胆的灵活运用它们来解决我们的问题。
在我的这些文章中也提到了邮件处理相关的内容
微软BI 之SSRS 系列 - 报表邮件订阅中 SMTP 服务器匿名访问与 Windows验证, 以及如何成功订阅报表的实例
更多 BI 文章请参看 BI 系列随笔列表 (SSIS, SSRS, SSAS, MDX, SQL Server) 如果觉得这篇文章看了对您有帮助,请帮助推荐,以方便他人在 BIWORK 博客推荐栏中快速看到这些文章。