随笔 - 249  文章 - 48  评论 - 5686 
      大约在两年前我写过一篇关于Discuz!NT缓存架构的文章,在那篇文章的结尾介绍了在IIS中如果开启多个
应用程序池会造成多个缓存实例之间数据同步的问题。虽然给出了一个解决方案,但无形中却把压力转移到了
磁盘I/O上(多个进程并发访问cache.config文件)。其实从那时起我就开始关注有什么更好的方案,当然今
天本文中所说的Memcached,以及Velocity等这类的分布式缓存方案之前都考虑过,但一直未能决定该使用那
个。起码Velocity要在.net 4.0之后才会提供,虽然是原生态,但有些远水解不了近火。

      我想真正等到Velocity能堪当重任还要等上一段时间。于是我就开始将注意力转移到了Memcached,必定
有Facebook这只“超级小白鼠”使用它并且反响还不错。所以就开始尝试动手在产品中集成Memcached。

      其实在之前的那篇关于Discuz!NT缓存架构的文章中已提到过,使用了设计模式中的“策略模式”来构造。
所以为了与以往使用缓存的代码格式相兼容,所以这里采用新添加MemCachedStrategy(MemCached策略)
来构造一个缓存策略类以便于当管理后台开启“MemCached”时以“MemCached策略模式”来做为当前系统默认
的策略模式。

    其代码段如下(Discuz.Cache/MemCached.cs):
    
/// <summary>
/// MemCache缓存策略类
/// </summary>
public class MemCachedStrategy : Discuz.Cache.ICacheStrategy
{

    
/// <summary>
    
/// 添加指定ID的对象
    
/// </summary>
    
/// <param name="objId"></param>
    
/// <param name="o"></param>
    public void AddObject(string objId, object o)
    {
        RemoveObject(objId);
        
if (TimeOut > 0)
        {
            MemCachedManager.CacheClient.Set(objId, o, System.DateTime.Now.AddMinutes(TimeOut));
        }
        
else
        {
            MemCachedManager.CacheClient.Set(objId, o);
        }
    }

    
/// <summary>
    
/// 添加指定ID的对象(关联指定文件组)
    
/// </summary>
    
/// <param name="objId"></param>
    
/// <param name="o"></param>
    
/// <param name="files"></param>
    public void AddObjectWithFileChange(string objId, object o, string[] files)
    {
        ;
    }

    
/// <summary>
    
/// 添加指定ID的对象(关联指定键值组)
    
/// </summary>
    
/// <param name="objId"></param>
    
/// <param name="o"></param>
    
/// <param name="dependKey"></param>
    public void AddObjectWithDepend(string objId, object o, string[] dependKey)
    {
        ;
    }

    
/// <summary>
    
/// 移除指定ID的对象
    
/// </summary>
    
/// <param name="objId"></param>
    public void RemoveObject(string objId)
    {
        
if (MemCachedManager.CacheClient.KeyExists(objId))
            MemCachedManager.CacheClient.Delete(objId);
    }

    
/// <summary>
    
/// 返回指定ID的对象
    
/// </summary>
    
/// <param name="objId"></param>
    
/// <returns></returns>
    public object RetrieveObject(string objId)
    {
        
return MemCachedManager.CacheClient.Get(objId);
    }

    
/// <summary>
    
/// 到期时间
    
/// </summary>
    public int TimeOut { setget; }
}

    

    上面类实现的接口Discuz.Cache.ICacheStrategy定义如下:
    
 
/// <summary>
 
/// 公共缓存策略接口
 
/// </summary>
 public interface ICacheStrategy
 {
     
/// <summary>
     
/// 添加指定ID的对象
     
/// </summary>
     
/// <param name="objId"></param>
     
/// <param name="o"></param>
     void AddObject(string objId, object o);

     
/// <summary>
     
/// 添加指定ID的对象(关联指定文件组)
     
/// </summary>
     
/// <param name="objId"></param>
     
/// <param name="o"></param>
     
/// <param name="files"></param>
     void AddObjectWithFileChange(string objId, object o, string[] files);

     
/// <summary>
     
/// 添加指定ID的对象(关联指定键值组)
     
/// </summary>
     
/// <param name="objId"></param>
     
/// <param name="o"></param>
     
/// <param name="dependKey"></param>
     void AddObjectWithDepend(string objId, object o, string[] dependKey);

     
/// <summary>
     
/// 移除指定ID的对象
     
/// </summary>
     
/// <param name="objId"></param>
     void RemoveObject(string objId);

     
/// <summary>
     
/// 返回指定ID的对象
     
/// </summary>
     
/// <param name="objId"></param>
     
/// <returns></returns>
     object RetrieveObject(string objId);

     
/// <summary>
     
/// 到期时间
     
/// </summary>
     int TimeOut { set;get;}
}


     当然在MemCachedStrategy类中还有一个对象要加以说明,就是MemCachedManager,该类主要是对
Memcached一些常操作和相关初始化实例调用的“封装”,下面是是其变量定义和初始化构造方法的代码:

