svētdiena, 2012. gada 18. marts

Service Broker: ārējā aktivizācija

Rakstā plašāk aprakstīta Service Broker ārējā aktivizācija, kura tika pieminēta rakstā Service Broker: aktivizācija. No rakstu sērijas SQL Server Service Broker.

Vairākos soļos sadalīju visu, kas nepieciešams, lai ārējais aktivators sāktu strādāt uz testa piemēra.

1. solis - izveidot lietotāju aktivācijas servisam.
Uz sava datora izveidoju lietotāju, ko izmantošu ārējās aktivizācijas servisam- "TestUser". Normālā gadījumā tam būtu jābūt domēna "Service account". Šeit jāņem vērā, ka uz datora, kura instalēs ārējo aktiviatoru, lietotājam jābūt tiesībām "Log on as a service".
Tiesības var pielikt:
Control Panel -> Administrative Tools -> Local Security Policy.
Tur jāizvēlas:
Local Policies -> User Rights Assignment -> Log on as service
un lietotājs jāpieliek šai grupai:

Par lietotāja tiesībām plašāk var lasīt šeit.

2. solis - uzinstalēt ārējo aktivatoru.
Lejupielādēt ārējās aktivizācijas servisu. To var atrast  Microsoft SQL Server 2008 Feature Pack sadaļā ar nosaukumu "Microsoft SQL Server 2008 R2 Service Broker External Activator". Tiek lejupielādēts msi fails, kuru jāuzinstalē uz datora, kura vēlaties, lai strādātu ārējais aktivizators. Manā gadījumā tas ir tas pats dators, uz kura atrodas SQL Server 2008 R2.
Instalācijas laikā tiek prasīts norādīt lietotāju, zem kura strādās Windows serviss- ārējais aktivators. Instalācijas laikā norādu iepriekšējā solī izveidoto "TestUser".

3. solis - sakārtot datu bāzi (servisi)
Šajā solī izveidošu vajadzīgos objektus datu bāzē. Izpildu mazliet mainītu skriptu no raksta "Service Broker: asinhrons trigeris". Atšķirības: sākumā arī izveidtoa datu bāze ar nosaukumu "TestDB" un rindai "ProcessQueue" nav izveidots apstrādes process:
Create Database TestDB
Go
ALTER DATABASE TestDB SET ENABLE_BROKER
Go
Use TestDB
Go
Create Table dbo.MyTable
(
    MyTableID int primary key identity,
    Stat bit default 0,
    Number int,
    DateInserted datetime default GetDate(),
    DateProcessed datetime
)
Go
Create Queue MyTableQueue;
Create Service MyTableService On Queue MyTableQueue([DEFAULT])
Create Queue  ProcessQueue;
Create Service ProcessService On Queue ProcessQueue([DEFAULT])
Go
Create Procedure dbo.usp_ProcessData(@data xml)
As
Begin
    Declare @docHandle int;
    EXEC sp_xml_preparedocument @docHandle OUTPUT, @data
    Update m
        Set Stat = 1,
            DateProcessed = GETDATE()
    From dbo.MyTable m inner join
        (
            Select *
            FROM OPENXML(@docHandle, '/DataInserted/row', 2)
            With (MyTableID int )
        ) i On m.MyTableID = i.MyTableID
    EXEC sp_xml_removedocument @docHandle
End
Go
Create Trigger tr_MyTable_insert
    On dbo.MyTable For Insert
As
Begin          
        Declare @h UniqueIdentifier;
        Declare @doc xml;
      
        Set @doc =
        (
            Select MyTableID From inserted
            For XML Raw, Elements, Type, Root('DataInserted')
        );
        Begin Dialog Conversation @h
        From Service MyTableService
            To Service 'ProcessService'
        With Encryption = OFF;

        Send On Conversation @h(@doc)
End
Go
Create Procedure dbo.sb_MyTableService
As
Begin
    Set NoCount On;

    Declare @c uniqueidentifier;
    Declare @h uniqueidentifier;
    Declare @m xml;
    Declare @t sysname;

    While 1 = 1
    Begin
        Begin Transaction
        WaitFor(Get Conversation Group @c From dbo.MyTableQueue), TimeOut 100;

        If @c is Null
        Begin
            Rollback Transaction;
            Break;
        End

        While 1 = 1
        Begin
            Receive Top(1)
                @h = conversation_handle,
                @t = message_type_name,
                @m = cast(message_body as xml)
            From dbo.MyTableQueue
            Where conversation_group_id = @c;
          
            If(@@ROWCOUNT <>1)
                Break;
            If @t = N'DEFAULT'
            Begin
                Exec dbo.usp_ProcessData @m
                Continue;
            End
          
            If @t = N'http://schemas.microsoft.com/SQL/ServiceBroker/EndDialog'
            Begin
                End Conversation @h;
                Continue;
            End
        End;
        Commit Transaction
    End  
End
Go
ALTER QUEUE dbo.MyTableQueue
    WITH ACTIVATION
    (
        STATUS=ON,
        PROCEDURE_NAME = dbo.sb_MyTableService,
        MAX_QUEUE_READERS = 1,
        Execute AS Self
    )
