Wcf通讯基础框架方案(五)——更新通知

本文涉及的产品
云数据库 Tair(兼容Redis),内存型 2GB
Redis 开源版,标准版 2GB
推荐场景:
搭建游戏排行榜
简介: 对于负载均衡环境,多服务器内存中缓存数据的话,需要解决的一个很重要的问题就是一旦数据库中数据有更新,怎么让缓存的数据立即更新? 如果可以容忍延迟或是差异性的话,可以考虑缓存的数据有一个过期时间。但是,最好的方式还是采用通知方式,或者说发布订阅方式。

对于负载均衡环境,多服务器内存中缓存数据的话,需要解决的一个很重要的问题就是一旦数据库中数据有更新,怎么让缓存的数据立即更新?

如果可以容忍延迟或是差异性的话,可以考虑缓存的数据有一个过期时间。但是,最好的方式还是采用通知方式,或者说发布订阅方式。

所有的客户端会订阅客户端配置修改的消息,所有的服务端会订阅服务端配置修改的消息,配置后台在修改后复杂发布这个消息。

在这里采用redis作为发布订阅的服务端,利用TCP双工特性与所有客户端和服务端保持长连接,进行消息的推送。

        string contract = "";
            using (WcfConfigDataContext data = new WcfConfigDataContext())
            {
                var s = data.ServerFarms.First();
                s.ServerFarmAddress = TextBox1.Text;
                data.SubmitChanges();
                contract = data.ServiceEndpoints.First().ServiceContractType;
            }

            using (WcfConfigDataContext data = new WcfConfigDataContext())
            {
                TextBox1.Text = data.ServerFarms.First().ServerFarmAddress;
            }
            using (var redisClient = cm.GetClient())
            {
                redisClient.PublishMessage("WcfConfigClientChange", contract);
            }

假设后台更新了服务集群的地址,势必要通知客户端来重新更新缓存的信道工厂。可以看到,在后台修改了配置之后立即向WcfConfigClientChange通道发布一个消息,消息内容就是契约的类型名。

在客户端的WcfServiceClientFactory中:

    [MethodImpl(MethodImplOptions.Synchronized)]
        private static void CreateRedisSubThread()
        {
            StartSub();
            redisCheckTimer = new Timer(obj =>
            {
                if (((TimeSpan)(DateTime.Now - lastMsg)).Seconds > 10)
                {
#if DEBUG
                    LocalLogService.Log("recreate redis sub thread");
#endif
                    try
                    {
                        redisSubThread.Abort();
                    }
                    catch
                    {

                    }
                    finally
                    {
                        StartSub();
                    }
                }
            }, null, 0, 5000);
        }

        private static void StartSub()
        {
            redisSubThread = new Thread(() =>
            {
                try
                {
                    using (var subscription = cm.GetClient().CreateSubscription())
                    {
                        subscription.OnUnSubscribe = channel =>
                        {
#if DEBUG
                            LocalLogService.Log("OnUnSubscribe");
#endif
                        };
                        subscription.OnMessage = (channel, msg) =>
                        {
                            try
                            {
                                if (msg == "heart")
                                {
                                    lastMsg = DateTime.Now;
                                }
                                ChannelFactory cf;
                                if (channelFactoryCache.TryGetValue(msg, out cf))
                                {
                                    lock (cacheLocker)
                                    {
                                        channelFactoryCache.Remove(msg);
#if DEBUG
                                        LocalLogService.Log("Remove channel factory cache");
#endif
                                    }
                                }
                            }
                            catch (Exception ex)
                            {
                                LocalLogService.Log(ex.ToString());
                            }
                        };

                        subscription.SubscribeToChannels(REDIS_MESSAGECHANNEL);
                    }
                }
                catch (Exception ex)
                {
                    LocalLogService.Log(ex.ToString());
                }
            });

            redisSubThread.IsBackground = true;
            redisSubThread.Start();
        }

可以看到,在初始化信道工程缓存的时候会调用CreateRedisSubThread方法,完成两个工作:

1) 启动心跳检测定时器,如果过长时间都没有收到订阅心跳消息的话,重新尝试建立订阅通道。

2) 后台线程接受订阅的消息,如果契约类型在当前的信道工厂缓存中的话,删除缓存等待重新建立。

客户端的缓存更新比较简单,服务端的话可能就涉及到关闭ServiceHost重新启动等问题了,这里不给出详细实现。

除了redis之外,当然也可以使用wcf框架做一个发布订阅功能:

1) 使用双工信道

2) 使用回调特性

简单起见,这里也没有使用实现wcf的发布订阅。

作者: lovecindywang
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore     ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库 ECS 实例和一台目标数据库 RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
相关文章
|
C++
WCF框架-分布式联调
WCF框架-分布式联调
111 0
|
网络协议 网络架构 Windows
框架学习——WCF框架
框架学习——WCF框架
304 0
|
API 数据库
如何使用WCF框架和EF框架实现对数据库的操作
如何使用WCF框架和EF框架实现对数据库的操作
|
数据库 C# 数据安全/隐私保护
使用分布式框架WCF出现的BUG记录
使用分布式框架WCF出现的BUG记录
|
监控 NoSQL
Wcf通讯基础框架方案(一)——基本结构
由于希望使用Wcf作为公司内的通讯框架,因此基于Wcf进行了一些扩展,主要的目的有以下几个方面: 1) 希望减少客户端调用的复杂度,调用方式简化为WcfServiceLocator.Create().Add(1,2)。
799 0
|
XML 监控 数据格式
Wcf通讯基础框架方案(二)——集中配置
从这次开始在几个方面简单阐述一下实现,集中配置是这个框架很大的一个目的,首先在数据库中会有这么一些表: 其实可以看到这些表的结构,应该是和配置节点中的层次有对应的 1) Service表描述的是服务,主要保存服务行为以及服务的配置。
598 0
|
前端开发
WCF更新服务引用报错的原因之一
WCF更新服务引用报错的原因之一
|
C# 数据安全/隐私保护
c#如何创建WCF服务到发布(SqlServer版已经验证)
c#如何创建WCF服务到发布(SqlServer版已经验证)
80 0
|
安全 数据库连接 数据库
WCF服务创建到发布(SqlServer版)
在本示例开始之前,让我们先来了解一下什么是wcf? wcf有哪些特点? wcf是一个面向服务编程的综合分层架构。该架构的项层为服务模型层。 使用户用最少的时间和精力建立自己的软件产品和外界通信的模型。它使得开发者能够建立一个跨平台的安全、可信赖、事务性的解决方案。且能与已有系统兼容写作。 简单概括就是:一组数据通信的应用程序开发接口。
122 0