/// <summary>
/// MemCache管理操作类
/// </summary>
public sealed class MemCachedManager
{
    
#region 静态方法和属性
    
private static MemcachedClient mc = null;

    
private static SockIOPool pool = null;

    
private static MemCachedConfigInfo memCachedConfigInfo = MemCachedConfigs.GetConfig();

    
private static string [] serverList = null;

    
static MemCachedManager()
    {
        CreateManager();
    }

    
private static void CreateManager()
    {
        serverList 
= Utils.SplitString(memCachedConfigInfo.ServerList, ""r"n");

        pool 
= SockIOPool.GetInstance(memCachedConfigInfo.PoolName);
        pool.SetServers(serverList);
        pool.InitConnections 
= memCachedConfigInfo.IntConnections;//初始化链接数
        pool.MinConnections = memCachedConfigInfo.MinConnections;//最少链接数
        pool.MaxConnections = memCachedConfigInfo.MaxConnections;//最大连接数
        pool.SocketConnectTimeout = memCachedConfigInfo.SocketConnectTimeout;//Socket链接超时时间
        pool.SocketTimeout = memCachedConfigInfo.SocketTimeout;// Socket超时时间
        pool.MaintenanceSleep = memCachedConfigInfo.MaintenanceSleep;//维护线程休息时间
        pool.Failover = memCachedConfigInfo.FailOver; //失效转移(一种备份操作模式)
        pool.Nagle = memCachedConfigInfo.Nagle;//是否用nagle算法启动socket
        pool.HashingAlgorithm = HashingAlgorithm.NewCompatibleHash;
        pool.Initialize();
        

        mc 
= new MemcachedClient();
        mc.PoolName 
= memCachedConfigInfo.PoolName;
        mc.EnableCompression 
= false;
    }

    
/// <summary>
    
/// 缓存服务器地址列表
    
/// </summary>
    public static string[] ServerList
    {
        
set
        {
            
if (value != null)
                serverList 
= value;
        }
        
get { return serverList; }
    }

    
/// <summary>
    
/// 客户端缓存操作对象
    
/// </summary>
    public static MemcachedClient CacheClient
    {
        
get
        {
            
if (mc == null)
                CreateManager();

            
return mc;
        }
    }

    
public static void Dispose()
    {
        
if (pool != null)
            pool.Shutdown();
    }
    

    
    上面代码中构造方法会初始化一个池来管理执行Socket链接,并提供静态属性CacheClient以便MemCachedStrategy
来调用。


