Wcf通訊基礎架構方案(三)——用戶端

來源:互聯網
上載者:User

假設定義了一個服務契約:

[ServiceContract(Namespace = "WcfExtension.Services.Interface")]    public interface ITestService    {        [OperationContract]        int Add(int x, int y);        [OperationContract]        [ServiceKnownType(typeof(TestContract))]        ITestContract TestData(ITestContract tc);                [OperationContract]        List<string> AddList(List<string> args);    }

首先看一下,用戶端是怎麼使用的:

Console.WriteLine(WcfServiceLocator.Create<ITestService>().Add(1, 3));

WcfServiceLocator.Create()出來的就是一個介面,可以保持這個引用以後調用,也可以每次直接這麼寫,那麼來看看WcfServiceLocator:

    public static T Create<T>() where T : class        {            return (T)(new ServiceRealProxy<T>().GetTransparentProxy());        }        public static IWcfLogService GetLogService()        {            if (enableRemoteLogService)                return (IWcfLogService)(new LogServiceRealProxy().GetTransparentProxy());            else                return new LocalLogService();        }

Create方法很簡單,只是返回一個ServiceRealProxy的透明代理。這個之後會詳細說,下面單專屬一個GetLogService專門用於擷取Log Service。這裡通過心跳來判斷遠端Log Service是否可用,如果不可用直接返回本地的Log Service。之所以Log Service的真實代理和其它商務服務的真實代理不同是因為,在商務服務中我們需要加入很多橫切日誌,在Log Service中不需要:

    public override IMessage Invoke(IMessage msg)        {            using (var client = WcfServiceClientFactory.CreateServiceClient<T>())            {                var channel = client.Channel;                IMethodCallMessage methodCall = (IMethodCallMessage)msg;                IMethodReturnMessage methodReturn = null;                object[] copiedArgs = Array.CreateInstance(typeof(object), methodCall.Args.Length) as object[];                methodCall.Args.CopyTo(copiedArgs, 0);                bool isSuccessuful = false;                var stopwatch = Stopwatch.StartNew();                try                {#if DEBUG                    Console.WriteLine("開始調用:" + methodCall.MethodName);#endif                    object returnValue = methodCall.MethodBase.Invoke(channel, copiedArgs);#if DEBUG                    if (ClientApplicationContext.Current != null)                        Console.WriteLine("這個請求由" + ClientApplicationContext.Current.ServerMachineName + "處理完成,服務端版本號碼:" + ClientApplicationContext.Current.ServerVersion);                    Console.WriteLine("結束調用:" + methodCall.MethodName);#endif                    methodReturn = new ReturnMessage(returnValue,                                                    copiedArgs,                                                    copiedArgs.Length,                                                    methodCall.LogicalCallContext,                                                    methodCall);                    isSuccessuful = true;                }                catch (Exception ex)                {                    var excepionID = ClientApplicationContext.Current.ServerExceptionID ?? "";                    if (ex.InnerException != null)                    {#if DEBUG                        Console.WriteLine("調用服務端方法出現異常:" + ex.InnerException.Message);#endif                        var exception = ex.InnerException;                        if (exception is FaultException)                        {                            exception = new Exception("Failed to call this Wcf service, remote exception message is:" + exception.Message);                            exception.HelpLink = "Please check this exception via ID : " + excepionID;                        }                        if (WcfLogManager.Current().ExceptionInfo.Client.Enabled)                        {                            var log = WcfLogProvider.GetClientExceptionInfo(                                typeof(T).FullName,                                ClientApplicationContext.Current.RequestIdentity,                                "ServiceRealProxy.Invoke",                                exception, excepionID);                            WcfServiceLocator.GetLogService().Log(log);                        }                        methodReturn = new ReturnMessage(exception, methodCall);                    }                    else                    {                        Console.WriteLine("調用方法出現異常:" + ex.Message);                        if (WcfLogManager.Current().ExceptionInfo.Client.Enabled)                        {                            var log = WcfLogProvider.GetClientExceptionInfo(                                typeof(T).FullName,                                ClientApplicationContext.Current.RequestIdentity,                                "ServiceRealProxy.Invoke",                                ex, excepionID);                            WcfServiceLocator.GetLogService().LogWithoutException(log);                            methodReturn = new ReturnMessage(ex, methodCall);                        }                    }                }                finally                {                    if (WcfLogManager.Current<T>().InvokeInfo.Client.Enabled)                    {                        var log = WcfLogProvider.GetClientInvokeLog(                           typeof(T).FullName,                           "ServiceRealProxy.Invoke",                           stopwatch.ElapsedMilliseconds,                           isSuccessuful,                           methodCall.MethodName,                           ClientApplicationContext.Current);                        WcfServiceLocator.GetLogService().LogWithoutException(log);                    }                }                return methodReturn;            }        }

這就是ServiceRealProxy的Invoke方法實現了,可以看到:

1) 在這裡我們using了WcfServiceClientFactory.CreateServiceClient返回的封裝後的Channel,讓每一次調用都可以正常關閉通道。

2) 在這裡我們加入了調用方法成功後的日誌,以及出錯時的異常日誌。

再來看一下WcfChannelWrapper:

internal sealed class WcfChannelWrapper<T> : IDisposable where T : class    {        private readonly T channel;        public WcfChannelWrapper(T channel)        {            this.channel = channel;        }        public T Channel { get { return channel; } }        #region IDisposable Members        public void Dispose()        {            Dispose(true);            GC.SuppressFinalize(this);        }        private bool disposed;        private void Dispose(bool disposing)        {            if (disposed) return;            if (disposing)            {                var commObj = channel as ICommunicationObject;                if (commObj != null)                {                    try                    {                        commObj.Close();                    }                    catch (CommunicationException)                    {                        commObj.Abort();                    }                    catch (TimeoutException)                    {                        commObj.Abort();                    }                    catch (Exception)                    {                        commObj.Abort();                        throw;                    }                }            }            disposed = true;        }        ~WcfChannelWrapper()        {            Dispose(false);        }        #endregion    }

使用最佳實務推薦的方式來關閉通道。ServiceRealProxy的另一個好處是,不再需要這麼調用服務:

            using (var scf = WcfServiceClientFactory.CreateServiceClient<IWcfConfigService>())                {                    return scf.Channel.GetWcfClientEndpoint(serviceContractType.FullName, versionstring, WcfLogProvider.MachineName);                }

使用的直接是契約介面,不需要scf.Channel.xx(),不需要using。最後,來看看LogService的真實代理,乾淨多了:

        public override IMessage Invoke(IMessage msg)        {            using (var client = WcfServiceClientFactory.CreateServiceClient<IWcfLogService>())            {                var channel = client.Channel;                IMethodCallMessage methodCall = (IMethodCallMessage)msg;                IMethodReturnMessage methodReturn = null;                object[] copiedArgs = Array.CreateInstance(typeof(object), methodCall.Args.Length) as object[];                methodCall.Args.CopyTo(copiedArgs, 0);                try                {                    object returnValue = methodCall.MethodBase.Invoke(channel, copiedArgs);                    methodReturn = new ReturnMessage(returnValue,                                                    copiedArgs,                                                    copiedArgs.Length,                                                    methodCall.LogicalCallContext,                                                    methodCall);                }                catch (Exception ex)                {                    if (ex.InnerException != null)                    {                        LocalLogService.Log(ex.InnerException.ToString());                        methodReturn = new ReturnMessage(ex.InnerException, methodCall);                    }                    else                    {                        LocalLogService.Log(ex.ToString());                        methodReturn = new ReturnMessage(ex, methodCall);                    }                }                return methodReturn;            }        }

即使出錯也不會再調用Log Service,而是記錄本地日誌,實現了一個比較簡單的本地Log Service:

    static LocalLogService()        {            var logPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "log");            if (!Directory.Exists(logPath))                Directory.CreateDirectory(logPath);            Debug.Listeners.Add(new TextWriterTraceListener(logPath + "/log" + DateTime.Now.ToString("yyyyMMdd") + ".txt"));            Debug.AutoFlush = true;        }        public static void Log(string text)        {            Debug.WriteLine("================================== START ======================================");            Debug.WriteLine(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"));            Debug.WriteLine(text);            Debug.WriteLine("=================================== END =======================================");        }

下一節會詳細介紹下四種日誌訊息:開機記錄、調用日誌、收發訊息日誌和異常日誌。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.