`
ayufox
  • 浏览: 276990 次
  • 性别: Icon_minigender_1
  • 来自: 深圳
社区版块
存档分类
最新评论

面向事件编程

阅读更多

1.前言
    当一个项目启动的时候,我们首先需要面向下面的问题:

  • 实现策略分离
  • 组件之间依赖绑定
  • 生命期控制
  • 需要发布一些事情告诉感兴趣的组件
  • (optional)组件隔离

      第一个问题通过面向接口编程的方式,可以让服务使用者不必关心服务是如何实现的,第二和第三个问题则是一个良好的IOC容器能够解决的,譬如Spring,第四个问题当然是一个事件通知机制,Spring对事件做了支持,第五个问题是OSGI能够处理的问题,一般没有那么严格高要求隔离化,这里不深入探讨。在面向这些问题的时候,我们基本上会选择Spring来解决,事实上Spring也做的足够好了,唯一的遗憾可能就是Spring现在实在是不能称之为一个“轻量级”的容器,即使只使用基础包也足有上M之多。
      在开发BlackStar的时候同样的面对这些问题,而这里,我们使用一种不同的方式来解决这些问题,即所谓的面向事件编程,使用事件机制来解决如上的问题。
2.BlackStar背景介绍
      在开始之前,我们先了解一下BlackStar的问题背景,BlackStar大概地分成如下几个部分:
2.1 基础组件:

  • 调度服务:很多其他组件需要定时执行的时候需要使用到调度服务,譬如JVM监控需要每分钟执行一次,该组件使用Quartz实现
  • Web服务:很多其他组件需要以Web的方式提供服务出去,譬如JMX Proxy需要以Hessian的方式提供JMX接入,UI组件需要展示页面信息,该组件使用Jetty实现
  • 本地JVM JMX管理服务:负责自动识别本地的JVM、并负责与本地JVM的连接,其他组件需要依赖于这个服务,譬如JMX Proxy需要获得所有的本地JVM JMX以进行Proxy、JVM Monitor需要获得本地JVM JMX的变更情况以对本地JVM进行监控,这个服务需要依赖于调度服务以进行周期性地检测本地JVM的变更

2.2插件组件:

  • JMX Proxy:负责将本地的JVM JMX服务Proxy给外部,外部可以使用JConsole接入,需要依赖于本地JVM JMX管理服务
  • JVM Monitor:负责监控本地JVM的数据(CPU、PermSpace、TenuredSpace、Thread等),需要依赖于本地JVM JMX管理服务和调度服务(定期检测)
  • Performance Stat:性能统计服务,需要依赖于调度服务以定时执行
  • UI:将统计数据以Web的方式提供给外部,需要依赖于Web服务和本地JVM JMX管理服务

3.BlackStar内部组成结构
     那么BlackStar是如何解决前言中提出的问题的呢?我们首先看下面的图,所有的组件都会以EventListener的方式存在,并注册到EventMediator上;基础组件部分提供一些服务事件出去,并监听这些事件;而服务使用者当需要使用到服务的时候发布相应的服务事件出去,基础组件收到事件后进行处理;EventMediator充当一个统一的中介者。当然,系统会有统一的生命周期事件(Startup、AfterStartup、BeforeShutdown、Shutdown),由系统统一发布。

4.具体实现
     我们来看看具体是如何实现的
4.1核心的Event部分:
     整个Event机制实现其实非常简单
4.1.1事件监听者EventListener,如果需要接收事件,实现这个接口

public interface EventListener<T extends Event>
{
    /**
     * 需要监听的事件
     * @return
     */
    Class[] events();
   
    /**
     * 事件处理
     * @param event
     * @throws Exception
     */
    void onEvent(T event) throws Exception;
}

4.1.2事件中间者,负责Listener注册和事件分发(EventMediator):

public abstract class EventMediator implements EventDispatcher
{
    private static EventMediator INSTANCE = new DefaultEventMediator();

    public static void setInstance(EventMediator instance)
    {
        if (instance == null)
        {
            throw new IllegalArgumentException("instance can't be null");
        }
        INSTANCE = instance;
    }

    public abstract void dispatch(Event event);

    public abstract void register(EventListener eventListener);

    /**
     * 注册Listener
     * @param eventListener
     */
    public static void registerListener(EventListener eventListener)
    {
        INSTANCE.register(eventListener);
    }

    /**
     * 分发事件被关注的监听者
     * @param event
     */
    public static void dispatchEvent(Event event)
    {
        INSTANCE.dispatch(event);
    }
}

4.1.2事件分发,如果需要事件分发,可以继承自EventDispatcherSupport

public interface EventDispatcher
{
    /**
     * 事件分发
     * @param event
     */
    void dispatch(Event event);
}

public class EventDispatcherSupport implements EventDispatcher
{
    public void dispatch(Event event)
    {
        EventMediator.dispatchEvent(event);
    }
}

4.2系统启动与系统事件
4.2.1
    现在我们的系统准备开始启动了,非常简单,就是从配置中读取所有的Listener,注册,并发送StartupEvent和AfterStartupEvent

   public static void main(String[] args) throws Exception
    {
        String listeners = AgentConfig.getProperty("listeners");
        String[] listenerArray = listeners.split(",");
        for (String listener : listenerArray)
        {
            final EventListener eventListener = (EventListener) Class.forName(
                    listener.trim()).newInstance();
            EventMediator.registerListener(eventListener);
            LOGGER.info("Regist EventListner[" + listener + "]");
        }
        EventMediator.registerListener(new ShutdownHook());

        LOGGER.info("Dispatch StartupEvent");
        EventMediator.dispatchEvent(new StartupEvent());
        LOGGER.info("Dispatch AfterStartupEvent");
        EventMediator.dispatchEvent(new AfterStartupEvent());
        LOGGER.info("Startup Success");
    }
 

4.2.2 从上面我们可以看到一个特殊的Listener——ShutdownHook,其负责向JVM注册ShutdownHook事件,在JVM关闭的时候向发布BeforeShutdownEvent和ShutdownEvent

public class ShutdownHook extends EventDispatcherSupport implements
        EventListener<StartupEvent>
{
    private final static Log LOGGER = LogFactory.getLog(ShutdownHook.class);

    private static boolean isShutdown = false;;

    public Class[] events()
    {
        return new Class[]
        { StartupEvent.class };
    }

    public void onEvent(StartupEvent event) throws Exception
    {
        Runtime.getRuntime().addShutdownHook(new Thread()
        {
            public void run()
            {
                synchronized (ShutdownHook.class)
                {
                    if (!isShutdown)
                    {
                        LOGGER.info("Dispatch BeforeShutdown");
                        dispatch(new BeforeShutdownEvent());
                        LOGGER.info("Dispatch Shutdown");
                        dispatch(new ShutdownEvent());
                        LOGGER.info("Shutdown Success");
                        isShutdown = true;
                    }
                    else
                    {
                        LOGGER.error("Duplicate Shutdown");
                    }
                }
            }
        });
    }
}

4.2.3系统事件
    从上面我们可以看到StartupEvent、AfterStartupEvent、BeforeShutdownEvent、ShutdownEvent,如果我们的Listener需要进行这些声明期的控制,譬如初始化、销毁对象之类的,可以注册这些事件,譬如如上的ShutdownHook,在其他Listener都初始化完成之后才去注册ShutdownHook
4.3如何使用一个服务
     现在,系统已经把我们的Listener注册进去,并发送了启动事件让我们可以从容进行初始化的工作,现在我们的服务需要依赖于其他的服务,该如何做呢?我们以Monitor组件为例,Monitor组件需要定时执行程序。
    首先,调度基础服务定义了调度事件,如下

public class ScheduleEvent extends Event
{
    private ScheduleTask task;
    private String cronExpression;

    public ScheduleEvent(String name, String cronExpression, ScheduleTask task)
    {
        super(name);
        this.cronExpression = cronExpression;
        this.task = task;
    }

    public ScheduleEvent(String cronExpression, ScheduleTask task)
    {
        this(null, cronExpression, task);
    }

    public String getCronExpression()
    {
        return cronExpression;
    }

    public ScheduleTask getTask()
    {
        return task;
    }
}

    我们的服务需要调度,则只需要发布这个事件即可以,调度服务自动会处理这个事件,如下

final Monitor monitor = monitorInfo.getMonitorTask();
monitor.init(proxy);
String jobName = "jvm" + proxy.getId() + "_"
                        + monitor.getName();
//分布定时调度
dispatch(new ScheduleEvent(jobName, monitorInfo
            .getCronExpression(), new ScheduleTask()
            {
                public void schedule()
                {
                    monitor.onTimeout();
                }
        }));

4.4服务如何处理服务事件
    在上面中,服务使用者发布了一个调度事件,则调度服务提供者接受到了这个事件,可以开始处理

public void onEvent(Event event) throws Exception
    {
        ……
        else if (event instanceof ScheduleEvent)
        {
            if (!scheduler.isShutdown())
            {
                ScheduleEvent se = (ScheduleEvent) event;
                if (se.getId() == null)
                {
                    schedule(se.getCronExpression(), se.getTask());
                } else
                {
                    schedule(se.getId(), se.getCronExpression(), se.getTask());
                }
            }
        ……
    }

4.5数据查询如何做
    数据查询是面向事件编程最难处理也处理地最不好的部分,不过我们还是补充上吧。提供数据查询服务的服务提供方需要将自己的接口发布出来,而服务使用者接收到这个发布声明后,将其作为自己的一个属性
    如下,服务发布方

public synchronized void startup()
    {
        ……

        dispatch(new JMXProxyManagerStartupEvent(this));
    }

    服务使用方

public class JMXProxyUtils implements
        EventListener<JMXProxyManagerStartupEvent>
{
    private static JMXProxyManager proxyManager;

    public Class[] events()
    {
        return new Class[]
        { JMXProxyManagerStartupEvent.class };
    }

    public void onEvent(JMXProxyManagerStartupEvent event) throws Exception
    {
        proxyManager = event.getProxyManager();
    }
     ……
}

4.6变更事件通知
    变更事件通知是面向事件的老本行,处理方式与其他一致,譬如当识别到本地的一个JVM时,则需要创建它,并将其发布出去,通知其他组件有一个新的JVM实例产生了

   protected synchronized boolean startupLocalJVM(LocalJVM localJVM)
    {
        if (!localJVM.start())
        {
            return false;
        }
        jvms.put(localJVM.getPid(), localJVM);

        dispatch(new JMXProxyStartupEvent(localJVM));

        return true;
    }

    其他组件接收到这个事件,则可以进行自己的处理

    public void onEvent(Event event) throws Exception
    {
        if (event instanceof JMXProxyStartupEvent)
        {
            startup(((JMXProxyStartupEvent) event).getJMXProxy());
        } else if (event instanceof JMXProxyShutdownEvent)
        {
            shutdown(((JMXProxyShutdownEvent) event).getJMXProxy());
        }
    }

5.结束语
    一般而言,面向接口编程混合事件编程是一种通用的处理问题的方式,但缺陷在于需要有一个良好的生命周期机制和一个依赖管理组件,解决这两个问题一般依赖于一个设计良好的IOC容器。而面向事件编程是一种相对没有那么自然的处理问题的方式,当我们不希望引入一个复杂的IOC容器来解决我们的问题的时候,应该算是一个不错的选择。当然,其固有的缺陷也是相当明显,当项目比较庞杂服务众多的时候,定义事件都会让人不胜其扰,组件之间查询类的依赖比较多的时候,基本上也不是一个好的选择。一般这种纯粹面向事件的方式比较适应于小规模的、核心服务相对比较稳定而且组件之间比较少依赖于查询(没有返回结果,允许异步化处理的类型)的独立应用。

3
3
分享到:
评论

相关推荐

    Labview面向对象编程

    5. **事件驱动编程**:Labview的面向对象特性与它的事件驱动模型相结合,使得程序能对用户交互或其他事件做出响应。事件结构是Labview中实现这一机制的关键,当特定事件发生时,对应的事件分支会被执行。 6. **接口...

    plc面向对象编程架构与实现

    面向对象编程是计算机语言的一种先进的编程模式,在工业控制系统的PLC程序中也可以采用这种设计思想,虽然我们无法实现面向对象的很多特点如“继承”,甚至于它根本就不具备面向对象编程语言的特点,但面向对象编程...

    java面向对象编程源码

    本书内容由浅入深,紧密结合实际,利用大量典型实例,详细讲解Java面向对象的编程思想、编程语法和设计模式,介绍常见Java类库的用法,总结优化 Java编程的各种宝贵经验,深入阐述Java虚拟机执行Java程序的原理。...

    漫画面向对象编程 Java

    借助于漫画展示的形式,面向对象的简、由类创建一个对象的方法、类的编写与对象的创建、类的构造函数、类的方法、修饰符、... 可以在轻松幽默的氛围中对面向对象编程产生浓厚的兴趣,从而为后续的编程进阶树立信心。

    matlab面向对象编程.pdf

    面向对象编程(OO)在软件开发中运用了识别模式和定义分类系统的标准科学与工程实践。分类系统和设计模式使工程师和科学家能够理解复杂系统,并通过重用他人的工作来提高效率。通过将分类系统和设计模式应用于编程,...

    Python 3面向对象编程

    《Python 3面向对象编程》通过Python 的数据结构、语法、设计模式,从简单到复杂,从初级到高级,一步步通过例子来展示了Python 中面向对象的概念和原则。, 《Python 3面向对象编程》不是Python 的入门书籍,适合...

    PLC的面向对象编程

    PLC(可编程逻辑控制器)面向对象编程是一种先进的编程模式,它借鉴了计算机高级语言中面向对象编程(OOP)的概念,并将其应用于工业控制系统中。尽管在PLC编程中无法完全实现面向对象语言的所有特点,比如“继承”...

    Matlab面向对象编程

    Matlab面向对象编程是一种在MATLAB环境下通过面向对象的方法来组织和构造程序代码的技术。面向对象编程(OOP)的基本特征包括抽象、封装、继承和多态。以下是根据给定的文件信息,对这些知识点的详细解释: 1. 抽象:...

    java面向对象编程pdf

    Java面向对象编程PDF 在这篇文章中,我们将对Java面向对象编程的基础知识进行总结和解释。面向对象编程(Object-Oriented Programming,OOP)是一种编程范式,它将程序设计看作是对象的交互,对象之间的关系和行为...

    LabVIEW面向对象编程技术.pdf

    LabVIEW面向对象编程技术.pdf LabVIEW面向对象编程技术是 LabVIEW 软件开发工具中的一种编程技术,旨在解决大型 LabVIEW 程序的开发和维护问题。该技术基于面向对象编程思想,引入了类和对象的概念,通过类的定义...

    c++面向对象编程实例大全

    《C++面向对象编程实例大全》是一本专为初级学习者设计的教程,旨在通过丰富的实例深入浅出地讲解C++的面向对象编程概念。面向对象编程(Object-Oriented Programming,简称OOP)是C++的核心特性,也是现代软件开发...

    用C-语言实现面向对象编程.pdf

    用 C 语言实现面向对象编程,我曾经在嵌入式控制系统工作过,苦于嵌入式系统编程一直是 C 语言,而没法用 C++或其他高级 语言的面向对象方法编程。经过研究生的学习和探索,偶然间发现高焕堂老师写 OOPC(面向对象 C...

    第16章 LabVIEW中的面向对象编程,labview面向对象的框架,LabView

    在LabVIEW中实现面向对象编程(Object-Oriented Programming, OOP)可以提升代码的可重用性、可维护性和组织性。本章将深入探讨LabVIEW中的面向对象编程框架及其应用。 面向对象编程的核心概念包括类(Class)、...

    (刘艺)《+Delphi面向对象编程思想》随书光盘下载+

    《Delphi面向对象编程思想》是一本专注于介绍如何在Delphi环境下运用面向对象编程技术的专业书籍。这本书由刘艺撰写,旨在帮助读者深入理解和熟练掌握面向对象编程的基本概念、原则和技巧,以及如何在实际的Delphi...

    c#面向对象编程的小案例 c#经典案例.pdf

    "C#面向对象编程小案例:模拟彩票选号器" 本资源详细介绍了C#面向对象编程的小案例,模拟彩票选号器的实现。该案例主要使用C#语言,通过面向对象编程的思想,实现了一个彩票选号器的模拟。 知识点1:System.Random...

    用C语言实现面向对象编程.pdf

    用C语言实现面向对象编程.pdf

    java中的面向接口编程

    "java中的面向接口编程" 面向接口编程是java编程中的一种重要思想,它强调在系统设计中,各个对象之间的协作关系的重要性。这种思想认为,在系统设计之初,各个对象内部的实现细节不那么重要,而各个对象之间的协作...

    写给大家看的面向对象编程书(高清完整第三版)

    面向对象编程(Object-Oriented Programming,简称OOP)是一种广泛应用的编程范式,它将程序设计中的实体抽象为对象,通过对象之间的交互来实现功能。《写给大家看的面向对象编程书》作为一本面向初学者和进阶者的...

    面向对象编程:C++与Java比较教程 英文版

    本书《面向对象编程:C++与Java比较教程》详细比较和对比了当今最流行的两种编程语言C++和Java,旨在教授互联网时代至关重要的技能和面向对象编程技术。作者Avinash C. Kak凭借其成功教授的课程,撰写了一本全面的...

    Delphi面向对象编程思想

    ### Delphi面向对象编程思想 在探讨Delphi的面向对象编程(Object-Oriented Programming,简称OOP)思想之前,我们需要明确几个概念:面向对象编程是一种编程范式,它使用“对象”来设计软件和实现计算机程序。这些...

Global site tag (gtag.js) - Google Analytics