使用Blueprint管理模块的加载、服务依赖和配置

本文深入探讨Blueprint在OSGi框架中的角色,介绍其依赖注入、服务管理和配置管理功能,详解bean、service、reference等核心元素,以及在Opendaylight平台上的MD-SALblueprint扩展。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

使用Blueprint管理模块的加载、服务依赖和配置

本文来自:http://www.deepsdn.com/archives/36

什么是blueprint?

Blueprint 是 OSGi Service Platform Enterprise Specification 标准的一 部分,被设计用来为OSGi容器提供依赖注入的框架. 它来源于Spring DM,因此它们很类似. Karaf中包括一个blueprint的实现,即 (the Apache Aries blueprint 实现).

Blueprint是以xml文档来构建应用,但它也有采用Annotation的方式,我们在此只介绍xml的方式。
在Bundle里,这个xml默认的位置在OSGI-INF/blueprint下,也可以在manifest.mf里指定其它位置上的xml文档。
当一个包含blueprint xml文档的bundle install并resolved,并且active后,Aries blueprint container就会开始解析这个文档。
在处理这些xml文档的过程中,bundle还会有个blueprint的状态,区别于bundle的状态。这些状态包括Graceperiod,Created,Fail。其中Created就是blueprint container已经解析完文档,并且文档中mandatory的依赖都得到满足了,这时blueprint的应用已经组装完毕。而Graceperiod则是应用正在组装中。Fail是当blueprint无法解析xml文档或者是mandatory的依赖在超时时间内未能获得满足。

在Blueprint里,还可以发布和组装OSGI service。

blueprint xml文档,有四种主要元素: beanservicereference and reference-list:

  • bean – 用来描述创建Java实例的元素,可以指定实例初始化的类名,构造方法,构造方法的入参及属性.
  • service – 把bean发布为OSGi service
  • reference – 通过接口名引用一个OSGi service,可以指定一个特定的属性过滤器
  • reference-list – 通过接口名引用多个 OSGi services ,可以指定一个特定的属性过滤器

1.引用基础的MD-SAL binging服务,比如DataBrokerRpcProviderRegistry, and NotificationPublishService

<?xml version="1.0" encoding="UTF-8"?>
<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"
                 xmlns:odl="http://opendaylight.org/xmlns/blueprint/v1.0.0"
    odl:use-default-for-reference-types="true">

  <reference id="dataBroker" interface="org.opendaylight.controller.md.sal.binding.api.DataBroker" odl:type="default"/>
  <reference id="rpcRegistry" interface="org.opendaylight.controller.sal.binding.api.RpcProviderRegistry"/>
  <reference id="notificationService" 
          interface="org.opendaylight.controller.md.sal.binding.api.NotificationPublishService"/>

</blueprint>

对于一个接口有多个实现的,odl:type可以用来确定某个具体实现,比如对于DataBroker,有一个默认实现和一个特殊实现PingPongDataBroker,如果不明确指定type,则获取的服务实现是不确定的,如果想要某个具体实现,必须指定type,odl:type=”default”表示默认实现,odl:type=”pingpong”表示PingPongDataBroker实现。

服务获取的动态性

在blueprint内部,reference 元素为服务实例创建了一个动态代理. 之所以如此做,就是考虑到OSGi 服务的动态本质,所有服务实现都可能因为bundle的动态启停,重启而生效或者失效. 当然,实际上,在像Opendaylight这样具有几百个bundle的大规模的平台里,真正实现完全的动态性是存在一定难度的(but that’s another topic).

在bundle启动时,blueprint container 会首先等待依赖的服务得到满足, 默认的等待超时时间是 5 分钟.如果超时,blueprint container会马上把bundle状态标记为失败. 另外,如果某个服务在启动后状态变为不可用,再调用该服务时,该调用将会被阻塞,直到新的服务实例可用或者超时. 如果服务调用超时,系统会抛出一个运行时异常. 这样就可以在应用代码里做相应处理以支持bundle的动态更新.

2.通过bean初始化业务逻辑类

<?xml version="1.0" encoding="UTF-8"?>
<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"
                 xmlns:odl="http://opendaylight.org/xmlns/blueprint/v1.0.0"
    odl:use-default-for-reference-types="true">

  <reference id="dataBroker" interface="org.opendaylight.controller.md.sal.binding.api.DataBroker"/>

  <bean id="foo" class="org.opendaylight.app.Foo" init-method="init" destroy-method="close">
    <property name="dataBroker" ref="dataBroker"/>
  </bean>

  <bean id="bar" class="org.opendaylight.app.Bar">
    <argument ref="foo"/>
  </bean>

</blueprint>
3. 发布服务
<?xml version="1.0" encoding="UTF-8"?>
<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"
                 xmlns:odl="http://opendaylight.org/xmlns/blueprint/v1.0.0">

  <bean id="fooImpl" class="org.opendaylight.app.FooImpl">
    <!-- constructor args -->
  </bean>

  <service ref="fooImpl" interface="org.opendaylight.app.Foo" odl:type="default"/>

