天天看点

Pandora Boot

Pandora Boot是在Pandora的基础之上,发展出的更轻量地使用Pandora的一种方式。

  • Pandora Boot基于Pandora和Fat Jar技术,可以直接在IDE里启动Pandora环境,大大提高您的开发调试效率。
  • Pandora Boot与Spring Boot AutoConfigure深度集成,让您同时可以享受Spring Boot框架带来的便利。

基于Pandora Boot来开发部署在EDAS上的应用,适用于需要使用HSF的Spring Boot用户以及已经使用过Pandora Boot的用户。

在阿里集团内部,几乎所有的应用都用到了各式各样的中间件,比如HSF、TDDL、Diamond等等。本身中间件之间可能就有版本依赖的问题,比如你的应用HSF和Diamond分别依赖了同名jar包的不同版本,maven只会引入其中一个版本。同样的中间件和应用之间也存在同样的Jar包依赖的问题,出于要解决这些依赖冲突的问题,阿里就开发了Pandora ,中文名潘多拉,简单的来说就是一个类隔离容器。

Pandora要解决的问题:

1. 二方包、三方包冲突:NoSuchMethodError

如果不采用类隔离,则应用依赖的所有外部类都植入到CLASSPATH下,都由AppClassLoader进行加载。假设类A依赖类B (version 1),类C依赖类B (version 2),并且使用了B (version2)中的一个新方法,如果此时AppClassLoader加载的是B(version),就会获得成就“NoSuchMethodError”。依赖管理工具Maven给出的解法是,手动将B(version1)排除,它会导致应用起不来。

2. 冲突排查浪费大量时间

试想,如果我们应用系统依赖的中间件逐渐增多时,排除包将是一件很恐怖的事情!那么中间件团队可能就要花大量时间在答疑上面,和应用开发一起折腾了。

可怕的远远不止此,排除好了,应用系统也跑起来了,但到了某个点某个场景可能就出bug了。

3.中间件升级困难

如果要实时升级中间件到推荐版本,但是发现并没有那么容易,应用中引入了十个八个中间件,隔三差五其中一个中间件就有小版本更新,每天盯着中间件的更新情况,那还有时间关心业务的开发了。

那Pandora要解决什么问题:

  • 实现类隔离,提供稳定的运行环境,Pandora实现了应用与中间件之间隔离、中间件与中间件之间隔离,保证了类的正确加载,而不会让依赖关系与依赖加载出现不一一对应的情况。
  • 中间件平滑升级,由于应用服务器会优先加载Pandora的类,因此只要升级Pandora中的插件即可,无需对应用中的pom.xml进行修改。
  • 中间件统一管理,降低中间件接入和使用成本,Pandora会统一管理中间的启动、初始化以及资源回收等一系列操作,开发者基本上只需要了解它的配置及使用特性就好

设计上要满足:

  • 类加载器隔离,Pandora容器本身和应用之间隔离
  • 中间件插件化,定义标准的启动、导出方式和使用方法
  • 集成到spring ioc,减少提供优化的API降低使用成本

Pandora Boot

Pandora Boot是在Pandora的基础之上,发展处的更轻量使用集团中间件的方式;它基于Pandora和FatJar基础,可以可以在IDE里启动Pandora环境,开发调试等效率大大提高。也就是Pandora Boot是Pandora与Spring Boot结合的产物,可以更方便的享受Spring Boot社区带来的便利。

Pandora Boot与Spring Boot的联系和区别

两者区别:

  • Spring Boot 通过 Maven 来管理依赖,是平板化的,最前面提到的二方包、三方包依赖问题,SpringBoot解决不了
  • Pandora Boot很好的管理了中间件应用,用户可以快速的引入各类中间件,平滑的保持中间件升级。
  • Pandora Boot 目前已经很好的集成了 autoconfig,外部也和 AONE2、PSP 等系统进行打通,开发起来更加的方便

关于启动原理

下图要结合内部组件图进行理解(生成的可运行的jar解压)会发现区别:Main-Class被替换成Pandora Boot的!

Pandora Boot

下图是spring boot的其实就是maven plugins的spring-boot-loader-tools把spring-boot-loader.jar打进去了。

