![如何解決分布式系統(tǒng)中的跨時(shí)區(qū)問題[實(shí)例篇]_第1頁](http://file3.renrendoc.com/fileroot_temp3/2022-3/2/c16b5a2f-b7ab-421b-be35-127caf2706a7/c16b5a2f-b7ab-421b-be35-127caf2706a71.gif)
![如何解決分布式系統(tǒng)中的跨時(shí)區(qū)問題[實(shí)例篇]_第2頁](http://file3.renrendoc.com/fileroot_temp3/2022-3/2/c16b5a2f-b7ab-421b-be35-127caf2706a7/c16b5a2f-b7ab-421b-be35-127caf2706a72.gif)
![如何解決分布式系統(tǒng)中的跨時(shí)區(qū)問題[實(shí)例篇]_第3頁](http://file3.renrendoc.com/fileroot_temp3/2022-3/2/c16b5a2f-b7ab-421b-be35-127caf2706a7/c16b5a2f-b7ab-421b-be35-127caf2706a73.gif)
![如何解決分布式系統(tǒng)中的跨時(shí)區(qū)問題[實(shí)例篇]_第4頁](http://file3.renrendoc.com/fileroot_temp3/2022-3/2/c16b5a2f-b7ab-421b-be35-127caf2706a7/c16b5a2f-b7ab-421b-be35-127caf2706a74.gif)
![如何解決分布式系統(tǒng)中的跨時(shí)區(qū)問題[實(shí)例篇]_第5頁](http://file3.renrendoc.com/fileroot_temp3/2022-3/2/c16b5a2f-b7ab-421b-be35-127caf2706a7/c16b5a2f-b7ab-421b-be35-127caf2706a75.gif)
版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡介
1、如何解決分布式系統(tǒng)中的跨時(shí)區(qū)問題實(shí)例篇關(guān)于如何解決分布式系統(tǒng)中的跨時(shí)區(qū)問題,上一篇詳細(xì)介紹了解決方案的實(shí)現(xiàn)原理,在這一篇中我們通過一個(gè)完整的例子來對這個(gè)問題進(jìn)行深入探討。盡管原理篇中介紹了那么多,解決方案的本質(zhì)就是:在進(jìn)行服務(wù)調(diào)用過程中將客戶端的時(shí)區(qū)信息作為上下文傳入服務(wù)端,并以此作為時(shí)間轉(zhuǎn)換的依據(jù)。我們首先定一個(gè)具體的類型來定義包含時(shí)區(qū)信息的上下文類型,我們將這個(gè)類型起名為ApplicationContext。一、通過CallContext實(shí)現(xiàn)ApplicationContext在通過WCF擴(kuò)展實(shí)現(xiàn)Context信息的傳遞一文中,我通過HttpSessionState和CallContext
2、實(shí)現(xiàn)了一個(gè)ApplicationContext類,為ASP.NET和其他類型的應(yīng)用提供上下文信息的容器。在這里進(jìn)行了簡化,僅僅實(shí)現(xiàn)了基于CallContext的部分。這樣一個(gè)ApplicationContext類型定義如下: 1: CollectionDataContract(Namespace=" 2: public class ApplicationContext:Dictionary<string, object> 3: 4: internal const string contextHeaderName = "ApplicationContext&quo
3、t; 5: internal const string contextHeaderNamespace = " 6: 7: private ApplicationContext() 8: public static ApplicationContext Current 9: 10: get 11: 12: if (null = CallContext.GetData(typeof(ApplicationContext).FullName) 13: 14: lock (typeof(ApplicationContext) 15: 16: if (null = CallContext.Ge
4、tData(typeof(ApplicationContext).FullName) 17: 18: var context = new ApplicationContext(); 19: context.TimeZone = TimeZoneInfo.Local; 20: CallContext.SetData(typeof(ApplicationContext).FullName, context); 21: 22: 23: 24: 25: return (ApplicationContext)CallContext.GetData(typeof(ApplicationContext).F
5、ullName); 26: 27: set 28: 29: CallContext.SetData(typeof(ApplicationContext).FullName, value); 30: 31: 32: public TimeZoneInfo TimeZone 33: 34: get 35: 36: return TimeZoneInfo.FromSerializedString(string)this"_TimeZone"); 37: 38: set 39: 40: this"_TimeZone" = value.ToSerializedSt
6、ring(); 41: 42: 43: 44: public static void Clear() 45: 46: CallContext.FreeNamedDataSlot(typeof(ApplicationContext).FullName); 47: 48: ApplicationContext繼承自Dictionary<string,object>類型,并被定義成集合數(shù)據(jù)契約。我們采用Singleton的方式來定義ApplicationContext,當(dāng)前上下文通過靜態(tài)方法Current獲取。而Current屬性返回的是通過CallContext的GetData方法獲取
7、,并且Key為類型的全名。便是當(dāng)前時(shí)區(qū)的TimeZone屬性的類型為TimeZoneInfo,通過序列化和反序列對當(dāng)前時(shí)區(qū)進(jìn)行設(shè)置和獲取。Clear則將整個(gè)ApplicationContext對象從CallContext中移除。二、創(chuàng)建一個(gè)用于時(shí)間轉(zhuǎn)化的DateTimeConverter服務(wù)端需要進(jìn)行兩種方式的時(shí)間轉(zhuǎn)化,其一是將可戶端傳入的時(shí)間轉(zhuǎn)換成UTC時(shí)間,其二就是將從數(shù)據(jù)庫獲取的UTC時(shí)間轉(zhuǎn)化成基于當(dāng)前時(shí)區(qū)上下文的Local時(shí)間。為此我定義了如下一個(gè)靜態(tài)的幫助類DateTimeConverter專門進(jìn)行這兩方面的時(shí)間轉(zhuǎn)換,而時(shí)間轉(zhuǎn)換依據(jù)的時(shí)區(qū)來源于當(dāng)前ApplicationContext
8、的TimeZone屬性。 1: public static class DateTimeConverter 2: 3: public static DateTime ConvertTimeToUtc(DateTime dateTime) 4: 5: if(dateTime.Kind = DateTimeKind.Utc) 6: 7: return dateTime; 8: 9: return TimeZoneInfo.ConvertTimeToUtc(dateTime, ApplicationContext.Current.TimeZone); 10: 11: 12: public stati
9、c DateTime ConvertTimeFromUtc(DateTime dateTime) 13: 14: if (dateTime.Kind = DateTimeKind.Utc) 15: 16: return dateTime; 17: 18: return TimeZoneInfo.ConvertTimeFromUtc(dateTime, ApplicationContext.Current.TimeZone); 19: 20: 三、通過WCF擴(kuò)展實(shí)現(xiàn)ApplicationContext的傳播讓當(dāng)前的ApplicationContext在每次服務(wù)調(diào)用時(shí)自動(dòng)傳遞到服務(wù)端,并作為服務(wù)端
10、當(dāng)前的ApplicationContext,整個(gè)過程通過兩個(gè)步驟來實(shí)現(xiàn):其一是客戶端將當(dāng)前ApplicationContext對象進(jìn)行序列化,并置于出棧消息的報(bào)頭(SOAP Header);其二是服務(wù)在接收到請求消息時(shí)從入棧消息中提取該報(bào)頭并進(jìn)行反序列化,最終將生成的對象作為服務(wù)端當(dāng)前的ApplicationContext??蛻舳藢Ξ?dāng)前ApplicationContext輸出可以通過WCF的MessageInspector對象來完成。為此,我們實(shí)現(xiàn)了IClientMessageInspector接口定義了如下一個(gè)自定義的MessageInspector:ContextMessageInspec
11、tor。在BeforeSendRquest方法中,基于當(dāng)前ApplicationContext創(chuàng)建了一個(gè)MessageHeader,并將其插入出棧消息的報(bào)頭集合中。該消息報(bào)頭對應(yīng)的命名空間和名稱為定義在ApplicationContext中的兩個(gè)常量。 1: public class ContextMessageInspector:IClientMessageInspector 2: 3: public void AfterReceiveReply(ref Message reply, object correlationState) 4: public object BeforeSendRe
12、quest(ref Message request, IClientChannel channel) 5: 6: MessageHeader<ApplicationContext> header = new MessageHeader<ApplicationContext>(ApplicationContext.Current); 7: request.Headers.Add(header.GetUntypedHeader(ApplicationContext.contextHeaderName, ApplicationContext.contextHeaderName
13、space); 8: return null; 9: 10: 相應(yīng)地,服務(wù)端對ApplicationContext的接收和設(shè)置可以通過WCF的CallContextInitializer來實(shí)現(xiàn)。為此,我們實(shí)現(xiàn)了ICallContextInitializer接口定義了如下一個(gè)自定義的CallContextInitializer:ContextCallContextInitializer。在BeforeInvoke方法中,通過相同的命名空間和名稱從入棧消息中提取ApplicationConntext作為當(dāng)前的ApplicationContext。為了避免當(dāng)前ApplicationContext用在
14、下一次服務(wù)請求處理中 (ApplicationContext保存在當(dāng)前線程的TLS中,而WCF采用線程池的機(jī)制處理客戶請求),我們在AfterInvoke方法中調(diào)用Clear方法將當(dāng)前ApplicationContext清除。 1: public class ContextCallContextInitializer: ICallContextInitializer 2: 3: public void AfterInvoke(object correlationState) 4: 5: ApplicationContext.Clear(); 6: 7: public object Before
15、Invoke(InstanceContext instanceContext, IClientChannel channel, Message message) 8: 9: var index = message.Headers.FindHeader(ApplicationContext.contextHeaderName, ApplicationContext.contextHeaderNamespace); 10: if (index >= 0) 11: 12: ApplicationContext.Current = message.Headers.GetHeader<App
16、licationContext>(index); 13: 14: return null; 15: 16: 用于ApplicationContext發(fā)送的ContextMessageInspector,和用于ApplicationContext接收的ContextCallContextInitializer,最終我們通過一個(gè)EndpointBehavior被應(yīng)用到WCF運(yùn)行時(shí)框架中。為此我們定義了如下一個(gè)自定義的EndpointBehavior:ContextBehavior。 1: public class ContextBehavior : IEndpointBehavior 2:
17、3: public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters) 4: public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime) 5: 6: clientRuntime.MessageInspectors.Add(new ContextMessageInspector(); 7: 8: public void ApplyDispat
18、chBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher) 9: 10: foreach (DispatchOperation operation in endpointDispatcher.DispatchRuntime.Operations) 11: 12: operation.CallContextInitializers.Add(new ContextCallContextInitializer(); 13: 14: 15: public void Validate(ServiceEndpoin
19、t endpoint) 16: 由于ContextBehavior這個(gè)終結(jié)點(diǎn)行為需要通過培植的方式來使用,我們需要定義它的BehaviorExtensionElement(本質(zhì)上是一個(gè)配置元素): 1: public class ContextBehaviorElement : BehaviorExtensionElement 2: 3: public override Type BehaviorType 4: 5: get return typeof(ContextBehavior); 6: 7: protected override object CreateBehavior() 8: 9
20、: return new ContextBehavior(); 10: 11: 四、建立一個(gè)Alertor Service來模擬跨時(shí)區(qū)場景image 到目前為止,所有基礎(chǔ)性編程已經(jīng)完成,我們現(xiàn)在創(chuàng)建一個(gè)具體的分布式應(yīng)用來使用上面定義的類型。為此,我們模擬一個(gè)用戶提醒服務(wù)(Alertor Service):我們?yōu)槟硞€(gè)人創(chuàng)建相應(yīng)的通知或者提醒,比如什么時(shí)候開會(huì),什么時(shí)候見客戶之類的。首先,所有的Alert條目被最終保存在數(shù)據(jù)庫中,對應(yīng)的表的結(jié)構(gòu)如右圖所示。四個(gè)字段分別表示Alert的Id、被通知的人、消息和被觸發(fā)的時(shí)間。這里的表示時(shí)間的類型就是我們常用的datetime(不具有時(shí)區(qū)偏移量信息)。與
21、這個(gè)數(shù)據(jù)表結(jié)構(gòu)相對應(yīng),一個(gè)Alert類型被創(chuàng)建出來表示一個(gè)具體的Alert條目。Alert被定義成數(shù)據(jù)契約,下面的代碼給出了該類的具體定義。 1: DataContract 2: public class Alert 3: 4: DataMember 5: public string Id get; private set; 6: DataMember 7: public string Person get; private set; 8: DataMember 9: public string Message get; private set; 10: DataMember 11: publ
22、ic DateTime Time get; set; 12: public Alert(string persone, string message, DateTime time) 13: 14: this.Id = Guid.NewGuid().ToString(); 15: this.Person = persone; 16: this.Message = message; 17: this.Time = time; 18: 19: 然后我們定義服務(wù)契約:IAlert接口。該結(jié)構(gòu)定義了兩個(gè)操作成員,CreateNewAlert用于創(chuàng)建一個(gè)信息的Alert條目;而GetAlerts則用于獲取
23、某個(gè)人對應(yīng)的所有Alert列表。 1: ServiceContract(Namespace = " 2: public interface IAlertor 3: 4: OperationContract 5: void CreateNewAlert(Alert alert); 6: OperationContract 7: IEnumerable<Alert> GetAlerts(string person); 8: 下面是實(shí)現(xiàn)上面這個(gè)服務(wù)契約的具體服務(wù)的實(shí)現(xiàn):AlertorService。DbHelper是我創(chuàng)建的一個(gè)簡單的進(jìn)行數(shù)據(jù)操作的幫助類,AlertorServ
24、ice用它來執(zhí)行一段參數(shù)化的SQL語句,以及執(zhí)行一段SELECT語句返回一個(gè)DbDataReader。對此你無需過多關(guān)注沒,你需要關(guān)注的是在CreateNewAlert方法中,在進(jìn)行數(shù)據(jù)保存之前先調(diào)用了DateTimeConverter的ConvertTimeToUtc將基于客戶端時(shí)區(qū)的本地時(shí)間轉(zhuǎn)化成了UTC時(shí)間;而在GetAlerts方法中在將從數(shù)據(jù)庫中返回的Alert列表返回給客戶端的時(shí)候,調(diào)用了DateTimeConverter的ConvertTimeFromUtc將UTC時(shí)間轉(zhuǎn)化成了基于客戶端時(shí)區(qū)的本地時(shí)間。 1: public class AlertorService:IAlerto
25、r 2: 3: private DbHelper helper = new DbHelper("TestDb"); 4: public void CreateNewAlert(Alert alert) 5: 6: alert.Time = DateTimeConverter.ConvertTimeToUtc(alert.Time); 7: var parameters = new Dictionary<string, object>(); 8: parameters.Add("id", alert.Id); 9: parameters.Add
26、("person", alert.Person); 10: parameters.Add("message", alert.Message); 11: parameters.Add("time", alert.Time); 12: helper.ExecuteNoQuery("INSERT INTO dbo.Alert(Id, Person, Message, Time) VALUES(id,person,message,time)", parameters); 13: 14: public IEnumerable
27、<Alert> GetAlerts(string person) 15: 16: var parameters = new Dictionary<string, object>(); 17: parameters.Add("person", person); 18: using ( reader = helper.ExecuteReader("SELECT Person, Message, Time FROM dbo.Alert WHERE Person = person", parameters) 19: 20: while (
28、reader.Read() 21: 22: yield return new Alert(reader0.ToString(),reader1.ToString(),DateTimeConverter.ConvertTimeFromUtc( (DateTime)reader2); 23: 24: 25: 26: 在對上面的服務(wù)進(jìn)行寄宿的時(shí)候,采用了如下的配置,將上面創(chuàng)建的ContextBehavior終結(jié)點(diǎn)行為應(yīng)用到了相應(yīng)的終結(jié)點(diǎn)上。 1: <?xml version="1.0" encoding="utf-8" ?> 2: <conf
29、iguration> 3: <system.serviceModel> 4: <behaviors> 5: <endpointBehaviors> 6: <behavior name="contextBehavior"> 7: <contextPropagtion /> 8: </behavior> 9: </endpointBehaviors> 10: </behaviors> 11: <extensions> 12: <behaviorExtensio
30、ns> 13: <add name="contextPropagtion" type="Artech.TimeConversion.ContextBehaviorElement, Artech.TimeConversion.Lib, Version=, Culture=neutral, PublicKeyToken=null" /> 14: </behaviorExtensions> 15: </extensions> 16: <services> 17: <service nam
31、e="Artech.TimeConversion.Service.AlertorService"> 18: <endpoint address=":3721/alertservice" behaviorConfiguration="contextBehavior" 19: binding="ws2007HttpBinding" bindingConfiguration="" contract="Artech.TimeConversion.Ser
32、vice.Interface.IAlertor" /> 20: </service> 21: </services> 22: </system.serviceModel> 23: </configuration>客戶端在通過如下的配置將ContextBehavior應(yīng)用到用于服務(wù)調(diào)用的終結(jié)點(diǎn)上: 1: <?xml version="1.0" encoding="utf-8" ?> 2: <configuration> 3: <system.serviceMode
33、l> 4: <behaviors> 5: <endpointBehaviors> 6: <behavior name="contextBehavior"> 7: <contextPropagation /> 8: </behavior> 9: </endpointBehaviors> 10: </behaviors> 11: <client> 12: <endpoint address=":3721/alertservice&q
34、uot; behaviorConfiguration="contextBehavior" 13: binding="ws2007HttpBinding" bindingConfiguration="" contract="Artech.TimeConversion.Service.Interface.IAlertor" 14: name="alertservice" /> 15: </client> 16: <extensions> 17: <behavior
35、Extensions> 18: <add name="contextPropagation" type="Artech.TimeConversion.ContextBehaviorElement, Artech.TimeConversion.Lib, Version=, Culture=neutral, PublicKeyToken=null" /> 19: </behaviorExtensions> 20: </extensions> 21: </system.serviceModel>
36、; 22: </configuration>而下面的代碼代表了客戶端程序:我們?yōu)槟硞€(gè)人(Foo)創(chuàng)建了三個(gè)Alert,主要這里指定的時(shí)間的DateTimeKind為默認(rèn)的DateTimeKind.Unspecified。然后調(diào)用服務(wù)或者這三條Alert對象,并將消息的時(shí)間打印出來。 1: public class Program 2: 3: static void Main(string args) 4: 5: CreateAlert("Foo", "Weekly Meeting with Testing Team", new DateTim
37、e(2010, 9, 1, 8, 0, 0); 6: CreateAlert("Foo", "Architecture and Design Training", new DateTime(2010, 9, 2, 8, 0, 0); 7: CreateAlert("Foo", "New Stuff Orientaion", new DateTime(2010, 9, 3, 8, 0, 0); 8: 9: foreach (var alert in GetAlerts("Foo") 10: 11: Console.WriteLine("Alert:t0", alert.Message); 12: Console.WriteLine("Time:t0n", ale
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲(chǔ)空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 2025-2030年中國餐飲連鎖行業(yè)運(yùn)營市場深度調(diào)查及投資策略研究報(bào)告
- 2025-2030年中國阿莫西林行業(yè)競爭現(xiàn)狀及投資戰(zhàn)略研究報(bào)告
- 2025-2030年中國鍍層鋼板市場運(yùn)營態(tài)勢與發(fā)展風(fēng)險(xiǎn)分析報(bào)告
- 2025-2030年中國酒石酸美托洛爾緩釋片行業(yè)發(fā)展趨勢及投資戰(zhàn)略研究報(bào)告
- 2025-2030年中國運(yùn)動(dòng)服飾行業(yè)運(yùn)行現(xiàn)狀及發(fā)展前景趨勢分析報(bào)告
- 2025-2030年中國西廚設(shè)備行業(yè)市場發(fā)展現(xiàn)狀及前景趨勢分析報(bào)告
- 2025-2030年中國營養(yǎng)保健食品市場發(fā)展?fàn)顩r及投資戰(zhàn)略研究報(bào)告
- 病人轉(zhuǎn)運(yùn)合同范本
- 2025河北省安全員B證(項(xiàng)目經(jīng)理)考試題庫
- 2025年廣東省安全員知識(shí)題庫及答案
- 肺栓塞患者護(hù)理查房課件
- 委托書之工程結(jié)算審計(jì)委托合同
- 《如何有效組織幼兒開展體能大循環(huán)活動(dòng)》課件
- (完整版)重力式擋土墻專項(xiàng)方案
- 花城版四年級(jí)音樂下冊全冊教案
- 精神分裂癥合并糖尿病患者護(hù)理查房課件
- 山東省2024屆科目一模擬考試100題(答案)
- 共享wifi貼合同范本
- 借款人借款合同
- 統(tǒng)戰(zhàn)工作先進(jìn)個(gè)人事跡材料
- 國能遼寧北票 200MW 風(fēng)力發(fā)電項(xiàng)目地質(zhì)災(zāi)害危險(xiǎn)性評(píng)估報(bào)告
評(píng)論
0/150
提交評(píng)論