    当然我还在这个管理操作类中添加了几个方法分别用于检测当前有效的分布式缓存服务器的列表,向指定(或全部)
缓存服务器发送特定stats命令来获取当前缓存服务器上的数据信息和内存分配信息等,相应的方法如下(详情见注释):

/// <summary>
/// 获取当前缓存键值所存储在的服务器
/// </summary>
/// <param name="key">当前缓存键</param>
/// <returns>当前缓存键值所存储在的服务器</returns>
public static string GetSocketHost(string key)
{
    
string hostName = "";
    SockIO sock 
= null;
    
try
    {
        sock 
= SockIOPool.GetInstance(memCachedConfigInfo.PoolName).GetSock(key);
        
if (sock != null)
        {
            hostName 
= sock.Host;
        }
    }
    
finally
    {
        
if (sock != null)
            sock.Close();
    }
    
return hostName;
}


/// <summary>
/// 获取有效的服务器地址
/// </summary>
/// <returns>有效的服务器地</returns>
public static string[] GetConnectedSocketHost()
{
    SockIO sock 
= null;
    
string connectedHost = null;
    
foreach (string hostName in serverList)
    {
        
if (!Discuz.Common.Utils.StrIsNullOrEmpty(hostName))
        {
            
try
            {
                sock 
= SockIOPool.GetInstance(memCachedConfigInfo.PoolName).GetConnection(hostName);
                
if (sock != null)
                {
                    connectedHost 
= Discuz.Common.Utils.MergeString(hostName, connectedHost);
                }
            }
            
finally
            {
                
if (sock != null)
                    sock.Close();
            }
        }
    }
    
return Discuz.Common.Utils.SplitString(connectedHost, ",");
}

/// <summary>
/// 获取服务器端缓存的数据信息
/// </summary>
/// <returns>返回信息</returns>
public static ArrayList GetStats()
{
    ArrayList arrayList 
= new ArrayList();
    
foreach (string server in serverList)
    {
        arrayList.Add(server);
    }
    
return GetStats(arrayList, Stats.Default, null);
}

/// <summary>
/// 获取服务器端缓存的数据信息
/// </summary>
/// <param name="serverArrayList">要访问的服务列表</param>
/// <returns>返回信息</returns>
public static ArrayList GetStats(ArrayList serverArrayList, Stats statsCommand, string param)
{
    ArrayList statsArray 
= new ArrayList();
    param 
=  Utils.StrIsNullOrEmpty(param) ? "" : param.Trim().ToLower();

    
string commandstr = "stats";
    
//转换stats命令参数
    switch (statsCommand)
    {
        
case Stats.Reset: { commandstr = "stats reset"break; }
        
case Stats.Malloc: { commandstr = "stats malloc"break; }
        
case Stats.Maps: { commandstr = "stats maps"break; }
        
case Stats.Sizes: { commandstr = "stats sizes"break; }
        
case Stats.Slabs: { commandstr = "stats slabs"break; }
        
case Stats.Items: { commandstr = "stats"break; }
        
case Stats.CachedDump:
        {
            
string[] statsparams = Utils.SplitString(param, " ");
            
if(statsparams.Length == 2)
                
if(Utils.IsNumericArray(statsparams))
                    commandstr 
= "stats cachedump  " + param;

            
break;                     
        }
        
case Stats.Detail:
            {
                
if(string.Equals(param, "on"|| string.Equals(param, "off"|| string.Equals(param, "dump"))
                    commandstr 
= "stats detail " + param.Trim();

                
break;
            }
        
default: { commandstr = "stats"break; }
    }
    
//加载返回值
    Hashtable stats = MemCachedManager.CacheClient.Stats(serverArrayList, commandstr);
    
foreach (string key in stats.Keys)
    {
        statsArray.Add(key);
        Hashtable values 
= (Hashtable)stats[key];
        
foreach (string key2 in values.Keys)
        {
            statsArray.Add(key2 
+ ":" + values[key2]);
        }
    }
    
return statsArray;
}

/// <summary>
/// Stats命令行参数
/// </summary>
public enum Stats
{
    
/// <summary>
    
/// stats : 显示服务器信息, 统计数据等
    
/// </summary>
    Default = 0,
    
/// <summary>
    
/// stats reset : 清空统计数据
    
/// </summary>
    Reset = 1,
    
/// <summary>
    
/// stats malloc : 显示内存分配数据
    
/// </summary>
    Malloc = 2,
    
/// <summary>
    
/// stats maps : 显示"/proc/self/maps"数据
    
/// </summary>
    Maps =3,
    
/// <summary>
    
/// stats sizes
    
/// </summary>
    Sizes = 4,
    
/// <summary>
    
/// stats slabs : 显示各个slab的信息,包括chunk的大小,数目,使用情况等
    
/// </summary>
    Slabs = 5,
    
/// <summary>
    
/// stats items : 显示各个slab中item的数目和最老item的年龄(最后一次访问距离现在的秒数)
    
/// </summary>
    Items = 6,
    
/// <summary>
    
/// stats cachedump slab_id limit_num : 显示某个slab中的前 limit_num 个 key 列表
    
/// </summary>
    CachedDump =7,
    
/// <summary>
    
/// stats detail [on|off|dump] : 设置或者显示详细操作记录   on:打开详细操作记录  off:关闭详细操作记录 dump: 显示详细操作记录(每一个键值get,set,hit,del的次数)
    
/// </summary>
    Detail = 8
}



    当然在配置初始化缓存链接池时使用了配置文件方式(memcached.config)来管理相关参数,其info信息
类说明如下(Discuz.Config/MemCachedConfigInfo.cs):

/// <summary>
/// MemCached配置信息类文件
/// </summary>
public class MemCachedConfigInfo : IConfigInfo
{
    
private bool _applyMemCached;
    
/// <summary>
    
/// 是否应用MemCached
    
/// </summary>
    public bool ApplyMemCached
    {
        
get
        {
            
return _applyMemCached;
        }
        
set
        {
            _applyMemCached 
= value;
        }
    }

    
private string _serverList;
    
/// <summary>
    
/// 链接地址
    
/// </summary>
    public string ServerList
    {
        
get
        {
            
return _serverList;
        }
        
set
        {
            _serverList 
= value;
        }
    }

    
private string _poolName;
    
/// <summary>
    
/// 链接池名称
    
/// </summary>
    public string PoolName
    {
        
get
        {
            
return Utils.StrIsNullOrEmpty(_poolName) ? "DiscuzNT_MemCache" : _poolName;
        }
        
set
        {
            _poolName 
= value;
        }
    }

    
private int _intConnections;
    
/// <summary>
    
/// 初始化链接数
    
/// </summary>
    public int IntConnections
    {
        
get
        {
            
return _intConnections > 0 ? _intConnections : 3;
        }
        
set
        {
            _intConnections 
= value;
        }
    }

    
private int _minConnections;
    
/// <summary>
    
/// 最少链接数
    
/// </summary>
    public int MinConnections
    {
        
get
        {
            
return _minConnections > 0 ? _minConnections : 3;
        }
        
set
        {
            _minConnections 
= value;
        }
    }

    
private int _maxConnections;
    
/// <summary>
    
/// 最大连接数
    
/// </summary>
    public int MaxConnections
    {
        
get
        {
            
return _maxConnections > 0 ?_maxConnections : 5;
        }
        
set
        {
            _maxConnections 
= value;
        }
    }

    
private int _socketConnectTimeout;
    
/// <summary>
    
/// Socket链接超时时间
    
/// </summary>
    public int SocketConnectTimeout
    {
        
get
        {
            
return _socketConnectTimeout > 1000 ? _socketConnectTimeout : 1000;
        }
        
set
        {
            _socketConnectTimeout 
= value;
        }
    }

    
private int _socketTimeout;
    
/// <summary>
    
/// socket超时时间
    
/// </summary>
    public int SocketTimeout
    {
        
get
        {
            
return _socketTimeout > 1000 ? _maintenanceSleep : 3000;
        }
        
set
        {
            _socketTimeout 
= value;
        }
    }

    
private int _maintenanceSleep;
    
/// <summary>
    
/// 维护线程休息时间
    
/// </summary>
    public int MaintenanceSleep
    {
        
get
        {
            
return _maintenanceSleep > 0 ? _maintenanceSleep : 30;
        }
        
set
        {
            _maintenanceSleep 
= value;
        }
    }

    
private bool _failOver;
    
/// <summary>
    
/// 链接失败后是否重启,详情参见http://baike.baidu.com/view/1084309.htm
    
/// </summary>
    public bool FailOver
    {
        
get
        {
            
return _failOver;
        }
        
set
        {
            _failOver 
= value;
        }
    }

    
private bool _nagle;
    
/// <summary>
    
/// 是否用nagle算法启动socket
    
/// </summary>
    public bool Nagle
    {
        
get
        {
            
return _nagle;
        }
        
set
        {
            _nagle 
= value;
        }
    }
}     



     这些参数我们通过注释应该有一些了解,可以说memcached的主要性能都是通过这些参数来决定的,大家
应根据自己公司产品和应用的实际情况配置相应的数值。

     当然,做完这一步之后就是对调用“缓存策略”的主体类进行修改来,使其根据对管理后台的设计来决定
加载什么样的缓存策略,如下:

/// <summary>
/// Discuz!NT缓存类
/// 对Discuz!NT论坛缓存进行全局控制管理
/// </summary>
public class DNTCache
{
    .
    
    
//通过该变量决定是否启用MemCached
    private static bool applyMemCached = MemCachedConfigs.GetConfig().ApplyMemCached;     

    
/// <summary>
    
/// 构造函数
    
/// </summary>
    private DNTCache()
    {
        
if (applyMemCached)
            cs 
= new MemCachedStrategy();
        
else
        {
            cs 
= new DefaultCacheStrategy();

            objectXmlMap 
= rootXml.CreateElement("Cache");
            
//建立内部XML文档.
            rootXml.AppendChild(objectXmlMap);

            
//LogVisitor clv = new CacheLogVisitor();
            
//cs.Accept(clv);

            cacheConfigTimer.AutoReset 
= true;
            cacheConfigTimer.Enabled 
= true;
            cacheConfigTimer.Elapsed 
+= new System.Timers.ElapsedEventHandler(Timer_Elapsed);
            cacheConfigTimer.Start();
        }
    }
    
    

    
    到这里,主要的开发和修改基本上就告一段落了。下面开始介绍一下如果使用Stats命令来查看缓存的
分配和使用等情况。之前在枚举类型Stats中看到该命令有几个主要的参数,分别是:
 
    stats
    stats reset
    stats malloc
    stats maps
    stats sizes
    stats slabs
    stats items
    stats cachedump slab_id limit_num
    stats detail [on|off|dump]
    
    而JAVAEYE的 robbin 写过一篇文章:贴一段遍历memcached缓存对象的小脚本,来介绍如何使用其中的  
“stats cachedump”来获取信息。受这篇文章的启发,我将MemCachedClient.cs文件中的Stats方法加以修
改,添加了一个command参数(字符串型),这样就可以向缓存服务器发送上面所说的那几种类型的命令了。

    测试代码如下:
    
protected void Submit_Click(object sender, EventArgs e)
{
    ArrayList arrayList 
= new ArrayList();
    arrayList.Add(
"10.0.1.52:11211");//缓存服务器的地址

    StateResult.DataSource 
= MemCachedManager.GetStats(arrayList, (MemCachedManager.Stats)         
                                     Utils.StrToInt(StatsParam.SelectedValue, 
0), Param.Text);
    StateResult.DataBind();            
}


    页面代码如下:
    
   

   

    

     我这样做的目的有两个,一个是避免每次都使用telnet协议远程登陆缓存服务器并输入相应的命令行
参数(我记忆力不好,参数多了之后就爱忘)。二是将来会把这个页面功能内置到管理后台上,以便后台
管理员可以动态监测每台缓存服务器上的数据。


     好了,到这里今天的内容就差不多了。在本文中我们看到了使用设计模式的好处,通过它我们可以让
自己写的代码支持“变化”。这里不妨再多说几句,大家看到了velocity在使用上也是很方便,如果可以
的话,未来可以也会将velocity做成一个“缓存策略”,这样站长或管理员就可以根据自己公司的实际情
况来加以灵活配置了。
    
    
      相关资料:   
      memcached 全面剖析.pdf   
      memcached 深度分析    

      Facebook 对memcached的提升

 

    原文链接:http://www.cnblogs.com/daizhj/archive/2009/02/09/1386652.html

    作者: daizhj, 代震军

    Tags: memcached,discuz!nt,缓存,设计模式,策略模式,strategy pattern
    
    网址: http://daizhj.cnblogs.com/  

代震军
关注 - 14
粉丝 - 98
荣誉:微软社区精英推荐博客
1
0
(请您对文章做出评价)
« 上一篇:Livemesh文件同步功能--使用图解
» 下一篇:快速浏览Silverlight3 Beta:在浏览器外运行Silverlight应用
posted on 2009-03-23 09:13 代震军 阅读(6606) 评论(57) 编辑 收藏 所属分类: Discuz!NT

 回复 引用 查看   
#1楼 2009-03-23 09:25 | codehunter008      
沙发
 回复 引用 查看   
#2楼[楼主] 2009-03-23 09:34 | 代震军      
@codehunter008
呵呵:)

 回复 引用 查看   
#3楼 2009-03-23 09:45 | gOODiDEA      
比起别的client,EnyimMemcached要更好一些
 回复 引用 查看   
#4楼 2009-03-23 09:45 | Aspgreener      
不错啊!~虽然没仔细看,但是感觉这个很有用。
-------------------------------------------------------
专注于自己的网站,http://www.xiaoshiyiluokuang.com

 回复 引用   
#5楼 2009-03-23 09:58 | 游客12344321[未注册用户]
Discuz!NT的话不一定非得用memcache的,可以用net自己实现缓存的分布式管理。 比如用wcf来实现等。
 回复 引用   
#6楼 2009-03-23 11:04 | objectprogram[未注册用户]
能不能把完整的源码给我发一份过来.
yaolintm@163.com
谢谢

 回复 引用 查看   
#7楼[楼主] 2009-03-23 11:06 | 代震军      
@gOODiDEA
回头参照一下:)

 回复 引用 查看   
#8楼[楼主] 2009-03-23 11:07 | 代震军      
@游客12344321
wcf的效率上不太适合,memcache是用c写的,并使用了池化的设计方式,对并发支持的很不错

 回复 引用 查看   
#9楼[楼主] 2009-03-23 11:08 | 代震军      
@objectprogram
目前源码将会随下个产品版本(discuznt)源码一起放出来,基本上没什么变化了

 回复 引用 查看   
#10楼 2009-03-23 11:22 | TerryLee      
Memcached估计是目前使用最广泛的分布式缓存框架之一了,国内一些门户网站也在使用。不过我更期待微软的Velocity :)
 回复 引用 查看   
#11楼[楼主] 2009-03-23 11:27 | 代震军      
@TerryLee
是呀,必定是原生态,不过也要等NET4发布之后了,并且还要参照一下在大并发下的实际运行结果。

