天天看點

Wcf通訊基礎架構方案(五)——更新通知

對于負載均衡環境,多伺服器記憶體中緩存資料的話,需要解決的一個很重要的問題就是一旦資料庫中資料有更新,怎麼讓緩存的資料立即更新?

如果可以容忍延遲或是差異性的話,可以考慮緩存的資料有一個過期時間。但是,最好的方式還是采用通知方式,或者說釋出訂閱方式。

所有的用戶端會訂閱用戶端配置修改的消息,所有的服務端會訂閱服務端配置修改的消息,配置背景在修改後複雜釋出這個消息。

在這裡采用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

本文版權歸作者和部落格園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連接配接,否則保留追究法律責任的權利。