OSGi博客笔记

本文是对killko一些博客的笔记,可以在《不可错过的OSGi入门学习资源》中找到,包括:

此外,还参考了:

阅读前建议先阅读

走近Java模块化系统OSGi

OSGi是什么

  1. OSGi作为Java的模块化规范,其目标(也是软件设计的目标)是复用、内聚、耦合。
  2. 被神化的“动态性”、“热插拔”的特性,是OSGi规范带来的一种可能,需要配合好的设计才能达到,并不是一定会有的。
  3. OSGi是不是一个应用层面的框架(如spring、structs等),而是设计层面规范,类似面向对象的设计。所以,不要问“怎么将spring和OSGI集成?”这样的问题。
  4. OSGi的目的是进行模块化,模块(bundle)的物理形式是Jar包。
  5. OSGi的课程主要是让你去设计应用的,是形而上需要个人领悟的,不是去学一套应用框架那种。

OSGi framework

OSGi规范定义了一个OSGi framework平台,它是运行在JVM上的应用,负责管理模块bundle。

bundle生命周期

bundle需要关注它的生命周期。

  1. install:框架通过classloader(类加载器)来装载bundle里的类和资源。
  2. resolve:检查bundle依赖的package是否可用。
  3. start:运行activator里的start方法,方法运行完毕后进入"ACTIVE"状态,至此bundle可用正式使用了。
生命周期层状态转移

bundle的隔离

  1. 模块以Jar包形式存在,Jar包的物理边界也是模块的物理边界。
  2. 在OSGi规范下,得显式地说明模块之间的依赖关系。OSGi是利用JVM的classloader和它的父委托模型(PDM:Parent Delegation Mode)来实现这点的。
  3. 每个bundle都分别用一个classloader来加载里面的类,所以不同bundle之间的类,在默认情况下,是互不可见的。
  4. 如果没有额外处理,一个bundle里的类要访问另一个bundle里的类时,通常会出现ClassNotFound的异常。

bundle之间的耦合

方式一:import/export package的机制

  • OSGi通过import/export package的机制来控制bundle间有限地耦合。
  • Export/Import package是通过bundle里的META-INF/manifest.mf文件里指定的。

方式二:OSGi service方式(更松散的耦合)

  • OSGi framework实现服务的注册、查找和使用。该服务是实现某种接口的bean实例。
  • 该服务是实现某种接口的bean实例,所以本质上osgi service就是一个bean。
  • 通常会把接口定义在bundle A里,接口的实现则在bundle B里,并将接口实现实例化后注册成osgi service,bundle C可以引用这个service。bundle A需export接口定义所在的package,而bundle B和C则需import这个package。bundle B和C之间就不需用export/import package来耦合了,实现了B和C之间的解耦。
  • OSGi应用中,会有大量的osgi service存在,可以说osgi service是osgi规范中最重要的机制,没有之一。

其它机制

OSGi规范还提供了Event、配置管理(ConfigAdmin)、声明式服务(Declarative Service)、Service Tracker、Blueprint等等运行时机制,方便我们构建模块化的应用系统。

创建OSGi Hello World工程

本节内容参考了《创建OSGi Hello World工程》,需要注意的是,这篇文章中maven配置文件pom.xml中的<name/>标签需要进行修改,不能以冒号分隔,示例如下:

<name>osgi-demo</name>
   

模块构建

1.创建Maven项目。
2.在包中创建 OSGI Activator。

package mindw.osgi.demo;

import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;

public class MyActivator implements BundleActivator {

    @Override
    public void start(BundleContext context) throws Exception {
         System.out.println("Hello world!");
    }

    @Override
    public void stop(BundleContext context) throws Exception {
         System.out.println("Stop bundle!");
    }

}

