Gallery:嵌入:基于事件的松耦合整合
基于事件的松耦合整合[ ]
该文档描述说明基于事件驱动同步的松耦合整合,其实现和备选方法,以及这些标准如何能够提高整个web应用程序群中应用程序的互通性的。
请参阅有关Gallery2嵌入的综述。
什么是基于事件的松耦合整合[ ]
基于事件的松耦合整合是将某个web应用程序整合或嵌入另一应用程序中的方法,如,将某个相片相册程序整合到某个内容管理系统中,或将某个blog与某个论坛进行合并。
其中的两个术语松耦合(loose-coupled)和基于事件(event-based)可以分开解释;但只有将两者结合起来才能够理解应用程序整合/合并的完整方法。
松耦合整合[ ]
松耦合的意思就是两个应用程序不被改变,各应用程序都暴露一个接口,而两应用程序间能通过此接口互访。。两个应用程序不依赖对方,但它们通过这些接口进行交流以承担相互间登入/验证的职责。
整合应用程序A至B中,通过顶部的一些胶水代码(glue code)/作为B的插件,呼叫A获取生成的输出(HTML),接着B将这些生成的输出置入自己的输出并将整个输出返回给用户。
用户 – 请求--> 应用程序B(如一个CMS) |____ B初始化。 |____ B认证 | 活动用户 |____ 如请求针对 -- B呼叫--> 应用程序A(如一个论坛) | B中某A的 |____ A 处理该请求 | 整合页面 并返回输出 | <- A 返回 - | |____ B组合整个 | 页面输出并将 | A的输出嵌入其中 |____ B向用户返回 | 被请求的页面 用户 <-- 输出--- |
应用程序A依靠应用程序B在其对A的呼叫中提供足够的信息。最重要的是有关经认证用户的信息。对于web应用程序来说,B告知A 所生成URL的表现方式也同样重要。
还有就是注意A和B通过各自经清晰定义的接口或协议互访,而他们不会直接对各自的数据结构或函数进行访问。
备忘:请介绍些有关: 接口,API,协议及主仆关系的信息。
基于事件的同步[ ]
由于应用程序是松耦合并且不共享数据的,它们就需要告知彼此相关的变更或足以影响彼此的事件。这些事件包括:
- 新用户的创建
- 某些用户数据的更新,如电子邮件地址
- 某个用户的删除
- 登入,登出
- 应用程序设定
- ...
因此每当新用户在某个应用程序中被创建时,它都会通过该事件经定义的接口告知另一个应用程序相关的用户名,用户数据等。接着后者就会做出适当的反应,在此例中就是创建一个对应的用户并按用户名或id编号使两者进行映射。
备忘:请介绍些有关: hook和事件的信息。
摘要[ ]
松耦合整合加上基于事件的通告将保证两应用程序:
- 可独立开发而无需forked / hacked
- 永不会失去同步
- 易于整合
通常整合都是单方的,正如例子中所见,A被嵌入到B中。B是主,A就是仆。此主仆关系中的交流/通知仅为simplex的。B通知A有关的变更,向A请求数据,并且这种方式永远不会倒过来。
为何要松耦合?[ ]
某个松耦合系统的另一选择就是紧致整合(tight integration),这里数据结构(数据库表格)和/或两应用程序的函数呼叫被交织为一点,这使得其无法维护。跨越式依赖性越来越普遍,而结果系统(几乎)成了两个应用程序的分支了。
如果其中某个应用程序有新版本时会怎样?该整合的编写者会在(旧的)分支整合系统中添加新的修改。或将所有内容应用于新版本。
这不是纸上谈兵,关于这样的整合有很多例子可以佐证,如PNphpBB (phpBB的PostNuke分支)。
这种紧致整合维护起来可不容易,且还需要配备专门的小组来保证其更新读,并从原版应用程序向分支整合不断导入安全性补丁和修正。因此将此产品级别的应用程序托付给某个第三方开发小组可不是好主意。开发者也会失去兴趣,因此将其托付给较大规模的能保证不间断支持和开发的开发团队是至关重要的举措。还有,及时快速的安全性补丁应用也是很关键的。
松耦合整合的主要目的就是让最终用户体验良好的整合性,并保持相关的应用程序在后端中尽量独立。较佳定义的接口和良好的设计将保证最终用户体验最佳的特点,相比紧致整合不会出现不足之处。
为何要基于事件?[ ]
基于事件的变更通知还有另一选择,就是即时通知。即,取代事件发生时立刻通知另一应用程序,另一程序则是当某用户请求下一个嵌入页面时被通知。 注:此处的即时指的是当有需要时才执行某操作,而非立刻的意思。
举例:
- 应用程序B中某新用户被创建(如一个CMS)
- 相同的用户访问B中A的某个嵌入页面
- 当A被B呼叫时, 会检查该用户是否已存在于A中,如为否定,则用户被创建。
- A处理任何情况下的请求并按通常方式返回数据
在此例中,我们谈到了即时的用户创建。
即使用户创建的问题和明显:
- 只要某个用户不访问B中A的某个嵌入页面,变更就不会被同步。比如,如果A具有向所有注册用户群发电邮的特点的话,在这种情况下,它就不会向所有注册用户群发邮件,因此仅有一个子集具有A中的某个帐户。
- 仅有很小一子类的变更能被即时同步。用户的删除,用户数据的变更(如,电子邮件的地址),登出,配置的变更就无法被同步—如果不采用各嵌入请求队列列表检查或定期进行同步的措施的话,这就无法达成。
所以,我们将即时同步归为针对无法充分提供事件/挂钩(hook)的应用程序的低端倒退式的解决方案。
实现[ ]
在此例中,我们使用前面用过的主仆关系。
主应用程序[ ]
主应用程序是接受并处理用户请求并服务于结果页面的主要应用程序。典型的例子就是内容管理系统CMS。
接着让我们假设主应用程序是模块化的,开发者可以使用插件(或模块,扩展及组件等)进行功能上的扩充。要在主应用程序中嵌入一个仆应用程序,如一个论坛或相片管理系统,你就要为主应用程序编写一个新插件。该插件包括一个小的封包脚本用于与仆应用程序进行交流。
封包脚本[ ]
封包脚本(wrapper script)样例:
$userId = getUserId(); // 获取主应用程序中当前活动用户的userId $baseUrl = getBaseUrl(); // 获取所有指向主应用程序URL的base URL include('some/path/to/slaveApplication/embed.php'); // 包括仆应用程序所暴露的接口 embed_init($userId, $baseUrl); // 使用经验证的用户和base URL初始化仆应用程序 $html = embed_run(); // 使仆应用程序处理当前请求并返回结果HTML
接着主应用程序就会将生成的$html接入其某个模板中并返回给用户完全渲染过的页面。
事件同步[ ]
我们还假设主应用程序具有一个内置事件或hook系统,能够允许其插件在事件发生时执行自己的代码,这些事件包括用户的创建,登入或配置的变更。某颇具说明性事件系统的实现就像这样:(我们不提倡使用global,但它们的代表性很强)
function create_user($username, $password) { ... // create the user as usual
global $hooks; if (!empty($hooks[CREATE_USER])) { // 检查是否存在任何已注册的事件侦听器或挂钩 foreach ($hooks[CREATE_USER] as $event_handler) { $event_handler->handle_event($username, $password); } } }
主应用程序某插件可按如下方式注册一个事件处理器:
$GLOBALS['hooks'][CREATE_USER][] = new my_create_user_handler();
而处理器本身就如:
class my_create_user_handler { function handle_event($username, $password) { // 将用户创建与我们嵌入的应用程序同步 include('path/to/slaveApplication/embed.php'); // 包括仆应用程序的接口 embed_init(); // 初始化仆应用程序 embed_create_user($username, $password); // 在嵌入的应用程序中创建用户 } }
当然你不能以这种方式实现一个事件/挂钩系统。但重要的是,主应用程序中某用户被创建的事件以及主应用程序的插件可以将自己注册为事件侦听器或挂入核心功能,这样它们就能够在某用户被创建时执行embed_create_user()函数了。
仆应用程序[ ]
仆应用程序的主要任务就是提供整合相关方法的接口或协议。该接口所提供的方法必须能够:
- 初始化该应用程序(作为一般index.php入口点的备用方法)
- 创建/更新/删除用户
- 登入/登出用户
- ...
接口的样例如下:
function embed_init($userId=null, $baseUrl=null) { include(..); // 包括某些文件,初始化应用程序 $user = get_user_by_mapped_external_userid($userId); set_active_user($user); } function embed_run() { $mode = 'embedded'; $html = main($mode); // 在嵌入模式中呼叫应用程序(返回html,而不是打印至浏览器) return $html; } function embed_create_user($username, $password) { some_api_call_to_create_user($username, $password); }
仆应用程序当然需要达到很多要求才能在嵌入模式中运行。但在此重要的一点就是,应用程序的内部构造是隐藏的,而仅稳定且简单的接口才会暴露给主应用程序。
标准化[ ]
将某web应用程序正如另一应用程序会十分复杂,因为仅有很低比例的应用程序被设计为在被嵌入时仍能运作,而仅有很少一些框架/应用程序提供了可嵌入其他应用程序于其中的手段。
如果应用程序的嵌入有标准化的方式的话,那么主仆应用程序都应符合要求。整合的编写会得到很大促进,每个人都能获益:
- 在为整合设计应用程序时,应有清晰的,标准化的方案及规则来遵循
- 应用程序大范围的互通性将得到保障
- 框架/CMS会因应用程序的完美整合获益
- 专门的应用程序(forums,commerce,photo album等)会找到更多嵌入CMS的方法
- 最终用户会因更好的总体解决方案而获益
- 对所有整合通用的工具将会被开发出来
主应用程序的关键要求就是事件或挂钩系统。如何实现该事件系统则根本不重要。
仆应用程序必须暴露一个经较佳定义的接口或协议。该接口的细节并不重要(目前我们可以在稍后的任何时候改进标准),只要该接口遵循相同的基本原则提供函数用以初始化,处理请求以及创建用户就可以了。
如果所有的接口都经标准化了,那么他就可以通过编写一些胶水代码组装一个具有丰富特点的论坛或gallery的CMS。为各CMS自己编写论坛解决方案(通常都缺乏特点)或多媒体框架,接着浪费自己的资源并让用户觉得不如特点丰富的解决方案,因此这样的一个标准制定是必要的。
一个标准应当有不同层次,意即并非所有的应用程序都能达到某个标准的所有要求,但尽管如此它们应当仍能兼容并作,仅仅是在降低了层次的标准上运作而已。
CMS开发者通常希望能够开发出属于自己的解决办法,以专门用于自己的论坛或gallery。因为他们相信总有一天会得到所有需要的特点,而且这回成为他们CMS的一部分,设计更佳而且代码也更好;当然,我们应当接受并欣赏这种想法,但有了整合/互通性标准,应用程序也能达到这种目的,而无需开发专门的解决方案,比如论坛系统。
支持基于事件整合的应用程序[ ]
主应用程序:
- Gallery2 (90% events / hooks for user / group create / update / delete, logout, login, ..missing: configuration changes)
- Xaraya (100% events / hooks for user / group create / update / delete, configuration changes, logout, login, ..)
- Wordpress (90% events / hooks for user / update / delete, rewrites, logout, login, ..)
- Drupal (80% maybe?)
- Joomla (100% joomla 1.5; login, logout, create, update, delete user, block, activation, system before and after start)
- Typo3 (50% maybe?, e.g. no unified create user event / hook)
- TikiPro (?%)
- ...
仆应用程序:
- Gallery 2
- Phorum.org
- 几乎所有的脚本,因为你总是添加一个层/接口,而它会呼叫内部函数并为其添加输出缓冲。但是它们需要提供基本的方法以指定URL的格式等。
参考[ ]
基于事件的松散整合是目前软件整合的最突出的手段了。下面则是有关该话题参考及论述:
- Daniel J. Barrett, Lori A. Clarke, Peri L. Tarr, Alexander E. Wise, "A Framework for Event-Based Software Integration" (Postscript, 461K). ACM Transactions on Software Engineering and Methodology (TOSEM), Volume 5 Number 4, October 1996. 另见:http://www.blazemonger.com/publications.html#ACADEMIC
更多专门涉及web/PHP互通性的链接: