今天主要是讨论分布式应用系统下服务器间缓存是如何同步,此篇文章主要是参考了Discuz!NT,Peter,以及微软的解决方案.

主要着重讲Discuz!NT和Peter的实现方式

 

Discuz!NT和Peter两者的解决方案基本思路其实是一致的.

先说下相同点:

都是将分布式布署的应用看成是一个个"客户端",在其中一台"客户端"发生更新时,需要实时更新其它"客户端",并以推送消息的方式去通知各个客户端,发出Request;

之后接受各个客户端的Response,如果得到的Response为OK,则同步成功,反之则通知同步失败.

具体实现代码:

WebRequest request = WebRequest.Create(new Uri("url"));  //首先发送web请求

//之后通过WebResponse接受对方的请求回复,

WebResponse response = request.GetResponse()

StreamReader reader = new StreamReader(response.GetResponseStream(), Encoding.UTF8)

//对方如果顺利进行解析并Sync缓存数据成功就会Response.Write("OK");本方如果接受到OK,说明一个完整的流程结束,否则给出异常信息.

 

再说下不同点:

一、request请求方式不同一个是POST,一个是GET。

二、Peter是定义CacheControlItem对象,对象中指定它的缓存策略(优先级,缓存生存时间)等信息,并将CacheControlItem对象序列化传到各个分布式服务器端,再由每个服务器端自己解析

并同步缓存数据.

    Discuz!NT是通过GET的方法通过URL提交数据,提交的数据有两个,一个是Cache的key,一个是PassKey,这个Passkey是用来加密的,在发送通知的请求时,对请求进行加密,以免该功能被其它恶意代码利用.

对方接受到数据后,再进行处理。这里对方是使用ashx文件来接受信息,可以通过它来调用HttpHandler类,直接处理http的请求,更方便.

 

我把主要是代码贴出来:

Discuz!NT

/// <summary>
/// 同步远程缓存信息
/// </summary>
/// <param name="cacheKey"></param>

 public static void SyncRemoteCache(string cacheKey)
{
    foreach (string webSite in syncCacheUrlList)
    {
       string url = string.Format("{0}?cacheKey={1}&passKey={2}",
                                       webSite,
                                       cacheKey,
                                       Discuz.Common.Utils.UrlEncode(Discuz.Common.DES.Encode(cacheKey, loadBalanceConfigInfo.AuthCode)));
       ThreadSyncRemoteCache src = new ThreadSyncRemoteCache(url);
       new Thread(new ThreadStart(src.Send)).Start();
    }
}

  public void Send()
  {
     try
     {
        //设置循环三次,如果某一次更新成功("OK"),则跳出循环
        for (int count = 0; count < 3; count++)
        {
            if (this.SendWebRequest(_url) == "OK")
               break;
            else
               Thread.Sleep(5000);  //如果更新不成功,则暂停5秒后再次更新
        }
      }
      catch { }
      finally
      {
          if (Thread.CurrentThread.IsAlive)
             Thread.CurrentThread.Abort();
      }
  }

  /// <summary>
  /// 发送web请求
  /// </summary>
  /// <param name="url"></param>
  /// <returns></returns>

  public string SendWebRequest(string url)
  {
      StringBuilder builder = new StringBuilder();
      try
      {
          WebRequest request = WebRequest.Create(new Uri(url));
          request.Method = "GET";
          request.Timeout = 15000;
          request.ContentType = "Text/XML";
          using (WebResponse response = request.GetResponse())
          {
             using (StreamReader reader = new StreamReader(response.GetResponseStream(), Encoding.UTF8))
             {
                builder.Append(reader.ReadToEnd());
             }
          }
       }
       catch
       {
           builder.Append("Process Failed!");
       }
       return builder.ToString();
   }

/// <summary>
/// 同步本地缓存
/// </summary>

