wcf分布式构架集群案例解决方案

 

首先声明此文章是源自博客园:http://www.cnblogs.com/huangxincheng/archive/2011/11/13/2246934.html

加上个人总结..先整理如下:

在wcf集群中,高性能的构架中一种常用的手法就是在内存中维护一个叫做“索引”的内存数据库,在实战中利用“索引”这个概念做出“海量数据的”秒杀。

首先,先上架构图:

大体讲解下该系统流程;所谓的Search集群就是图中WCF模块,它分布于不同的服务器上,为了提供高效率的数据检索,我们分步骤进行:

1、我们将数据库中的数据通过程序加载到内存数据库中

2、通过各个服务器中的WCFSearch服务为IIS提供内存数据中的数据检索

3、为了管理不同服务器中的WCFSearch服务,我们利用了“心跳检测”,实现WCFSearch和IIS的搭桥工作

4、在IIS中获取数据索引,然后向本地数据库中提取数据,实现客户端数据提取。

下面重点分析这里面的“心跳检测”的实战手法:

第一步:项目准备,为了显示出该系统构架的优越性我们先新建立个数据库,上图:

类似电子商务中的用户和店铺的关系,一个用户可以开多个店:

我们向这两张表中插入数据,插入百万级别的数据,晒下SQL数据:

------------------------------------------------
declare @a int --定义变量
declare @m int

declare @ShopName char(50)
declare @ShopUrl nchar(50)


declare @UserName nchar(50)
declare @Password nchar(50)

set @a=0 --初始值

set @m=0

set @ShopName='淘宝店'
set @ShopUrl='www.baidu.com'

set @UserName='小吴'
set @Password='110'

while @a<=1800000 ---循环插入User表180W数据
begin
insert into [User] (UserName,Passwrod) values (@UserName,@Password)

declare @UserID int
set @UserID=(select @@identity)

while @m<=10
begin
insert into dbo.Shop (UserID,ShopName,ShopUrl,User_UserID) values (@UserID,@ShopName,@ShopUrl,null)
set @m=@m+1
continue
end
set @m=0 --内循环重置为0
set @a=@a+1
continue
end

---------------------------------------------------------

select * from Shop

select * from [User]

delete from Shop
delete from [User]

truncate table Shop --重置Shop标识列
dbcc checkident('[User]',reseed,1) ---重置标识列
----------------------------------------------------------

至此,数据库已经准备完毕,我们开始建立项目。

第二步:先晒项目结构:

第三步:先解析LoadDBService项目,该项目实现的是数据库内容的加载,不废话,晒代码:

/**
*心跳检测机制
*模拟数据库加载到内存中,形成内存中的数据库
*/
namespace xinTiaoTest
{
class Program
{
static void Main(string[] args)
{
//这里的Dicionary用来表示“一个注册用户用过了多少个店铺”,即UserID和ShopID的一对多关系
SerializableDictionary<int, List<int>> dic = new SerializableDictionary<int, List<int>>();
List<int> shopIDList = new List<int>();
for (int shopID = 3000; shopID < 3050; shopID++)
{
shopIDList.Add(shopID);

}
int UserID = 15;
//假设这里已经维护了UserID与shopID的关系
dic.Add(UserID, shopIDList);

XmlSerializer xml = new XmlSerializer(dic.GetType());

var memoryStrean = new MemoryStream();

xml.Serialize(memoryStrean, dic);//将dic对象写入ID流
memoryStrean.Seek(0, SeekOrigin.Begin); //从0开始读取

//将Dicrionary持久化,相当于模拟保存在Mencache里面
File.AppendAllText("E://1.txt", Encoding.UTF8.GetString(memoryStrean.ToArray()));

Console.WriteLine("将数据加载成功");
Console.Read();

}
}
}

为了序列化List列表,我们引入了该类的序列化:

 ///<summary>
///13 /// 标题:支持 XML 序列化的 Dictionary
///</summary>
///<typeparam name="TKey"></typeparam>
///16 ///<typeparam name="TValue"></typeparam>
[XmlRoot("SerializableDictionary")]
public class SerializableDictionary<TKey, TValue> : Dictionary<TKey, TValue>, IXmlSerializable
{
public SerializableDictionary()
: base()
{

}
public SerializableDictionary(IDictionary<TKey, TValue> dictionary)
: base(dictionary)
{

}
public SerializableDictionary(IEqualityComparer<TKey> comparer)
: base(comparer)
{

}
public SerializableDictionary(int capacity)
: base(capacity)
{
}
public SerializableDictionary(int capacity, IEqualityComparer<TKey> comparer)
: base(capacity, comparer)
{
}
protected SerializableDictionary(SerializationInfo info, StreamingContext context)
: base(info, context)
{
}
public System.Xml.Schema.XmlSchema GetSchema()
{
return null;
}
///<summary> 54 /// 从对象的 XML 表示形式生成该对象 55 ///</summary>
///56 ///<param name="reader"></param>
public void ReadXml(System.Xml.XmlReader reader)
{
XmlSerializer keySerializer = new XmlSerializer(typeof(TKey));
XmlSerializer valueSerializer = new XmlSerializer(typeof(TValue));
bool wasEmpty = reader.IsEmptyElement; reader.Read();
if (wasEmpty)
return;
while (reader.NodeType != System.Xml.XmlNodeType.EndElement)
{
reader.ReadStartElement("item");
reader.ReadStartElement("key");
TKey key = (TKey)keySerializer.Deserialize(reader);
reader.ReadEndElement();
reader.ReadStartElement("value");
TValue value = (TValue)valueSerializer.Deserialize(reader);
reader.ReadEndElement();
this.Add(key, value);
reader.ReadEndElement();
reader.MoveToContent();
}
reader.ReadEndElement();
}
/**/
///<summary> 83 /// 将对象转换为其 XML 表示形式
///84 ///</summary> 85 ///<param name="writer"></param> 86
public void WriteXml(System.Xml.XmlWriter writer)
{
XmlSerializer keySerializer = new XmlSerializer(typeof(TKey));
XmlSerializer valueSerializer = new XmlSerializer(typeof(TValue));
foreach (TKey key in this.Keys)
{
writer.WriteStartElement("item");
writer.WriteStartElement("key");
keySerializer.Serialize(writer, key);
writer.WriteEndElement();
writer.WriteStartElement("value");
TValue value = this[key];
valueSerializer.Serialize(writer, value);
writer.WriteEndElement();
writer.WriteEndElement();
}
}
}
}

这样我们就实现了数据库中索引保存为内存数据库中的,当然为了演示我们将其持久化,另存为txt文件。

第四步:新建WCFSearch,用于从内存数据中读取索引,为IIS提供服务,不废话,晒代码:

namespace SearhService
{
// 注意: 使用“重构”菜单上的“重命名”命令,可以同时更改代码和配置文件中的接口名“IIProduct”。
[ServiceContract]
public interface IProduct
{
[OperationContract]
List<int> GetShopListByUserID(int userID);
[OperationContract]
void TestSrarch();

}
}

实现方法:

namespace SearhService
{
// 注意: 使用“重构”菜单上的“重命名”命令,可以同时更改代码和配置文件中的类名“IProduct”。
public class Product : IProduct
{

public List<int> GetShopListByUserID(int userID)
{
//模拟从Memcache中读取索引
SerializableDictionary<int, List<int>> dic = new SerializableDictionary<int, List<int>>();
byte[] bytes = Encoding.UTF8.GetBytes(File.ReadAllText("E://1.txt", Encoding.UTF8));
var memoryStream = new MemoryStream(); //新建缓存区域
memoryStream.Write(bytes, 0, bytes.Count()); //使用从缓冲区读取的数据将字节块写入当前流。
memoryStream.Seek(0, SeekOrigin.Begin);

XmlSerializer xml = new XmlSerializer(dic.GetType());
var obj = xml.Deserialize(memoryStream) as Dictionary<int, List<int>>; //反序列化生成对象

return obj[userID];

}

public void TestSrarch()
{
throw new NotImplementedException();
}
}
}


发布服务:

namespace SearhService
{
public class SearchHost : ServiceHeartBeat.IAddressCallback
{

static DateTime startTime;
public static void Main()
{
ServiceHost host = new ServiceHost(typeof(Product));
host.Open();
AddSearch();
Console.Read();
}
private static void AddSearch()
{
startTime = DateTime.Now;
Console.WriteLine("Search服务发送中.....\n\n*************************************************\n");
try
{
var heartClient = new ServiceHeartBeat.AddressClient(new InstanceContext(new SearchHost()));
string search = ConfigurationManager.AppSettings["search"]; //获取配置的本机service地址

heartClient.AddSearch(search); //添加到连接。
}
catch (Exception err)
{
Console.WriteLine("Search服务发送失败:" + err.Message);
}
}

//服务端回调函数
public void LiveAddress(string address)
{
Console.WriteLine("恭喜你,"+address+"已经被心跳成功接受");
Console.WriteLine("发送时间:"+startTime+"\n接收时间:"+DateTime.Now.ToString());
}
}
}