 回复 引用   
#12楼 2009-03-23 11:34 | 游客12344321[未注册用户]
--引用--------------------------------------------------
代震军: @游客12344321
wcf的效率上不太适合,memcache是用c写的,并使用了池化的设计方式,对并发支持的很不错
--------------------------------------------------------


效率上来说确实是没可比性的,毕竟memcache是c而且是很纯粹的数据存储的,不过dz论坛针对的用户群应该在这部分还是不太会体现的出来。

用wcf来实现的话性能上是没优势的,但因为他的缺点有时候也是优点:可以有逻辑处理,所以某些时候用起来会方便很多,呵呵

我现在也是在做论坛,我的暂时是memcache和wcf混合存在的,因为需要缓存集合并且在集合里做查询,暂时没能找到可以在memcache上解决的好的方案,不知楼主是否有好的建议。

 回复 引用 查看   
#13楼[楼主] 2009-03-23 11:40 | 代震军      
@游客12344321
如果缓存中有集合的话,并且集合中的数据量不多(也就100多行数据的话),还是用memcache缓存一下,并用static来标识返回来的集合结果,并在static变量中进行数据查询操作,这样就把对MEMCACHED的访问压力降到当前的工作者进程中了。

 回复 引用   
#14楼 2009-03-23 11:47 | 游客12344321[未注册用户]
--引用--------------------------------------------------
代震军: @游客12344321
如果缓存中有集合的话,并且集合中的数据量不多(也就100多行数据的话),还是用memcache缓存一下,并用static来标识返回来的集合结果,并在static变量中进行数据查询操作,这样就把对MEMCACHED的访问压力降到当前的工作者进程中了。
--------------------------------------------------------

不幸的是集合数据量是比较大的,因为追求高的访问速度,所以虽然没达到facebook几乎拿内存当硬盘用的地步但缓存的数据挺多。。。