[WebService(Namespace = "
http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
public class SyncLocalCache : IHttpHandler
{
   public void ProcessRequest(HttpContext context)
    {
      context.Response.ContentType = "text/plain";
      string cacheKey = context.Request.QueryString["cacheKey"];
      string passKey = context.Request.QueryString["passKey"];

      if (Utils.StrIsNullOrEmpty(cacheKey))
      {
         context.Response.Write("CacheKey is not null!");
         return;
      }
      if (!cacheKey.StartsWith("/Forum"))
      {
         context.Response.Write("CacheKey is not valid!");
         return;
      }
      if (passKey != Discuz.Common.DES.Encode(cacheKey, Discuz.Config.LoadBalanceConfigs.GetConfig().AuthCode))
      {
         context.Response.Write("AuthCode is not valid!");
         return;
      }

      Discuz.Cache.DNTCache cache = Discuz.Cache.DNTCache.GetCacheService();
      cache.LoadCacheStrategy(new DefaultCacheStrategy());
      cache.RemoveObject(cacheKey);
      cache.LoadDefaultCacheStrategy();

      context.Response.Write("OK");
   }

}

Peter的代码:

   public enum CacheControlAction
    {
        AddItem =1,
        RemoveItem=2
    }

    /// <summary>
    /// Represents a serializable wrapper class to hold a Cache item
    /// </summary>
   
    [Serializable]
   public class CacheControlItem
    {
        public string Key;
        public object Item;
        public DateTime AbsoluteExpiration;
        public TimeSpan SlidingExpiration;
        public System.Web.Caching.CacheItemPriority Priority;
        // sorry, Dependency not marked as Serializable...class is sealed. Too bad!
        //public System.Web.Caching.CacheDependency Dependency;
        public int Action;

        public CacheControlItem(string Key, object Item, DateTime AbsoluteExpiration,
                TimeSpan SlidingExpiration,CacheItemPriority Priority, CacheControlAction Action)
        {
            this.Key=Key;
            this.Item=Item;
            this.AbsoluteExpiration=AbsoluteExpiration;
            this.SlidingExpiration=SlidingExpiration;
            this.Priority=Priority;
            this.Action=(int)Action;
        }
    }

   public static MemoryStream Serialize(CacheControlItem item)
   {
       try
       {
          MemoryStream ms = new MemoryStream();
          BinaryFormatter b = new BinaryFormatter();
          b.Serialize(ms, item);
          return ms;
       }
       catch (Exception ex)
       {
          throw new ApplicationException("Formatter error", ex);
       }
    }


    public static CacheControlItem Deserialize(MemoryStream msInput)
    {
        try
        {
           BinaryFormatter b = new BinaryFormatter();
           CacheControlItem c = (CacheControlItem)b.Deserialize(msInput);
           return c;
        }
        catch (Exception ex)
        {
           throw new ApplicationException("Deserialize error", ex);
        }
    }

    public static void AddCacheControlItem(string key, object value,
         System.Web.Caching.CacheDependency dependsOn, DateTime absoluteExpiration,
         TimeSpan slidingExpiration, CacheItemPriority priority)
    {
        CacheItemRemovedCallback CacheOut = new CacheItemRemovedCallback(OnCacheOut);
        HttpContext.Current.Cache.Add(key, value, dependsOn, absoluteExpiration, slidingExpiration, priority, CacheOut);
        CacheControlItem cci = new CacheControlItem(key, value, absoluteExpiration, slidingExpiration, priority, CacheControlAction.AddItem);
        MemoryStream ms = Serialize(cci);
        byte[] dataToSend = ms.ToArray();

        for (int i = 0; i < sServers.Length; i++)
        {
           //webrequest here to send update to server list

           if (!sendUpdate(dataToSend, "http://" + sServers[i] + "/CacheControl.aspx"))
              throw new ApplicationException("Update Error");

        }
     }


     public static bool sendUpdate(byte[] dataToSend, string sFullUri)
     {

        try
        {
           HttpWebRequest req = (System.Net.HttpWebRequest)WebRequest.Create(sFullUri);
           WebRequest request = WebRequest.Create(new Uri(sFullUri));
           req.Method = "POST";
           Stream stm = req.GetRequestStream();
           stm.Write(dataToSend, 0, dataToSend.Length);
           stm.Close();
           System.Net.WebResponse resp;
           resp = req.GetResponse();
           stm = resp.GetResponseStream();
           StreamReader r = new StreamReader(stm);

           byte[] bytRes = System.Text.Encoding.UTF8.GetBytes(r.ReadToEnd());
           string res = System.Text.Encoding.ASCII.GetString(bytRes);

           Debug.Write(res);
           if (res.StartsWith("OK"))
           {
              return true;
           }
           else
           {
              return false;
           }

        }
        catch (Exception ex)
        {
           throw new HttpException("Update Error", ex);
        }

    }

    public static void OnCacheOut(string key, object val, CacheItemRemovedReason r)
    {

       // a cache item was removed - expired, etc.
       // do WebRequest to other servers from servers[] array to remove item

       CacheControlItem cci = new CacheControlItem(key, val, System.DateTime.MaxValue, new TimeSpan(0, 0, 0, 0, 0), CacheItemPriority.Low, CacheControlAction.RemoveItem);
       MemoryStream ms = Serialize(cci);
       byte[] dataToSend = ms.ToArray();

       for (int i = 0; i < sServers.Length; i++)
       {
          // webrequest here to send update to server list

          if (!sendUpdate(dataToSend, "http://" + sServers[i] + "/CacheControl.aspx"))
             throw new ApplicationException("Update Error");

       }

    }

 

 

最后简单来看看MS, 它的想法类似,只不过它不在WebFarm的成员服务器之间同步缓存,而只是保证每台机器不要读到已经失效的缓存数据,当缓存数据失效时(相关依赖数据被 更新), 通过维护一个需要通知的服务器列表依次调用每台服务器上的WebService,如果当前服务器上存在这键值的缓存则使它失效
 

 

posted on 2013-08-22 17:59  tzj19810812  阅读(544)  评论(0编辑  收藏  举报