我有一个使用C#开发的WCF服务和客户端,它处理从服务器上的SQL服务器数据库到客户端上的SQL服务器数据库的数据传输。我正面临着当前架构的一些问题,并计划将其修改为我的想法,并且想知道是否可以实现它,或者我如何最好地修改架构以满足我的需求。
服务器端数据库服务器是SQL 2008 R2 SP1,客户端服务器是SQL 2000
在我陈述这个想法之前,下面是我正在使用的架构设计的概述和当前缺点。
概述:
客户请求表格数据。
WCF服务在服务器数据库中查询所请求表的所有待处理数据。此数据将加载到数据集中。
WCF使用GZIP压缩压缩数据集并将其转换为字节以供客户端下载。
客户端收到字节流,将其解压缩并将数据从数据集复制到客户端数据库上的物理表。这些数据是逐行插入的,因为需要将主键列归档到服务器,以便将其标记为已传输。
客户端完成数据复制后,会将成功的行主键字段上传回服务器,然后服务器逐个更新每个字段。 上述过程使用基本的http绑定,具有流传输模式。
缺点:
这对于小数据非常有用,但是当涉及到批量数据时,在下载正在进行时在内存中维护数据集以及在复制正在进行时在客户端维护数据集变得不可能,因为有时数据集大小达到4gb。服务器可以容纳这么多数据,因为它是一个32GB的RAM服务器,但在客户端,由于客户端机器有2GB RAM,我得到系统内存不足。
由于选择查询正在运行,因此在更新时会有很多死锁,因为我使用事务模式作为读取已提交。
对于批量数据,它非常慢并且在DTS正在进行时完全挂起客户端计算机。
记住想法:
保持逐行传输的相同服务和逻辑,因为由于数据的敏感性我无法更改此数据,而是下载批量数据我计划使用http://code.msdn.microsoft.com/Custom-WCF-Streaming-436861e6中给出的样本。 因此,新流程将如下:
收到下载请求后,服务器将使用快照隔离作为事务级别打开与DB的连接。
在服务器上逐行构建对象并将其发送到请求通道上的客户端,因为客户端接收到每个行对象,它会被处理,成功或失败响应将被发送回服务器相同的方法相同的通道,因为我需要在同一个快照事务上更新数据。 这样,我将减少内存中的批量对象,并依赖SQL来创建在事务启动后将在temdb中维护的快照数据。
挑战:
如何在发送下一个对象之前发送行对象并等待确认,因为必须在同一个快照事务上对服务器行进行更新。因为如果我在服务上创建另一个方法来执行标记关闭,快照将会有所不同,这将导致数据完整性出现问题,以防数据在启动快照事务后发生更改。
如果这是错误的方法,那么请建议一个更好的方法,因为我愿意接受任何建议。
如果我对快照隔离的理解是错误的,请在我不熟悉时纠正我。
更新1:
当客户端请求客户端时,我想实现类似的目标:
//Client Invokes this method on the server
public Stream GetData(string sTableName)
{
//Open the Snapshot Transaction on the Server
SqlDataReader rdr = operations.InitSnapshotTrans("Select * from " + sTableName + " Where Isnull(ColToCheck,'N') <> 'Y'");
//Check if there are rows available
if(rdr.HasRows)
{
while rdr.read()
{
SendObj sendobj = Logic.CreateObejct(rdr);
//Here is where i am stuck
//At this point I want to write the object to the Stream
...Write sendobj to Stream
//Once the client is done processing it reverts with a true for success or false for failuer.
if (returnObj == true)
{
operations.updateMovedRecord(rdr);
}
}
}
}
对于服务器发送,我已将代码编写为Such(我使用了Pub Sub Model):
public void ServerData(string sServerText)
{
List<SubList> subscribers = Filter.GetClients();
if (subscribers == null) return;
Type type = typeof(ITransfer);
MethodInfo publishMethodInfo = type.GetMethod("ServerData");
foreach (SubList subscriber in subscribers)
{
try
{
//Open the Snapshot Transaction on the Server
SqlDataReader rdr = operations.InitSnapshotTrans("Select * from " + sTableName + " Where Isnull(ColToCheck,'N') <> 'Y'");
//Check if there are rows available
if(rdr.HasRows)
{
while rdr.read()
{
SendObj sendobj = Logic.CreateObejct(rdr);
bool rtnVal = Convert.ToBoolean(publishMethodInfo.Invoke(subscriber.CallBackId, new object[] { sendobj }));
if (rtnVal == true)
{
operations.updateMovedRecord(rdr);
}
}
}
}
catch (Exception ex)
{
Debug.WriteLine(ex.Message);
}
}
}
答案 0 :(得分:0)
就在我的头顶,听起来可能需要更长的时间。这可能是也可能不是一个问题。
鉴于挑战1中的要求(一切都发生在一个方法调用的上下文中),听起来实际需要发生的是服务器调用上的方法客户端,发送记录,然后等待客户端返回确认。这样,所有需要发生的事情都发生在一次调用(服务器到客户端)的上下文中。我不知道你的情况是否可行。
另一种选择可能是使用某种双队列系统(可能使用MSMQ?),以便服务器和客户端可以在单个会话中维持正在进行的对话。
我认为这就是为什么你不能将要下载的数据划分为可管理的块并在块上重复执行原始进程的原因。这听起来是最没有雄心的选择,但如果满足您的所有需求,您可能已经做到了。