 回复 引用 查看   
#15楼[楼主] 2009-03-23 11:54 | 代震军      
@游客12344321
如果缓存的数据是那种只读的,或不经常变的话,也可以用我说的方案,即:“
还是用memcache缓存一下,并用static来标识返回来的集合结果,并在static变量中进行数据查询操作,这样就把对MEMCACHED的访问压力降到当前的工作者进程中了”

 回复 引用   
#16楼 2009-03-23 12:06 | 游客12344321[未注册用户]
@代震军
是为了减轻当前工作进程的压力所以才放过去的,所以这个方案是不太合适了,而且会经常需要改变集合中的单个数据, 挺恼人的,不过现在用wcf顶着暂时还蛮Ok



还是php的版本好,.net版本的基本不考虑。以前还出过bug……因为那个bug,服务器被拿下了,汗……

 回复 引用   
#18楼 2009-03-23 12:30 | 游客12344321[未注册用户]
@LeadNT.NET - 无技术,不网摘
我的.net版很稳定啊,上限后就没去管过他

 回复 引用 查看   
#19楼 2009-03-23 12:36 | Steven Chen      
lz好久没有发表咚咚了

velocity ctp2的性能让我崩溃 而传言中的ctp3一直没有找到
但愿ms不要跳水

 回复 引用 查看   
#20楼[楼主] 2009-03-23 12:41 | 代震军      
@Steven Chen
是吗,没有安装测试过,不好说