Pandora Boot

Pandora Boot原理也是,它复杂点最终呈现的是spring-boot-loader.jar和pandora-boot-loader.jar。

Pandora Boot

OSGI

前景

  • 可插拔的系统,部分的路由器都支持模块的热插拔,硬件界的这种热插拔技术一直就是软件界所追求的,而 OSGI则使得热插拔技术在软件界成为现实。
  • 可动态改变行为的系统,在业界可插拔的系统其实并不少,但可动态改变行为的系统并不多,,OSGI 有一整套完整的机制去实现动态改变系统行为。
  • 稳定、高效的系统,基于OSGI的系统采用的是微核机制,微核机制保证了系统的稳定性,微核机制的系统只要微核是稳定运行的,那么系统就不会崩溃。
  • 规范的、可积累的模块,为什么大部分软件公司都形成不了规范的模块开发方式呢?因为没有统一的规范的基础架构体系的定义,往往每个项目、每个产品都会因为架构师的偏好、技术的发展而导致模块的开发方式完全不同,这就使得软件公司在人员技能要求、培养上很难形成统一,而 OSGI 为这个问题提供了解决方案,基于 OSGI 的系统采用规范的模块开发、部署方式构建系统。当然,采用 OSGI 作为规范的模块开发、部署方式自然给现有梯队提出了新的要求,对于设计师而言,需要学习新的基于 OSGI 的模块分解、设计方式,对于开发人员而言,需要学习新的基于 OSGI 的开发方式,但对于公司形成规范的模块开发方式能带来的回报而言,这样的付出是值得的,而且,这个学习成本并不是很高。

名词解释

 Bundle

Bundle其实就是一个jar文件,这个jar文件和普通的jar文件唯一不同的地方就是Meta-inf目录下的MANIFEST.MF文件的内容,关于Bundle的所有信息都在MANIFEST.MF中进行描述,说的时髦点,可以称它为bundle的元数据,这些信息中包含有象Bundle的名称、描述、开发商、classpath、需要导入的包以及输出的包等等。

 Service

一个OSGI Service就是注册到OSGI框架中的一个java对象。在注册时可以设置这个Service的属性。而在获取Service的时候可以根据属性进行过滤(Filter)。Bundle可以通过Bundle的上下文去注册Service或去查询Service。

Service-Oriented Component Model

简称面向服务的组件模型,提出为了不通过代码的方式注册和获取服务的实例,而Delareative Service就能够使我们梦想成真。

Declarative Service

简称声明式服务或DS容器,对OSGI服务层提出的一直标准(可以看DI Container的影子)。OSGI的发展恰逢2005 SOA风潮最盛,目标是建立一个向后兼容的、内存可控的、可延迟服务激活的、使用配置来代替编码并消除平台侵入的新服务管理模型。

因此在OSGI R4 的重大改进,它对OSGI 的 Core Framework 进行了完善和提升,在 OSGI Core Framework对于系统采用的是 Module+Service的开发方式 ,但按照分结构的开发方式而言,系统按照的是 Module 分解为 Component+Service。

Component

定义(和Service 定义差不多)


对外提供 Service;
使用其他 Component 提供的 Service;
交由 OSGI 框架管理生命周期;      
类型


Immediate:立即激活;
Delayed:延迟激活;
Factory:与Immediate基本相同只有在调用 ComponentFactory 的newInstance后才会激活里面的各个组件      
引用策略(设置policy属性)


Static:默认的方式,在 Component 启动后,如果引用的 service 发生变动时,整个Component会重启;
Dynamic:而采用 dynamic 策略的情况下,在 Component 启动后,如果引用的 service 发生变动,并不会导致整个 Component 重启,而只会调用其中的 unbind和 bind 方法。      

Service Hook

简称服务钩子,是开发人员干预Osgi框架和具体服务间交互过程的工具,R4.2版正式列入Osgi规范。本质上也是一个Osgi服务,提供了观察和空值服务注册表的相关操作,可以限定某个事件可以被那些Bundle接受到,也可以限定某个Bundle可以申请到哪些服务的应用。必须实现 org.osgi.framework.hooks.service.EventListenerHook 接口。

