1. Adım: Bildirimleri almak için bir hizmet ve bir kuyruk oluşturun:
use msdb;
go
create queue dbm_notifications_queue;
create service dbm_notification_service
on queue dbm_notifications_queue
([http://schemas.microsoft.com/SQL/Notifications/PostEventNotification]);
go
create event notification dbm_notifications
on server
for database_mirroring_state_change
to service N'dbm_notification_service', N'current database';
go
Unutmayın ki msdb
bu bir kaza değil. Sunucu düzeyinde olay bildirimleri de gönderildiği msdb
için, karşıt konuşma uç noktasını da (hedef) msdb
oluşturursanız, hedef hizmet ve kuyruğun da konuşlandırılması gerektiği anlamına gelir msdb
.
2.Adım: Etkinlik bildirimi işleme prosedürünü oluşturun:
use msdb;
go
create table dbm_notifications_errors (
incident_time datetime not null,
session_id int not null,
has_rolled_back bit not null,
[error_number] int not null,
[error_message] nvarchar(4000) not null,
[message_body] varbinary(max));
create clustered index cdx_dbm_notifications_errors
on dbm_notifications_errors (incident_time);
go
create table mirroring_alerts (
alert_time datetime not null,
start_time datetime not null,
processing_time datetime not null,
database_id smallint not null,
database_name sysname not null,
[state] tinyint not null,
[text_data] nvarchar(max),
event_data xml not null);
create clustered index cdx_mirroring_alerts
on mirroring_alerts (alert_time);
go
create procedure dbm_notifications_procedure
as
begin
declare @dh uniqueidentifier, @mt sysname, @raw_body varbinary(max), @xml_body xml;
begin transaction;
begin try;
receive top(1)
@dh = conversation_handle,
@mt = message_type_name,
@raw_body = message_body
from dbm_notifications_queue;
if N'http://schemas.microsoft.com/SQL/Notifications/EventNotification' = @mt
begin
set @xml_body = cast(@raw_body as xml);
-- shred the XML and process it accordingly
-- IMPORTANT! IMPORTANT!
-- DO NOT LOOK AT sys.database_mirroring
-- The view represents the **CURRENT** state
-- This message reffers to an **EVENT** that had occured
-- the current state may or may no be relevant for this **PAST** event
declare @alert_time datetime
, @start_time datetime
, @processing_time datetime = getutcdate()
, @database_id smallint
, @database_name sysname
, @state tinyint
, @text_data nvarchar(max);
set @alert_time = @xml_body.value (N'(//EVENT_INSTANCE/PostTime)[1]', 'DATETIME');
set @start_time = @xml_body.value (N'(//EVENT_INSTANCE/StartTime)[1]', 'DATETIME');
set @database_id = @xml_body.value (N'(//EVENT_INSTANCE/DatabaseID)[1]', 'SMALLINT');
set @database_name = @xml_body.value (N'(//EVENT_INSTANCE/DatabaseName)[1]', 'SYSNAME');
set @state = @xml_body.value (N'(//EVENT_INSTANCE/State)[1]', 'TINYINT');
set @text_data = @xml_body.value (N'(//EVENT_INSTANCE/TextData)[1]', 'NVARCHAR(MAX)');
insert into mirroring_alerts (
alert_time,
start_time,
processing_time,
database_id,
database_name,
[state],
text_data,
event_data)
values (
@alert_time,
@start_time,
@processing_time,
@database_id,
@database_name,
@state,
@text_data,
@xml_body);
end
else if N'http://schemas.microsoft.com/SQL/ServiceBroker/Error' = @mt
begin
set @xml_body = cast(@raw_body as xml);
DECLARE @error INT
, @description NVARCHAR(4000);
WITH XMLNAMESPACES ('http://schemas.microsoft.com/SQL/ServiceBroker/Error' AS ssb)
SELECT @error = CAST(@xml_body AS XML).value('(//ssb:Error/ssb:Code)[1]', 'INT'),
@description = CAST(@xml_body AS XML).value('(//ssb:Error/ssb:Description)[1]', 'NVARCHAR(4000)');
insert into dbm_notifications_errors(
incident_time,
session_id,
has_rolled_back,
[error_number],
[error_message],
[message_body])
values (
getutcdate(),
@@spid,
0,
@error,
@description,
@raw_body);
end conversation @dh;
end
else if N'http://schemas.microsoft.com/SQL/ServiceBroker/EndDialog' = @mt
begin
end conversation @dh;
end
commit;
end try
begin catch
declare @xact_state int = xact_state(),
@error_number int = error_number(),
@error_message nvarchar(4000) = error_message(),
@has_rolled_back bit = 0;
if @xact_state = -1
begin
-- Doomed transaction, it must rollback
rollback;
set @has_rolled_back = 1;
end
else if @xact_state = 0
begin
-- transaction was already rolled back (deadlock?)
set @has_rolled_back = 1;
end
insert into dbm_notifications_errors(
incident_time,
session_id,
has_rolled_back,
[error_number],
[error_message],
[message_body])
values (
getutcdate(),
@@spid,
@has_rolled_back,
@error_number,
@error_message,
@raw_body);
if (@has_rolled_back = 0)
begin
commit;
end
end catch
end
go
Servis aracısı prosedürünü yazmak, çalışma ortamı kodunuz değildir. Bazı standartlara uymak zorundadır ve bataklık bölgelerine sapmak çok kolaydır. Bu kod bazı iyi uygulamaları gösterir:
- ileti ayrışmasını ve işlemeyi bir işleme sarın. Beyin yok, belli.
- her zaman alınan mesaj türünü kontrol edin. İyi bir servis broker prosedürü olmalıdır işlemek
Error
ve EndDialog
's taraftan iletişim sonlandırarak uygun mesajlar. Bunu yapmazsanız, kol sızıntıları ortaya çıkar ( sys.conversation_endpoints
büyür)
- her zaman bir iletinin ALICI tarafından alınıp alınmadığını kontrol edin. Bazı örnekler @@ rowcount sonra kontrol eder
RECEIVE
, bu mükemmel bir sorun. Bu örnek kod, ileti adı denetimine dayanır (hiçbir ileti NULL ileti türü adını ima etmez) ve bu durumu dolaylı olarak işler.
- işleme hataları tablosu yarat. SSB tarafından etkinleştirilen prosedürlerin arka plan yapısı, mesajlar iz bırakmadan ortadan kalktığında hataların giderilmesini gerçekten zorlaştırır.
Ayrıca, bu kod aynı zamanda eldeki görevle (DBM'nin izlenmesi) ilgili bazı iyi uygulama kodları da yapar:
post_time
( bildirim ne zaman gönderildi? ), start_time
( bildirimi tetikleyen eylem ne zaman başladı? ) ve processing_time
( bildirim ne zaman işlendi? ) arasında ayrım yapın . post_time
ve start_time
muhtemelen aynı veya çok yakın olacak, ancak processing_time
saniye, saat, gün dışında olabilir post_time
. Denetim için ilginç olan genellikle post_time
.
- ve
post_time
ve processing_time
farklı olduğundan, bildirim etkinleştirilmiş bir yordamdaki bir DBM izleme görevinin görünüme bakansys.database_mirroring
bir işi olmadığı açık olmalıdır . Bu görüş , işlem sırasında o anki olayla ilgili olan veya olmayan geçerli durumu gösterir . İşlem, olay gönderildikten uzun bir süre sonra gerçekleşirse (bakım duruş süresini düşünün) sorun açıktır, ancak DBM durumu çok hızlı değiştirir ve bir (veya daha fazla) olayı satır (sık sık gerçekleşir): bu durumda, yayınladığınız kodda olduğu gibi, işlem olayı gerçekleştiği anda denetler, ancak geçerli, son durumu kaydeder . Böyle bir denetimi okumak daha sonra kafa karıştırıcı olabilir.
- her zaman orijinal XML etkinliğini denetleyin. Bu şekilde daha sonra bu XML'yi denetim tablosundaki sütunlara 'parçalanmamış' herhangi bir bilgi için sorgulayabilirsiniz.
3.Adım: Prosedürü kuyruğa ekleyin:
alter queue dbm_notifications_queue
with activation (
status=on,
procedure_name = [dbm_notifications_procedure],
max_queue_readers = 1,
execute as owner);