zoukankan      html  css  js  c++  java
  • Apply SOA Design Patterns with WCF (3) Automatic Service Locating (自动化服务定位)

    Original (原创) by Teddy’s Knowledge Base

    Content (目录)

    (1) WCF Configuration Centralization (WCF配置集中管理)

    (2) WCF Automatic Deployment (WCF自动化部署)

    (3) WCF Automatic Service Locating (WCF自动化服务定位)

    (4) WCF Database Paging & Sorting (WCF数据库分页和排序)

    (5) WCF Based ASP.NET DataSouce Control (基于WCF的数据源控件)

    (6) 1 + 2 + 3 + 4 + 5 = ?

    English Version

    摘要

    本文提供一种在WCF服务消费应用程序中通过与服务提供应用程序共享WCF服务契约接口来自动化定位WCF服务实现的方案。

    正文

    什么是服务定位?

    服务定位的意思是服务的消费者给定服务的接口类型,由一个称作服务定位器的对象返回这个服务接口的实现类,这样,服务的消费者对服务的实现类就没有依赖关系。服务定位器这个设计模式的思路诞生于JAVA世界。这个模式又发展成了IoC容器模式。Martin Folwer对这个设计模式已经讨论很多了

    WCF的ChannelFactory类实现了服务定位器设计模式

    实际上,WCF的ChannelFactory类也实现了服务定位器模式,它提供了一个接受一个端点配置名称的构造函数,通过这个名称,ChannelFactory就能从应用程序配置文件中读取配置信息,构造绑定信息。构造了ChannelFactory实例以后,就能调用它的CreateChannel()方法来创建服务契约的代理了。一个代理其实是一个动态实现的服务契约的实现类或子类。下面是一个使用ChannelFactory的示例:

    1     ChannelFactory<IRequestChannel> factory 
    2 
    3       = new ChannelFactory<IRequestChannel>("MyEndpoint");
    4 
    5     IRequestChannel proxy = factory.CreateChannel();
    6 
    7     //using proxy instance…

    和ServiceHost类类似,ChannelFactory类也提供接受Binding类实例的构造函数。所以,也可以用编程方式构造一个Binding类的实例来构造一个ChannelFactory。

    包装ChannelFactory类

    ChannelFactory类有下面这些限制:

    • 作为一个服务定位器的实现,它仅支持从应用程序配置文件中读取配置信息。
    • 就像我在第一章的“提示”部分介绍的,客户程序必须总是很小心的关闭ChannelFactory以释放它占用的资源。

    要克服这些限制,我们可以考虑对将ChannelFactory类包装成一个安全的WCF服务定位器。这样一个服务定位器的实现要注意下面几点:

    • 这个WCF服务定位器应该可以从其他地方如集中化的配置存储读取用于构造Binding的配置信息,而不是只能从应用程序配置文件中。
    • 这个WCF服务定位创建的ChannelFactory的资源释放应该以最佳实现包装在这个WCF服务定位器的IDsiposable接口和析构函数的实现中。客户程序原则上应该总是要记得在消费完服务以后显式的地调用WCF服务定位器的Dispose()方法。尽管,即使漏了显式调用,他的析构函数也应该能在实例被垃圾回收的时候调用Dispose()。
    • 这个WCF服务定位器不应该被全局缓存或实现为单例模式,因为构造和保持ChannelFactory的资源开销是很昂贵的,所以应该尽早在消费完服务以后释放。

    下面是一个WCF服务定位器的示例:

      1     public sealed class WcfServiceLocator : IServiceLocator
      2     {
      3         #region Private Members
      4 
      5             //
      6 
      7         #endregion
      8 
      9         #region Public Methods
     10 
     11         /// <summary>
     12         /// Generic version of GetService
     13         /// </summary>
     14         /// <typeparam name="T">The service contract type</typeparam>
     15         /// <returns>The service proxy instance</returns>
     16         public T GetService<T>()
     17         {
     18             if (!_channelFactories.ContainsKey(typeof(T)))
     19             {
     20                 lock (_channelFactories)
     21                 {
     22                     if (!_channelFactories.ContainsKey(typeof(T)))
     23                     {
     24                         var cf = CreateChannelFactory<T>();
     25                         if (cf != null)
     26                         {
     27                             _channelFactories.Add(typeof(T), cf);
     28                             try
     29                             {
     30                                 _serviceProxies.Add(typeof(T), cf.CreateChannel());
     31                             }
     32                             catch
     33                             {
     34                                 _serviceProxies.Add(typeof(T), null);
     35                             }
     36                         }
     37                         else
     38                         {
     39                             _channelFactories.Add(typeof(T), null);
     40                             _serviceProxies.Add(typeof(T), null);
     41                         }
     42                     }
     43                 }
     44             }
     45 
     46             return (T)_serviceProxies[typeof(T)];
     47         }
     48 
     49         #endregion
     50 
     51         #region IDisposable Members
     52 
     53         /// <summary>
     54         /// The dispose method,
     55         /// closing all created channel factories in this service locator
     56         /// </summary>
     57         public void Dispose()
     58         {
     59             Dispose(true);
     60             GC.SuppressFinalize(this);
     61         }
     62 
     63         private bool disposed;
     64 
     65         private void Dispose(bool disposing)
     66         {
     67             if (disposed) return;
     68             if (disposing)
     69             {
     70                 foreach (var item in _channelFactories.Values)
     71                 {
     72                     if (item != null)
     73                     {
     74                         //close channel factory in best practice
     75                         //refer to: http://bloggingabout.net/blogs/erwyn/archive/2006/12/09/WCF-Service-Proxy-Helper.aspx
     76                         try
     77                         {
     78                             item.Close();
     79                         }
     80                         catch (CommunicationException)
     81                         {
     82                             item.Abort();
     83                         }
     84                         catch (TimeoutException)
     85                         {
     86                             item.Abort();
     87                         }
     88                         catch (Exception)
     89                         {
     90                             item.Abort();
     91                             throw;
     92                         }
     93                     }
     94                 }
     95             }
     96 
     97             disposed = true;
     98         }
     99 
    100         ~WcfServiceLocator()
    101         {
    102             Dispose(false);
    103         }
    104 
    105         #endregion
    106     } 

    整合第三方的IoC容器

    如果所有的服务全都用WCF服务实现,那么上面这个WCF服务定位器就足够好了。但是,大多数程序同时会使用诸如辅助工具、日志记录、第三方组件包装等等本地服务。这样一来我们就同样需要诸如Unity和Castle IoC容器这样的本地服务定位器。所以,如果我们能够整合WCF服务定位器和第三方的服务定位器,那就更好了。

    下面是一个示例的ServiceManager类,它拥有整合第三方服务定位器和上述WCF服务定位器的能力:

      1     public interface IServiceLocator : IDisposable
      2     {
      3         /// <summary>
      4         /// Get service
      5         /// </summary>
      6         /// <param name="serviceContract">The service contract type</param>
      7         /// <returns>The service instance</returns>
      8         object GetService(Type serviceContract);
      9 
     10         /// <summary>
     11         /// Generic version of GetService
     12         /// </summary>
     13         /// <typeparam name="T">The service contract type</typeparam>
     14         /// <returns>The service instance</returns>
     15         T GetService<T>();
     16     }
     17 
     18     public sealed class ServiceManager
     19     {
     20         #region Private Singleton
     21 
     22         private static readonly ServiceManager _singleton = new ServiceManager();
     23 
     24         #endregion
     25 
     26         #region Private Constructor
     27 
     28         private readonly Type _externalServiceLocatorType;
     29         private readonly List<Type> _cachedExternalServiceTypes = new List<Type>();
     30         private readonly List<Type> _cachedWcfServiceTypes = new List<Type>();
     31 
     32         private ServiceManager()
     33         {
     34             var serviceLocatorTypeName = ConfigurationManager.AppSettings[Constants.ExternalServiceLocatorTypeAppSettingName];
     35             if (!string.IsNullOrEmpty(serviceLocatorTypeName))
     36             {
     37                 var serviceLocatorType = Type.GetType(serviceLocatorTypeName);
     38                 if (serviceLocatorType != null)
     39                 {
     40                     if (serviceLocatorType != typeof(WcfServiceLocator))
     41                     {
     42                         _externalServiceLocatorType = serviceLocatorType;
     43                     }
     44                 }
     45             }
     46         }
     47 
     48         #endregion
     49 
     50         #region Public Methods
     51 
     52         /// <summary>
     53         /// Get service locator of specified service contract type
     54         /// </summary>
     55         /// <param name="serviceContract">The service contract type</param>
     56         /// <returns>The service instance</returns>
     57         public static IServiceLocator GetServiceLocator(Type serviceContract)
     58         {
     59             if (serviceContract == null)
     60                 throw new ArgumentNullException("serviceContract");
     61 
     62             if (_singleton._externalServiceLocatorType != null)
     63             {
     64                 if (!_singleton._cachedExternalServiceTypes.Contains(serviceContract))
     65                 {
     66                     if (_singleton._cachedWcfServiceTypes.Contains(serviceContract))
     67                     {
     68                         return new WcfServiceLocator();
     69                     }
     70                     lock (_singleton)
     71                     {
     72                         if (!_singleton._cachedExternalServiceTypes.Contains(serviceContract))
     73                         {
     74                             if (_singleton._cachedWcfServiceTypes.Contains(serviceContract))
     75                             {
     76                                 return new WcfServiceLocator();
     77                             }
     78 
     79                             //rethrow the exception in initializing the locator instance directly
     80                             var locator = (IServiceLocator)Activator.CreateInstance(_singleton._externalServiceLocatorType);
     81 
     82                             object service = null;
     83                             try
     84                             {
     85                                 service = locator.GetService(serviceContract);
     86                                 if (service != null)
     87                                 {
     88                                     _singleton._cachedExternalServiceTypes.Add(serviceContract);
     89                                     return locator;
     90                                 }
     91                             }
     92                             catch
     93                             {
     94                                 //if could not locate the service
     95                                 _singleton._cachedWcfServiceTypes.Add(serviceContract);
     96                                 return new WcfServiceLocator();
     97                             }
     98                             finally
     99                             {
    100                                 if (service != null)
    101                                 {
    102                                     var disposable = service as IDisposable;
    103                                     if (disposable != null)
    104                                         disposable.Dispose();
    105                                 }
    106                             }
    107                         }
    108                     }
    109                 }
    110                 else
    111                 {
    112                     var locator = (IServiceLocator)Activator.CreateInstance(_singleton._externalServiceLocatorType);
    113                     return locator;
    114                 }
    115             }
    116 
    117             _singleton._cachedWcfServiceTypes.Add(serviceContract);
    118             return new
     WcfServiceLocator();
    119         }
    120 
    121         /// <summary>
    122         /// Get service locator of specified service contract type
    123         /// </summary>
    124         /// <typeparam name="T">The service contract type</typeparam>
    125         /// <returns></returns>
    126         public static IServiceLocator GetServiceLocator<T>()
    127         {
    128             return GetServiceLocator(typeof(T));
    129         }
    130 
    131         #endregion
    132     }

    注意:

  • ServiceManager类的GetService()的返回类型应该要是IServiceLocator,而不是service本身的实例,因为对于WCF服务定位器来说,我们应该在消费完服务之后立即显式调用Dispose()方法来释放被占用的资源。
  • 这个ServiceManager类也可以用单例模式实现代替这里的静态类方式。
  • 注意ServiceManager类中的高亮代码,它为服务契约和服务定位器的映射的缓存实现了一个双重检测锁,确保其线程安全。
  • 提示

  • 要使用本文提到的WCF服务定位器或ServiceManager类,应该让服务的消费程序和提供程序都直接引用共同的定义了服务契约的DLL。不要让客户程序使用生成工具通过发布的元数据生成的代理类。
  • 参考

    (1) SOA Design Pattern Catalog: http://www.soapatterns.org/

    //我是结尾符,待续…

查看全文
  • 相关阅读:
    [Erlang 0106] Erlang实现Apple Push Notifications消息推送
    一场推理的盛宴
    [Erlang 0105] Erlang Resources 小站 2013年1月~6月资讯合集
    [Erlang 0104] 当Erlang遇到Solr
    [Erlang 0103] Erlang Resources 资讯小站
    history.go(-1)和History.back()的区别
    [Java代码] Java用pinyin4j根据汉语获取各种格式和需求的拼音
    spring中context:property-placeholder/元素
    Java中的异常处理:何时抛出异常,何时捕获异常?
    用Jersey构建RESTful服务1--HelloWorld
  • 原文地址:https://www.cnblogs.com/teddyma/p/1422776.html
  • Copyright © 2011-2022 走看看