这里有几个点需要注意,在我们首先要引用“心跳服务”,然后将自己的发布的服务地址发送给"心跳服务“,在程序中也就是实现 ServiceHeartBeat.IAddressCallback接口,这是一个”心跳检测“中设置的一个回调接口,目的是实现告诉Search,我已经接受了你的服务,实现数据的推送....

下面是该服务的配置文件:

<?xml version="1.0"?>
<configuration>

<!--设置本service的地址-->
<appSettings>
<add key="search" value="net.tcp://localhost:8732/Design_Time_Addresses/SearhService/IProduct/"/>
</appSettings>

<system.serviceModel>

<!--添加服务引用生成的客户端的配置资料 -->
<bindings>
<netTcpBinding>
<binding name="NetTcpBinding_IAddress"
closeTimeout="00:01:00"
openTimeout="00:01:00"
receiveTimeout="00:10:00"
sendTimeout="00:01:00"
transactionFlow="false"
transferMode="Buffered"
transactionProtocol="OleTransactions"
hostNameComparisonMode="StrongWildcard"
listenBacklog="10"
maxBufferPoolSize="524288"
maxBufferSize="65536"
maxConnections="10"
maxReceivedMessageSize="65536">

<readerQuotas maxDepth="32"
maxStringContentLength="8192"
maxArrayLength="16384"
maxBytesPerRead="4096"
maxNameTableCharCount="16384"/>
<reliableSession ordered="true"
inactivityTimeout="00:10:00" enabled="false"/>
<security mode="Transport">
<transport clientCredentialType="Windows" protectionLevel="EncryptAndSign"/>
<message clientCredentialType="Windows"/>
</security>
</binding>
</netTcpBinding>
</bindings>
<client>
<endpoint address="net.tcp://localhost:8888/Heart" binding="netTcpBinding" bindingConfiguration="NetTcpBinding_IAddress" contract="ServiceHeartBeat.IAddress" name="NetTcpBinding_IAddress">
<identity>
<dns value="localhost"/>
</identity>
</endpoint>
</client>

<!--End 添加服务引用生成的客户端的配置资料 -->
<!--添加本机服务配置 -->
<behaviors>
<serviceBehaviors>
<behavior name="IProductBehavior">
<serviceMetadata httpGetEnabled="false"/>
<serviceDebug includeExceptionDetailInFaults="false"/>
</behavior>
</serviceBehaviors>
</behaviors>

<services>
<service behaviorConfiguration="IProductBehavior" name="SearhService.Product">
<endpoint address=""
binding="netTcpBinding"
contract="SearhService.IProduct">
<identity>
<dns value="localhost"/>
</identity>
</endpoint>
<endpoint
address="mex"
binding="mexTcpBinding"
contract="IMetadataExchange"/>
<host>
<baseAddresses>
<add baseAddress="net.tcp://localhost:8732/Design_Time_Addresses/SearhService/IProduct/"/>
</baseAddresses>
</host>
</service>
</services>
</system.serviceModel>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0"/>
</startup>
</configuration>


这里面有个技巧是在我们在vs2010中新建服务时,添加的服务地址里面包含Design_Time_Addresses路径,这个是vs在安装的时候自己注册的一个安全配置,也就是说在这里面普通的的用户就能够发布服务..为了能发布任何服务,我们在运行vs2010的时候要选择“管理员权限”运行。

第五步:新建ClientService项目,该项目代表的是IIS端,实现的是从内存数据库中获取索引值,也就是连接Srerch,废话不多说,上代码:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel;

namespace ClientService
{
[ServiceContract]
public interface IServiceList
{
[OperationContract]
void AddSearchList(List<string> search);
}
}

提供一个AddSearchList方法,目的是让“心跳检测”向IIS端发送Search的地址。

实现代码为:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;

namespace ClientService
{
public class ServiceList : IServiceList
{
public static List<string> serarchilist = new List<string>();
static object obj = new object();
public static string Search
{
get
{
//如果心跳没及时返回地址,客户端就在等候,如果有的话,就随机获取其地址
while (serarchilist.Count == 0)
{
Thread.Sleep(1000);
}
return serarchilist[new Random().Next(0, serarchilist.Count)];

}
set
{
}
}
public void AddSearchList(List<string> search)
{
lock (obj)
{
serarchilist = search;
Console.WriteLine("心跳发来searche信息************************************");
Console.WriteLine("当前存活的search为:");
foreach (var single in serarchilist)
{
Console.WriteLine(single);
}
}
}
}
}


