Apache OFbiz service engine 源码解读

上一篇看完了ofbiz entity engine,这篇再来过一下ofbiz的service engine。service engine层在设计模式的使用上跟entity engine有些类似,最典型的就是“业务代表”模式。service engine跟entity engine是紧密相关的,大部分的业务系统所要执行的服务都是跟关系数据库相关的。

service engine对于服务编写的方式有着非常广泛的“自由”,你可以选择它内置引擎支持的任何一种方式来编写服务。同时对于eca的支持对于事务的支持以及对于权限的支持都非常的完善。

Service执行方式的抽象——LocalDispatcher

该接口提供了ofbiz中service调度执行器基本协议方法的定义,包括:

  • 同步执行
  • 异步执行(其实也是基于job加多线程)
  • 定时执行(基于job以及job pool)
  • 添加【提交/回滚事务时执行的】service
  • 一些关键“传输对象”的访问器
dispatcher相关的类继承关系:

备注:此接口是service engine中比较关键的“业务代表”接口,其取名为LocalDispatcher是为了以示跟后面提及的RemoteDispatcher(用于支持RMI的remoteEngine)进行区分。

LocalDispatcher的抽象实现——GenericAbstractDispatcher

GenericAbstractDispatcher提供了对LocalDispatcher中定义的部分方法的实现(主要涉及到定时执行、事务相关的几个接口方法)。它内部依赖几个关键对象,是在其子类中注入的,这几个关键对象名:
  • ctx:serviceengine的上下文对象
  • dispatcher:ServiceDispatcher的实例,是真正的服务执行者

LocalDispatcher的标准通用实现——GenericDispatcher

上文提到GenericAbstractDispatcher中有几个关键对象是在其子类中被初始化的。其实,就是在GenericDispatcher中(GenericDispatcher是GenericAbstractDispatcher的唯一子类)。关键对象的初始化位于init方法中,而init方法又是被GenericDispatcher的构造方法调用。此处需要注意的是,GenericDispatcher的两个构造器(包含无参构造器)都被设置为protected。也就是说你无法在外部通过调用无参构造器来初始化GenericDispatcher的实例(如果允许这种行为,那么其父类GenericAbstractDispatcher的那两个关键对象将不能保证被实例化)。那么GenericDispatcher是在哪里被实例化的?这需要谈到GenericDispatcher提供的几个公有静态方法:

如果你关注一下他们的返回值类型,你就能知道为什么之前提到LocalDispatcher是“业务代表”对象了。它将成为应用程序其他组件与service engine打交道的主要代理人(就跟entity engine层的Delegator接口一样),因此在这些方法中实例化的GenericDispatcher对象都向上转型为了LocalDispatcher。

因为它是关键对象创建它的代价比较大,考虑到性能因素GenericDispatcher为所有的dispatcher对象提供缓存。另外一个需要关注的是:GenericDispatcher大量依赖ServiceDispatcher(从其构造器需要注入ServiceDispatcher的实例就可以看出来)。或者你可以看到下面实现的所有的runXXX接口方法基本都是依赖ServiceDispatcher去执行的。

真正的服务调度器——ServiceDispatcher

上面说到其实之前的Dispatcher都不是服务的真正执行者。服务的真正执行者是:ServiceDispatcher。千万不要被上面的那些XXXDispatcher后缀的类所欺骗,它们只不过是官方代表而已。ServiceDispatcher并没有继承以及实现任何东西,但是它拥有一些强大的部件:
protected Delegator delegator = null;
protected GenericEngineFactory factory = null;
protected Authorization authz = null;
protected Security security = null;
protected Map<String, DispatchContext> localContext = null;
protected Map<String, List<GenericServiceCallback>> callbacks = null;
protected JobManager jm = null;
protected JmsListenerFactory jlf = null;

ServiceDispatcher的构造方式跟GenericDispatcher类似。都是将构造器设置为protected,对外通过静态方法获得实例,同时拥有对它自身实例对象的缓存。

其实,在业务系统中大部分的操作都是跟数据库打交道,而Delegator又是entity层的业务代表,所以ServiceDispatcher非常依赖于Delegator。

而服务最终到底是怎样被执行的呢?最终每个服务都是依赖于符合它执行条件的执行引擎来执行的!(关于各种引擎,后面会介绍)。上面的关键部件里有个GenericEngineFactory的实例,它用于创建合适的执行引擎。

