2010-02-08 7 views
1

서비스 브로커를 메시징 시스템으로 사용하여 작업을 예약하고 실행합니다. Eash 작업은 엔진이라고하는 여러 작업 또는 단계로 구성됩니다.Service Broker 외부 활성화가 여러 번 실행됩니다.

내 서비스 브로커 개체는 다음과 같습니다

  • MessageTypes : SubmitJob, JobResponse, SubmitTask, TaskResponse
  • 계약 : JobContract, TaskContract
  • 대기열 : ClientQueue, JobQueue, EngineQueue, ExternalActivatorQueue
  • 서비스 : ClientService, JobService, EngineService, ExternalActivatorService
  • 이벤트 알림 : EventNotificationEngineQueue
  • ,

는 I는 jobqueue에 내부 활성화 (저장 PROC)를 갖는다. SubmitJob MessageTypes의 경우 저장된 proc은 해당 작업의 첫 번째 작업을 가져 와서 EngineService와 대화 상자를 시작하고 해당 대기열 (StartTask)에 메시지를 보냅니다. TaskResponses MessageType의 경우이 작업에 대해 더 많은 작업이 있는지 확인합니다 다음이 작업에 대한 작업이 모두 훌륭한 일 것 같다 (메시지를 보내고 청소.)

완료하지 않을 경우 그들은는 EngineQueue에 제출받을 수 있습니다. 그러나 EngineQueue 메시지를 처리 ​​할 외부 응용 프로그램 (엔진)이 필요합니다. 그래서 저는 Microsoft의 외부 활성화 메커니즘 (ssbeas.exe)을 사용하고 있습니다. 오랜 시간이 걸렸지 만 마침내 작동하게되었습니다. 메시지가 EngineQueue로 이동하면 EventNotificationEngineQueue가 내 애플리케이션을 실행하고 대기열을 배출합니다. 여태까지는 그런대로 잘됐다. 그러나, 내 애플 리케이션을 여러 번 실행하는 것 같다. 내 테스트 응용 프로그램이 완료되면 전자 메일을 보내도록 구성됩니다. 한 작업으로 하나의 작업 만 보내지 만 여러 전자 메일이 표시됩니다 (프로그램이 여러 번 실행되었음을 나타냄).

다음은 내 응용 프로그램 (vb.net)의 코드입니다 (브로커는 서비스 브로커 서비스를 캡슐화하는 개체입니다). (등, 수신, 전송) :.

While True 

      oBroker.tran = oBroker.cnn.BeginTransaction 

      oBroker.Receive("EMGQueue", msgType, msg, serviceInstance, dialogHandle) 

      If dialogHandle = System.Guid.Empty Then 
       'Console.WriteLine("An Error Occurred. Program Terminated.") 
       oBroker.tran.Commit() 
       Exit While 
      End If 

      ConsoleWriteLine("Received: " & msgType) 

      If (msg Is Nothing) Then 
       ConsoleWriteLine("commiting and exiting") 
       oBroker.tran.Commit() 
       Exit While 

      Else 
       Select Case (msgType) 
        Case "SubmitTask" 
         ProcessMsg(oBroker.cnn, oBroker.tran, msgType, msg, iTaskID, iTaskKey) 
         oBroker.Send(dialogHandle, "<TaskStatus>1</TaskStatus>'") 

        Case "http://schemas.microsoft.com/SQL/ServiceBroker/EndDialog" 
         oBroker.EndDialog(dialogHandle) 

        Case "http://schemas.microsoft.com/SQL/ServiceBroker/Error""" 
         oBroker.EndDialog(dialogHandle) 

       End Select 
      End If 


      ConsoleWriteLine("commiting...") 
      oBroker.tran.Commit() 

     End While 

을 나는 앱이 여러 번 실행하는 이유를 이해 해달라고하지만, 그 이상 내가 후속 버전이 여전히 큐에서 메시지를 볼 수있는 이유를 이해 해달라고 결국, 첫 번째 화신은 큐에 메시지를 잠궈 야합니다. 쿼리 관리자를 사용하여 테스트하여 응용 프로그램이 실행되고 차단 된 동안 메시지를 수신 할 수 있었기 때문에 큐를 잠급니다.

EAService.config의 동시성 값으로 재생 해 보았습니다. min = "0"및 max = "1"로 설정하면 앱이 두 번 실행되는 것처럼 보인 횟수를 줄였습니다 이전에는 min = "0"및 max = "10"을 사용하여 실행 중이었습니다 그것은 18 부 같았다.