配置服务,发布服务

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel;
using System.Configuration;
using SearhService;
using Common;
namespace ClientService
{
class Program:ServiceHeartBeat.IAddressCallback
{
static void Main(string[] args)
{
ServiceHost host = new ServiceHost(typeof(ServiceList));
host.Opened += delegate
{
Console.WriteLine("IIS服务已经启动");
};
host.Open();


//向心跳添加链接,获取service。。
var client = new ServiceHeartBeat.AddressClient(new InstanceContext(new Program()));
//获取配置文件中的获取IIS的地址
var iis = ConfigurationManager.AppSettings["iis"];
//发送IIS地址给心跳
client.GetService(iis);


//从集群中获取search地址来对search服务进行调用
var factory = new ChannelFactory<SearhService.IProduct>(new NetTcpBinding(SecurityMode.None), new EndpointAddress(ServiceList.Search));
//根据userID获取了shopId的集合
//比如说这里的ShopIDList是通过索引交并集获取分页的一些shopID
var shopIDList = factory.CreateChannel().GetShopListByUserID(15);



var strSql = string.Join(",", shopIDList);

System.Diagnostics.Stopwatch watch = new System.Diagnostics.Stopwatch(); //定义时间检测量

watch.Start();
SqlHelper.Query("select s.ShopID,u.UserName,s.ShopName from [User] as u ,Shop as s where s.ShopID in(" + strSql + ")");
watch.Stop();//停止检测

Console.WriteLine("通过wcf索引获取的ID>>>>>花费时间:" + watch.ElapsedMilliseconds);


//普通的sql查询花费的时间
StringBuilder builder = new StringBuilder();
builder.Append("select * from ");
builder.Append("(select ROW_NUMBER() over(order by s.ShopID) as NumberID, ");
builder.Append(" s.ShopID, u.UserName, s.ShopName ");
builder.Append("from Shop s left join [User] as u on u.UserID=s.UserID ");
builder.Append("where s.UserID=15) as array ");
builder.Append("where NumberID>300000 and NumberID<300050");
watch.Start();
SqlHelper.Query(builder.ToString());
watch.Stop();
Console.WriteLine("普通的sql分页 >>>花费时间:" + watch.ElapsedMilliseconds);

Console.Read();
}

public void LiveAddress(string address)
{
Console.WriteLine("心跳检测到了你的IIS地址为:" + address );
Console.WriteLine("\n接收时间:" + DateTime.Now.ToString());
}
}
}

同样的实现ServiceHeartBeat.IAddressCallback,“心跳检测”随时的告诉IIS,已经连接的SearchService,当然在里面在获得了SearchService的时候我们对其进行了连接,然后进行了索引的查询...下面的内容是为了比较这种方法的速度如何,稍后晒测试结果。

下面是该服务的配置文件:

<?xml version="1.0"?>
<configuration>
<appSettings>
<add key="iis" value="net.tcp://localhost:2345/ServiceList"/>
</appSettings>
<system.serviceModel>
<client>
<endpoint address="net.tcp://localhost:8888/Heart" binding="netTcpBinding"
bindingConfiguration="NetTcpBinding_IAddress" contract="ServiceHeartBeat.IAddress"
name="NetTcpBinding_IAddress">
<identity>
<dns value="localhost" />
</identity>
</endpoint>
</client>
<bindings>
<netTcpBinding>

<!--客户端验证模式为空-->
<binding name="ClientBinding">
<security mode="None" />
</binding>

<binding name="NetTcpBinding_IAddress"
closeTimeout="00:01:00"
openTimeout="00:01:00"
receiveTimeout="00:10:00"
sendTimeout="00:01:00"
transactionFlow="false"
transferMode="Buffered"
transactionProtocol="OleTransactions"
hostNameComparisonMode="StrongWildcard"
listenBacklog="10"
maxBufferPoolSize="524288"
maxBufferSize="65536"
maxConnections="10"
maxReceivedMessageSize="65536">
<readerQuotas
maxDepth="32"
maxStringContentLength="8192"
maxArrayLength="16384"
maxBytesPerRead="4096"
maxNameTableCharCount="16384" />
<reliableSession ordered="true" inactivityTimeout="00:10:00"
enabled="false" />