</blueprint>

MD-SAL blueprint扩展

odl mdsal对blueprint的扩展(xmlns:odl=”http://opendaylight.org/xmlns/blueprint/v1.0.0″)

i. Global RPC注册

<?xml version="1.0" encoding="UTF-8"?>
<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"
                 xmlns:odl="http://opendaylight.org/xmlns/blueprint/v1.0.0">

  <bean id="fooRpcService" class="org.opendaylight.app.FooRpcServiceImpl">
    <!-- constructor args -->
  </bean>

  <odl:rpc-implementation ref="fooRpcService"/>
</blueprint>

ii. Global RPC的获取

<?xml version="1.0" encoding="UTF-8"?>
<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"
                 xmlns:odl="http://opendaylight.org/xmlns/blueprint/v1.0.0">

  <odl:rpc-service id="fooRpcService" interface="org.opendaylight.app.FooRpcService"/>

  <bean id="bar" class="org.opendaylight.app.Bar">
    <argument ref="fooRpcService"/>
  </bean>

</blueprint>

iii. Routed RPC的注册

<?xml version="1.0" encoding="UTF-8"?>
<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"
                 xmlns:odl="http://opendaylight.org/xmlns/blueprint/v1.0.0">

  <bean id="fooRoutedRpcService" class="org.opendaylight.app.FooRoutedRpcServiceImpl">
    <!-- constructor args -->
  </bean>

  <odl:routed-rpc-implementation id="fooRoutedRpcServiceReg" ref="fooRoutedRpcService"/>

  <bean id="bar" class="org.opendaylight.app.Bar">
    <argument ref="fooRoutedRpcServiceReg"/>
  </bean>

</blueprint>

iv. NotificationListenser的注册

<?xml version="1.0" encoding="UTF-8"?>
<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"
                 xmlns:odl="http://opendaylight.org/xmlns/blueprint/v1.0.0">

  <bean id="fooListener" class="org.opendaylight.app.FooNotificationListener">
    <!-- constructor args -->
  </bean>

  <odl:notification-listener ref="fooListener"/>

</blueprint>

还有一个需要了解的知识是blueprint的配置管理,可以直接使用blueprint的ConfigAdmin来配置某些本地参数,配置项是写在etc/*.cfg文件里的blueprint xml里要写上对namespace.

“http://aries.apache.org/blueprint/xmlns/blueprint-cm/v1.1.0″的使用,如下

<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0" 
xmlns:odl="http://opendaylight.org/xmlns/blueprint/v1.0.0" 
xmlns:cm="http://aries.apache.org/blueprint/xmlns/blueprint-cm/v1.1.0" >
  <cm:property-placeholder persistent-id="org.opendaylight.foo" update-strategy="none">
    <cm:default-properties>
      <cm:property name="max-retries" value="5"/>
    </cm:default-properties>
  </cm:property-placeholder>

  <bean id="foo" class="org.opendaylight.app.Foo">
    <property name="maxRetries" value="${max-retries}"/>
  </bean>

</blueprint>

也可以通过ODL的datastore实现集群内的全局配置

Opendaylight实现了一个不lueprint扩展,clustered-app-config,它可以令应用方便使用yang定义的配置数据,当然,这些配置必须定义在yang的顶层container或list内.

标签clustered-app-config从MD-SAL datastore里读取应用配置数据并封装配置数据为bean,这些配置bean具有与OSGi服务同样的依赖方式。如果配置数据不能从datastore读取并初始化(比如ODL 的datastore还没启动好),blueperint将会一直等待配置数据的依赖直至超时失败。

clustered-app-config元素内部注册了 ClusteredDataTreeChangeListener 监听器,以便在配置数据变化或者删除时,重启blueprint container.

使用 YANG container 实现应用配置

像下面这样定义一个 yang顶层 container:

module my-app-config {
    yang-version 1;
    namespace "urn:opendaylight:myapp:config";
    prefix my-app-config;

    description
      "Configuration for ...";

    revision "2016-06-24" {
        description
            "Initial revision.";
    }

    container my-config {
        leaf id {
            type string;
            mandatory true;
        }

        leaf connection-timeout {
            type uint16;
            default 3000;
        }
    }
}

在你的blueprint配置XML里, 添加 clustered-app-config 标签,该标签的binding-class属性设置为由my-config这个container生成的带包路径的Java interface名称.

<odl:clustered-app-config id="myConfig"
      binding-class="org.opendaylight.yang.gen.v1.urn.opendaylight.myapp.config.rev160624.MyConfig">
</odl:clustered-app-config>

clustered-app-config 元素从MD-SAL data store里获取数据,生成绑定的DataObject对象bean(这里就是MyConfig的对象),该bean可以被注入到其他bean里。如果datastore里该数据为空,则由yang模型里默认值生成一个bean实例,在上面例子里,会创建一个connection-timeout等于3000,id为null的MyConfig实例

如果不想用yang里定义的默认值,你可以自己通过default-config子标签在blueprint xml提供默认值..

<odl:clustered-app-config id="myConfig"
       binding-class="org.opendaylight.yang.gen.v1.urn.opendaylight.myapp.config.rev160624.MyConfig">
  <odl:default-config><![CDATA[
    <my-config xmlns="urn:opendaylight:myapp:config">
      <id>foo</id>
    </my-config>
  ]]></odl:default-config>
</odl:clustered-app-config>

这里,我们对id字段提供了一个默认值,default-config元素里包含了yang定义的数据的XML表示(包括namespace),数据体用 CDATA段封装,以让blueprint container不把这部分数据作为blueprint标记来处理,这部分的数据表示形式与通过RESTCONF设置datastore时的XML格式一致.

默认值也可以通过外部文件指定. 在自动化测试脚本中,这非常有用,而且,你能在控制器没有运行的情况下方便的修改配置。 clustered-app-config 将在目录etc/opendaylight/datastore/initial/config下查找形如 <yang module name>_<container name>.xml 的文件. 比如,上面的例子,就会查找etc/opendaylight/datastore/initial/config/my-app-config_my-config.xml. XML文件名也可以用属性default-config-file-name明确指定.

注意: 默认值只是在datastore里没有数据时的初始值.一旦在datastore里设置了值,默认值就没用了.

如果是在同一个文件里,可以直接把MyConfig通过bean id注入到业务逻辑的bean.

<?xml version="1.0" encoding="UTF-8"?>
<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"
                 xmlns:odl="http://opendaylight.org/xmlns/blueprint/v1.0.0">

  <odl:clustered-app-config id="myConfig"
        binding-class="org.opendaylight.yang.gen.v1.urn.opendaylight.myapp.config.rev160624.MyConfig">
  </odl:clustered-app-config>
  
  <bean id="foo" class="org.opendaylight.myapp.Foo">
    <argument ref="myConfig"/>
  </bean>

</blueprint>

org.opendaylight.myapp.Foo类的构造方法入参就是类org.opendaylight.yang.gen.v1.urn.opendaylight.myapp.config.rev160624.MyConfig.

你可能还想直接把container里某个leaf字段的值注入到业务逻辑bean,而并不是整个container对象. 比如, 我们定义一个类 org.opendaylight.myapp.Bar  with ,在构造方法里希望能把MyConfig里的idconnection-timeout传进来.

public class Bar {
    public Bar(String id, int connectionTimeout) {
    }
}

这可以通过为构造方法的入参定义内部bean来实现,例子如下:

<bean id="foo" class="org.opendaylight.myapp.Bar">
  <argument>
    <bean factory-ref="myConfig" factory-method="getId" />
  </argument>
  <argument>
    <bean factory-ref="myConfig" factory-method="getConnectionTimeout" />
  </argument>
</bean>

使用 YANG list 实现配置

如果你想同时初始化某个组件的多个实例, 每个都有自己的单独的配置, 不需要为每一个实例定义特定的container, 使用顶层的 list 元素就能实现.

我们象下面这样定义一个顶层的 list:

module my-component-config {
    yang-version 1;
    namespace "urn:opendaylight:myapp:config";
    prefix my-component-config;

    description
      "Configuration for ...";

    revision "2016-06-24" {
        description
            "Initial revision.";
    }

    list my-component-config {
        key "instance-name";
        leaf instance-name {
            type string;
        }

        leaf connection-timeout {
            type uint16;
            default 3000;
        }
    }
}

元素clustered-app-config 支持设置一个类型为type的 list key. 这个 key唯一标示一个实例. 通过属性list-key-value指定he instance value must be specified via the list-key-value attribute.

<odl:clustered-app-config id="myComponentConfig1"
      binding-class="org.opendaylight.yang.gen.v1.urn.opendaylight.myapp.config.rev160624.MyComponentConfig"
      list-key-value="instance1">
  <odl:default-config><![CDATA[
    <my-component-config xmlns="urn:opendaylight:myapp:config">
      <instance-name>instance1</instance-name>
      <connection-timeout>1000</connection-timeout>
    </my-component-config>
  ]]></odl:default-config>
</odl:clustered-app-config>

<bean id="foo1" class="org.opendaylight.myapp.Foo">
  <argument ref="myComponentConfig1"/>
</bean>

<odl:clustered-app-config id="myComponentConfig2"
      binding-class="org.opendaylight.yang.gen.v1.urn.opendaylight.myapp.config.rev160624.MyComponentConfig"
      list-key-value="instance2">
 <odl:default-config><![CDATA[
    <my-component-config xmlns="urn:opendaylight:myapp:config">
      <instance-name>instance2</instance-name>
      <connection-timeout>2000</connection-timeout>
    </my-component-config>
  ]]></odl:default-config>
</odl:clustered-app-config>

<bean id="foo2" class="org.opendaylight.myapp.Foo">
  <argument ref="myComponentConfig2"/>
</bean>

这里我们定义了两个配置,每个对应一个org.opendaylight.myapp.Foo的实例。

我们也为每个配置分别设置了默认值,对于yang list元素,在XML中,必须明确使用 list-key-value对list的key赋值。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值