Pandora Boot

ServiceFactory

无论多少个Bundle请求这个服务,获取的的服务对象实例都是同一个,也就是说这个服务是单例的,OSGI中默认的服务就是单例服务,服务对象中跟所有的方法都保持了无状态性。很多时候我们有这样的需求,服务的延迟创建(真正使用服务的时候再创建),无法满足。因此提出了服务工厂。通过implements ServiceFactory<T>来完成。

注:服务工厂返回的“多例”是相对于多个Bundle调用而言的,即多个Bundle多个实例。在同一个Bundle中,Osgi容器对服务工厂的返回结果做了缓存,即使多次调用返回的仍然是同一个实例。

ServiceTracker

服务追踪器,如果系统使用了成百上千个服务,为了解决这些服务的相互依赖而去调配启动级别是一件非常可怕的工作,没有人愿意干这个事情。更何况,OSGI是天然的动态化执行环节,为了不让服务的使用者感到迷惑和复杂,需要一个能够持续追踪服务可用性的解决方案代替使用者去处理服务可用性的问题。

需要导入org.osgi.util.tracker,创建一个类implements ServiceTrackerCustomizer,完成addingService、modifiedService、removedService方法的重写。

ServiceReference

引用服务,有的同学会注意到我们从BundleContext、ServiceReference和ServiceTracker中获取服务引用,将其存放至方法的局部变量中,在方法结束时随方法的栈帧一起销毁。为什么呢?另外Osgi设计的服务引用架构为什么每次使用服务都花费一次查询服务的开销?之所以这样设计,主要是考虑到自身动态执行环境,服务在Osgi环境中随时都可能被停用。

如果长期持有服务实例会有什么后果呢?当服务提供方的这个Bundle被停止后,服务引用依然是有效的,执行这个实例的接口不会报错的,当然这点也并不神秘,它只是一个普通的java对象,存在引用的java对象不可能自动销毁。更可怕的事情是,它的接口、实现类、实现类中引用的其他类以及加载这些类的类加载器都无法被垃圾回收器回收,当Bundle多次更新后,很可能形成内存泄露。

设计理念

由于基于 OSGI 搭建系统带来的是架构级别的改变,带来的最大的影响莫过于设计层面,同时对于系统开发和部署也产生了影响。

模块化设计

尽管大家在设计大部分的系统时候确实是按照模块化的思想去进行的,但可能不会考虑的象 OSGI 定义 Bundle那么的清晰,但这点相信大家都能很快的适应,毕竟模块化的思想大致仍然是相同的,基于 OSGI 就可以按照统一的标准进行模块的设计,同时也会提升在模块设计时对于模块的输入(依赖)  、输出(功能) 、扩展的关注,从而使得模块的设计更加的完善和规范。

因为OSGi中的模块化是物理隔离的,而不基于OSGi的话很难做到物理隔离方式的模块化实现,也就很难使系统真正的做到模块化,通常切换到基于OSGi后就会发现以前的模块化设计做的还是很不足。基于OSGi进行模块化的设计时和传统做设计时的模块设计并没有多大的差别,均为定义模块的范围、模块对外提供的服务和所依赖的服务。

面向服务的组件模型设计

面向服务的组件模型的设计思想是OSGi的核心设计思想,推崇系统采用Bundle的方式来划分,Bundle由多个Component来实现,Component 通过引用Service 或暴露 Service 来完成模块中用例的实现。应该说,这种基于OSGi实现的系统自然就是符合 SOA 体系架构的。

面向服务的组件模型设计更加强调在设计时分解模块中用例的实现(形成组件和服务)以及组件依赖的关注。基于 OSGI 框架构建模块、Component、Service 的依赖时,体现出了超越传统 IoC 容器的优势,在依赖的构建上 OSGI 更是提供了多种灵活的依赖控制方式,例如属性过滤、版本过滤以及延迟加载设置等。

在OSGi中Component以POJO的方式编写,通过DI的方式注入其所引用的服务,以一个标准格式的 XML 描述 Component 引用服务的方式、对外提供的服务以及服务的属性。

动态性设计