<security mode="Transport">
<transport clientCredentialType="Windows" protectionLevel="EncryptAndSign" />
<message clientCredentialType="Windows" />
</security>
</binding>
</netTcpBinding>
</bindings>

<!--定义behaviors-->
<behaviors>
<serviceBehaviors>
<behavior name="IProductBehavior">
<serviceMetadata httpGetEnabled="false"/>
<serviceDebug includeExceptionDetailInFaults="true"/>
</behavior>
</serviceBehaviors>
</behaviors>

<services>
<service name="ClientService.ServiceList">
<endpoint address="net.tcp://localhost:2345/ServiceList"
binding="netTcpBinding"
contract="ClientService.IServiceList"
bindingConfiguration="ClientBinding">
<identity>
<dns value="localhost" />
</identity>
</endpoint>
</service>
</services>

</system.serviceModel>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0"/>
</startup>
</configuration>

这里面的几个简单的配置需提醒,像客户端验证模式一定要匹配,当然在该项目中我们做了简单的配置,在实际应用中我们会对其作进一步详细配置,以确保服务的流畅性。

第六步:这时候该我们的重头戏出场了..心跳检测的的项目,实现两者的互联,上代码:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel;


namespace HeartBeatService
{

//callbackContract:这个就是Client实现回调接口,方便服务器通知客户端
[ServiceContract(CallbackContract = typeof(ILiveAddressCallback))]
public interface IAddress
{
//此方法用于Search方法启动后,将Search地址插入此处
[OperationContract(IsOneWay=true)]
void AddSearch(string address);

//此方法用于IIS获取Search地址
[OperationContract(IsOneWay=true)]
void GetService(string address);
}

}

两个方法,第一个实现Search地址的获取,第二个实现IIS的检测,上面加了一个回调接口:ILiveAddressCallback方法,实现的是客户端数据的应答。方法如下:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel;

namespace HeartBeatService
{
///<summary>10 /// 等客户端实现后,让客户端约束一下
///,只能是这个LiveAddress方法11 ///</summary>
public interface ILiveAddressCallback
{
[OperationContract(IsOneWay = true)]
void LiveAddress(string address);
}
}

下面是心跳检测的核心代码,上代码:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel;
using System.Timers;
using System.Configuration;
using SearhService;
using ClientService;

namespace HeartBeatService
{
//InstanceContextMode:只要是管理上下文的实例,此处是single,也就是单体
//ConcurrencyMode:主要是用来控制实例中的线程数,此处是Multiple,也就是多线程
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single, ConcurrencyMode = ConcurrencyMode.Multiple)]

public class Address : IAddress
{

static List<string> search = new List<string>();
static object obj = new object();

//构造函数用来检测Search的个数
static Address()
{
Timer timer = new Timer();
timer.Interval = 6000;
timer.Elapsed += (sender, e) =>
{
Console.WriteLine("\n》》》》》》》》》》》》》》》》》》》》》》");
Console.WriteLine("当前存活的search为:");
lock (obj)
{
//遍历当前存活的search
foreach (var single in search)
{
ChannelFactory<IProduct> factory = null;
try
{
//当search存活的话,心跳服务就要检测search是否死掉,也就是定时检测连接Search来检测
factory = new ChannelFactory<IProduct>(new NetTcpBinding(SecurityMode.None), new EndpointAddress(single));
factory.CreateChannel();
factory.Close();

Console.WriteLine(single);
}
catch (Exception err)
{
Console.WriteLine(err.Message);

//如果抛出异常,则说明此search已经挂掉
search.Remove(single);
factory.Abort();
Console.WriteLine("\n当期时间:" + DateTime.Now + ",存活的search有:" + search.Count() + "");
}
}
//最后统计下存活的search有多少个
Console.WriteLine("\n当前时间:" + DateTime.Now + " ,存活的Search有:" + search.Count() + "");
}
};
timer.Start();

}
public void AddSearch(string address)
{
lock (obj)
{
//是否包含相同的Search地址
if (!search.Contains(address))
{
search.Add(address);
//search添加成功后就要告诉来源处,此search已经被成功载入。
var client = OperationContext.Current.GetCallbackChannel<ILiveAddressCallback>();
client.LiveAddress(address);
}
}
}

public void GetService(string address)
{
Timer timer = new Timer();
timer.Interval = 1000;
timer.Elapsed += (obj, sender) =>
{
try
{
//这个是定时的检测IIS是否挂掉
var factory = new ChannelFactory<IServiceList>(new NetTcpBinding(SecurityMode.None),
new EndpointAddress(address));
factory.Opened += delegate
{
Console.WriteLine("IIS("+address+")已经启动..正在发送searchSerive地址...");
};
factory.CreateChannel().AddSearchList(search);
factory.Close();

timer.Interval = 20000;
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
};
timer.Start();
}
}

}

