BatchFileProcessing(3)-使用SSB与数据库通信

上一节,我们已经将数据从xls,csv,txt等文件中的解析出来,进行必须的数据验证,
然后将正确的数据以XML格式保存到磁盘,并将事务型数据更新到DB,
比如生成的磁盘文件名,CheckResult,CheckMemo等。
我们再一起回顾下数据库表设计:

USE  SSB3
GO

CREATE   TABLE   [ BatchInventoryQueue ]
(
TransactionNumber 
INT   IDENTITY ( 1 , 1 NOT   NULL ,
BatchFileID 
INT   NOT   NULL ,
RowIndex 
INT   NOT   NULL ,
ItemNumber 
INT   NOT   NULL ,
[ FileName ]   NVARCHAR ( 256 NULL ,
HasCheck 
CHAR ( 1 NULL ,
CheckResult 
CHAR ( 1 NULL ,
CheckMemo 
NVARCHAR ( 2000 NULL ,
HasSendSSB 
CHAR ( 1 NULL -- 是否尝试发送过SSB
SSBSendResult  CHAR ( 1 NULL , -- 发送SSB是否成功
SSBMemo  NVARCHAR ( 2000 NULL , -- SSB处理结果
CONSTRAINT  PK_TransactionNumber_BatchInventoryQueue  PRIMARY   KEY   CLUSTERED
(
    TransactionNumber 
ASC
)
)

下面我们要将正确的数据以XML格式发送到数据库中。因为我们面临的数据量非常大,
所以我们需要考虑负载均衡,比如多台服务器部署。那么就可能会面临数据冲突的问题。
我这里的情况是要求多台服务器部署,那么如何给服务器分数据,又不造成冲突呢?
方案一:用表的主键TransactionNumber与服务器数据取模

1  SELECT   TOP ( @BatchSize ) TransactionNumber,
2       [ FileName ]
3  FROM  dbo.BatchInventoryQueue
4  WHERE  HasCheck = ' Y '   AND  CheckResult = ' S '
5  AND   [ FileName ]   IS   NOT   NULL
6  AND  HasSendSSB  IS   NULL   AND  TransactionNumber % @Throtting = @TrottingMod

优点:实现简单。
缺点:如果其中一台服务器失败,那么应该属于它处理的数据将一直得不到处理。
方案二:结合SQL Server的锁特性,在查询数据时避免冲突如下:

1  UPDATE   TOP ( @BatchSize ) dbo.BatchInventoryQueue
2  SET  HasSendSSB = ' I ' -- inprocessing
3  OUTPUT DELETED.TransactionNumber,
4      DELETED. [ FileName ]
5  WHERE  HasCheck = ' Y '   AND  CheckResult = ' S '
6  AND   [ FileName ]   IS   NOT   NULL
7  AND  HasSendSSB  IS   NULL

将HasSendSSB更新为I,标示正在发送。由于在更新数据时,进程会获取UPDLOCK,那么下
一个服务器再查询数据时就必须等待直到第一个进程更新完毕。并发执行情况下,难免会有问题,
因此需要考虑容错机制。即用另一个Job定时监视(SSBSendResult IS NULL OR SSBSendResult='N')
AND HasSendSSB='I'的数据,并将状态清空(SET HasSendSSB=NULL,SSBSendResult=NULL),
等待程序下次再次处理。不过这种情况应该非常少。

1  UPDATE  dbo.BatchInventoryQueue
2  SET  HasSendSSB = NULL ,
3  SSBSendResult = NULL
4  WHERE  (SSBSendResult  IS   NULL   OR  SSBSendResult = ' N ' )
5  AND  HasSendSSB = ' I '

接着我们要生成SSBMessage,这里我使用VTemplate模版引擎来生成.代码如下:
SSBMessageBase:

 1  public   abstract   class  SSBMessageBase
 2  {
 3       public   string  Subject {  get set ; }
 4 
 5       public   string  FromService {  get set ; }
 6 
 7       public   string  ToService {  get set ; }
 8 
 9       public  SSBMessageHead Head {  get set ; }
10  }

SSBMessageHead:

1  public   class  SSBMessageHead
2  {
3       public   string  Action {  get set ; }
4 
5       public   string  TransactionCode {  get set ; }
6  }

SSBMessageFromFile:

1  public   class  SSBMessageFromFile : SSBMessageBase
2  {
3       public   string  FileName {  get set ; }
4  }

 VTemplate:

 1  < vt:template >
 2  < Publish >
 3  < Subject > {$:ssb.Subject}/Subject>
 4  < FromService > {$:ssb.FromService} </ FromService >
 5  < ToService > {$:ssb.ToService} </ ToService >
 6  < Message >
 7       < Head >
 8           < Action > {$:ssb.Action} </ Action >
 9           < TransactionCode > {$:ssb.TransactionCode} </ TransactionCode >
10       </ Head >
11       < Body >
12       < vt:output  file ="$ssb.FileName"  charset ="utf-8"   />
13       </ Body >
14  </ Message >
15  </ Publish >
16  </ vt:template >

SSBUtility:

 1  public   class  SSBUtility
 2  {
 3       private   string  VtSSBMessage(SSBMessageBase ssb)
 4      {
 5           string  fileName  =  Path.Combine(AppDomain.CurrentDomain.BaseDirectory, @" Templates\SSB.vt " );
 6         
 7          TemplateDocument doc  = new  TemplateDocument(fileName, Encoding.UTF8);
 8          doc.SetValue( " ssb " , ssb);
 9 
10          StringBuilder sb  =   new  StringBuilder();
11          StringWriter sw  =   new  StringWriter(sb);
12          doc.Render(sw);
13          sw.Close();
14 
15           return  sb.ToString();
16      }
17 
18       public   void  SendSSB(SSBMessageBase ssb)
19      {
20           string  msg  =  VtSSBMessage(ssb);
21           using  (SqlConnection conn  =   new  SqlConnection(JobConfigs.SSBConnectionString))
22          {
23               using  (SqlCommand cmd  =   new  SqlCommand())
24              {
25                  cmd.Connection  =  conn;
26                  cmd.CommandType  =  CommandType.StoredProcedure;
27                  cmd.CommandText  =  JobConfigs.SSBSendProc; // dbo.[UP_Send_Inventory]
28                  SqlParameter p  =   new  SqlParameter( " @Message " , SqlDbType.Xml);
29                  p.Value  =  msg;
30                  cmd.Parameters.Add(p);
31                  conn.Open();
32                  cmd.ExecuteNonQuery();
33                  conn.Close();
34              }
35          }
36      }
37  }

再接着就是SSB创建部分,下面列举代码示例:

  1  USE [master]
  2  GO
  3 
  4  IF EXISTS(SELECT  *  FROM sys.databases  where  [name] = ' SSB ' )
  5      DROP DATABASE SSB
  6 
  7  CREATE DATABASE SSB
  8  GO
  9 
 10  -- enable service broker on database
 11  ALTER DATABASE SSB
 12  SET TRUSTWORTHY ON
 13  GO
 14 
 15  USE SSB
 16  GO
 17 
 18  -- create a test table
 19  CREATE TABLE dbo.Inventory
 20  (
 21  [ItemNumber] INT IDENTITY( 1 , 1 ) PRIMARY KEY,
 22  [Inventory] INT NOT NULL
 23  )
 24 
 25  INSERT INTO dbo.Inventory([Inventory])
 26  VALUES( 1 )
 27 
 28  GO
 29 
 30  -- create message type and contract
 31  CREATE MESSAGE TYPE [Message_Type_Inventory]
 32  VALIDATION  =  WELL_FORMED_XML;
 33 
 34  CREATE CONTRACT [Contract_Inventory]
 35  (
 36  [Message_Type_Inventory] SENT BY INITIATOR
 37  )
 38  GO
 39 
 40  -- create queue, send service and receive
 41  CREATE QUEUE [Queue_Inventory]
 42  WITH STATUS = ON,
 43  RETENTION = OFF;
 44 
 45  CREATE SERVICE [Service_Send_Inventory]
 46  ON QUEUE [Queue_Inventory]([Contract_Inventory])
 47  GO
 48 
 49  CREATE SERVICE [Service_Receive_Inventory]
 50  ON QUEUE [Queue_Inventory]([Contract_Inventory])
 51  GO
 52 
 53  -- receive procedure
 54  CREATE PROCEDURE dbo.[UP_Receive_Inventory]
 55  AS
 56  BEGIN
 57      SET NOCOUNT ON;
 58      DECLARE @ConversionHandle UNIQUEIDENTIFIER,
 59          @MessageType SYSNAME,
 60          @Message XML,
 61          @ItemNumber CHAR( 50 ),
 62          @Inventory INT
 63 
 64      WHILE( 1 = 1 )
 65      BEGIN
 66          WAITFOR(   
 67          RECEIVE TOP( 1 ) @ConversionHandle = conversation_handle,
 68                  @MessageType = message_type_name,
 69                  @Message = CAST(message_body AS XML)
 70              FROM [Queue_Inventory]
 71          ),TIMEOUT  1000
 72          IF(@@ROWCOUNT = 0 )
 73              BREAK;
 74 
 75          SELECT @ItemNumber = @Message.value( ' (/Publish/Message/Body/Inventory/ItemNumber/text())[1] ' , ' INT ' ),
 76              @Inventory = @Message.value( ' (/Publish/Message/Body/Inventory/Inventory/text())[1] ' , ' INT ' )
 77         
 78          UPDATE dbo.Inventory
 79          SET Inventory = @Inventory
 80          WHERE ItemNumber = @ItemNumber
 81      END
 82  END
 83  GO
 84 
 85  -- activate queue
 86  ALTER QUEUE [Queue_Inventory]
 87  WITH ACTIVATION
 88  (
 89  PROCEDURE_NAME = [UP_Receive_Inventory],
 90  MAX_QUEUE_READERS = 5 ,
 91  EXECUTE AS OWNER
 92  )
 93  GO
 94 
 95  -- send procedure
 96  CREATE PROCEDURE dbo.[UP_Send_Inventory]
 97  (
 98      @Message XML
 99  )
100  AS
101  BEGIN
102      SET NOCOUNT ON;
103      DECLARE @ConversationHandle UNIQUEIDENTIFIER
104     
105      BEGIN DIALOG CONVERSATION @ConversationHandle
106      FROM SERVICE [Service_Send_Inventory]
107      TO SERVICE  ' Service_Receive_Inventory '
108      ON CONTRACT [Contract_Inventory]
109      WITH ENCRYPTION  =  OFF;
110 
111      SEND ON CONVERSATION @ConversationHandle
112      MESSAGE TYPE [Message_Type_Inventory](@Message)
113     
114      END CONVERSATION @ConversationHandle WITH CLEANUP;
115  END
116  GO
117 
118  -- test data
119  DECLARE @Message XML
120  SELECT @Message = '
121  < Publish >
122  < Subject > BatchInventory </ Subject >
123  < FromService > Service_Send_Inventory </ FromService >
124  < ToService > Service_Receive_Inventory </ ToService >
125  < Message >
126       < Head >
127           < Action > UpdateInventory </ Action >
128           < TransactionCode > 123 </ TransactionCode >
129       </ Head >
130       < Body >
131           < Inventory >
132               < ItemNumber > 1 </ ItemNumber >
133               < Inventory > 200 </ Inventory >
134           </ Inventory >
135       </ Body >
136  </ Message >
137  </ Publish > '
138 
139  SELECT  *  FROM dbo.[Inventory]
140  -- send
141  EXEC dbo.[UP_Send_Inventory] @Message
142 
143  WAITFOR DELAY  ' 00:00:30 '
144  SELECT  *  FROM Inventory
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值