 回复 引用 查看   
#21楼[楼主] 2009-03-23 12:42 | 代震军      
@代震军 就目前性能和安全性上,.net版已很稳定了。
 回复 引用 查看   
#22楼 2009-03-23 13:27 | 黑羽飘舞      
内容不错。
 回复 引用 查看   
#23楼[楼主] 2009-03-23 14:07 | 代震军      
@黑羽飘舞
:)

 回复 引用 查看   
#24楼 2009-03-23 14:53 | 溜园      
关注中……
 回复 引用 查看   
#25楼 2009-03-23 15:03 | 梁逸晨      
代老师上面说了一个关键问题: 这东西是用C写的。
这就引发了另一个问题:

大家做网站,技术是其次,最重要的是首先要有个稳定的环境。目前国内的情况是闹得鸡飞狗跳,彼此心知肚明就不细说了。

现在DNT官方论坛已经有不少帖子在关注godaddy上面安装的解决方案。国外空间对于权限的设置很严格(例如SQLITE无论怎么封装都不行,因为它不是纯.NET代码),本来就已经有些问题的DNT,如果此时再引入一个不是纯.NET写的东西,恐怕不是一件好事。

 回复 引用 查看   
#26楼[楼主] 2009-03-23 15:15 | 代震军      
@梁逸晨
呵呵,看来您一直很关注我们的产品。其实在考虑引入MEMCACHED的时候,我有一个思想,就是只在适合的场景中使用它,所以说这个东西是对那些对数据库访问压力大的那些站点来说,将来会考虑将更多的数据访问转向到缓存中。我在本地做过压力测试,在5000访问量(64线程)的访问下性能与本地进程内缓存的速度已很接近。
另外你所说的非.net体系的顾虑,我想这个不会造成什么问题。还有就是我在文章中也说了,将来也会很容易的引入velocity的支持,其实在这里我是给用户提供了一组选项的,站长会根据身身的实际情况来决定使用那一种缓存方式,并且可以说这三种方式互不影响和冲突,所以大可放心。

 回复 引用   
#27楼 2009-03-23 15:18 | 游客12344321[未注册用户]
@梁逸晨
可能你没太了解memcache,和你程序用什么语言开发是没关系的,你甚至可以直接在控制台发送命令去操作他

 回复 引用 查看   
#28楼 2009-03-23 15:49 | khaos      
@代震军

下个版本什么时候出来啊?

 回复 引用 查看   
#29楼 2009-03-23 16:48 | lovecherry      
@代震军
問個問題,memcached好像每一個結點只能存儲1M以內的數據
如果我的數據很大,好像一定要拆分為不同的KEY去存儲
這樣就很麻煩,樓主遇到過這個問題嗎

 回复 引用 查看   
#30楼[楼主] 2009-03-23 17:36 | 代震军      
@lovecherry
好像每个节点是2g或4g(具体数据记不清了)。而你说的情况应该是通过KEY来让相临的数据指定存储到相应的缓存服务器上,以提升缓存的读取速度。这块确实是要认真研究和解决的,况且MEMCACHED也支持批量获取数据,如果使用得当效率也是很高的。
因为我们目前所缓存的数据还很少,不过将来如果将用户数据也缓存起来的话,一台缓存服务器就够呛了。到那时是要认真测试生产环境下的效率和速度,以找出最佳的系统配置参数的。

 回复 引用 查看   
#31楼[楼主] 2009-03-23 17:37 | 代震军      
@khaos
这块只能关注一下官方的信息了,我不好多说什么。
我们目前正在重构代码以加强代码的可读性和运行效率。

 回复 引用 查看   
#32楼 2009-03-23 17:40 | lovecherry      
@代震军
不好意思,我可能沒表達清楚
我意思是一個KEY存儲的VALUE,這個VALUE不能超過1M
這個限制很不方便
網上有個文章說
同时Memcached对于value的大小支持到1M,大于1M的内容不适合Memcached存储。其实在Cache的设计中这样的情况发生本来就证明设计有问题,Cache只是加速,一般保存都是较小的id或者小对象,用来验证以及为数据定位作精准细化,而大数据量的内容还是在数据库等存储中。

http://groups.google.com/group/memcached/browse_thread/thread/dddcb96bc35a1535/49e498a23f0b018a

 回复 引用 查看   