为确保不丢掉任何的Search服务,我们采用多线程,InstanceContextMode:只要是管理上下文的实例,此处是single,也就是单体     ConcurrencyMode:主要是用来控制实例中的线程数,此处是Multiple,也就是多线程。

1、构造函数的静态方法实现当前服务的检测和显示,采用Timer定时检测...

2、AddSearch方法实现Search的添加,并回复客户端

3、GetService方法实现的是检测IIS是否等待连接,并且将Search集合发送给它,同样是定时发送。

下面晒配置文件:

<?xml version="1.0"?>
<configuration>
<system.web>
<compilation debug="true"/>
</system.web>
<!-- 部署服务库项目时,必须将配置文件的内容添加到
主机的 app.config 文件中。System.Configuration 不支持库的配置文件。-->
<system.serviceModel>
<services>
<service name="HeartBeatService.Address" behaviorConfiguration="myBehavior">
<endpoint address="net.tcp://localhost:8888/Heart"
binding="netTcpBinding"
contract="HeartBeatService.IAddress">
<identity>
<dns value="localhost"/>
</identity>
</endpoint>

<!--定义引用地址元数据的接受方式,此处基地址(baseAddress)定义的协议Http所以binding同样为mexHttpBinding方式-->
<endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/>
<host>
<baseAddresses>
<add baseAddress="http://localhost:9999/Heart"/>
</baseAddresses>
</host>
</service>
</services>

<behaviors>
<serviceBehaviors>
<behavior name="myBehavior">
<!-- 为避免泄漏元数据信息,
请在部署前将以下值设置为 false 并删除上面的元数据终结点 -->
<serviceMetadata httpGetEnabled="True"/>
<!-- 要接收故障异常详细信息以进行调试,
请将以下值设置为 true。在部署前设置为 false
以避免泄漏异常信息-->
<serviceDebug includeExceptionDetailInFaults="False"/>
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel>

<startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0"/></startup></configuration>

至此,我们已经成功顺利的搭建完心跳检测的流程,我们先运行下“心跳”项目,它将等待Search发送连接过来,并检查IIS时候等候状态...

 

然后我们再运行Search...向“心跳”抛出橄榄枝...

 

嘿嘿,两者已经顺利握手...下面咱把IIS启动,看看“心跳检测”是否能将这个SearchService发送给它:

同样成功,这说明我们的心跳服务还是能实时的为IIS提供连接地址的..

好,下面我们测试该框架性能如何,首先我们执行LoadDBService,将数据库加载到内存中...结果如下:

 

 

我们用该框架测试下速度和普通的查询方式进行比较:

先晒SQL语句,看两者差距性:

select * from         
(
select ROW_NUMBER() over(order by s.ShopID) as NumberID,
s.ShopID,
u.UserName,
s.ShopName
from Shop s left join [User] u
on u.UserID=s.UserID
where s.UserID=150
) as array
where NumberID>0 and NumberID<50

-------------------分页查询-------------------------
select s.ShopID,u.UserName,s.ShopName
from [User] as u ,
Shop as s
where s.ShopID in(100,200,3000,5000,4000,201)

select * from shop

select * from [user]

 


我们在IIS端进行加载检测:

 

晒运行结果:

提示了一个错误:

我们将此服务的连接时间该的长一点:

这个错误是因为和客户端验证模式不对,我们在这里面做了设置,将其设置为None,为了确保从内存数据库中读取完毕,我们将应答时间该的稍长点。我们下面晒结果:

这里看样子也快不了多少,那是因为我们在wcf里查询的条数设置和SQL里面的语法查询条数不同,我们将改一下,还有就是在此服务已有一个,并且内存数据库实现的方式是通过本地化文件操作,很显然是费时费力的工作,就性能均摊,可扩展性等方面,无疑该框架是利器...好了先解析到此..

 

posted @ 2011-11-22 15:06  指尖流淌  阅读(6124)  评论(7编辑  收藏  举报