3.配置pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns:pom="http://maven.apache.org/POM/4.0.0">
    <modelVersion>4.0.0</modelVersion>
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>
    <groupId>mindw.osgi</groupId>
    <artifactId>demo</artifactId>
    <packaging>jar</packaging>
    <version>0.1</version>
    
    <name>osgi-demo</name>
   
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                    <encoding>${project.build.sourceEncoding}</encoding>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-jar-plugin</artifactId>
                <configuration>
                    <archive>
                        <manifest>
                            <addClasspath>true</addClasspath>
                            <classpathPrefix>lib/</classpathPrefix>
                        </manifest>
                        <manifestEntries>
                            <Class-Path>${project.build.finalName}.jar</Class-Path>
                            <Built-By>Ponder</Built-By>
                            <Bundle-ManifestVersion>2</Bundle-ManifestVersion>
                            <Bundle-Name>${project.groupId}.${project.ArtifactId}</Bundle-Name>
                            <Bundle-SymbolicName>${project.name}</Bundle-SymbolicName>
                            <Bundle-Version>${project.version}</Bundle-Version>
                            <Bundle-Vendor>${project.groupId}</Bundle-Vendor>
                            <Bundle-Activator>${project.groupId}.${project.ArtifactId}.MyActivator</Bundle-Activator>
                            <Export-Package>${project.groupId}.${project.ArtifactId};version="0.1"</Export-Package>
                            <Import-Package>org.osgi.framework</Import-Package>
                        </manifestEntries>
                    </archive>
                </configuration>
            </plugin>
        </plugins>
    </build>
    <dependencies>
        <dependency>
            <groupId>org.osgi</groupId>
            <artifactId>org.osgi.core</artifactId>
            <version>4.2.0</version>
            <type>jar</type>
        </dependency>
    </dependencies>
</project>

4打包生成 demo-0.1.jar。

servicemix安装

为了使用模块,需要一个支持模块化运行的环境,参考文章的作者使用了servicemix。

servicemix介绍

  • Apache ServiceMix是一个灵活、开源的集成容器。
  • 它可以将Apache ActiveMQ、Camel、CXF和Karaf集成为一个强大的运行平台。
  • 你可以通过它来建立自己的集成解决方案。
  • 它提供了一个由OSGi支持的完整、企业级的ESB(企业服务总线)。

安装

  • servicemix官网下载zip压缩文件。(版本都可,5.4.0版本86M左右,7.0.1版本113M左右)。
  • 解压压缩包即可。

模块使用(生命周期变化)

  • 将demo-0.1.jar放入servicemix的deploy目录下,本人放在 apache-servicemix-5.4.0\deploy 中。
  • 进入 apache-servicemix-5.4.0\bin 目录,运行 servicemix.bat。可以看到以下界面。"Hello world!"表示模块已经加载。
模块启动
  • 使用list命令,可以查看模块状态。可以看到模块ID为220,状态为 Active。
模块状态查看
  • 输入stop 220,可以看到"Stop bundle!",然后再输入list,可以看到当前模块处于Resolved状态。
模块停止
  • 输入uninstall 220,模块就被卸载了,再输入list就看不到模块信息了。
  • conrtrol + d 可以结束servicemix容器运行。

Package与服务

该部分可以参考《OSGi中Bundle间的耦合:Export/Import Package与服务》

注册服务

在BundleActivator的start函数中可以通过BundleContext注册服务。

public class MyActivator implements BundleActivator {

    @Override
    public void start(BundleContext context) throws Exception {
        Dictionary<String, String> props = new Hashtable<String, String>();
        props.put("ServiceName", "Calculation");
        context.registerService(ICalculation.class.getName(), new Calculation(), props);
        System.out.println("Service registered!");
    }

    ...
}

引用服务

在BundleActivator的start函数中可以通过BundleContext获得服务。

public class MyActivator implements BundleActivator {

    @Override
    public void start(BundleContext context) throws Exception {
         ServiceReference[] refs = context.getServiceReferences(ICalculation.class.getName(), "(ServiceName=Calculation)");
         System.out.println("demo3:"+ICalculation.class.getName());
         if(refs!=null && refs.length>0){
             ICalculation service=(ICalculation)context.getService(refs[0]);
             System.out.println("1+1="+service.add(1, 1));
             System.out.println("2-1="+service.sub(2, 1));
             System.out.println("2*3="+service.sub(2, 3));
         }
    }
    ...
}

依赖疑问

在开发中,Demo2中提供服务(包含接口定义),Demo3中使用服务(不包含接口,但依赖接口),但是编译器环境中OSGi模块依赖不能解析,如何表明依赖?翻看作者代码后,发现只要正常添加maven依赖即可,打包的时候不用包含进去。

