開篇介紹
大多數情況下我們的 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 連接管理器。
以我的郵箱為例子 BIWORK@126.com 發送郵箱服務器是 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 博客推薦欄中快速看到這些文章。