要充分的发挥基于 OSGI 的动态性,就要完全的采用面向接口的设计方式,而不是去依赖实现,要记住基于 OSGI 搭建的系统是在运行期才构成依赖的,这和传统的系统有很大的差别,应该在运行期由 OSGI 框架自动的注入。OSGI 提供的对于依赖的各种管理策略(如版本过滤、多样性设置等)在保证系统动态性的同时也保证了系统获取到所需的模块、Component或Service。

在设计时要记住的是所依赖的OSGi服务或Bundle都是有可能动态的卸载或安装的。为保持系统的动态性,在设计时要遵循的原则是不要静态化的依赖任何服务,避免服务不可用时造成系统的崩溃,另外保证系统的“即插即用,即删即无”。

可扩展的设计

OSGi在设计时提倡采用可扩展式的设计,即可通过系统中预设的扩展点来扩充系统的功能。有两种方式来实现:

  • 引用服务的方式

通过在组件中允许引用服务接口的多个实现来实现组件功能的不断扩展,例如A组件的作用为显示菜单,其通过引用菜单服务接口来获取系统中所有的菜单服务,此时系统中有两个实现此服务的组件,分别为文件菜单组件和编辑菜单组件,那么 A组件相应的就会显示出文件菜单和编辑菜单,而当从系统中删除编辑菜单的组件时,A组件显示的菜单就只剩文件菜单了,如此时再部署一个实现菜单服务接口的视图菜单组件模块到系统中,那么显示出来的菜单则会为文件、视图。

  • 定义扩展点的方式

按照 Eclipse 推荐的扩展点插件的标准格式定义 Bundle 中的扩展点,其他需要扩展的 Bundle 可通过实现相应的扩展点来扩展该 Bundle的功能。

面向接口的开发

OSGI 提供为每个 Bundle 提供独立的 ClassLoader 机制的方法则让我们可以真正的做到面向接口的开发。

OSGI R4

OSGI R4 规范由 Framework、Standard Services、Framework Services、System Services、Protocol Services、Miscellaneous Services 共同组成,在规范中对以上的各个方面进行了详细的介绍和规定,在这个章节中会深入的介绍 OSGI R4 中的关键部分,同时结合理论对之前的用户登录验证模块的实现进行讲解和改善。

Core Framework

Pandora Boot
Pandora Boot
Pandora Boot

Module Layer

在OSGI规范中,将Module命名为Bundle,所以,在OSGI框架中是采用Bundle的方式来组织和部署系统的。包含:

  • Module的定义:定义MANIFEST.MF的元数据信息;
  • Module的package共享机制:包含Export-Package、Import-Package、Dynamic Imports(区别在于OSGI框架在resolve Bundle时就不会对其import的package做检测,到使用是才去获取);
  • Module的Classloader机制;
  • Module的国际化;
  • Module的校验;
  • 三种特殊形式的Bundle:包含Require Bundles、Fragment Bundles、Extension Bundles。

Bundle 的通讯机制

Bundle之间通过Service和Packages两种方式来进行通讯。Packages方式是通过定义Bundle的Import-Package和Export-Package来实现,Service机制可以实现引用其他 Bundle 中提供的类的实例。

Pandora Boot

Bundle事件监听

Pandora Boot

LifeCycle Layer

Lifecycle Layer基于Module Layer,使得基于OSGI框架可以动态的对Bundle的生命周期进行管理。 

包含Bundle的状态、管理Bundle的状态、监听Bundle的状态。

Bundle的状态

INSTALLED:Bundle已经成功的安装了。
RESOLVED:Bundle中所需要的类都已解析好,可启动或者说 Bundle 已被停止。
STARTING:Bundle正在启动中,BundleActivator的start方法已经被调用,但没返回。
ACTIVE:Bundle已启动,并在运行中。
STOPPING:Bundle正在停止中,BundleActivator的stop方法已经被调用,但没返回。
UNINSTALLED:Bundle 已经被卸载。      

监听Bundle的状态