4. solis - Aktivācijas rinda un Event Notification
Izveido servisu un tam atbilstošu rindu, kurai ziņot par nepieciešamību aktivizēt ārējo apstrādes procesu. Uzmanība jāievērš kontraktam (PostEventNotification):
Use TestDB
Go
Create Queue ExternalActivatorQueue;
Create Service ExternalActivatorService On Queue ExternalActivatorQueue
([http://schemas.microsoft.com/SQL/Notifications/PostEventNotification])
Un izmantoju Even Notification, lai ziņotu par procedūras aktivizācijas nepieciešamību:
Use TestDB
Go
Create Event Notification NotifyExternalActivator
    On Queue dbo.ProcessQueue
    For QUEUE_ACTIVATION
    To Service 'ExternalActivatorService', 'current database'
5. solis - konsoles aplikācijas izveide
Instrukcija kā veidot ir atrodama rakstā "Service Broker: CLR". Šeit ir pavisam nelielas modifikācijas kodā.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using Microsoft.Samples.SqlServer;
using System.Data.SqlClient;
using System.IO;

namespace SBBlogam
{
    class Program
    {
        static void Main(string[] args)
        {
            if (args.Length != 1)
                throw new Exception("fuj!");
            SqlConnection con = new SqlConnection(args[0]);
            con.Open();
            MyService s = new MyService(con);
            s.Run(true, con, null);
        }

        class MyService : Service
        {
            public MyService(SqlConnection con)
                : base("ProcessService", con)
            {
                // gaidīs jaunas ziņas trīs sekundes. ja nekas neparādās- beidz darbu
                this.WaitforTimeout = TimeSpan.FromSeconds(3);
            }

            [BrokerMethod("DEFAULT")]
            public void Apstradat(Message msg, SqlConnection con, SqlTransaction tran)
            {
                TextReader reader = new StreamReader(msg.Body, Encoding.Unicode);
                string recMsg = reader.ReadToEnd();

                System.Threading.Thread.Sleep(3000); // gaida trīs sekundes

                msg.Conversation.Send(msg, con, tran); // atbildes ziņojums
                msg.Conversation.EndConversation(con, tran);
            }
        }
    }
}
Konsoles aplikāciju nokompilēju un novietoju mapē "c:\TestSB\" (protams, arī ServiceBrokerInterface.dll)

6. solis - DB Lietotāja tiesības
Uz sava datora izveidoju lietotāju "TestUser" (Windows autentifikācija!). Šo lietotāju pielieku serverim un piešķiru nepieciešamās tiesības- jānomaina "Domens" uz nepieciešamo:
Use master;
Create Login [Domens\TestUser] From Windows;
Use TestDB;
Create User TestUser From Login [Domens\TestUser];
Grant Receive On dbo.ExternalActivatorQueue To TestUser;
Grant Receive On dbo.ProcessQueue To TestUser;
GRANT VIEW DEFINITION ON SERVICE::ExternalActivatorService TO TestUser;
GRANT VIEW DEFINITION ON SERVICE::ProcessService TO TestUser;
7. solis - ārējā aktivatora konfigurācijas faila sakārotšana
1. solī instalētais pēc noklusējuma atrodas mapē "%ProgramFiles%\Service Broker\External Activator". Konfigurācijas fails atrodas "%ProgramFiles%\Service Broker\External Activator\Config\EAService.config".
Šis fails jāmodificē, lai rezultāts būtu līdzīgs šim (jānomaina servera nosaukums):
<?xml version="1.0" encoding="utf-8"?>
<Activator xmlns="http://schemas.microsoft.com/sqlserver/2008/10/servicebroker/externalactivator"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="http://schemas.microsoft.com/sqlserver/2008/10/servicebroker/externalactivator EAServiceConfig.xsd"
           >
  <NotificationServiceList>
    <NotificationService name="ExternalActivatorService" id="100" enabled="true">
      <Description>My test notification service</Description>
      <ConnectionString>
        <!-- All connection string parameters except User Id and Password should be specificed here -->
        <Unencrypted>server=Domens\Instance;database=TestDB;Application Name=External Activator;Integrated Security=true;</Unencrypted>
      </ConnectionString>
    </NotificationService>
  </NotificationServiceList>
  <ApplicationServiceList>
    <ApplicationService name="SBBlogam" enabled="true">
      <OnNotification>
        <ServerName>
Domens\Instance</ServerName>
        <DatabaseName>TestDB</DatabaseName>
        <SchemaName>dbo</SchemaName>
        <QueueName>ProcessQueue</QueueName>
      </OnNotification>
      <LaunchInfo>
        <ImagePath>C:\TestSB\SBBlogam.exe</ImagePath>
        <CmdLineArgs>"server=Zvers-PC\ZIZU;database=TestDB;Application Name=External Activator;Integrated Security=true;"</CmdLineArgs>
        <WorkDir>C:\TestSB</WorkDir>
      </LaunchInfo>
      <Concurrency min="1" max="10" />
    </ApplicationService>
  </ApplicationServiceList>
  <LogSettings>
    <LogFilter>
    </LogFilter>
  </LogSettings>
</Activator>
8. solis - Ārējā aktviatora palaišana.
Ejam uz Control panel -> Administrative Tools -> Services.
Atrodam "Service Broker External Activator" un palaižam to "Start" (vai nokonfigurējam, lai sāk darbu kopā ar datoru):

Problēmu gadījumā visas kļūdas tiek izvadītas datora "Application Log".

9. solis - pārbaude.
Datu bāzē izveidototajā tabulā (3. solī) ievietoju pāris rindiņas:
USE TestDB;
Set NoCount On;
Declare @i int = 0
while @i < 10000
Begin
    Insert Into MyTable(Number) Values(@i)
    Set @i += 1; -- šī sintakse tikai > SQL Server 2008
End
Dēļ tā, ka konfigurācijā norādīju, ka maksimāli var startēt 10 procesus (7. solis), varu varu vērot, kā pamazām ieskrienas un strādā 10 rindas apstrādes procesi uz mana datora:

Protams, ir iespējams pamainīt maksimālo procesu skaitu (konfigurācijas failā). Tādā gadījumā ir jāpārstartē ārējais aktivators.
Un procesa monitorēšanai: Service Broker: rindu monitorings.

Nav komentāru:

Komentāra publicēšana