zoukankan      html  css  js  c++  java
  • 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里面的语法查询条数不同,我们将改一下,还有就是在此服务已有一个,并且内存数据库实现的方式是通过本地化文件操作,很显然是费时费力的工作,就性能均摊,可扩展性等方面,无疑该框架是利器...好了先解析到此..

  • 相关阅读:
    windows批处理
    网络设备巡检命令
    DOS笔记
    通过一台服务器ssh多台主机远程修改网卡ip
    DELL服务器PXE前期处理
    PXE推一半失败,HP服务器、曙光服务器删除数据
    IBM存储降级告警等一些服务器问题/dd/ethtool
    bond下改变网卡
    AndroidStudio 点9图片文件报错
    在AndroidStudio不能找到so文件问题:couldn't find libweibosdkcore.so
  • 原文地址:https://www.cnblogs.com/zhijianliutang/p/2258844.html
Copyright © 2011-2022 走看看