下面简述一个通用服务的执行过程:
  • 定义或者初始化一堆执行过程中需要用到的参数
  • 检测服务是否带有锁标识:如果有,则创建一个锁,并获得锁对象
  • 创建一个上下文对象,如果服务的执行过程中需要携带参数,将参数都加入上下文对象
  • 检测上下文对象的Locale
  • 对当前service进行log
  • 检测当前服务是否定义有event,如果有,一次全部取出
  • 取得合适的执行引擎
  • 更新上下文对象中,IN 类型参数的默认值
  • 如果该服务被定义为在事务中执行,则启用事务
  • 依次执行“global-rollback”、“global-commit”、“auth”相关event的action,并带出执行结果
  • 检查pre-auth的执行结果是否为失败或错误
  • 检查上下文对象中键值对参数的权限,同时设置【key为"userLogin"】的value为【用户信息对象】
  • 检查userLogin对应的用户信息对象,如果权限验证失败,则抛出异常
  • 执行“in-validate”预检查事件,同时带出对action的执行结果
  • 检查是否有失败或错误产生
  • 检查所有上下文参数中输入参数是否合法
  • 执行“invoke”事件,同时带出action的执行结果
  • 检查是否有失败或错误产生
  • 通过服务执行引擎来执行服务并取得执行结果
  • 通过服务执行引擎发送服务回调,如果执行结果不为空,则将其加入result结果集
  • 检查是否有错误或失败产生
  • 错误处理
  • 重新构建一个eca的上下文对象
  • 执行“out-validate”事件,同时带出结果集
  • 验证结果集中得输出参数是否合法
  • 执行“commit”事件,同时带出结果集
  • 检测结果集是否含有失败或者错误
  • 执行“global-commit-post-run”事件,同时带出结果
  • 进入异常处理部分:通过服务引擎,发送带异常的回调,回滚事务
  • 进入finally部分:如果有错误,回滚事务,否则提交事务,调用notification,这里的事件是由结果集来决定的
  • 进入外层finally部分:如果锁没有被释放,则释放锁。恢复父级事务
  • 执行“return”事件,同时带出结果集
  • 返回结果集,方法结束

服务调度的上下文——DispatchContext

在之前的不少地方其实已经用到过DispatchContext,它为服务的执行提供上下文(主要是一些关键对象及参数)。它主要提供了一堆get访问器,以及一些辅助方法,比如加载本地servicemap以及globalservice map等。

异步请求器——GenericRequester


该接口定义了两个回调方法:一个用于接受一个map(通常是上面serviceDispatcher的执行结果),另一个接受一个Throwable对象。它主要的用途就是在调用runAsync方法的时候(异步执行服务),将该接口的实现者作为参数传入(可以理解为回调对象)。主要的过程是这样的:

首先,ServiceDispatcher实例调用runAsync方法,在该方法接受一个GenericRequester类型的参数,在方法内部真正的执行引擎会调用它自己的runAsync方法,该接口的实例继续向前传递作为参数。而执行引擎对该runAsync的实现主要在GenericAsyncEngine中,实现的方式是这样的:它最终会用GenericRequester的实例以及一些其他对象创建一个我们下面会提到的GenericServiceJob的实例。最终所谓的Async只不过是转化为了其他线程去独立执行一个Job而已,在该job的exec方法中,dispatcher会同步执行runSync,然后得到结果,提供给GenericRequester调用(上面截图中得方法)。因此这里所谓的runAsync其实是在另外一个独立的线程上去执行runSync,然后将结果传递给回调对象,执行回调方法而已。

Service中的任务——Job

job在serviceengine中有非常重要的作用,除了用于支持定时执行外,所谓的异步执行也是通过job来实现的。

上图为ofbizservice engine中的Job继承链。其中,Job接口定义了在serviceengine中作为一个Job应该遵守的“协议”。协议列表如下:

其中有两个关键方法:
  • exec: 定义如何执行一个job
  • queue: 标识一个job是否“已压入队列”

用于执行服务的抽象Job——AbstractJob


从之前的继承关系图可见,它实现了Job接口,并对其进行了一定的扩展。它提供了一个带两个参数的构造方法,用于注入jobId/jobName。并额外定义了两个protected的 成员变量:runtime/sequence。

对于最关键的exec方法,此处仍然将其标记为abstract,以待继承者实现。

通用服务Job的异步执行类——GenericServiceJob

