OSGI设计模式之whiteboard pattern
赵光赫
2023-12-01
OSGI的模块化带来的一个好处就是动态化。动态化就是要求软件构件之间的耦合是松散的,不会因为被依赖的构件的撤走而导致依赖它的构件的崩溃。所以,我们说在OSGI里提供了export-package/import-package的机制,但如非必要,它不能过分使用,因为如果export package的bundle被卸掉后,import package的那个bundle也会从active或resovled的状态转为installed的状态。而采用osgi service的方式则是被推荐的bundle间耦合方式(被引用的服务不存在时,bundle不会从active或resovled的状态转为installed的状态)。
从上面的描述可以看出,OSGI模块化、动态化不是说将jar包改造成bundle,并放在osgi framework上运行就能达到的,更多是和设计相关。
我们下面来看看osgi常用的whiteboard设计模式。
最开始,我是从httpServcie应用的whiteboard pattern开始认识whiteboard pattern的。但后来发现whiteboard pattern并不仅限于用在httpService方面。我们先看看httpService应用的whtieboard pattern:
一般来说,提供httpService服务的bundle(以下简称为BundleA)有点类似一个servlet容器,它会在某个端口上监听Http请求,然后发布出HttpService服务。其它bundle(以下简称为BundleB)可以引用这个HttpService,通过这个HttpService的registerServlet的方法来注册自己实现的servlet,使BundleA知道有这样的servlet(会和url作映射,和web.xml里注册servlet有个ServletMapping一样)。这样,当BundleA收到http请求后,根据url的映射,找到BundleB的servlet,并交由它处理,获得Http response,从而实现对Http请求的相应。
如果有很多个bundle各自提供了自己的servlet的话,我们就需要在每个这样的bundle里写注册自己的servlet的代码,而且由于OSGI环境的动态性,HttpService有可能在任何时间发布出来或被撤下,BundleB还不能在activator里做servlet的注册,得通过ServiceListener或ServiceTracker之类的方式来处理这些问题(当然用DS或Blueprint的方式可以简化这个操作)。这样一个小小的servlet就需要写大量的辅助代码才能达到动态性目的,而且每个这类的bundle都得做一次。另外还有Servlet的反注册问题呢!所以,显然这不是个好的解决方案。
这时我们就需要用到whiteboard pattern来简化这个工作。继续上面提及的BundleB,将BundleB的servlet发布成OSGI service(注意:不是在HttpService上注册),并将mapping pattern作为这个servlet服务的一个属性。接着,我们再做一个whiteboard Bundle(以下简称BundleC),它负责引用HttpService服务,同时监听servlet服务发布的事件。当BundleC收到servlet服务发布的事件时,就调用HttpService来注册这个Servlet。也就是说提供Servlet的所有bundle都不再需要自己写代码来注册servlet了,全由BundleC来做这个注册的工作,一劳永逸了。提供Servlet的所有bundle都只需要发布servlet服务即可。
所以,从whiteboard的字面上来理解,相当于BundleB这类提供servlet服务的bundle将servlet的相关信息“写到”osgi service registry这个“whiteboard”上,而BundleA也将自己提供的HttpService服务“写到”osgi service registry这个“whiteboard”上,BundleC则利用这个“whiteboard”上的“信息”来完成注册servlet的工作。
无论BundleC是用ServiceListener还是ServiceTracker来处理动态性,这些代码都由BundleC封装了,其它bundle就只需要发布Servlet服务即可。