您现在的位置是:首页 > cms教程 > Discuz教程Discuz教程
Discuz实现NT跨站缓存同步的方法
从蓉2025-06-24Discuz教程已有人查阅
导读在之前的文章中,提到了在DiscuzNT中进行缓存分层的概念。之前在产品中也实现了其中的构想,但该方案有一个问题,就是如果将产品进行分布式布署之后,如果某一站点发生数据变化时
在之前的文章中,提到了在DiscuzNT中进行缓存分层的概念。之前在产品中也实现了其中的构想,但该方案有一个问题,就是如果将产品进行分布式布署之后,如果某一站点发生数据变化时,只能更新本地缓存和Memcached缓存信息,而其它分布式布署的站点则无法收到缓存数据已修改的‘通知’,导致数据不同步而成为‘脏数据’。
虽然在之前的文章中提到通过将本地缓存失效时间‘缩短’(比如15秒后即失效),以便在相对较短的时间内让本地数据失效从而再次从Memcached读取较新的数据,但这必定不符合我们设计的基本思路,并且导致程序的运行效率低,同时会造成过于频繁的访问Memcached,无形中增加了与Memcached的socket开销。所以才有了今天的这篇文章。
首先要说明的是,这个方案只有DiscuzNT的企业版(EntLib)中提供,所以在普通的版本中是找不到它的影子的,下面我就简要说明一下其实现思路。
因为在传统的WEB应用开发中,一般都是采用get的方式来获得所需要的数据,也就是通过从客户端向服务端发送get请求来获得数据。而如果要实现下面的流程:
当本地缓存数据变化-->更新memcached-->(通知notify)其它分布式应用
这里我借助主动发送的模式来实现,为了便于理解,我这里将memcached变成服务端,将分布式布署的应用看成是一个个‘客户端’,而当‘客户端’将数据更新到memcached时,通过发送http通知的方式来通知其它‘客户端’,所以我们要实现的代码包括两部分,一部分实现上面流程中的‘将本地数据变化告之到memcached’。这块代码已在之前的文章中被实现了,而我们只要在相应的‘RemoveObject’方法后跟上一行‘通知其它分布式应用’即可(代码位于Discuz.EntLib\Memcached\MemCachedStrategy.cs),如下面所示:
代码
1.首先我们需要一下记录着分布式布署应用的网站列表,它主要是一链接串,比如下面这个格式(用逗号分割):
2.为了安全起见,在发送通知的请求时,需要对请求进行加密,以免该功能被其它恶意代码利用,从而造成系统安全性和效率受到影响,所以我这里提供了认证码,即:
了解这些内容之后,我们看一下相应的实现代码以验证一下所说的设计思想(Discuz.EntLib\SyncLocalCache\SyncCache.cs):
对一个分布式应用发送三次请求,如果其中某一次返回结果为ok时,则不再向其发送其余请求了。如果上一次请求不成功,则当前线程暂停五秒后再次发送请求,直到三次请求用完为止。这样主要是考虑到远程应用上的主机可能某一时刻处于忙碌状态而无法响应,所以采用发送三次(每次间隔五秒)的方式。
下面就是它的主要实现代码:
虽然在之前的文章中提到通过将本地缓存失效时间‘缩短’(比如15秒后即失效),以便在相对较短的时间内让本地数据失效从而再次从Memcached读取较新的数据,但这必定不符合我们设计的基本思路,并且导致程序的运行效率低,同时会造成过于频繁的访问Memcached,无形中增加了与Memcached的socket开销。所以才有了今天的这篇文章。
首先要说明的是,这个方案只有DiscuzNT的企业版(EntLib)中提供,所以在普通的版本中是找不到它的影子的,下面我就简要说明一下其实现思路。
因为在传统的WEB应用开发中,一般都是采用get的方式来获得所需要的数据,也就是通过从客户端向服务端发送get请求来获得数据。而如果要实现下面的流程:
当本地缓存数据变化-->更新memcached-->(通知notify)其它分布式应用
这里我借助主动发送的模式来实现,为了便于理解,我这里将memcached变成服务端,将分布式布署的应用看成是一个个‘客户端’,而当‘客户端’将数据更新到memcached时,通过发送http通知的方式来通知其它‘客户端’,所以我们要实现的代码包括两部分,一部分实现上面流程中的‘将本地数据变化告之到memcached’。这块代码已在之前的文章中被实现了,而我们只要在相应的‘RemoveObject’方法后跟上一行‘通知其它分布式应用’即可(代码位于Discuz.EntLib\Memcached\MemCachedStrategy.cs),如下面所示:
代码
/// <summary>
/// 移除指定ID的对象
/// </summary>
/// <param name="objId"></param>
public override void RemoveObject(string objId)
{
//先移除本地cached,然后再移除memcached中的相应数据
if (base.RetrieveObject(objId) != null)
base.RemoveObject(objId);
if (MemCachedManager.CacheClient.KeyExists(objId))
MemCachedManager.CacheClient.Delete(objId);
Discuz.EntLib.SyncCache.SyncRemoteCache(objId);//通知其它分布式应用
}
下面就是‘同步其它分布式应用缓存数据’的代码了。在介绍代码之前,先要将‘发送缓存数据修改通知’的设计思想介绍一下:1.首先我们需要一下记录着分布式布署应用的网站列表,它主要是一链接串,比如下面这个格式(用逗号分割):
<SiteUrl>http://10.0.2.137:8088/tools/,http://10.0.2.150:8088/tools/,http://10.0.2.136:8088/tools/</SiteUrl>
我们需要将上面的链接串分割之后加上相应的更新缓存工具页面(稍后介绍)来实现移除(相当时同步)的功能。2.为了安全起见,在发送通知的请求时,需要对请求进行加密,以免该功能被其它恶意代码利用,从而造成系统安全性和效率受到影响,所以我这里提供了认证码,即:
<AuthCode>123123</AuthCode>
这样,认证码加密的请求只有在被同步工具正确解析后,才会更新相应的缓存数据。了解这些内容之后,我们看一下相应的实现代码以验证一下所说的设计思想(Discuz.EntLib\SyncLocalCache\SyncCache.cs):
/// <summary>
/// 同步缓存类
/// </summary>
public class SyncCache
{
/// <summary>
/// 除本站之外的负载均衡站点列表
/// </summary>
static List<string> syncCacheUrlList = null;
static LoadBalanceConfigInfo loadBalanceConfigInfo = LoadBalanceConfigs.GetConfig();
static SyncCache()
{
syncCacheUrlList = new List<string>();
syncCacheUrlList.AddRange(loadBalanceConfigInfo.SiteUrl.
Replace("tools/", "tools/SyncLocalCache.ashx").Split(','));
int port = HttpContext.Current.Request.Url.Port;
string localUrl = string.Format("{0}://{1}{2}{3}",
HttpContext.Current.Request.Url.Scheme,
HttpContext.Current.Request.Url.Host,
(port == 80 || port == 0) ? "" : ":" + port,
BaseConfigs.GetForumPath);
Predicate<string> matchUrl = new Predicate<string>
(
delegate(string webUrl)
{
return webUrl.IndexOf(localUrl) >= 0; //移除本地站点链接,因为当前站点缓存已被移除。
}
);
syncCacheUrlList.RemoveAll(matchUrl);
}
首先我们在静态构造方法中读取相应url链接列表(loadBalanceConfigInfo配置文件),然后将其中的本地应用链接去掉,这样就不会造成反复更新本地缓存数据(从而造成死循环)的问题了。接着就是使用一个线程来发送相应的同步数据请求到各个分布式应用上,如下(包括使用认证码加密链接信息):
/// <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();
}
}
这里我们使用线程方式来更新相应的分布式应用,思路是:对一个分布式应用发送三次请求,如果其中某一次返回结果为ok时,则不再向其发送其余请求了。如果上一次请求不成功,则当前线程暂停五秒后再次发送请求,直到三次请求用完为止。这样主要是考虑到远程应用上的主机可能某一时刻处于忙碌状态而无法响应,所以采用发送三次(每次间隔五秒)的方式。
下面就是它的主要实现代码:
/// <summary>
/// 多线程更新远程缓存
/// </summary>
public class ThreadSyncRemoteCache
{
public string _url;
public ThreadSyncRemoteCache(string url)
{
_url = url;
}
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;
}
//更新本地缓存(注:此处不可使用MemCachedStrategy的RemoveObject方法,因为该方法中有SyncRemoteCache的调用,会造成循环调用)
Discuz.Cache.DNTCache cache = Discuz.Cache.DNTCache.GetCacheService();
cache.LoadCacheStrategy(new DefaultCacheStrategy());
cache.RemoveObject(cacheKey);
cache.LoadDefaultCacheStrategy();
context.Response.Write("OK");
}
public bool IsReusable
{
get
{
return false;
}
}
}
上面代码首先会获取请求过来的缓存键值和passKey(即认证码加密后的链接),然后在本地进行数据有效性校验,如果认证通过的话,就可以对其要移除的缓存数据进行操作了,并在操作成功之后返回ok信息。该页面采用synclocalcache.ashx文件进行声明,如下:
<%@ WebHandler Language="C#" Class="Discuz.EntLib.SyncLocalCache" %>
到这里,只要将该ashx文件放到站点的tools/文件夹下,就可以实现跨站同步缓存数据的功能了。目前考虑的场景还是比较单一的,所以实现的代码也相对简单,不排除随着业务逻辑复杂度不断提升而做重新设计的可能性。
本文标签:
很赞哦! ()
相关教程
图文教程
discuz插件制作方法步骤流程
插件制作的基本思路是:(初学者适用)1.形成插件思路2.制作插件界面3.构架程序模块4.搭建存储数据5.填充功能语句6.检查应用错误7.完善插件功能
centos7安装部署Discuz论坛的方法
安装Discuz,目前是201912的较新版环境:centos7(已部署lnmp环境)说一下,为啥写成脚本,一方面,镜像不能做太多,整天瞎搞环境容易坏,重新部署的时候比较方便,另一方面,锻炼自己写shell脚
jsp应用系统整合discuz论坛用户系统
关键代码,整合函数,调用方法:login.jsp,logout.jsp,
Discuz模板解析语法示例
在Discuz!程序执行中可以通过 include template('模板文件夹/模板名称无后缀');的方式进行解析,前提是您使用的Discuz!程序已经包含了 ./source/function/function_core.php 的函数库
相关源码
-
(自适应响应式)蓝色律师事务所法务团队网站pbootcms模板为律师事务所和法律服务机构打造的专业网站模板,展现法律专业性与权威性,手工编写标准DIV+CSS代码,结构清晰优化,确保高效运行,自动适配电脑、平板和手机等各类设备,提供更好浏览体验查看源码 -
(响应式)蓝色智能摄像头安防防盗电子设备免费pbootcms源码下载这是一款针对智能安防行业特点设计的网站模板,采用蓝色系配色方案,体现科技感和安全性。模板包含产品展示、解决方案、技术支持和新闻中心等核心模块,能够全面展示智能安防设备的技术特点和行业应用。查看源码 -
(自适应手机端)英文外贸电子产品通用pbootcms模板源码下载为电子产品外贸企业设计的响应式网站模板,采用PbootCMS开发内核,支持多语言展示。模板默认集成产品展示系统、询价表单模块和企业资质展示区,满足跨境贸易基础需求。整站采用模块化设计,便于扩展业务场景。查看源码 -
(PC+WAP)蓝色公司注册财务会计公证律师网站源码下载本模板基于PbootCMS内核开发,为财务会计事务所、律师公证机构等专业服务机构打造。采用自适应设计,确保在各类设备上都能呈现专业视觉效果,帮助机构建立值得信赖的线上形象。查看源码 -
(自适应)蓝色沙盘复古建筑模型制作网站模板源码下载为建筑沙盘模型企业设计的响应式网站模板,通过三维空间展示技术结合产品参数可视化,有效提升模型作品的线上呈现效果与客户咨询转化率。查看源码 -
(自适应html5)自媒体运营培训教程个人博客pbootcms模板本模板基于PbootCMS系统开发,特别适合自媒体运营培训、知识付费类网站使用。采用响应式设计,能够适配各类终端设备,为内容创作者提供专业的内容展示平台。查看源码
| 分享笔记 (共有 篇笔记) |