在监听 Bundle 的状态上 OSGI 采用的是典型的 Java 中的事件机制,在 OSGI中事件分为 Framework Event和 Bundle Event 两种,Framework Event 用于报告 Framework 已启动、改变了 StartLevel、刷新了 packages或是出现了错误;而 Bundle Event 则用于报告 Bundle 的生命周期的改变。可通过实现BundleListener或SynchronousBundleListener来监听Bundle Event,可通过实现 FrameworkListener来监听 Framework Event。

Pandora Boot

Service Layer

Service Layer定义了Bundle 动态协作的服务发布、查找和绑定模型,Service Layer基于 Module Layer和 Lifecycle Layer,使得 OSGI 形成了完整的动态模型。包含:

  • 服务的注册;
  • 服务的获取;
  • 服务的监听

可以实现ServiceListener可监听Service的状态,推出Declarative Services之后,Service Layer其实就已经成为鸡肋了,Declarative Services提供了更好的服务注册、获取、监听等方式,使得其成为了OSGI R4 中的重要角色,并由此替代了Service Layer。

Service事件监听

SeviceEvent的枚举值

Pandora Boot

服务层的所有事件分派都是同步的,OSGI规范定义了ServiceListener接口的ServiceChanged方法来实现监听服务编号时的执行逻辑。其实服务追踪器能感知添加和移除的编号,就是依赖这个完成的。ServiceTracker的内部类Tracked和AllTracked就是实现了ServiceListener接口,它的open()和close()方法分别调用了BundleContext.addServiceListener和BundleContext.removeServiceListener。

释放和注销服务

 释放服务是指服务使用者声明自己已经不再需要使用该服务对象的行为,服务提供者在OSGI框架注册了服务后,框架会使用引用计数的方式来跟踪服务对象实例是否被使用及被多少处使用。使用BundleContext.getService()申请服务时,计数器值+1,服务使用完后,通过BundleContext.ungetService(),计数器值-1。

注销服务的本质是把OSGI的服务从注册表中异常,当被移除后,与该服务相关的ServiceReference对象就失效了(不是绝对的,请看ServiceReference)。

注:一定要注意保存良好的习惯,否则会出现垃圾回收一直无法被回收的问题。

Security Layer

Security Layer在 OSGI 中采用的主要是 java 本身的 security策略和数字签名策略。

StartLevel Service

StartLevel Service 是 OSGI 规范中的核心服务,是 OSGI 框架必须实现的服务,通过 StartLevel Service 可以动态的设置和改变 Bundle 的启动顺序, OSGI 框架在启动Bundle 时按照启动顺序和 Bundle ID的倒序来启动系统。

  • 静态设置:config.ini设置 osgi.bundles.defaultStartLevel 的值来改变 Bundle 的默认启动级别
  • 动态设置:import org.osgi.service.startlevel

onfiguration Admin Service

Configuration Admin Service 是 OSGI 中非常重要的一个服务,它用于动态的管理Bundle 的配置的属性,而这些属性的存储可能是在本地、也有可能是在远端、甚至可能会在某些设备上,基于 Configuration Admin Service 就可以统一的、动态的、远程的完成 Bundle 配置属性的管理工作了。

Event Admin Service

OSGI提供了Event Admin Service以方便开发人员实现自定义的事件发布和事件处理,和 Java的事件处理模型基本是一致的。事件发布者通过调用 Event Admin Service 的 sendEvent 或postEvent 方法对外发布事件,事件订阅者则通过实现 Event Handler并注册为 EventHandler的服务来订阅感兴趣的事件。

OSGI R5

OSGi Bundle Repository(OBR)

Repository服务规约中提出的,就是OSGI的Bundle仓库,和maven类似都是组件参考,Apache Felix是OBR的一种实现,简化Bundle的部署和使用,另一方面也鼓励了独立Bundle的开发,OBR所以得功能都存在客户端,客户端只需要OBR的xml格式元文件就可以工作。这个文件定义了资源、依赖关系、源码位置、Bundle的位置信息。

Subsystems 服务规约

日益庞大的程序以及显得有些力不从心了,Osgi R5提出一个全新的、面向一组Bundle集合的管理系统和声明周期模型。

服务目标要解决的是资源隔离、资源共享、运行期动态变化等问题,站在战略层面上看为日OSGI in Clould做准备。