在 Windows 应用程序的监控与管理领域,WMI(Windows Management Instrumentation)技术广为人知。它经过了广泛的测试,与编程语言无关,并且被众多管理应用程序所支持。.NET Framework 也提供了对 WMI 的支持(通过 System.Management 库)。然而,当考虑是否要构建具有相似功能的自定义解决方案时,需要考虑以下几点:
.NETFramework 对WMI的支持主要集中在使用提供的管理对象上。构建自己的提供者相对复杂。
WMI 是一种重量级技术。实例化 WMI 对象会消耗大量的内存和 CPU 周期。
那么,如果只需要管理自己的系统(而不是例如整个域),或者需要快速为现有的 .NET 应用程序(例如,用 C# 编写的 Windows 服务)添加远程管理和监控功能,WMI 并不适合这种情况。作者在面对这样的问题时,最初考虑快速构建一个脏库,使用反射来工具化对象,并将它们的操作和属性远程发布。然后作者意识到,在 Java 中有类似的东西,叫做 JMX(Java Management Extensions)。作者决定创建一个 .NET 版本的 JMX - NetMX。
作者的目标是在现有的 Windows 服务中添加监控和管理功能。其中一部分是一个导出组件,它从数据库中获取数据(消息)并将其写入磁盘上的文件。作者想知道自服务启动以来导出了多少消息(按类型分组)。组件定义是一个简单的类,如下所示:
public class Exporter {
public void ExportMessages() {
// Export...
}
}
如果这是一个 Java 类,使用 JMX,作者可以通过定义以下接口将其转换为所谓的标准 MBean:
public interface ExporterMBean {
int getType1ExportedCount();
int getType2ExportedCount();
...
}
作者希望在 .NET 下拥有相同的功能。
在实现方面,作者应该在这里赞扬 JMX 规范的架构师。它非常强大,同时不失简单性。值得一提的是,它非常通用,以至于整个应用服务器都建立在 JMX 微核(如 JBoss)之上。意识到这些人比作者更有经验,作者决定在将 JMX 移植到 .NET 时尽可能少地改变 API。作者决定引入的变更包括:
在标准 MBean 中属性的其他表示。属性在.NET中的自然表示是属性,作者决定用它们代替 getXxx/setXxx 命名约定。
默认和强制的远程访问实现在 JMX 中使用 RMI。在 NetMX 中,它使用.NET远程。
所有工厂使用配置节(System.Configuration)作为其数据源。
回调接口被委托替换。这是直观的,并且更适合 .NET 风格。
标准 MBean 可以在其管理接口中定义事件,从而成为通知发射器。
让离开上述提到的实际 NetMX 使用场景,尝试从抽象功能开始,即计数器。计数器是一个设备,应该具有以下特性:
允许获取或设置步长值。
允许按步长值递增。
允许通过任意提供的值更改值。
可以重置(其值设置为 0)。
在值更改时通知感兴趣的各方。
这些要求可以翻译成以下 MBean 接口规范:
public interface CounterMBean {
[Description("Gets or sets step value")]
int Step { get; set; }
[Description("Increments counter value")]
void Increment();
[Description("Adds provided value to counter value")]
void Add(int value);
[Description("Resets counter")]
void Reset();
[MBeanNotification("Counter.ValueChanged")]
event EventHandler ValueChanged;
}
使用 Description 属性的可能性是 JMX 规范的扩展(它不使用注释 - Java 等价于属性)。同样,定义 ValueChanged 通知的方式是 NetMX 独有的。在标准 JMX 中,实现 CounterMBean 的类必须自己实现 NotificationEmitter 接口(或派生自辅助基类)。在这里,一切都是通过通知完成的,管道由容器(MBean 服务器)提供。
计数器的实现如下所示:
public class Counter : CounterMBean {
private int value;
private int step;
public int Step {
get {
return step;
}
set {
step = value;
}
}
public void Increment() {
value += step;
OnValueChanged();
}
public void Add(int value) {
this.value += value;
OnValueChanged();
}
public void Reset() {
value = 0;
OnValueChanged();
}
public event EventHandler ValueChanged;
private void OnValueChanged() {
if (ValueChanged != null) {
ValueChanged(this, new NotificationEventArgs("Value changed", value));
}
}
}
这就是需要发布计数器功能的全部。执行此发布的代码也很简单:
IMBeanServer server = MBeanServerFactory.CreateMBeanServer();
Counter o = new Counter();
ObjectName name = new ObjectName("Counter:");
server.RegisterMBean(o, name);
Uri serviceUrl = new Uri("tcp://localhost:1234/MBeanServer.tcp");
using (INetMXConnectorServer connectorServer = NetMXConnectorServerFactory.NewNetMXConnectorServer(serviceUrl, server)) {
connectorServer.Start();
Console.WriteLine("Press any key to quit");
Console.ReadKey();
}
当然,NetMX 将提供一组标准的 ASP.NET 控件,这些控件可以与服务器建立连接,并通过 HTTP 远程控制计数器。
作者在开始 NetMX 项目时设定的目标似乎是可以实现的。尽管 NetMX 远未完成,但现在看来,关于性能(即创建管理对象)的假设以及对现有类进行工具化的少量编码工作是正确的。应该再次强调,NetMX 不是WMI的竞争对手。它只是对于那些被 WMI 压倒并寻求更轻量级(在所有方面)解决方案的人的另一种选择。