它继承了AbstractJob类,并注入了两个“上下文”实例。
  • dctx: DispatchContext类的实例
  • context: Map<String, Object>的实例

同时还通过构造器注入了GenericRequester类的一个实例,来达到它异步获取结果的目的。

在exec方法的实现中,它首先通过dctx获得LocalDispatcher类的实例。然后通过调用其runSync的实例方法来获得一个Map的结果对象。如果执行结果出现错误,则进入错误处理流程,否则通过GenericRequester的实例requester来接收结果。大致的方式就是在另一个线程上同步执行服务。

在此基础上,GenericServiceJob又定义了一个public方法:getServiceName,后面我们会看到它的继承类会override该方法。

对持久化的job的调度执行——PersistedServiceJob

该类实现了entity service job。支持将job持久化到数据库中,然后经过调度再执行。这里牵扯到数据库中的一张表:JOB_SANDBOX(该表位于数据库ofbiz中)。它用于存储持久化到数据库中的job信息。

因此该类的构造函数包含一个GenericValue类型的实例,用来表示JOB_SANDBOX中的一条job信息(在之前的讲ofbiz entity engine的文章中,我们提到过GenericValue,它用于表示一个通用的数据实体)。

queue方法的实现:

首先,它调用父类的queue方法(父类的实现,没有什么特别的意义)。然后它通过重新获得该job对象,并显式刷新它(以防缓存等导致数据不一致),如果产生异常,则直接将runtime设置为-1,在AbstractJob中对isVaild的实现么,如果runtime小于0,则该方法返回false。接着,如果通过了验证,我们还需要check该job的“cancelDateTime”、“startDateTime”属性,如果它们两个其一不为null则说明该job不可用(已经执行过了),仍然将runtime设置为-1。否则就设置startDateTime,statusId并将其持久化到数据库。

另一个关键方法exec,在这里没有被override,而是沿用父类的实现。但该类提供了很多私有的辅助方法,来处理相关逻辑。比如如何初始化,如果重试,如果处理失败以及完成逻辑。

job调用执行器——JobInvoker

JobInvoker专门用于执行job。它提供多个接口用于对job的执行进行控制,包括:开启执行、停止执行、唤醒、杀死执行线程、以及提供对一系列执行参数的获取。它实现了Runnable接口,可见它利用多线程技术来执行job。

构建它需要至少一个参数:JobPoller的实例(JobPoller是后面要谈到的job的轮询器)。

来看下它的构造方法:

public JobInvoker(JobPoller jp, int wait) {
        this.created = new Date();
        this.run = true;
        this.count = 0;
        this.jp = jp;
        this.wait = wait;

        // service dispatcher delegator name (for thread name)
        String delegatorName = jp.getManager().getDelegator().getDelegatorName();

        // get a new thread
        this.thread = new Thread(this);
        this.name = delegatorName + "-invoker-" + this.thread.getName();

        this.thread.setDaemon(false);
        this.thread.setName(this.name);

        if (Debug.verboseOn()) Debug.logVerbose("JobInvoker: Starting Invoker Thread -- " + thread.getName(), module);
        this.thread.start();
    }

在初始化了相关参数之后,它将自身实例用于构造它的内部thread变量,在将线程显式设置为非daemon之后,即刻启动该线程的执行。这种构建方式可以使得从对象被创建开始,它就以一个独立的线程开始运行,同时因为该thread成为它的一个内部成员变量,它拥有对该thread的完全控制权。

在正常情况下,run方法内运行着一个while循环,它首先从jobPoller获得下一个待执行的job,如果为空(代表暂时没有等待执行的Job)则进入等待状态。如果获取到Job则执行。

持久化Job的轮询器——JobPoller

JobInvoker负责job的调用执行,在jobinvoker中我们看到它是通过jobpoller的next方法获得即将执行的job的。所以,真实提供job的是JobPoller!

在JobPoller内部维护着两个关键变量:
  • pool:一个“线程池”,它是List<JobInvoker>类型的,前面提及到因为JobInvoker是一个独立的线程,所以pool是它的一个集合。
  • run:是一组待运行的job集合

在构建jobPoller的时候,通过构造方法注入一个JobManager的实例(在run方法中,真正获取job的就是JobManager的实例)。在初始化相关参数之后,JobPoller以跟JobInvoker相同的方式启动线程。

线程启动,执行run方法,它首先睡眠30s(这可以让它等待jobManager重新加载crashedjob)。然后通过jobManager获取一组joblist。将他们一个个入队,如果产生任何异常则执行stop动作。