길이에 대해 유감스럽게 생각합니다. 누구나 여기서 어떤 일이 일어나고 있는지 아이디어가 있습니까? 내 .net 앱 코딩에 실수를 한 적이 있습니까?

감사

편집 마틴 : 엔진이 실행 된 후 생성 로그 추가 :

2010-02-08 9시 31분 39초을 - 홈페이지
2010-02-08 9시 31분 : 39 - 수신 : SubmitTask
2010-02-08 9시 31분 39초 - ProcessMsg
2010-02-08 9시 31분 39초 - <Task><TaskID> 5</TaskID><TaskKey>2</TaskKey></Task>
2010-02-08 9시 31분 39초 - DoWork
을 2010-02-08 09:31:39 - 메일 보내기
2010-02-08 09:31:40 - 커미팅 ...
2010-02-08 09:31:40 - 잠자기
2010-02-08 09:32:10 - 잠자고 있습니다.
2010-02-08 09:32:10 - Main Complete
2010-02-08 09:32:10 - 외부 활성화 된 응용 프로그램이 지금 성공하고 종료됩니다.
2010-02-08 9시 32분 10초 - 홈페이지
2010-02-08 9시 32분 10초는 - 수신 : SubmitTask
2010-02-08 9시 32분 10초 - ProcessMsg
2010-02- 08 9시 32분 10초 - <Task><TaskID> 5</TaskID><TaskKey> 2</TaskKey></Task>
2010-02-08 9시 32분 10초 - DoWork
2010-02-08 9시 32분 10초 - 이메일
2010-02-08 9시 32분 10초 보내기 - 커밋 ...
2010-02-08 09:32:10 - 잠자기
2010-02-08 09:32:40 - 잠자고 있습니다.
2010-02-08 09:32:40 - Main Complete
2010-02-08 09:32:40 - 외부 활성화 된 응용 프로그램이 이제 성공하고 종료됩니다. 그 가져 여기에 (모든 문 debuggin 등) 저장 프로 시저의 최신 화신은 다음과 같습니다

당신은 그것을 두 번 전체 응용 프로그램을 통과 볼 수 있습니다

편집 2 (. 주, dowork, sendemail, 전체를 받았다) 작업이 대기열에 제출 될 때 활성화 :

