SQL Server 2005 的一個主要成就是可以實現可靠、可擴展且功能完善的數據庫應用程序。與 .Net Framework 2.0 公共語言運行庫 (CLR) 的集成使開發人員可以將重要的業務邏輯與存儲過程合並,而 T-SQL 和 XML 中的新增功能擴展了數據操作的可用范圍以及開發人員可用的存儲功能。另一個重要功能是 SQL Server Service Broker,它為數據庫應用程序增加了可靠、可擴展、分布式異步功能。
在開發 SQL Server 2005 時,我們與成功開發過大型可擴展數據庫應用程序的人員進行了交談。結果發現他們幾乎所有的應用程序中都有一個或多個操作是以異步排隊方式執行的。股票交易系統中的結算活動是排隊的,這樣可以在後台進行結算,在前端處理其他交易。訂單輸入系統中的發貨信息放在一個排隊中,稍後將由另一台服務器(通常位於其他位置)上運行的發貨系統讀取。旅行預訂系統在客戶填寫完路線後再進行實際的預訂,並在預訂完成後發送確認電子郵件。在所有這些示例中,許多工作都是通過異步方式完成的,從而提高了系統的響應速度,因此用戶無須等待工作完成就可以收到響應。
在大多數大型系統中,經過仔細分析後都可以發現,許多處理都可以通過異步方式完成。雖然應用程序中的排隊任務無須立即完成,但系統必須確保這些任務能夠可靠地完成。Service Broker 使異步排隊任務的執行可靠並且易於實現。
使應用程序中的部分任務異步執行的另一個優勢是這些任務的處理工作可以在資源可用時完成。如果訂單輸入系統中的發貨任務可以從隊列中執行,發貨系統就無需具有處理峰值訂單負載的能力。在許多訂單輸入系統中,峰值訂單率可能是平均訂單率的兩倍或三倍。由於訂單輸入系統必須具有處理峰值負載的能力,因此大量處理能力在很大一部分時間內都處於閒置狀態。如果在出現峰值時對異步任務進行排隊並在空閒時執行,將顯著提高系統的利用率。
既然異步排隊應用程序具有許多優勢,為什麼不以這種方式編寫所有應用程序?這是因為編寫異步排隊應用程序相當困難!許多嘗試在應用程序中使用數據庫表作為隊列的開發人員發現,實際操作遠比想象的困難。作為隊列的表中包含多個進程,包括同時插入、讀取和刪除幾條記錄。這將帶來並發問題、降低系統性能並導致頻繁死機。盡管許多開發人員都成功地解決了這些難題,但實際操作卻非常困難。Service Broker 通過使隊列成為 SQL Server 2005 數據庫中的第一級數據庫對象解決了這些問題。編寫隊列的大多數難題都已解決,因此開發人員可以專注於編寫異步應用程序,而不是編寫排隊基礎結構。本文的其余內容將討論編寫排隊應用程序涉及到的難題,並解釋 Service Broker 如何幫助您解決這些難題。
在許多異步排隊應用程序中,排隊的消息實際上是有價值的業務對象。例如在訂單輸入系統中,如果將發貨信息放入隊列中以便稍後處理,那麼丟失排隊的數據將意味著無法按訂單發貨。許多可靠的消息傳送系統都將消息存儲在文件系統中,因此磁盤驅動器的損壞將導致消息丟失。Service Broker 將消息存儲在隱藏的數據庫表中,因此 SQL Server 為保護數據而提供的所有數據完整性功能同樣可用於保護排隊的消息。如果使用數據庫鏡像進行故障恢復,當因數據庫故障而轉移到第二個數據庫後,所有消息都會轉移到第二個數據庫中,這樣 Service Broker 應用程序將繼續運行而不會丟失任何數據。
多讀取器隊列是一種擴展異步應用程序的最有效的方式。為證明這一點,我將通過大家都熟悉的兩個示例說明如何進行排隊。
大多數雜貨店都可以增設多個隊列。每個收銀台都有其自己的隊列,因此要購買某些商品,您需要選擇一個隊列。如果您像我一樣,大多數時候選擇的隊列中都有一個滿載而歸的顧客排在您前面,他/她購買的很多商品都需要核實價格,而且要使用第三方遠期支票進行付款。在輪到您結帳之前,可能在您開始排隊時還沒有出門的顧客都已經為他們購買的商品付完了款。這個例子說明了使用多個隊列時的一個問題:即,如果排在前面的任務需要很長時間才能處理完,後面的任務將無法得到及時處理。
使用多個隊列的另一個主要問題是,添加一個隊列後需要在各隊列之間重新分配任務,在隊列之前來回移動任務可能會浪費很多時間。還以上面的雜貨店為例,當一個新的收銀台開始工作時,想象一下許多顧客推著購物車蜂擁而至的場面吧。
雖然航空公司並非高效率的典范,但大多數售票點的工作效率都比雜貨店要高,這是因為多個票務代理都在為一個隊列服務。由於只有一個隊列,因此您不必擔心排錯了隊。如果某個乘客占用了較長的時間,其他票務代理仍可以繼續為其他乘客服務(假定有多個票務代理)。
具有多個讀取器的一個隊列也可以輕松擴展。如果隊列太長,可以增加代理而不必打斷隊列。某些代理也可以在接待完當前乘客後離開,而不會在隊列中造成混亂。
盡管可能會偏離主題,但我們仍以機場隊列為例來說明基於隊列的應用程序的另一個常見問題。假定排在一個隊列中的幾個人屬於一個團隊,比如說,我和我的家人正在登記准備去旅行。再假定我的家人由於到達時間不同而分散排在一個隊列中。如果我們希望坐在一起,那麼代理將需要預留一排坐位。如果我和妻子同時來到不同的票務代理處,則我可能預訂了第 4 排的 5 個座位,而我的妻子預訂了第 47 排的 5 個座位。這是多讀取器隊列的一個主要問題:即,如果在不同的線程中同時處理相關的消息,將很難進行協調。請想象一下同時處理訂單標題和訂單內容的情景。處理訂單內容的讀取器會認為沒有相關的訂單標題,因為訂單標題還未讀入數據庫。為了正確工作,訂單內容必須多次重新查找訂單標題,以確保訂單標題只是被延遲而不是不存在。
一種更簡單的方法是讓第一個到達售票處的乘客將所有相關乘客都叫到前面來,以便由一個售票處為所有這些乘客提供服務。 Service Broker 的功能與此類似,它在接收到一條消息後鎖定相關的其他消息。持有鎖定的讀取器可以接收隊列中屬於同一組的所有其他消息,而其他讀取器都不能讀取這些消息。在提交事務之前,消息將一直保持鎖定狀態。該鎖定稱為“會話組”鎖定。會話組是開發人員定義的相關消息組。例如,處理特定訂單所需的所有對話(訂單標題、訂單內容、庫存、發貨、帳單等等)可能被放入同一個會話組中。從其中一個會話讀取消息時,將鎖定會話組中的其他消息,這樣只有持有鎖定的讀取器可以處理隊列中的所有相關消息。請注意,只鎖定一個組中的消息,這一點非常重要。盡管可以同時處理上百個組,但任一時刻一個線程只處理一個組。在提交或回滾線程創建的事務之前,組將一直處於鎖定狀態。
我想通過這個例子說明的最後一個問題是,當持有會話組的事務被提交後,該會話組中的另一條消息到達時會發生什麼情況。還以售票處為例,當我的家人都登記後,我的一個孩子才到達機場。由於原始事務已經結束,最後一位乘客可以由任一票務代理服務。只有在原始代理留下便條,說明該組中其他人員的位置時,新代理才能知道如何為最後一位乘客安排座位。同樣,當處理相關消息組的事務完成時,必須記錄會話的“狀態”以便當該組中下一條消息到達時,接收該消息的隊列讀取器了解上一個事務停止時的位置。因為這是數據庫應用程序,所以存儲該狀態的位置應該是一張數據庫表。Service Broker 提供了一個會話組 ID,可以使用它來方便地將會話狀態和會話中的消息相關聯。這是與會話組中每條消息一起顯示的唯一標識符。如果唯一標識符用作存儲狀態的表中的密鑰,則消息處理邏輯可以很容易地找到與接收到的每條消息相關聯的狀態。另外,因為每次只有一個隊列讀取器可以處理來自特定會話組的消息,所以開發人員不必擔心同時有兩個事務更新狀態行從而導致丟失狀態信息。
以上示例說明,多讀取器隊列是擴展大型應用程序的一種簡單而有效的方法。Service Broker 提供的會話組鎖定機制使編寫使用多讀取器隊列的應用程序像編寫使用單讀取器隊列的應用程序一樣容易。
前面討論的隊列都假定位於一個數據庫中。為了開發適用於多種業務情況的松散耦合的分布式數據庫應用程序,我們需要進行擴展,以包括分散在網絡中(通過可靠的消息傳送進行通信)的多個數據庫中的隊列。我們需要可靠的消息傳送,因為如果使用數據庫確保隊列中消息的完整性,但消息卻在傳送到其他數據庫的過程中丟失,所有工作都將是徒勞的。
Service Broker 使用稱為“對話協議”的可靠消息傳送協議來確保發送到遠程隊列的消息按順序到達並且僅到達一次。正如對話是雙向會話一樣,對話協議同時支持雙向的消息傳送。
對話消息具有標題,可確保消息按正確順序安全地傳送到正確的目標位置。它包含序列號、消息所在對話的標識符、要發送到的服務的名稱、安全性信息以及用於控制消息傳送的一些其他信
消息傳送系統在傳送較大的消息時經常會遇到問題。發送以 GB 為單位的消息會花幾分鐘時間,從而會占用一段時間的網絡連接。如果發生網絡錯誤導致重新發送消息多次,將會嚴重影響網絡性能。為了解決此問題,Service Broker 對話協議將大型消息拆分成多個較小的片段,然後再單獨發送每個片段。如果發生網絡錯誤導致重新發送,則只重新發送傳送失敗的消息片斷。因此,Service Broker 最大可以支持 2GB 的消息,而許多可靠的消息傳送系統只能發送 100MB 或更小的消息。
“僅一次”消息處理需要使用事務性消息。為了說明此問題,假定某個應用程序在處理消息的過程中發生了錯誤。當應用程序重新啟動時,它如何才能知道是否要處理發生錯誤時正在處理的消息呢?數據庫中可能已經更新了消息處理的結果,因此重新處理消息可能產生重復的數據。唯一安全的處理方式是使接收消息成為更新數據庫的同一事務的一部分。在系統崩潰時數據庫更新和消息接收都回滾,因此數據庫和消息隊列的狀態與系統崩潰前的狀態相同。
因為所有 Service Broker 操作都在數據庫事務環境中發生,所以保證了消息傳送操作的 事務完整性。典型的 Service Broker 消息處理事務包括以下步驟:
1.
開始事務。
2.
從會話組中接收一條或多條消息。
3.
從狀態表中檢索會話的狀態。
4.
處理消息並根據消息內容對應用程序數據進行一項或多項更新。
5.
發送一些 Service Broker 消息:即,將響應發送到傳入的消息或將消息發送到處理傳入消息所需的其他服務。
6.
如果此會話組包含其他消息,則讀取和處理此會話組中的其他消息。
7.
使用新的會話狀態更新會話狀態表。
8.
提交事務
Service Broker 事務性消息傳送的一項重要功能是在任何時間如果崩潰或應用程序發生錯誤,事務將回滾並且一切都返回到事務開始時的狀態,即狀態不變、應用程序數據不變、消息未發送並且接收的消息返回到隊列中。這使此類應用程序中的錯誤處理非常簡單。
在 Service Broker 應用程序的消息處理過程中,隊列讀取器首先從隊列接收消息。由於消息始終從隊列中拉出,因此在消息到達隊列時接收應用程序必須正在運行。許多異步消息應用程序都面臨著一個問題,即如何確保隊列讀取器在需要時處於運行狀態?有兩種傳統的方法:讓隊列讀取器成為連續運行的服務,或者使用觸發器,在每條消息到達時,都將觸發消息傳送系統。Windows NT 服務方法是指即使在不處理消息時也運行應用程序。由於隊列讀取器頻繁開始和停止,觸發器方法可能會遇到性能問題。
Service Broker 采用稱為激活的中間狀態方法來管理隊列讀取器。為設置激活,DBA 將存儲過程與 Service Broker 隊列相關聯。當第一條消息到達隊列時,激活邏輯將啟動指定的存儲過程。存儲過程負責接收和處理消息,直到隊列為空。隊列為空後,存儲過程可以終止以節省資源。
如果 Service Broker 確定消息添加到隊列的速度比存儲過程能夠處理的速度快,激活邏輯將開始其他的存儲過程,直到存儲過程能夠處理傳入的消息或者達到了為隊列配置的存儲過程的最大數量。由於為隊列提供服務的隊列讀取器數量隨著傳入消息速率的更改增大或減小,因此所有時間都將運行適當數量的隊列讀取器。
關於 Service Broker 的一個常見問題是“為什麼要將消息傳送功能內置在數據庫中?數據庫外部難道沒有足夠可靠的消息傳送系統?”
我希望前面的信息已經說明了為什麼要在數據庫引擎中內置 Service Broker,下面提供了要在數據庫中傳送消息的一些其他理由:
•
消息和數據的單客戶端連接。除了上一部分中提到的統一編程模型,還具有一些其他顯著的優勢:
•
當在任何可以連接到數據庫的客戶端上運行時,應用程序可以通過事務接收消息。當接收程序與隊列在同一計算機上運行時,許多消息傳送系統只允許事務性接收。
•
與在數據庫中不存儲消息的消息系統不同,事務性消息傳送不需要分布式事務或兩階段提交。
•
數據和消息之間集成的管理、部署和操作。用於保護和管理數據庫數據的所有工具和技術同樣適用於消息:
•
備份和恢復數據庫還可以備份和恢復消息隊列。
您正在看的SQLserver教程是:SQL Server 2005 Service Broker 初探。 •
如果使用群集或數據庫鏡像來保護數據庫以避免出現故障,則您的消息享有相同的保護。
•
由於隊列具有相關的視圖,因此確定隊列中的操作非常簡單。要了解隊列中有多少條消息嗎?請從隊列中選擇 count(*)。要了解有多少條消息尚未傳送嗎?請從 sys.transmission_queue 中選擇 *。如果消息中包含 XML 數據,則可以使用 XQuery 進行搜索。可以使隊列中的消息與狀態信息甚至數據表相結合,以確定訂單輸入系統中特定訂單的完成狀態。
•
將消息傳送功能內置在數據庫中還有一些顯著的性能優勢。
•
正如前面所述,事務性消息不要求兩階段提交。
•
消息更新、狀態更改和數據更新都記錄在同一事務日志中,因此在提交事務時只需寫入單個日志。
•
可靠的消息傳送通常將消息從傳送隊列傳送到接收隊列。如果 Service Broker 檢測到接收隊列與傳送隊列位於同一數據庫實例中,則消息直接放入接收隊列中,從而節省了額外的輸入/輸出並減少了事務提交的次數。
Service Broker 功能通過 SQL Server 中的新對象啟用,這些新對象可以由一組 T-SQL 擴展來創建和操作。為了數據庫程序員的方便,使用了他們熟悉的用於配置其他數據庫對象的 CREATE、ALTER 和 DROP DDL 語句來配置 Service Broker 應用程序。用於創建 Service Broker 對話以及在對話中發送和接收消息的命令是 Transact SQL 語言的 DML 擴展。接收命令的語法與選擇命令相似,它返回包含消息的行集,就像選擇命令返回包含行的行集一樣。熟悉 Transact SQL 編程的開發人員會發現學習 Service Broker 編程非常容易。用於為 Service Broker 編程的客戶端 API 與用於為所有數據庫編程的 API 相同,例如 OLEDB、ODBC、ADO、ADO.Net 等等。下面是 Service Broker 使用的對象。
Service Broker 使用隊列在消息發送程序和消息接收程序之間提供松散耦合。發送程序可以使用 SEND 命令將消息放到隊列中,然後應用程序繼續操作並依靠 Service Broker 來確保消息到達其目標位置。
隊列允許較大的計劃靈活性。例如,發送程序可以發送多條消息以供多個接收程序並行處理。接收程序可能在消息發送很長時間後才處理消息,但由於傳入消息進行了排隊,因此接收程序可以按其自己的速率處理消息,而且發送程序無須等待接收程序完成處理便可繼續操作。
Service Broker 還實現了對話,對話是兩個端點之間的雙向消息流。對話中的所有消息都進行了排序,而且對話消息總是按照發送的順序傳送。該順序在事務、輸入線程、輸出線程以及系統崩潰和重新啟動過程中都保持不變。有些消息系統可以確保單個事務中發送或接收的消息順序,但不能確保多個事務中的順序,因此 Service Broker 對話具有獨特的優勢。
對話是一種會話類型。Service Broker 會話是持久可靠的通信通道。在以後的 SQL Server 版本中,Service Broker 將包括一對多的單向會話,也稱為可靠的“發布-訂閱”。在 SQL Server 2005 中,對話是唯一的會話類型,因此這兩個術語同義。
每條消息都包括唯一標識與它相關的對話的會話句柄。例如,某個訂單輸入應用程序可能同時使用發貨應用程序、庫存應用程序和帳單應用程序打開了對話。因為每個應用程序中的消息都具有唯一的會話句柄,所以可以輕松地確定發送每條消息的應用程序。
當前,所有 Service Broker 消息都與特定的消息類型相關聯。消息類型是與消息一起傳送的標簽,因此接收消息的應用程序可以確定所接收消息的類型。此外,如果消息包含 XML 文檔,則消息類型可以與 XML 架構集合相關聯。如果為某個消息類型指定了架構集合,則所接收的該類型消息將在收到時根據架構集合進行驗證,而沒有通過架構驗證的消息將會被拒絕。
Service Broker 規范是一個消息類型集合。一個對話總是與一個規范相關聯,而規范定義哪些消息類型可以通過對話發送。
Service Broker 服務將一個或多個規范與一個隊列相關聯。規范定義可以將哪些消息類型發送到隊列。服務名稱用於建立對話的端點。服務名稱用作實際隊列的別名,因此您可以編寫引用服務名稱的 Service Broker 程序,然後在部署應用程序時將它與實際隊列相關聯。
大多數編程語言書籍都以“Hello World”示例開始,因此我們也在 Service Broker 中使用 Hello World 作為示例。為簡化示例,我使用可以從 SQL Server Management Studio 查詢窗口運行的 T-SQL 進行編寫。大多數 Service Broker 應用程序將作為普通的數據庫應用程序,這些應用程序由通過 ADO 或 ADO.NET 與數據庫進行通信的可執行程序和使用 T-SQL 或 .Net 語言編寫的 SQL Server 存儲過程構成。
要發送和接收消息,必須首先創建向 Service Broker 描述應用程序的消息傳送部分的元數據對象。在 SQL Server 2005 中,使用新添加的 DDL 語句來創建、修改和刪除 Service Broker 元數據。
-- 創建要使用的數據庫 Create Database HelloWorldDB go Use HelloWorldDB go -- 創建要使用的兩種消息類型。我們要使用的消息將是 -- 字符串而不是 XML - 因此無需進行驗證 CREATE MESSAGE TYPE [HelloWorldRequest] VALIDATION = NONE CREATE MESSAGE TYPE [HelloWorldResponse] VALIDATION = NONE -- 創建一個限制此對話框中消息類型 -- 的規范。請求由對話框的初始化程序發出 -- 響應消息由對話框目標發送。 CREATE CONTRACT [HelloWorldC
您正在看的SQLserver教程是:SQL Server 2005 Service Broker 初探。ontract] ( [HelloWorldRequest] SENT BY initiator, [HelloWorldResponse] SENT BY target ) -- 創建對話框在其間通信的兩個隊列。A -- 對話框請求兩個隊列。 CREATE QUEUE [HelloWorldTargetQueue] CREATE QUEUE [HelloWorldInitiatorQueue] -- 創建命名對話框端點的服務。服務會將 -- 會話端點連接到隊列。 CREATE SERVICE [HelloWorldRequestService] ON QUEUE [HelloWorldTargetQueue] ( [HelloWorldContract] ) CREATE SERVICE [HelloWorldResponseService] ON QUEUE [HelloWorldInitiatorQueue] go
現在已經設置了元數據,可以發送消息了。請注意,由於初始化程序和目標服務位於同一 SQL Server 實例中,因此消息將直接轉到目標隊列而不會通過傳送隊列傳送。由於 Service Broker 內置在數據庫中,因此可以進行此項性能優化。
Use HelloWorldDB go SET NOCOUNT ON DECLARE @conversationHandle uniqueidentifIEr Begin Transaction -- 開始 Hello World 服務的對話 BEGIN DIALOG @conversationHandle FROM SERVICE [HelloWorldResponseService] TO SERVICE 'HelloWorldRequestService' ON CONTRACT [HelloWorldContract] WITH ENCRYPTION = OFF, LIFETIME = 600; -- 發送消息 SEND ON CONVERSATION @conversationHandle MESSAGE TY
PE [HelloWorldRequest] (N'Hello World') commit
讓我們查看目標隊列以確保成功發送了消息。
Use HelloWorldDB go -- 檢查目標隊列以確認消息已送達 select * from [HelloWorldTargetQueue] go -- 將消息主體轉換為字符串,以便我們查看其中包含的內容 select cast(message_body as nvarchar(MAX)) from [HelloWorldTargetQueue] go
現在可以從目標隊列中接收消息並將響應發送回初始化程序。
-- 使用 Receive 命令可從隊列接收消息 -- 聲明變量以存儲接收到的數據 SET NOCOUNT ON DECLARE @conversationHandle uniqueidentifIEr declare @message_body nvarchar(MAX) declare @message_type_name sysname; -- Service Broker 命令總是位於事務中 Begin Transaction; -- Receive 命令的格式類似於一個選擇列表。首先列出 -- 要獲取的列,然後指定要從中獲取消息 -- 的隊列 RECEIVE top(1) -- 只接收一條消息,因此我們可以直接保存到變量中。 @message_type_name=message_type_name, -- 接收的消息類型 @conversationHandle=conversation_handle, -- 對話的標識符 -- 我們通過下列語句接收該消息 @message_body=message_body -- 作為 -- varbinary(MAX) blob 的消息內容 FROM [HelloWorldTargetQueue] print @message_body -- 如果這是一條 hello world 消息,則用相應的問候語回答 if @message_type_name = N'HelloWorldRequest' Begin SEND ON CONVERSATION @conversationHandle -- 使用下列消息接收語句的相同會話 MESSAGE TYPE [HelloWorldResponse] (N'Hello From '+@@servername ) -- 這是我們希望從初始化程序接收的唯一消息,因此 -- 現在可以安全地結束對話。 END CONVERSATION @conversationHandle End -- 提交事務 -- 如果此時我們回滾,所有內容將退回到 -- 我們開始時的狀態 – 消息會返回到隊列,並且沒有發送響應 Commit go -- 確認我們從隊列中刪除了消息 select cast(message_body as nvarchar(MAX)) from [HelloWorldTargetQueue] go
響應已在對話中發送回初始化程序隊列,現在檢查響應是否成功到達:
Use HelloWorldDB go select cast(message_body as nvarchar(MAX)) from [HelloWorldInitiatorQueue] go
... 最後接收並顯示響應消息:
RECEIVE cast(message_body as nvarchar(MAX)) FROM [HelloWorldInitiatorQueue]
現在已經成功地向 Service Broker 發送了消息並收到了響應。如果希望查看剛剛運行的命令的詳細說明,請參見 SQL Server 聯機書籍所提供的豐富消息。如果希望擴展此示例以在兩個數據庫之間使用,您需要添加從發起程序服務器到目標服務器的路由和從目標返回到初始化程序的路由。Service Broker 路由將 BEGIN DIALOG CONVERSATION 命令中指定的服務名稱映射到發送消息所需的實際網絡地址信息。有關路由和安全性的詳細信息,請參見 SQL Server 聯機書籍。
現在您已經看到了 Service Broker 是如何工作的,下面將介紹可以使用 Service Broker 開發的部分應用程序。
許多程序都使用訂單輸入作為示例,因為大多數人都了解該過程。即使沒有開發過訂單輸入系統的開發人員至少都使用過訂單輸入系統進行訂貨。
基於 Service Broker 的訂單輸入系統使用隊列來將系統的子系統連接到一起。這不僅增加了並行度並且提高了效率,而且使配置和體系結構具有非常大的靈活性。
在本示例中,Service Broker 用於連接四個松散耦合的服務以處理每個訂單。在訂單輸入服務將訂單標題和訂單內容插入到數據庫中時,它根據計費、發貨、庫存和信貸限額服務對消息進行排隊以完成對訂單的處理。Service Broker 允許四項服務並行運行,從而縮短了系統的響應時間。
根據系統的業務需求,訂單輸入服務可以等待收到所有四項服務的響應後返回到用戶,也可以在提交初始事務後立即返回而在後台執行其余的服務。實現每個操作都會對訂單輸入服務進行一些更改。每種情況中的其他服務保持不變。
使用 Service Broker 鏈接服務還提供了幾個部署選項。所有五項服務可以在同一台服務器上運行,也可以根據需要分散在五台服務器(在服務的負載平衡時可以為更多的服務器)上運行以提供所需的效率。服務可以部署為存儲過程或外部應用程序。這種靈活性對 ISV 非常有用,ISV 可以根據客戶對性能、冗余和效率的需求創建單個代碼庫並在各種配置中進行部署。
您正在看的SQLserver教程是:SQL Server 2005 Service Broker 初探。
圖 1:使用 Service Broker 鏈接五項訂單輸入和處理服務
最初發明觸發器時,數據庫公司通常對庫存表使用觸發器,在庫存量太低時自動發出訂單。雖然這是一個很好的概念,但是只有很少的系統使用了觸發器,因為在觸發器中執行大段代碼的額外開銷會使數據庫的更新速度非常慢。Service Broker 允許觸發器對要完成的工作進行排隊,以便觸發器可以完成和允許提交初始事務,從而使這類應用程序可以被廣泛采用。然後,排隊的工作在單獨的事務中完成,甚至可以在不同的數據庫中完成。SQL Server 2005 對“查詢通知”和“事件通知”使用此模型。
Service Broker 還允許存儲過程並行開始幾個其他的存儲過程。此功能可以顯著縮短響應時間。例如,某個呼叫中心應用程序對傳入的呼叫使用呼叫方 ID 查尋有關正在呼叫的客戶的所有信息,因此客戶服務代表可以獲取所有相關信息。在許多情況下,必須從不同系統上的不同數據庫中獲取這些信息。可以使用遠程查詢獲取所有這些信息,但響應時間可能太長。Service Broker 可以同步將請求排隊到所有遠程服務並在其輸入隊列上等待結果。因為並行處理所有請求,所以總響應時間將縮短。
圖 2:並行處理示例
批處理系統中經常會使用 Service Broker 應用程序。大多數批處理由許多小型的半獨立處理構成,必須對這些半獨立處理進行計劃和協調。獨立執行子過程允許每個子系統按其最佳速度執行,因此可以提高效率。
在本示例中,將會不間斷地在輸入隊列上累計對批計劃過程的輸入,包括預購、預測、返回等等。當計劃引擎運行時,它閱讀隊列中的輸入、進行分析、然後將請求排隊到處理計劃輸出的子過程。輸出隊列允許在一台服務器或多台服務器上並行地單獨執行子過程。這允許根據需要擴展任意多台服務器以滿足處理負載的需求。
圖 3:大批量處理示例
以前,我經常為碩士研究生講授分布式數據庫類。我曾經使用的一個分布式事務的示例是旅行社在同一事務中預訂了飛機票、旅館房間和租車服務,因為客戶在無法進行航空旅行時不會預訂旅館。像許多示例一樣,直到有一天一位來自真正旅行社的程序員來到了教室裡,我們才了解真實的情況。他告訴我真正的旅行社不是這樣工作的。在預訂旅館房間時,航空公司不允許任何人鎖定其預訂表格和座位安排。預訂根據當前的可用性完成,並且如果在實施預訂時已經沒有空余的座位或房間,將給客戶打電話以確保新的預訂滿足客戶需求。
在旅行預訂 Web 站點示例中,預訂根據有關航空公司和旅店可用性的數據表來完成。這些表包含來自航空公司或旅館的信息。信息會經常更新,但總是會有一點過時。實際的預訂在客戶預約旅行後才發生,並可能在客戶取消旅行後被取消。
這種延遲的活動非常適合 Service Broker 體系結構。記錄客戶旅行安排的事務將消息提交到進行實際預訂的後端服務。預訂服務讀取隊列中的消息並在單獨的事務中處理每個預訂。預訂服務通過各種協議與預訂航空公司和旅館服務的系統進行通信。這些通信可能包括 Web 服務、SNA、HTTP、EDI、傳真、Service Broker 等等。由於對到預訂服務的輸入進行了排隊,因此這些協議的不同滯後時間不會帶來問題。如果隊列太長,Service Broker 激活可以啟動更多的隊列讀取器來處理負載。如果負載達到預訂服務器能夠處理的最大值,則可以通過向路由表添加行來添加更多的服務器。
在成功完成特定路線的所有預訂後,會將一條消息排隊到 SQLiMail 服務器以向客戶發送確認電子郵件。如果一項或多項預訂未能完成,將通知客戶服務代表來幫助重新預訂旅行。
“處理更新”服務在後台運行,並定期從旅館和航空公司接收可用性信息。這些信息被轉換為常見的 XML 格式,然後通過通知服務發布到 Web 集群中的服務器以更新旅館和航空公司的可用性表。
松散耦合的 Service Broker 體系結構允許通過加載軟件、配置數據庫和訂閱可用性反饋來將 Web 服務器添加到服務器集群。可以編寫腳本以使進行操作時只需最少的手動干預。為向預訂服務添加更多後端服務器,必須安裝和配置該服務,然後必須將新的服務器地址添加到 Web 服務器中的路由表。
圖 4:旅行預訂示例
SQL Server 2005 Service Broker 是用於生成分布式異步數據庫應用程序的新平台。SQL Server 數據庫中包括的可靠異步消息功能使您可以生成各種以前難以生成甚至無法生成的數據庫應用程序。SQL Server 2005 集成了 Service Broker 以及 CLR 和 XML 數據類型等其他開發人員功能,成為生成高度可擴展數據庫應用程序的一個主要平台。