动态的OSGi服务

该部分可以参考《动态的OSGi服务》。包括:

  • OSGI服务的动态性
  • Service Listener
  • Service Tracker

初次接触OSGI Blueprint

该部分可以参考《初次接触OSGI Blueprint》。包括:

  • Blueprint简介
  • Blueprint的入门例子:OSGI服务的注册
  • Blueprint的入门例子:OSGI服务的引用
  • OSGI blueprint的机制原理

OSGi的配置管理:ConfigAdmin

该部分可以参考《OSGi的配置管理:ConfigAdmin》。包括:

  • 动态的OSGI配置
  • 利用blueprint来实现ConfigAdmin

OSGi还有用武之地吗?

本人在网上搜索博客的时候,发现OSGi近几年的博客介绍很少,关注度不高,而且学习的OSGi中文网站2015年后也不怎么更新文章了。在分布式微服务流行的现在,OSGi的确有些尴尬,但是个人感觉对于一些复杂的单体应用,OSGi还是有用武之地的,比如Java 9学习了OSGi,增加了模块化功能并重构了Java库。此外,OSGi解决模块依赖的方法和基于接口和模块的设计理念还是很有价值的。

接下来将对反面观点进行一下介绍。

明显不足

OSGi的关注不是很高,该文作者也谈了它本身的一些不足:相对来说适合单体应用(但单体应用也可以通过容器的等帮助部署),不适合分布式。具体如下所示。

关于隔离与奔溃

OSGi最终基于jvm,无法对bundle对应的服务实现计算资源的隔离,一个服务的故障依然会导致整个jvm crush,这使得在一个运行时的osgi上部署模块级服务只获得了模块部署和启停隔离。

关于扩展能力

服务明确依赖的好处,但是没办法实现计算节点的线性扩展,在当前分布式,微服务,网络计算的趋势下,使得osgi只适合构建单一服务节点的内部应用,但是其分离的bundle的部署负担对于微服务架构来说,有点用大炮打蚊子的臭味。

推荐的应用架构方式

目标

  • 采用“进程间构建的分布式应用”和“进程内的单一应用”分开来进行架构设计。
  • 对于进程间构建的分布式应用,采取基于soa的理念进行容器模式的服务部署模式,服务交互基于远程服务交互相关协议,采用可忍受网络失败的架构设计原则。
  • 对于进程内的应用,如果需要模块级的独立生命周期热部署和模块管理,可以考虑采用OSGi。

容器更方便

但是,容器内基于本地进程间通信的模块交付方式不仅能提供同样的独立生命热部署和模块管理,而且具备随时脱离出去部署成单独容器级服务应用的能力,加速进程间的服务交付提供的整体管理和监视环境基础。

OSGi比较适合嵌入式

OSGi还有用武之地吗?当然我前述都是以构建分布式企业和面向互联网这类应用为前提来讨论的,对于嵌入式的jvm应用,比如著名的osgi案例宝马的车载系统,osgi依然是最好的原则,不过我怀疑基于andriod系统的机制构建类似应用,osgi的采用依然值得商榷。因此,osgi确实面临鸡肋之嫌。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,132评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,802评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,566评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,858评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,867评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,695评论 1 282
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,064评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,705评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,915评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,677评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,796评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,432评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,041评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,992评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,223评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,185评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,535评论 2 343

推荐阅读更多精彩内容

  • 本文是对《OSGi入门教程》课程的笔记。 OSGi基础概念 基本概念 OSGi的一些定义 Open Service...
    天未的博客阅读 8,520评论 0 8
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,598评论 18 139
  • Java 9 中一个重要的新特性就是模块化。它的实现机制是什么那?它和已有的模块框架OSGi有什么差异那?为了回答...
    天未的博客阅读 6,852评论 0 12
  • 场景 单机应用已经越来越不能符合目前越来越复杂的产品需求了。即使是小型应用,至少也需要部署2台以上的服务器做集群。...
    IvanEye阅读 3,592评论 3 11
  • 夜幕降临,高二年级的晚自习下课铃声响起,学生们像往常一样,离开教学楼,一部分回到宿舍。古辉是高二的一名学生,与关才...
    拥抱你的影子阅读 262评论 1 0