下面我们来看入队流程:

首先,它将当前处理的job加入上面的run变量中。然后根据当前run的size以及pool的size重新计算是否需要扩大pool(创建新的jobInvoker,来增加吞吐量)。

另外pool里的每个jobInvoker实例都是从jobpoller的next方法获得job,而next方法就是从run集合里删除并返回一个job。

pool的初始化是通过createThreadPool方法来完成的。它会创建最小数量的JobInvoker,而上面我们也提到过,JobInvoker是创建即执行的线程。

job 的控制中心——JobManager

上面有提到过JobManager,事实上它是 “传输对象”模式的应用。它集成了JobPoller/Delegator等关键对象来完成对job的管理。

提供的功能包括:触发运行一个job、获取job list、以及创建schedulejob、获取job执行的相关运行时信息、对job执行器的控制等。

引擎的接口抽象——GenericEngine

是时候关注serviceengine中那些默默无闻的工作者——服务执行引擎。上面说到ServiceDispatcher是真实的执行服务的所在地,其实,说到最后它还是借助于这些引擎来完成的!

GenericEngine定义了引擎的一些契约方法,主要包含了服务的同步/异步调用。以及发送服务回调方法。

可以看到所有runXXX方法的第一个参数都是类型为String,形参名为localName的参数。该参数用于传入指定的LocalDispatcher的名称。第二个共有参数ModelService类型的实例:它是一个通用的服务Model封装类,封装了执行一个服务所需要的一切(后面会提到)。第三个共有参数为一个上下文对象。

抽象执行引擎——AbstractEngine

AbstractEngine作为一个抽象的服务引擎,实现了GenericEngine。这个实现主要提供了两个保护级别的变量:
  • dispatcher:ServiceDispatcher的实例
  • locationMap:一个map,用于存储servicename跟该service的location之间的对应关系
其实GenericEngine还有一个实现类:InterfaceEngine,这里称之为接口引擎,但因为在ofbiz中很少用到,所以这里不作过多介绍。

JMS服务引擎——JmsServiceEngine

该实现在AbstractEngine的基础上,提供了几个跟jms相关的方法:
  • runTopic - 以jms的“topic”模式运行(publish/subscribe)
  • runQueue - 以jms的“queue”模式运行
  • runXaQueue - 以jms的“xaQueue”模式运行
当然还有一些辅助方法,比如查找服务列表,构建消息对象等。

通用异步引擎——GenericAsyncEngine

该抽象类是对AbstractEngine的两个实现者之一(另一个就是刚刚提到的JmsServiceEngine)。同时,该抽象类也是众多子引擎的公共父类。

它将runSync的一系列重载方法标识为“Abstract”,只实现了runAsync相关的方法。

简单来理一下对runAsync方法的实现:

如果该服务/job是需要持久化:

它首先往数据表RUNTIME_DATA中插入一条数据,并将上下文对象序列化入字段“runtimeInfo”。同时往表JOB_SANDBOX中插入一条数据

如果无需持久化,它直接构建一个GenericServiceJob的实例运行job。

Entity服务引擎——EntityAutoEngine

该类提供对entity engine的实现,主要针对entity的CRUD操作。它继承了上述的GenericAsyncEngine。并不表示它在语义上就被定义为async执行模式的引擎,而事实上这里的继承主要的作用是代码复用(从而不必多次实现runAsync),下同。

SOAP客户端引擎——SOAPClientEngine

内部的serviceInvoker方法,主要提供了对调用remotesoap 服务的实现。而该方法内部采用apacheaxis 2来实现远程soap服务的调用实现。

脚本引擎——ScriptEngine

这是一个脚本引擎,用于对多种脚本语言提供支持。它基于J2SE提供的ScriptEngine来完成脚本的执行。在方法内部会封装一个上下文参数,将其传递给Java提供的ScriptEngine,最终通过调用Java提供的ScriptEngine的eval方法。

服务组引擎——ServiceGroupEngine

这个引擎在ofbiz中没有使用,不作过多介绍。

标准Java方法服务引擎——StandardJavaEngine

该引擎用于执行标准的Java方法,在内部它通过反射来实现。首先通过传入的ModelService的实例,获得其定义的location,再通过classloader加载服务类的Class实例,最终通过反射执行给定的方法。

Groovy脚本服务引擎——GroovyEngine