ALTER PROCEDURE [dbo].[pr_ProcessJob] AS BEGIN 

    DECLARE  @message_type_name  sysname 
    DECLARE  @dialog    uniqueidentifier 
    DECLARE  @message_sequence_number bigint 
    DECLARE  @error_message_sequence_number bigint 
    DECLARE  @message_body   xml 
    DECLARE  @cgid    uniqueidentifier 
    DECLARE  @JobID    int 
    DECLARE  @Params    varchar(MAX) 

    DECLARE  @ErrorNumber   bigint 
    DECLARE  @ErrorText   nvarchar(MAX) 

    DECLARE   @TaskID    int 
    DECLARE  @TaskService   varchar(100) 
    DECLARE  @TaskKey   int 
    DECLARE  @chEngine   uniqueidentifier 
    DECLARE  @Step    int 
    DECLARE  @NextStep   int 
    DECLARE  @jobch    uniqueidentifier 
    DECLARE  @EngineMsg   XML 
    DECLARE  @TimeStarted   datetime 
    DECLARE  @TaskStatus   int 

    -- This procedure will just sit in a loop processing Task messages in the queue 
    -- until the queue is empty 
    SET NOCOUNT ON 
    SET @error_message_sequence_number = -100 


    PRINT 'pr_ProcessJob: Start' 

    WHILE (1=1) BEGIN 

     BEGIN TRY 

      PRINT 'pr_ProcessJob: BEGIN TRANSACTION' 
      BEGIN TRANSACTION 

      -- first lets get the conversation group id for the next message. 
      WAITFOR (
       GET CONVERSATION GROUP @cgid FROM [JobQueue] 
      ), TIMEOUT 1000 

      IF (@@ROWCOUNT = 0) BEGIN 

       PRINT 'pr_ProcessJob: ROLLBACK TRANSACTION (GET CONVERSATION)' 
       ROLLBACK TRANSACTION 
       BREAK 
      END 

      PRINT @CGID 

      -- Inner Loop (Message Processing) 
      WHILE (1=1) BEGIN 

       -- Receive the next available message 
       PRINT 'Receiving Message.' 
       WAITFOR (
        RECEIVE top(1) -- just handle one message at a time 
         @message_type_name=message_type_name, --the type of message received 
         @message_body=CAST(message_body AS XML),  -- the message contents 
         @message_sequence_number=message_sequence_number, 
         @dialog = conversation_handle -- the identifier of the dialog this message was received on 
         FROM [JobQueue] 
         WHERE [email protected] 
       ), TIMEOUT 3000 -- if the queue is empty for three seconds, give up and go away 

       -- If we didn't get anything, the queue is empty so bail out 

       IF (@@ROWCOUNT = 0) BEGIN    
        PRINT 'pr_ProcessJob::WaitFor - No messages for conversation group bailing out' 
        BREAK 
       END --IF (@@ROWCOUNT = 0) 

       PRINT 'Message Received: ' + @message_type_name 
       SAVE TRANSACTION MessageReceivedSavePoint 

       -- Handle the End Conversation Message 
       IF (@message_type_name = 'http://schemas.microsoft.com/SQL/ServiceBroker/EndDialog') BEGIN 
        -- When we receive an End Dialog, we need to end also. 
        PRINT 'ENDING CONVERSATION' 
        END CONVERSATION @dialog 
       END -- IF (@message_type_name = 'http://schemas.microsoft.com/SQL/ServiceBroker/EndDialog') BEGIN 
       ELSE BEGIN 
        -- Handle the Conversation Error Message 
        IF (@message_type_name = 'http://schemas.microsoft.com/SQL/ServiceBroker/Error') BEGIN 
         -- We can't return anything here because the dialog at the other end is closed so just log 
         -- an error and close our end of the conversation. 

         PRINT 'ENDING CONVERSATION w/Error' 
         END CONVERSATION @dialog 
        END -- (@message_type_name = 'http://schemas.microsoft.com/SQL/ServiceBroker/Error') 
        ELSE BEGIN 
         IF (@message_type_name = 'SubmitJob') BEGIN -- Process normal Job messages.. 

          PRINT 'pr_ProcessJob:: Message Type SubmitJob received.' 

          -- Pull the information out of the task message with XQuery 
          SELECT @JobID = @message_body.value('(/Job/JobID)[1]', 'int'), 
           @Params = @message_body.value('(/Job/Params)[1]', 'varchar(MAX)') 

          PRINT 'pr_ProcessJob::@JobID = ' + cast(@jobID as varchar(10)) 
          PRINT 'pr_ProcessJob::@Params = ' + @Params 


          SELECT @ErrorNumber = 0, @ErrorText = N'' 

          -- Do something with the job 
          -- save state 

          -- we are looking for the first step 
          SET @Step=1 

          PRINT 'Selecting from JobTask' 
          --------------------------------------------------------- 
          -- Get the next task 
          --------------------------------------------------------- 
          SELECT TOP 1 
           @TaskID=task.TaskID, 
           @TaskService=tt.TaskService, 
           @TaskKey =Task.TaskKey 
          FROM JobTask task INNER JOIN TaskType tt 
           ON task.TaskTypeID = tt.TaskTypeID  
          WHERE [email protected] AND task.enabled=1 and task.step>[email protected] 
          ORDER BY Task.step 
          --------------------------------------------------------- 
          PRINT 'Selecting from JobTask: complete' 

          PRINT 'Step='+cast(@step as varchar(max))    
          PRINT 'TaskID='+cast(@TaskID as varchar(max)) 
          PRINT 'TaskService='+cast(@TaskService as varchar(max)) 
          PRINT'TaskKey='+cast(@TaskKey as varchar(max))      

          PRINT 'BEGIN DIALOG with ' + @TaskService 

          BEGIN DIALOG @chEngine 
           FROM SERVICE [JobService] 
           TO SERVICE @TaskService 
           ON CONTRACT [TaskContract] 
           WITH [email protected]; 

          PRINT 'BEGIN DIALOG with ' + @TaskService+' completed.' 

          SET @EngineMsg = CAST('<Task><TaskID>'+ str(@TaskID)+'</TaskID><TaskKey>'+ str(@Taskkey)+'</TaskKey></Task>' as XML); 

          PRINT CAST(@EngineMsg as varchar(max)) 

          PRINT 'Sending Message Type SubmitTask to Engine.'; 

          SEND ON CONVERSATION @chEngine 
           MESSAGE TYPE SubmitTask 
           (@EngineMsg) 

          PRINT 'Inserting into jobstate' 

          INSERT INTO JobState(cgid, jobch, jobID, step) VALUES(@cgid, @dialog, @jobid, @step) 

         END -- IF (@message_type_name = 'SubmitJob') 
         ELSE BEGIN 

          IF (@message_type_name = 'TaskResponse') BEGIN 

           PRINT 'Processing MessageType TaskResponse' 

           SELECT @TaskStatus = @message_body.value('(/TaskStatus)[1]', 'int') 

           PRINT 'pr_ProcessJob::@TaskStatus = ' + cast(@TaskStatus as varchar(10)) 

           PRINT 'Loading State' 

           --LoadState 
           SELECT @JobID=jobid, 
            @Step=Step, 
            @jobch=jobch, 
            @TimeStarted=sysdate  
           FROM Jobstate 
           WHERE [email protected] 

           PRINT 'Loading State complete' 

           PRINT @jobch 

           PRINT 'Selecting from JobTask' 
           --------------------------------------------------------- 
           -- Get the next task 
           --------------------------------------------------------- 
           SELECT TOP 1 
            @TaskID=task.TaskID, 
            @TaskService=tt.TaskService, 
            @TaskKey =task.TaskKey, 
            @NextStep = task.Step 

           FROM JobTask task INNER JOIN TaskType tt 
            ON task.TaskTypeID = tt.TaskTypeID  
           WHERE [email protected] AND task.enabled=1 and task.step>@step 
           ORDER BY Task.step 
           --------------------------------------------------------- 
           PRINT 'Selecting from JobTask: complete' 

           PRINT 'NextTask: ['[email protected]+']' 

           if (@TaskService is null) BEGIN 

            PRINT '@TaskService is NULL: BEGIN' 

            -- no more tasks 
            --END CONVERSATION @jobch 

            PRINT 'Removing from state table'        
            DELETE FROM JobState 
            WHERE @cgid=cgid 
            PRINT @@ROWCOUNT 
            PRINT 'Removing from state table-completed' 

            DECLARE @ResponseDoc xml 

            -- Send a response message saying we're done 
            DECLARE @Time nvarchar(100)     
            SET @Time = cast(getdate() as nvarchar(100)) 

            DECLARE @TimeStartedText nvarchar(100) 
            SET @TimeStartedText = cast(@TimeStarted as nvarchar(100)) 

            SET @ResponseDoc = N'<Job/>' 
            SET @ResponseDoc.modify(
            'insert (<JobID>{ sql:variable("@JobID") }</JobID>, 
            <JobStatus>{ sql:variable("@ErrorNumber") }</JobStatus>, 
            <ErrorNumber>{ sql:variable("@ErrorNumber") }</ErrorNumber>, 
            <ErrorText>{ sql:variable("@ErrorText") }</ErrorText>, 
            <TimeStarted>{ sql:variable("@TimeStartedText") }</TimeStarted>, 
            <TimeCompleted>{ sql:variable("@Time") }</TimeCompleted>) 
            as last into /Job [1]'); 

            SEND ON CONVERSATION @jobch 
             MESSAGE TYPE [JobResponse] (@ResponseDoc) 

            END CONVERSATION @jobch    

            PRINT '@TaskService is NULL: END' 

           END --if (@TaskService is null) BEGIN 
           ELSE BEGIN 
            -- there are more tasks 
            PRINT '@TaskService is not null: BEGIN' 

            PRINT 'BEGIN DIALOG with ' + @TaskService 

            --another task 
            BEGIN DIALOG @chEngine 
             FROM SERVICE [JobService] 
             TO SERVICE @TaskService 
             ON CONTRACT [TaskContract] 
             WITH [email protected]; 

            SET @EngineMsg = CAST('<Task><TaskID>'+ str(@TaskID)+'</TaskID><TaskKey>'+ str(@Taskkey)+'</TaskKey></Task>' as XML); 

            PRINT 'SEND ' +cast(@EngineMsg as varchar(max)); 

            SEND ON CONVERSATION @chEngine 
            MESSAGE TYPE SubmitTask (@EngineMsg)          

            PRINT 'SAVING State: ' +str(@step) 

            -- save state 
            Update JobState 
             SET step = @NextStep 
            FROM JobState 
            WHERE [email protected] 

            PRINT '@TaskService is not null: END' 

           END -- ELSE (@TaskService is NOT NULL) 

           PRINT 'Processing MessageType TaskResponse...Complete' 

          END -- IF (@message_type_name = 'TaskCompleted') 

         END -- ELSE IF (@message_type_name <> 'JobRequest') 
        END -- ELSE (@message_type_name <> 'http://schemas.microsoft.com/SQL/ServiceBroker/Error') 
       END -- ELSE (@message_type_name <> 'http://schemas.microsoft.com/SQL/ServiceBroker/EndDialog') 
      END -- WHILE (1=1) BEGIN 

      PRINT 'COMMIT TRANSACTION' 
      COMMIT TRANSACTION 

     END TRY 
     BEGIN CATCH 

      --rollback transaction 

      DECLARE @ErrNum int 
      DECLARE @ErrMsg varchar(max) 


       SELECT 
        ERROR_NUMBER() AS ErrorNumber 
        ,ERROR_SEVERITY() AS ErrorSeverity 
        ,ERROR_STATE() AS ErrorState 
        ,ERROR_PROCEDURE() AS ErrorProcedure 
        ,ERROR_LINE() AS ErrorLine 
        ,ERROR_MESSAGE() AS ErrorMessage; 

      PRINT 'pr_ProcessJob: ROLLBACK (CATCH)' 


      if (error_number()=1205) BEGIN 
       -- a deadlock occurred. We can try it again. 
       PRINT 'pr_ProcessJob: ROLLBACK TRANSACTION (CATCH)' 
       ROLLBACK TRANSACTION 
       --CONTINUE 
      END --if (error_number()=1205) 
      ELSE BEGIN 
       if (error_number()=9617) BEGIN 
        PRINT 'pr_ProcessJob: ROLLBACK TRANSACTION (CATCH)' 
        ROLLBACK TRANSACTION 
       END 
       ELSE BEGIN -- (error_number()<>9617) 
        -- another error occurred. The message cant be procesed sucessfully 
        PRINT 'pr_ProcessJob: ROLLBACK TRANSACTION to MessageReceivedSavePoint (CATCH)' 
        ROLLBACK TRANSACTION MessageReceivedSavePoint 

       END --ELSE (error_number()<>9617)  
      END -- if (error_number()<>1205) 
     END CATCH 

    END -- while loop 