#33楼[楼主] 2009-03-23 17:43 | 代震军      
@lovecherry
这个问题到没太关注过,我会去查一下相关资料的。
如果是您所说的情况,那我想可以通过分割数据的方式将一个大表分成几个小的集合分别存储,查询时专门写一个方法来获取各个集合的数据并组合起来提供给前台进行操作。

 回复 引用   
#34楼 2009-03-27 16:33 | elevenbus[未注册用户]
代老师,我有个问题
discuznt数据访问采用大接口,单例模式访问
我参考该方法进行了一个设计,我发现并发访问时,单例模式会出错。
如:
/// <summary>
/// 利用单例模式创建数据层实例
/// </summary>
public sealed class DataAccess
{
private static GuanBar.IDAL.IDataFunction singletonInstance = null;
private static readonly object thislock = new object();
private static readonly string dataNameSpace = ConfigurationManager.AppSettings["DAL"];
private DataAccess(){}
public static GuanBar.IDAL.IDataFunction CreateInstance()
{
string className = dataNameSpace + ".DataFunction";
if (singletonInstance == null)
{
lock (thislock)
{
if (singletonInstance == null)
singletonInstance = (GuanBar.IDAL.IDataFunction)System.Reflection.Assembly.Load(dataNameSpace).CreateInstance(className);
}
}
return singletonInstance;
}
}
而静态类却不会出错,
如:
public sealed class DataAccess1
{
private static readonly string dataNameSpace = ConfigurationManager.AppSettings["DAL"];
private DataAccess1() { }
public static GuanBar.IDAL.IDataFunction myInstance()
{
string className = dataNameSpace + ".DataFunction";
return (GuanBar.IDAL.IDataFunction)System.Reflection.Assembly.Load(dataNameSpace).CreateInstance(className);
}

}
有什么地方没有弄好么?谢谢

 回复 引用   
#35楼 2009-03-28 10:49 | elevenbus[未注册用户]
终于知道错误的地方了,但是并不知道原因
我先在方法外定义变量

C# code
/// <summary>
/// StringBuilder的公共变量,已经初始化
/// </summary>
private StringBuilder strSql = new StringBuilder(300);




然后在方法中使用

C# code
/// <summary>
/// 增加库室
/// </summary>
/// <param name="libraryplace">库室表实体</param>
/// <returns></returns>
public bool AddLibraryPlace(LibraryPlace libraryplace)
{
strSql = new StringBuilder(100);//注意这里并不是 StringBuilder strSql = new StringBuilder(100);
strSql.Append("INSERT INTO [LibraryPlace]( ");
strSql.Append("[SchoolID],[LibraryName], [LibraryAddress] )");
strSql.Append("VALUES (");
strSql.Append("@SchoolID,@LibraryName, @LibraryAddress ) ");
DbParameter[] parms = new SqlParameter[3];
parms[0] = DbHelper.ParaInstance("@LibraryName", (DbType)SqlDbType.NVarChar, 50, libraryplace.LibraryName, ParameterDirection.Input);
parms[1] = DbHelper.ParaInstance("@LibraryAddress", (DbType)SqlDbType.NVarChar, 100, libraryplace.LibraryAddress, ParameterDirection.Input);
parms[2] = DbHelper.ParaInstance("@SchoolID", (DbType)SqlDbType.Int, 4, libraryplace.SchoolID, ParameterDirection.Input);
flag = DbHelper.ExecuteNonQuery(strSql.ToString(), CommandType.Text, DbHelper.GuanBar_Book_ConnString, parms);
return flag;
}



如果开发中测试。一次插入再多数据也不回出错,但是并发测试,一测就出错
如果改为 StringBuilder strSql = new StringBuilder(100);
那么就不会出错,有点奇怪

 回复 引用 查看   
#36楼[楼主] 2009-03-31 09:16 | 代震军      
@elevenbus
就目前你所说的情况来看并不是什么单体模式导致的并发问题。应该是频繁的使用对象实例化绑定的问题。其中在这种数据访问的地方直接用string要比stringbuider还要方便高效。因为stringbuilder在装填大量内容时和相应的字符替换时要快不少,而一个sql的长度基本上也要百十个字符就可以搞定了。
还有就是在这种方法中(AddLibraryPlace)如果要应用程序要经常并发访问该方法的话,在这块要加互斥锁到其它解决并发的方式。以免出现问题。

 回复 引用   
#37楼 2009-04-01 11:40 | elevenbus[未注册用户]
我想经过很多测试,我们应该弄清楚了错误原因:
为什么其他地方,同样是公用变量,而不会数据错乱,而偏偏数据层使用公用变量会出现数据错乱
因为数据层使用单例模式,类成员变量始终只有一个公用变量实例,因此不同的方法使用公用变量会出错
而其他地方每次都实例化,因此不会出错,不知道对不对?

 回复 引用 查看   
#38楼 2009-07-02 09:44 | skyaspnet      
有个问题,这个缓存系统应该是分布式的,在网上查了一些资料,如果用于单台服务器会导致性能下降,DNT应用这个缓存系统是不是能起到真正的性能提升呢?谢谢
 回复 引用 查看   