该引擎用于支持groovyscript在ofbiz上运行,ofbiz中对groovy的依赖还是蛮大的。GroovyEngine与ScriptEngine有些类似,需要提供script的location,以及执行的参数作为上下文。最后通过第三方groovy运行时实现执行groovyscript。

HTTP 引擎——HttpEngine

该引擎用于执行httppost请求,并返回请求结果。

该service的location是其URL。通过传递该service的location以及相关的参数,构造一个HTTPClient的实例来触发post请求。

BeanShell引擎——BeanShellEngine

这个不作过多介绍,在ofbiz使用groovy作为数据访问的脚本语言之前,主要使用beanshell做这件事,但后来被groovy所替代。

BSF 引擎——BSFEngine

BSF 是一系列的Java类的集合,它为Java应用程序提供对脚本语言的支持。在runSync内部,它借助apache的BSFEngine来执行。

XML RPC引擎——XMLRPCClientEngine

ofbiz提供了对RPC的支持。在实现的时候,首先有个对客户端配置的封装类,需要设置基本的配置信息,比如身份认证。然后根据配置信息创建一个XmlRpcClient。它封装了具体的实现细节(具体内部也是通过apache的xmlrpcclient库来实现的)。最终调用XmlRpcClient实例的execute方法。

Rmi 服务引擎——RmiServiceEngine

RmiServiceEngine的实现借助于ofbiz的RemoteDispatcher。顾名思义,它是一个remotedispatcher,但它只是定义了远程方法调用的接口。

RmiServiceEngine先通过service的location查找到属于该service的RemoteDispatcher然后调用它的runSync方法。

通用消息监听器——GenericMessageListener

GenericMessageListener是ofbiz service engine里关于jms的顶层接口。它继承了j2ee规范中关于jms的MessageListener(该接口需要实现一个方法:onMessage)。而GenericMessageListener又定义了几个新的接口方法:

  • close:关闭listener以及所有的连接
  • load:启动listener以及所有的连接
  • refresh:刷新连接
  • isConnected:判断连接是否可用

Jms listener的抽象实现——AbstractJmsListener

AbstractJmsListener实现了GenericMessageListener以及j2ee的ExceptionListener接口。

在构造方法中,它初始化了一个LocalDispatcher类的实例。

对onMessage方法,它在接受到message之后,首先判断它是否是MapMessage的实例,如果是则运行runService方法。

而runService方法接受的参数就是最终在onMessage中拆封服务信息的message,然后通过localDispatcher执行。

refresh方法的实现非常简单,先close然后再load

onException方法在产生异常的时候将会被触发。首先,它先将当前连接的状态设置为close状态。然后一直不停得调用refresh方法,直到isConnected方法返回true。当然,一旦产生异常就会睡眠10秒钟。

Jms topic listener——JmsTopicListener

jms将消息传递模式分为两大类:topic/queue,其中topic模式支持publish/subscribe。

JmsTopicListener继承自AbstractJmsListener,重新实现了两个方法:
  • close:除了关闭连接,它还需要关闭topicsession
  • load:做一些初始化动作,同时开始建立连接
在load方法中,先通过jndi找到相关定义,然后创建topicsubscriber

Jms queue listener——JmsQueueListener

主要的实现同JmsTopicListener,它也重新实现了close/load方法,只不过不同的是:在load方法中,创建的是queuereceiver。

Jms listener创建工厂——JmsListenerFactory

JmsTopicListener与JmsQueueListener的构造器的注释中说明了:建议不要自己手动创建它们,而是通过JmsListenerFactory来创建。

JmsListenerFactory实现了Runnable接口,因此它利用的是多线程技术。在它的构造器中,他就会启动自身实例的run方法,并将自身设置为非daemon线程。在启动之后,它会根据xml定义,去创建listener。另外,它还定义了关闭/刷新listener的方法。

远程调用的dispatcher接口——RemoteDispatcher

之前我们谈到ofbiz支持的众多service engine中就有一个RmiServiceEngine,它用于远程方法调用。而它的执行则依赖于RemoteDispatcher。

之所以最开始我们介绍的“业务代理”称之为LocalDispatcher,就是为了跟这边的RemoteDispatcher以示区别。

RemoteDispatcher实现了JDK提供的Remote接口(该接口未提供任何接口方法的定义,属于标记接口)。在RemoteDispatcher中定义了一系列运行service的方法:

你可能感兴趣的:(apache,Engine,service,ofbiz)