PRINT 'pr_ProcessJob: Complete' 

END -- CREATE PROCEDURE [dbo].[ProcessJobProc] 
+0

그리고 이러한 작업 메시지를 보내는 논리는 무엇입니까? –

+0

안녕하세요 당신은이 문제를 해결 했습니까? 비슷한 문제가 생겼습니다. 대기열에 제출합니다. 내부 활성 자 프로세스는 작업 대기열에서 들어오는 모든 외부 활성기로 통지를 사용하여 작업 대기열로 보냅니다.하지만 모든 것이 여러 번 실행됩니까? –

답변

1

앱 후속 인스턴스가 메시지를 볼 수 있다면, 그것은 단지 한 가지 의미 : 이전 인스턴스가 수신 롤백해야합니다. 첫눈에 당신이 제공 한 코드는 괜찮아 보입니다. 그래서 제가 사용하고있는 객체 모델에서 오류를 찾아 보겠습니다. 메서드 중 하나에서 예외가 발생하면 앱이 종료되고 메시지를받은 트랜잭션이 자동으로 롤백됩니다.

한 번에 하나의 앱 인스턴스 만 실행하려면 최대 설정을 1로 유지하십시오. 그렇지 않으면 기본적으로로드를 따라 가기 위해 더 많은 인스턴스를 동시에 실행합니다.

+0

먼저 도움에 감사드립니다. 또한 응용 프로그램 수준에서 일종의 예외/롤백이라고 생각하여 파일에 로깅하기 시작했습니다 (여기에 맞지 않으므로 내 질문에 로그 예제를 추가했습니다.) 알 수 있듯이, 응용 프로그램은 커밋 문을 두 번 포함합니다. btw, 그것은 외부 활성화시에만 이것을 수행합니다. VS 또는 명령 줄에서 응용 프로그램을 실행하면이 응용 프로그램은 한 번만 실행됩니다. 나는 당황 스럽다. 다시 한번 감사드립니다. – user33209

+0

실제로 두 개의 메시지를 보내지 않겠습니까? 이 작업 메시지를 보내는 논리 (코드)는 무엇입니까? –

+0

내부 활성화가 설정된 작업 대기열이 있습니다. 저장 프로시 저는 작업 및 현재 상태 (저장되지 않음)를 기반으로 다음 작업을 가져옵니다. 작업은 유형에 따라 처리되어 해당 대기열로 전송됩니다. SP는 여기에 게시하는 것이 중요하지만 원래 게시물로 편집 할 수 있는지 확인할 것입니다. – user33209

관련 문제