Discuz!NT中的Redis架构设计
2011-02-21 11:01
417 查看
在之前的Discuz!NT缓存的架构方案中,曾说过Discuz!NT采用了两级缓存方式,即本地缓存+memcached
方式。在近半年多的实际
运行环境下,该方案经受住了检验。现在为了提供多样式的解决方案,我在企业版里引入了Redis这个目前炙手可热的缓存架构产品,即将memcached
与Redis作为可选插件方式来提供了最终用户,尽管目前测试的结果两者的差异不是很大(毫秒级),但我想多一种选择对用户来说也是好的。
闲话不多说了,开始今天的正文吧。
熟悉我们产品的开发者都知道,我们的产品在缓存设计上使用了策略模式(Stratety
Pattern),之前在系统中就已实现了DefaultCacheStrategy和MemCachedStrategy,前者用于本地缓存方式,后者
顾名思义,就是memcached缓存方式。所以只要我们实现一个RedisStrategy策略,并适当修改加载缓存策略的代码,就可以将
memcached缓存方式替换成Redis,如下图:
![](http://images.cnblogs.com/cnblogs_com/daizhj/Discuz-redis.png)
下面我先将RedisStrategy的部分代码放上来,大家一看便知:
![](http://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![](http://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
///
<summary>
///
企业级Redis缓存策略
类
///
</summary>
public
class
RedisStrategy : DefaultCacheStrategy
{
///
<summary>
///
添加指
定ID的对象
///
</summary>
///
<param name="objId"></param>
///
<param name="o"></param>
public
override
void
AddObject(
string
objId,
object
o)
{
if
(
!
objId.StartsWith(
"
/Forum/ShowTopic/
"
))
base
.AddObject(objId, o, LocalCacheTime);
using
(IRedisClient Redis
=
RedisManager.GetClient())
{
Redis.Set
<
byte
[]
>
(objId,
new
ObjectSerializer().Serialize(o));
}
}
///
<summary>
///
加入当
前对象到缓存中
///
</summary>
///
<param name="objId">
对象的键值
</param>
///
<param name="o">
缓存的对象
</param>
///
<param name="o">
到期时间,单位:秒
</param>
public
override
void
AddObject(
string
objId,
object
o,
int
expire)
{
//
凡是以"/Forum/ShowTopic/"为前缀不添加到本地缓存中,现类似键值包括: "/Forum/ShowTopic/Tag
/{topicid}/" , "/Forum/ShowTopic/TopList/{fid}"
if
(
!
objId.StartsWith(
"
/Forum/ShowTopic/
"
))
base
.AddObject(objId, o, expire);
using
(IRedisClient Redis
=
RedisManager.GetClient())
{
//
永不过期
if
(expire
==
0
)
Redis.Set
<
byte
[]
>
(objId,
new
ObjectSerializer().Serialize(o));
else
Redis.Set
<
byte
[]
>
(objId,
new
ObjectSerializer().Serialize(o), DateTime.Now.AddSeconds(expire));
}
}
///
<summary>
///
移除指
定ID的对象
///
</summary>
///
<param name="objId"></param>
public
override
void
RemoveObject(
string
objId)
{
//
先移除本地cached,然后再移除memcached中的相应数据
base
.RemoveObject(objId);
using
(IRedisClient Redis
=
RedisManager.GetClient())
{
Redis.Remove(objId);
}
Discuz.EntLib.SyncCache.SyncRemoteCache(objId);
}
public
override
object
RetrieveObject(
string
objId)
{
object
obj
=
base
.RetrieveObject(objId);
if
(obj
==
null
)
{
using
(IRedisClient Redis
=
RedisManager.GetClient())
{
obj
=
new
ObjectSerializer().Deserialize(Redis.Get
<
byte
[]
>
(objId));
if
(obj
!=
null
&&
!
objId.StartsWith(
"
/Forum/ShowTopic/
"
))
//
对ShowTopic页面缓存数据不放到本地缓存
{
if
(objId.StartsWith(
"
/Forum/ShowTopicGuestCachePage/
"
))
//
对游客缓存页面ShowTopic数据缓存设置有效时间
base
.TimeOut
=
GeneralConfigs.GetConfig().Guestcachepagetimeout
*
60
;
if
(objId.StartsWith(
"
/Forum/ShowForumGuestCachePage/
"
))
//
对游客缓存页面ShowTopic数据缓存设置有效时间
base
.TimeOut
=
RedisConfigs.GetConfig().CacheShowForumCacheTime
*
60
;
else
base
.TimeOut
=
LocalCacheTime;
base
.AddObject(objId, obj, TimeOut);
}
}
}
return
obj;
}
///
<summary>
///
到期时
间,单位:秒
///
</summary>
public
override
int
TimeOut
{
get
{
return
3600
;
}
}
///
<summary>
///
本地缓
存到期时间,单位:秒
///
</summary>
public
int
LocalCacheTime
{
get
{
return
RedisConfigs.GetConfig().LocalCacheTime;
}
}
///
<summary>
///
清空的
有缓存数据
///
</summary>
public
override
void
FlushAll()
{
base
.FlushAll();
using
(IRedisClient Redis
=
RedisManager.GetClient())
{
Redis.FlushAll();
}
}
}
可以看出RedisStrategy类继承自DefaultCacheStrategy,这一点与MemCachedStrategy实现如出一辙,唯一
不同是其缓存数据加载和设置的方式有所不同,而具体的用法可以参见我之前写的这篇文章
中的“object序列化方式存储
” 。
当然上面代码中有两个类未说明,一个是RedisConfigs,一个是RedisManager,前者是配置文件信息类,我们产品因为使用了统一的序列
化接口实现方式(参见该文
),所以其实现方式比较清晰,其序列化类的结构如
下:
![](http://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![](http://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
///
<summary>
///
Redis配置信息类文件
///
</summary>
public
class
RedisConfigInfo : IConfigInfo
{
private
bool
_applyRedis;
///
<summary>
///
是否应
用Redis
///
</summary>
public
bool
ApplyRedis
{
get
{
return
_applyRedis;
}
set
{
_applyRedis
=
value;
}
}
private
string
_writeServerList;
///
<summary>
///
可写的
Redis链接地址
///
</summary>
public
string
WriteServerList
{
get
{
return
_writeServerList;
}
set
{
_writeServerList
=
value;
}
}
private
string
_readServerList;
///
<summary>
///
可读的
Redis链接地址
///
</summary>
public
string
ReadServerList
{
get
{
return
_readServerList;
}
set
{
_readServerList
=
value;
}
}
private
int
_maxWritePoolSize;
///
<summary>
///
最大写
链接数
///
</summary>
public
int
MaxWritePoolSize
{
get
{
return
_maxWritePoolSize
>
0
?
_maxWritePoolSize :
5
;
}
set
{
_maxWritePoolSize
=
value;
}
}
private
int
_maxReadPoolSize;
///
<summary>
///
最大读
链接数
///
</summary>
public
int
MaxReadPoolSize
{
get
{
return
_maxReadPoolSize
>
0
?
_maxReadPoolSize :
5
;
}
set
{
_maxReadPoolSize
=
value;
}
}
private
bool
_autoStart;
///
<summary>
///
自动重
启
///
</summary>
public
bool
AutoStart
{
get
{
return
_autoStart;
}
set
{
_autoStart
=
value;
}
}
private
int
_localCacheTime
=
30000
;
///
<summary>
///
本地缓存到期时间,该设置会与memcached搭配使用,单位:秒
///
</summary>
public
int
LocalCacheTime
{
get
{
return
_localCacheTime;
}
set
{
_localCacheTime
=
value;
}
}
private
bool
_recordeLog
=
false
;
///
<summary>
///
是否记录日志,该设置仅用于排查redis运行时出现的问题,如redis工作正常,请关闭该项
///
</summary>
public
bool
RecordeLog
{
get
{
return
_recordeLog;
}
set
{
_recordeLog
=
value;
}
}
private
int
_cacheShowTopicPageNumber
=
5
;
///
<summary>
///
缓存帖子列表分页数(showtopic页数使用缓存前N页的帖子列表信息)
///
</summary>
public
int
CacheShowTopicPageNumber
{
get
{
return
_cacheShowTopicPageNumber;
}
set
{
_cacheShowTopicPageNumber
=
value;
}
}
///
<summary>
///
缓存
showforum页面分页数
///
</summary>
public
int
CacheShowForumPageNumber{
set
;
get
;}
///
<summary>
///
缓存
showforum页面时间(单位:分钟)
///
</summary>
public
int
CacheShowForumCacheTime{
set
;
get
;}
}
其序列化出来的xml文件格式形如:
![](http://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![](http://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
<?
xml version
=
"
1.0
"
?>
<
RedisConfigInfo xmlns:xsi
=
" http://www.w3.org/2001/XMLSchema-instance "
>
<
ApplyRedis
>
true
</
ApplyRedis
>
<
WriteServerList
>
10.0
.
4.210
:
6379
</
WriteServerList
>
<
ReadServerList
>
10.0
.
4.210
:
6379
</
ReadServerList
>
<
MaxWritePoolSize
>
60
</
MaxWritePoolSize
>
<
MaxReadPoolSize
>
60
</
MaxReadPoolSize
>
<
AutoStart
>
true
</
AutoStart
>
<
LocalCacheTime
>
180
</
LocalCacheTime
>
<!--
单位:秒
-->
<
RecordeLog
>
false
</
RecordeLog
>
<!--
缓存帖子列表分页数
(showtopic页数使用缓存前N页的帖子列表信息)
-->
<
CacheShowTopicPageNumber
>
2
</
CacheShowTopicPageNumber
>
<!--
缓存showforum页面分页数
-->
<
CacheShowForumPageNumber
>
2
</
CacheShowForumPageNumber
>
<!--
缓存showforum页面时间(单位:分钟)
-->
<
CacheShowForumCacheTime
>
10
</
CacheShowForumCacheTime
>
</
RedisConfigInfo
>
之前所说的RedisManager类则是一个RedisClient客户端实现的简单封装,主要为了简化基于链接池的Redis链接方式的使用。其结构
如下:
![](http://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![](http://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
using
System.Collections;
using
Discuz.Config;
using
Discuz.Common;
using
ServiceStack.Redis;
using
ServiceStack.Redis.Generic;
using
ServiceStack.Redis.Support;
namespace
Discuz.EntLib
{
///
<summary>
///
MemCache
管理操作类
///
</summary>
public
sealed
class
RedisManager
{
///
<summary>
///
redis
配置文件信息
///
</summary>
private
static
RedisConfigInfo redisConfigInfo
=
RedisConfigs.GetConfig();
private
static
PooledRedisClientManager prcm;
///
<summary>
///
静态构
造方法,初始化链接池管理对象
///
</summary>
static
RedisManager()
{
CreateManager();
}
///
<summary>
///
创建链
接池管理对象
///
</summary>
private
static
void
CreateManager()
{
string
[] writeServerList
=
Utils.SplitString(redisConfigInfo.WriteServerList,
"
,
"
);
string
[] readServerList
=
Utils.SplitString(redisConfigInfo.ReadServerList,
"
,
"
);
prcm
=
new
PooledRedisClientManager(readServerList, writeServerList,
new
RedisClientManagerConfig
{
MaxWritePoolSize
=
redisConfigInfo.MaxWritePoolSize,
MaxReadPoolSize
=
redisConfigInfo.MaxReadPoolSize,
AutoStart
=
redisConfigInfo.AutoStart,
});
}
///
<summary>
///
客户端
缓存操作对象
///
</summary>
public
static
IRedisClient GetClient()
{
if
(prcm
==
null
)
CreateManager();
return
prcm.GetClient();
}
}
}
上面的代码主要将redis.config的配置文件文件信息加载到程序里并实始化PooledRedisClientManager对象,该对象用于池
化redis的客户端链接,具体方式参见这篇文章
好了,到这里主要的内容就介绍完了。
注:本文的部分代码位于企业版产品中,目前暂未开源所以大家可能
没有拿到,我们计划今年开源企业版1.0的代码,包括本文中代码部分,以便从社区中获取更多经验和反馈,同时希望大家支持和关注我们的产品。
原文链接:http://www.cnblogs.com/daizhj/archive/2011/02/21/1959511.html
作者: daizhj, 代震军
Tags: discuz!nt,redis
方式。在近半年多的实际
运行环境下,该方案经受住了检验。现在为了提供多样式的解决方案,我在企业版里引入了Redis这个目前炙手可热的缓存架构产品,即将memcached
与Redis作为可选插件方式来提供了最终用户,尽管目前测试的结果两者的差异不是很大(毫秒级),但我想多一种选择对用户来说也是好的。
闲话不多说了,开始今天的正文吧。
熟悉我们产品的开发者都知道,我们的产品在缓存设计上使用了策略模式(Stratety
Pattern),之前在系统中就已实现了DefaultCacheStrategy和MemCachedStrategy,前者用于本地缓存方式,后者
顾名思义,就是memcached缓存方式。所以只要我们实现一个RedisStrategy策略,并适当修改加载缓存策略的代码,就可以将
memcached缓存方式替换成Redis,如下图:
![](http://images.cnblogs.com/cnblogs_com/daizhj/Discuz-redis.png)
下面我先将RedisStrategy的部分代码放上来,大家一看便知:
![](http://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![](http://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
///
<summary>
///
企业级Redis缓存策略
类
///
</summary>
public
class
RedisStrategy : DefaultCacheStrategy
{
///
<summary>
///
添加指
定ID的对象
///
</summary>
///
<param name="objId"></param>
///
<param name="o"></param>
public
override
void
AddObject(
string
objId,
object
o)
{
if
(
!
objId.StartsWith(
"
/Forum/ShowTopic/
"
))
base
.AddObject(objId, o, LocalCacheTime);
using
(IRedisClient Redis
=
RedisManager.GetClient())
{
Redis.Set
<
byte
[]
>
(objId,
new
ObjectSerializer().Serialize(o));
}
}
///
<summary>
///
加入当
前对象到缓存中
///
</summary>
///
<param name="objId">
对象的键值
</param>
///
<param name="o">
缓存的对象
</param>
///
<param name="o">
到期时间,单位:秒
</param>
public
override
void
AddObject(
string
objId,
object
o,
int
expire)
{
//
凡是以"/Forum/ShowTopic/"为前缀不添加到本地缓存中,现类似键值包括: "/Forum/ShowTopic/Tag
/{topicid}/" , "/Forum/ShowTopic/TopList/{fid}"
if
(
!
objId.StartsWith(
"
/Forum/ShowTopic/
"
))
base
.AddObject(objId, o, expire);
using
(IRedisClient Redis
=
RedisManager.GetClient())
{
//
永不过期
if
(expire
==
0
)
Redis.Set
<
byte
[]
>
(objId,
new
ObjectSerializer().Serialize(o));
else
Redis.Set
<
byte
[]
>
(objId,
new
ObjectSerializer().Serialize(o), DateTime.Now.AddSeconds(expire));
}
}
///
<summary>
///
移除指
定ID的对象
///
</summary>
///
<param name="objId"></param>
public
override
void
RemoveObject(
string
objId)
{
//
先移除本地cached,然后再移除memcached中的相应数据
base
.RemoveObject(objId);
using
(IRedisClient Redis
=
RedisManager.GetClient())
{
Redis.Remove(objId);
}
Discuz.EntLib.SyncCache.SyncRemoteCache(objId);
}
public
override
object
RetrieveObject(
string
objId)
{
object
obj
=
base
.RetrieveObject(objId);
if
(obj
==
null
)
{
using
(IRedisClient Redis
=
RedisManager.GetClient())
{
obj
=
new
ObjectSerializer().Deserialize(Redis.Get
<
byte
[]
>
(objId));
if
(obj
!=
null
&&
!
objId.StartsWith(
"
/Forum/ShowTopic/
"
))
//
对ShowTopic页面缓存数据不放到本地缓存
{
if
(objId.StartsWith(
"
/Forum/ShowTopicGuestCachePage/
"
))
//
对游客缓存页面ShowTopic数据缓存设置有效时间
base
.TimeOut
=
GeneralConfigs.GetConfig().Guestcachepagetimeout
*
60
;
if
(objId.StartsWith(
"
/Forum/ShowForumGuestCachePage/
"
))
//
对游客缓存页面ShowTopic数据缓存设置有效时间
base
.TimeOut
=
RedisConfigs.GetConfig().CacheShowForumCacheTime
*
60
;
else
base
.TimeOut
=
LocalCacheTime;
base
.AddObject(objId, obj, TimeOut);
}
}
}
return
obj;
}
///
<summary>
///
到期时
间,单位:秒
///
</summary>
public
override
int
TimeOut
{
get
{
return
3600
;
}
}
///
<summary>
///
本地缓
存到期时间,单位:秒
///
</summary>
public
int
LocalCacheTime
{
get
{
return
RedisConfigs.GetConfig().LocalCacheTime;
}
}
///
<summary>
///
清空的
有缓存数据
///
</summary>
public
override
void
FlushAll()
{
base
.FlushAll();
using
(IRedisClient Redis
=
RedisManager.GetClient())
{
Redis.FlushAll();
}
}
}
可以看出RedisStrategy类继承自DefaultCacheStrategy,这一点与MemCachedStrategy实现如出一辙,唯一
不同是其缓存数据加载和设置的方式有所不同,而具体的用法可以参见我之前写的这篇文章
中的“object序列化方式存储
” 。
当然上面代码中有两个类未说明,一个是RedisConfigs,一个是RedisManager,前者是配置文件信息类,我们产品因为使用了统一的序列
化接口实现方式(参见该文
),所以其实现方式比较清晰,其序列化类的结构如
下:
![](http://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![](http://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
///
<summary>
///
Redis配置信息类文件
///
</summary>
public
class
RedisConfigInfo : IConfigInfo
{
private
bool
_applyRedis;
///
<summary>
///
是否应
用Redis
///
</summary>
public
bool
ApplyRedis
{
get
{
return
_applyRedis;
}
set
{
_applyRedis
=
value;
}
}
private
string
_writeServerList;
///
<summary>
///
可写的
Redis链接地址
///
</summary>
public
string
WriteServerList
{
get
{
return
_writeServerList;
}
set
{
_writeServerList
=
value;
}
}
private
string
_readServerList;
///
<summary>
///
可读的
Redis链接地址
///
</summary>
public
string
ReadServerList
{
get
{
return
_readServerList;
}
set
{
_readServerList
=
value;
}
}
private
int
_maxWritePoolSize;
///
<summary>
///
最大写
链接数
///
</summary>
public
int
MaxWritePoolSize
{
get
{
return
_maxWritePoolSize
>
0
?
_maxWritePoolSize :
5
;
}
set
{
_maxWritePoolSize
=
value;
}
}
private
int
_maxReadPoolSize;
///
<summary>
///
最大读
链接数
///
</summary>
public
int
MaxReadPoolSize
{
get
{
return
_maxReadPoolSize
>
0
?
_maxReadPoolSize :
5
;
}
set
{
_maxReadPoolSize
=
value;
}
}
private
bool
_autoStart;
///
<summary>
///
自动重
启
///
</summary>
public
bool
AutoStart
{
get
{
return
_autoStart;
}
set
{
_autoStart
=
value;
}
}
private
int
_localCacheTime
=
30000
;
///
<summary>
///
本地缓存到期时间,该设置会与memcached搭配使用,单位:秒
///
</summary>
public
int
LocalCacheTime
{
get
{
return
_localCacheTime;
}
set
{
_localCacheTime
=
value;
}
}
private
bool
_recordeLog
=
false
;
///
<summary>
///
是否记录日志,该设置仅用于排查redis运行时出现的问题,如redis工作正常,请关闭该项
///
</summary>
public
bool
RecordeLog
{
get
{
return
_recordeLog;
}
set
{
_recordeLog
=
value;
}
}
private
int
_cacheShowTopicPageNumber
=
5
;
///
<summary>
///
缓存帖子列表分页数(showtopic页数使用缓存前N页的帖子列表信息)
///
</summary>
public
int
CacheShowTopicPageNumber
{
get
{
return
_cacheShowTopicPageNumber;
}
set
{
_cacheShowTopicPageNumber
=
value;
}
}
///
<summary>
///
缓存
showforum页面分页数
///
</summary>
public
int
CacheShowForumPageNumber{
set
;
get
;}
///
<summary>
///
缓存
showforum页面时间(单位:分钟)
///
</summary>
public
int
CacheShowForumCacheTime{
set
;
get
;}
}
其序列化出来的xml文件格式形如:
![](http://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![](http://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
<?
xml version
=
"
1.0
"
?>
<
RedisConfigInfo xmlns:xsi
=
" http://www.w3.org/2001/XMLSchema-instance "
>
<
ApplyRedis
>
true
</
ApplyRedis
>
<
WriteServerList
>
10.0
.
4.210
:
6379
</
WriteServerList
>
<
ReadServerList
>
10.0
.
4.210
:
6379
</
ReadServerList
>
<
MaxWritePoolSize
>
60
</
MaxWritePoolSize
>
<
MaxReadPoolSize
>
60
</
MaxReadPoolSize
>
<
AutoStart
>
true
</
AutoStart
>
<
LocalCacheTime
>
180
</
LocalCacheTime
>
<!--
单位:秒
-->
<
RecordeLog
>
false
</
RecordeLog
>
<!--
缓存帖子列表分页数
(showtopic页数使用缓存前N页的帖子列表信息)
-->
<
CacheShowTopicPageNumber
>
2
</
CacheShowTopicPageNumber
>
<!--
缓存showforum页面分页数
-->
<
CacheShowForumPageNumber
>
2
</
CacheShowForumPageNumber
>
<!--
缓存showforum页面时间(单位:分钟)
-->
<
CacheShowForumCacheTime
>
10
</
CacheShowForumCacheTime
>
</
RedisConfigInfo
>
之前所说的RedisManager类则是一个RedisClient客户端实现的简单封装,主要为了简化基于链接池的Redis链接方式的使用。其结构
如下:
![](http://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![](http://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
using
System.Collections;
using
Discuz.Config;
using
Discuz.Common;
using
ServiceStack.Redis;
using
ServiceStack.Redis.Generic;
using
ServiceStack.Redis.Support;
namespace
Discuz.EntLib
{
///
<summary>
///
MemCache
管理操作类
///
</summary>
public
sealed
class
RedisManager
{
///
<summary>
///
redis
配置文件信息
///
</summary>
private
static
RedisConfigInfo redisConfigInfo
=
RedisConfigs.GetConfig();
private
static
PooledRedisClientManager prcm;
///
<summary>
///
静态构
造方法,初始化链接池管理对象
///
</summary>
static
RedisManager()
{
CreateManager();
}
///
<summary>
///
创建链
接池管理对象
///
</summary>
private
static
void
CreateManager()
{
string
[] writeServerList
=
Utils.SplitString(redisConfigInfo.WriteServerList,
"
,
"
);
string
[] readServerList
=
Utils.SplitString(redisConfigInfo.ReadServerList,
"
,
"
);
prcm
=
new
PooledRedisClientManager(readServerList, writeServerList,
new
RedisClientManagerConfig
{
MaxWritePoolSize
=
redisConfigInfo.MaxWritePoolSize,
MaxReadPoolSize
=
redisConfigInfo.MaxReadPoolSize,
AutoStart
=
redisConfigInfo.AutoStart,
});
}
///
<summary>
///
客户端
缓存操作对象
///
</summary>
public
static
IRedisClient GetClient()
{
if
(prcm
==
null
)
CreateManager();
return
prcm.GetClient();
}
}
}
上面的代码主要将redis.config的配置文件文件信息加载到程序里并实始化PooledRedisClientManager对象,该对象用于池
化redis的客户端链接,具体方式参见这篇文章
好了,到这里主要的内容就介绍完了。
注:本文的部分代码位于企业版产品中,目前暂未开源所以大家可能
没有拿到,我们计划今年开源企业版1.0的代码,包括本文中代码部分,以便从社区中获取更多经验和反馈,同时希望大家支持和关注我们的产品。
原文链接:http://www.cnblogs.com/daizhj/archive/2011/02/21/1959511.html
作者: daizhj, 代震军
Tags: discuz!nt,redis
相关文章推荐
- Discuz!NT中的Redis架构设计
- Discuz!NT中的Redis架构设计
- Discuz!NT中的Redis架构设计
- Discuz!NT中的Redis架构设计
- Discuz!NT中的Redis架构设计
- Discuz!NT 中的LLServer架构设计
- 高性能网站架构设计之缓存---redis集群搭建
- 高性能网站架构设计之缓存篇(5)- Redis 集群(中)
- Codis作者黄东旭细说分布式Redis架构设计和踩过的那些坑们
- 高性能网站架构设计之缓存篇(6)- Redis 集群(中)
- Discuz!NT 系统架构分析
- 高性能网站架构设计之缓存篇(4)- Redis 主从复制
- 分析Redis架构设计
- 分析Redis架构设计
- Discuz!NT前台模型架构(MVC)
- [转]Discuz!NT缓存设计简析
- Discuz!NT 系统架构分析
- Codis作者黄东旭细说分布式Redis架构设计和踩过的那些坑们
- 架构设计:系统存储(18)——Redis集群方案:高性能
- 高性能网站架构设计之缓存篇(3)- Redis 的配置