#39楼[楼主] 2009-07-02 10:34 | 代震军      
@skyaspnet
这个功能是一个扩展,并不是其默认开启实现的方式,如果用户的服务器有几台并希望分布式布署的话,就可以开启它了。

 回复 引用 查看   
#40楼 2009-07-03 21:41 | skyaspnet      
想请教一个问题,就是如果用的是DNT默认的缓存方案的话,那么关键字是怎么处理的呢?是用一个表记录所有的关键字吗?比如说添加一个就在表里加一个,修改一个就在表里修改一个,或者是删除一个就在表里删除一个?这个表是用纸记录还是电子文档呢?谢谢!
 回复 引用 查看   
#41楼[楼主] 2009-07-06 08:52 | 代震军      
@skyaspnet
是采用静态字段的方式来处理的,这样就可以让静态变量来存储相应关键字和值信息,这一块内容在http://www.cnblogs.com/daizhj/archive/2007/08/15/855163.html已做了介绍

 回复 引用 查看   
#42楼 2009-07-06 19:16 | skyaspnet      
引用代震军:
@skyaspnet
是采用静态字段的方式来处理的,这样就可以让静态变量来存储相应关键字和值信息,这一块内容在http://www.cnblogs.com/daizhj/archive/2007/08/15/855163.html已做了介绍

想问下像已有的缓存项是不是一个一个设计好并作好记录的? 例如帖子总数, 会员总数? 而且添加一个就在设计的缓存项目表里加一个,修改一个就在缓存项目表里修改一个,或者是删除一个就在缓存项目表里删除一个? 谢谢!

 回复 引用 查看   
#43楼[楼主] 2009-07-07 09:05 | 代震军      
@skyaspnet
是的,从创建更新再到移除都是以关键字来标识的

 回复 引用   
#44楼 2009-07-07 15:26 | mclkyo[未注册用户]
我请想问你用的memcached是什么版本的,为什么我安装的版本执行stats detail 和stats maps这几个命令返回的结果都是ERROR呢?
 回复 引用 查看   
#45楼[楼主] 2009-07-07 15:57 | 代震军      
@mclkyo
应该是1.2.5吧

 回复 引用   
#46楼 2009-07-07 16:13 | mclkyo[未注册用户]
我的是1.2.1,难道版式本会有问题?
就是两个stats detail dump和stats maps这两个命令不能,像stats items跟stats cachedump这些都可以啊
还有我这里是注册为服务来运行的,不知道有没有关系啊?
能不能发一份1.2.5给我呢?谢谢

 回复 引用 查看   
#47楼[楼主] 2009-07-07 17:33 | 代震军      
 回复 引用 查看   
#48楼 2009-07-08 12:05 | skyaspnet      
您好,想请教一个问题,关于.NET序列化的疑惑,在网上看到很多资料都是介绍序列化的方法,而不是讲解它在项目中的实际运用,只是笼统地讲为了传输方便,DNT应该也使用了序列化吧? 能麻烦您简单介绍下它在实际中的应用吗?或者能介绍点这方面的资料,给个链接吗?非常感谢!:)
 回复 引用 查看   
#49楼[楼主] 2009-07-08 12:19 | 代震军      
@skyaspnet
你好,关于序列化的问题。我之前写过一篇文章介绍了在我们这个产品中是如何进行序列化设计的:
http://www.cnblogs.com/daizhj/archive/2007/12/10/989747.html

并在文章的结尾处引出了两个关于序列化讨论的链接,详见:
http://www.cnblogs.com/lixiong/archive/2007/10/26/938430.html
http://support.microsoft.com/kb/886385/en-us

 回复 引用 查看   
#50楼 2009-09-18 09:57 | Bazishan      
使用memcached策略时,config/cache.config文件就没有用了吧?不在构建动态XML文档来将节点的路径作为键值了吧?
@Bazishan
是的

 回复 引用 查看   
#52楼 2009-10-26 17:00 | delphi2007      
为什么我在使用stats cachedump 18 0的时候,只显示第一行的服务器IP:端口呢?经过调试,命令也没有错,但是返回的Hashtable里就是空的,这是为什么?不知道你的是不是也这样
 回复 引用 查看   
#53楼 2010-01-03 20:55 | skyaspnet      
您好,请问dnt的memcached开启是在哪个页面进行设置呢?找了半天没有发现,期待您的解答,谢谢!
 回复 引用 查看   
#54楼 2010-02-10 12:28 | 青羽      
我下载了dnt3.1.0。
memcachedStrategy是放在Discuz.EntLib里的?
我没找到Discuz.EntLib.dll。

 回复 引用 查看   
#55楼[楼主] 2010-02-10 13:02 | 代震军      
@青羽
Discuz.EntLib.dll这个dll,目前暂不对外公布,不过你可以下载3.1之前的版本,本文中的内容主要是那时候写的,呵呵。

 回复 引用 查看   
#56楼 2010-02-10 13:14 | 青羽      
@代震军
请教几个关于memcache的问题,已给您发站内信了。

 回复 引用 查看   
#57楼 2010-02-22 16:03 | cnbloger      
Discuz.Cache/MemCached 下面的类 楼主是从哪里下载的啊?
这些类都是必要的吗?