随着公司业务的不断扩展,业务人员向IT部门提出了更多的需求,为了快速响应业务人员的需求,尽快的交付成果,码农们日以继夜的将代码往现有系统中不断的堆叠,直到某一天,码农们发现无法再继续往系统上堆叠新的代码,它们不得不停下了工作,经过一番争吵之后,他们决定将整个系统推翻重建。诸如此类的场景不断的重演,就好比地球自转,周而复始,永不停息。虽然对于某些人来说这或许是一件好事情,因为他们可以利用重建系统的机会尝试新的技术框架和整理凌乱的业务逻辑,但是对于绝大多数人来说,那简直就是噩梦,或许会因此而丢到自己的工作。

OSGi为解决诸如此类的问题提供了很好的解决方案。架构师在设计一个新系统的时候,不可能面面俱到,因为世界上所有的事物都是千变万化的,所以预知未来并不是架构师所必须具备的技能。我们能做的只是尽早的发现问题并解决问题,将风险控制在可控范围内。通过OSGi的模块化设计思想,我们可以最大程度的拆分日益臃肿的系统。通过划分清晰的系统边界,可以将整个系统分而治之,使每个子系统的功能足够的内聚,只开放有限的接口和其他子系统进行交互,从而构建一个分布式的OSGi应用平台。接下去我们通过一个具体的实例来说明在基于OSGi的企业级开发框架中如何的发布远程服务以及调用远程服务。

首先我们需要在common-service-facade Bundle中编写一个接口,该接口定义了供其他系统调用的服务方法。如下所示:

package org.storevm.helloworld.common.service.facade;/*** * @author Administrator* @version $Id: DistributedOsgiService.java, v 0.1 2013-2-27 下午3:06:16 Administrator Exp $*/
public interface DistributedOsgiService {/*** 服务* @param text* @return*/String getValue(String text);
}

在上述代码中,我们定义了getValue方法,它返回String类型的的值,并接收一个String类型的参数。如果其他系统需要调用这个服务,只要引入这个Bundle就可以。

接着我们要暴露出该服务的Package以便其他Bundle可以引入,在common-service-facade Bundle中的MANIFEST.MF文件内加入一行,如下所示:

Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: Helloworld Common Service Facade Bundle
Bundle-SymbolicName: helloworld-common-service-facade;singleton:=true
Bundle-Version: 1.0.0
Export-Package: org.storevm.helloworld.common.service.facade

粗体字部分就是我们加入的配置。接着我们在common-service-client Bundle中编写实现类,代码如下所示:

package org.storevm.helloworld.service.client;import org.apache.log4j.Logger;
import org.springframework.stereotype.Component;
import org.storevm.eosgi.core.annotation.Service;
import org.storevm.eosgi.core.annotation.ServiceType;
import org.storevm.eosgi.core.utils.LogUtils;
import org.storevm.helloworld.common.service.facade.DistributedOsgiService;/*** * @author Administrator* @version $Id: DistributedOsgiServiceImpl.java, v 0.1 2013-2-27 下午4:34:05 Administrator Exp $*/
@Component("/distributedOsgiService")
@Service(serviceInterface = DistributedOsgiService.class, serviceType = ServiceType.HESSIAN)
public class DistributedOsgiServiceImpl implements DistributedOsgiService {private static final Logger LOGGER = Logger.getLogger(DistributedOsgiServiceImpl.class);/** * @see org.storevm.helloworld.common.service.facade.DistributedOsgiService#getValue(java.lang.String)*/@Overridepublic String getValue(String text) {LogUtils.info(LOGGER, "接收到参数, text={0}", text);return "调用了DistributedOsgiService, text=" + text;}}

这里需要关注的是@Service注解,该注解用以发布一个远程服务,远程服务使用的是Hessian框架(另外还支持RMI和HTTP远程服务)。让我们再次的启动Eclipse IDE中的OSGi容器,当你看到如下提示信息时,表示我们的远程服务发布成功了。

(图一)

我们可以验证一下服务是否启动成功,请打开任意浏览器,比如IE。然后输入如下地址:

http://localhost:8660/helloworld/services/distributedOsgiService

按回车,会显示如下页面:

(图二)

红色框中说明了,Hessian只支持POST请求,这证明我们的远程服务发布成功了。开发框架默认的远程服务端口是8660,如果要修改该端口,请打开jetty-config Bundle中的jetty.xml文件,如下图所示:

(图三)

将红色框中的8660修改成自己的端口号,然后重启OSGi容器即可。这个例子比较简单,接下来我们稍微修改一下,让远程服务调用biz-service-impl Bundle中的OSGi服务。首先在biz-service-impl Bundle中定义一个接口,代码如下:

package org.storevm.helloworld.biz.service.impl;/*** * @author Administrator* @version $Id: DistributedOsgiInvokeService.java, v 0.1 2013-2-27 下午5:41:48 Administrator Exp $*/
public interface DistributedOsgiInvokeService {/*** 调用* @param text* @return*/String invoke(String text);
}

然后编写一个实现类,代码如下:

package org.storevm.helloworld.biz.service.impl;import org.storevm.eosgi.core.annotation.OsgiService;/*** * @author Administrator* @version $Id: DistributedOsgiInvokeServiceImpl.java, v 0.1 2013-2-27 下午5:43:28 Administrator Exp $*/
@OsgiService(interfaces = { DistributedOsgiInvokeService.class })
public class DistributedOsgiInvokeServiceImpl implements DistributedOsgiInvokeService {/** * @see org.storevm.helloworld.biz.service.impl.DistributedOsgiInvokeService#invoke(java.lang.String)*/@Overridepublic String invoke(String text) {return "调用了DistributedOsgiInvokeService服务,text=" + text;}}

请注意这里的@OsgiService注解,如果对此注解还不够了解,请看上一篇文章(OSGi Annotations)中的相关内容。接着我们需要将Bean注册到Spring上下文中,如下所示:

<bean id="distributedOsgiInvokeService" class="org.storevm.helloworld.biz.service.impl.DistributedOsgiInvokeServiceImpl"/>

最后我们需要暴露biz-service-impl Bundle中的package,请在MANIFEST.MF文件中添加如下内容:

Export-Package: org.storevm.helloworld.biz.service.impl

OSGi服务发布完成了,然后我们修改DistributedOsgiServiceImpl实现类的代码,如下所示:

package org.storevm.helloworld.service.client;import org.apache.log4j.Logger;
import org.springframework.stereotype.Component;
import org.storevm.eosgi.core.annotation.Service;
import org.storevm.eosgi.core.annotation.ServiceReference;
import org.storevm.eosgi.core.annotation.ServiceType;
import org.storevm.eosgi.core.utils.LogUtils;
import org.storevm.helloworld.biz.service.impl.DistributedOsgiInvokeService;
import org.storevm.helloworld.common.service.facade.DistributedOsgiService;/*** * @author Administrator* @version $Id: DistributedOsgiServiceImpl.java, v 0.1 2013-2-27 下午4:34:05 Administrator Exp $*/
@Component("/distributedOsgiService")
@Service(serviceInterface = DistributedOsgiService.class, serviceType = ServiceType.HESSIAN)
public class DistributedOsgiServiceImpl implements DistributedOsgiService {private static final Logger          LOGGER = Logger.getLogger(DistributedOsgiServiceImpl.class);private DistributedOsgiInvokeService distributedOsgiInvokeService;/** * @see org.storevm.helloworld.common.service.facade.DistributedOsgiService#getValue(java.lang.String)*/@Overridepublic String getValue(String text) {LogUtils.info(LOGGER, "接收到参数, text={0}", text);return distributedOsgiInvokeService.invoke(text);}/*** Setter method for property <tt>distributedOsgiInvokeService</tt>.* * @param distributedOsgiInvokeService value to be assigned to property distributedOsgiInvokeService*/@ServiceReferencepublic void setDistributedOsgiInvokeService(DistributedOsgiInvokeService distributedOsgiInvokeService) {this.distributedOsgiInvokeService = distributedOsgiInvokeService;}}

这里需要关注的是如何的用Annotation引入OSGi服务。好了,再次重启Eclipse IDE中的OSGi容器,如果一切正常,将看到如下所示的信息提示:

(图四)

接下去我们将再创建一个新的基于OSGi的企业级开发框架,用来调用Helloworld系统发布的远程服务。关于如何创建基于OSGi的企业级开发框架,请查看《基于 OSGi的企业级开发框架实践——开发框架的创建》中的相关介绍。这里假设你已经新建了另外的一个系统,我们取名为:InvokeHelloworld。如下图所示的是我创建的InvokeHelloworld系统的项目结构:

(图五)

好吧,让我把它run起来,结果报了一大堆异常信息。这是因为我们同时启动了2个OSGi容器,而我们开发框架使用atomikos做为JTA分布式事务支持,因此同时启动2个JTA容器造成log冲突。解决办法是,修改2个系统中任意一个的jta-config Bundle中的jta.properties文件(我修改的是invokeHelloworld系统),修改一下log的路径,不要冲突即可。如下图所示:

(图六)

再次分别重启2个系统,这次不再会有异常报错信息了。

接下去我们在invokeHelloworld系统中的common-service-integration Bundle中编写一个调用Helloworld系统的远程服务的接口(之前的文章中介绍过,common-service-integration的职责是接入其他系统的远程服务,因此将所有调用远程服务的类都写在这个Bundle中)。代码如下:

package org.storevm.invokeHelloworld.common.service.integration;/*** * @author Administrator* @version $Id: InvokeRemoteServiceClient.java, v 0.1 2013-2-27 下午6:25:16 Administrator Exp $*/
public interface InvokeRemoteServiceClient {/*** 远程调用* @param text* @return*/void invokeRemoteService();
}

在写实现类之前,我们还需要做其他的一些事情,由于InvokeHelloworld系统需要引入Helloworld系统的common-service-facade Bundle,所以我们要先在Helloworld项目根目录下执行Maven的install命令,将所有的jar包安装的本地仓库中,如下所示:

mvn clean install -Dmaven.test.skip=true

然后将common-service-facade JAR包配置到InvokeHelloworld系统的总控pom.xml文件中,如下图所示:

(图七)

在InvokeHelloworld系统中的common-service-integration项目下的pom.xml文件中添加如下图所示的配置信息:

(图八)

最后在InvokeHelloworld项目根目录下执行:

mvn eclipse:eclipse

接着执行:

mvn clean install -Dmaven.test.skip=true

执行成功之后刷新整个项目目录结构。通过《基于 OSGi的企业级开发框架实践——开发框架的创建》中介绍的导入第三方Bundle的方法,将helloworld-common-service-facade Bundle(该Bundle在maven本地仓库目录中,比如我的目录为:D:\repository\org\storevm\helloworld\helloworld-common-service-facade\1.0.0)导入到Eclipse IDE中,如下图所示:

(图九)

好了,现在可以编写调用远程服务的实现类了,如下代码所示:

package org.storevm.invokeHelloworld.common.service.integration;import org.storevm.helloworld.common.service.facade.DistributedOsgiService;/*** * @author Administrator* @version $Id: InvokeRemoteServiceClientImpl.java, v 0.1 2013-2-27 下午6:44:48 Administrator Exp $*/
public class InvokeRemoteServiceClientImpl implements InvokeRemoteServiceClient {private static final Logger    LOGGER = Logger.getLogger(InvokeRemoteServiceClient.class);/* 远程服务接口 */private DistributedOsgiService distributedOsgiService;/** * @see org.storevm.invokeHelloworld.common.service.integration.InvokeRemoteServiceClient#invokeRemoteService()*/@Overridepublic void invokeRemoteService() {String value = distributedOsgiService.getValue("调用方:invokeRemoteService");LOGGER.info(value);}/*** Setter method for property <tt>distributedOsgiService</tt>.* * @param distributedOsgiService value to be assigned to property distributedOsgiService*/public void setDistributedOsgiService(DistributedOsgiService distributedOsgiService) {this.distributedOsgiService = distributedOsgiService;}}

接下去,我们需要配置远程服务的代理Bean(这里用到的是Spring提供的remote框架),如下所示 (定义在bundle-context.xml文件中):

<!-- 远程服务调用 -->
<bean id="distributedOsgiService" class="org.springframework.remoting.caucho.HessianProxyFactoryBean"><property name="serviceUrl" value="http://localhost:8660/helloworld/services/distributedOsgiService"/><property name="serviceInterface" value="org.storevm.helloworld.common.service.facade.DistributedOsgiService"/><property name="overloadEnabled" value="true"/><property name="readTimeout" value="60000"/>
</bean>

其中,serviceUrl:远程服务的URL地址。serviceInterface:远程服务接口类型全限定名称。overloadEnabled:是否允许调用重载的远程服务方法。readTimeout:访问远程服务的超时时间(单位:毫秒)。

为了方便测试,我们需要将InvokeRemoteServiceClientImpl实现类注册到Spring上下文中,配置如下:

<bean class="org.storevm.invokeHelloworld.common.service.integration.InvokeRemoteServiceClientImpl" init-method="invokeRemoteService"/>

好了,重启InvokeHelloworld项目的OSGi容器。如果一切正常,我们会在Console中看到如下的日志信息:

(图十)

从上图我们可以发现,调用Helloworld系统的远程服务成功了,并打印出正确的日志信息。另外你也可以将integration Bundle中的类发布为OSGi服务,这样其他的Bundle就可以执行调用远程服务的方法了,这里我们就不再讨论,有兴趣的同学可以自己尝试一下。

下一篇文章我们将介绍开发框架提供的OSGi集成测试工具,它可以帮助我们在不启动OSGi容器的情况下进行OSGi服务的集成测试。敬请关注!

基于OSGi的企业级开发框架实践——发布和使用分布式OSGi服务相关推荐

  1. 基于OSGi的企业级开发框架实践——序篇

    OSGi就好比达摩克利斯之剑一般,在其锋利而强大的背后却隐藏着不可预知的危险.我的形容好像有点夸张,不过大多数的研发团队基本上都认为OSGi并非像各类评论文章中介绍的那样光彩熠熠,而更多的是疑惑.怀疑 ...

  2. 基于 OSGi的企业级开发框架实践——认识OSGi和SpringDM

    一. OSGi基础 1. 什么是OSGi OSGi--Open Service Gateway Initiative,最初的目的是为各种嵌入式设备提供通用的软件运行平台.后来经过10年的发展和壮大,O ...

  3. 基于OSGi的企业级开发框架实践——运行开发框架

    在上一篇文章中我们介绍了如何创建基于OSGi的企业级开发框架,并将开发框架导入到Eclipse中.接下去,我们将在Eclipse中让开发框架运行起来,一来可以检测一下我们的开发框架是否完整无缺,二来在 ...

  4. 基于OSGi的企业级开发框架实践——OSGi Annotations

    我们的开发框架之所以选择使用Spring框架,是因为它提供了一个简单易用的Bean编程模型(采用IoC和AOP设计模式),通过XML配置文件简化了复杂而冗长的Bean初始化以及依赖关系的定义.不过随着 ...

  5. 基于 OSGi的企业级开发框架实践——开发框架的创建

    终于到了主角登场的时刻了!之前化了不少笔墨介绍有关OSGi和Spring DM框架的内容,目的就是为了我们开发框架的出场做铺垫.在序篇中我已经介绍了做为应用程序开发框架所应具备的要素.其中最为关键的是 ...

  6. kbengine0.2.3发布,开源分布式游戏服务端引擎

    2019独角兽企业重金招聘Python工程师标准>>> v0.2.3 memory profile 支持 加入jemalloc并在linux下默认使用 客户端重连机制 服务端自动安装 ...

  7. 基于Docker和Kubernetes的企业级DevOps实践训练营

    基于Docker和Kubernetes的企业级DevOps实践训练营 课程准备 离线镜像包 百度:https://pan.baidu.com/s/1N1AYGCYftYGn6L0QPMWIMw 提取码 ...

  8. 基于 PAI 搭建企业级个性化推荐系统 最佳实践

    场景描述 本方案结合阿里云 PAI 团队预置的基础版算法方案为例,演示如何以阿里云提供的数据.AI 类产品为基础,离线部分采用Maxcompute&Dataworks&PAI的大数据& ...

  9. 汉得宣布开源:基于容器的企业级应用 PaaS 平台

    开发四年只会写业务代码,分布式高并发都不会还做程序员? >>>   2018年5月20日,Choerodon猪齿鱼正式发布 0.5.0 版本,同时汉得公司宣布Choerodon猪齿鱼 ...

最新文章

  1. Udacity机器人软件工程师课程笔记(二十八) - 卷积神经网络实例 - Fashion-MNIST数据集
  2. 【IOC 控制反转】Android 事件依赖注入 ( 事件三要素 | 修饰注解的注解 | 事件依赖注入步骤 )
  3. 【Spring学习】使用Spring的jdbcTemplate简化JDBC操作
  4. 洛谷 - P1725 琪露诺(动态规划+单调队列优化)
  5. CF Gym100917 C
  6. Linux开关命令(shutdown,reboot,halt,init)
  7. [linux学习] 字符界面linux安装vmtools
  8. 飞猪信息流内容推荐探索
  9. html中的灵动标签,《帝国网站管理系统》一招鲜吃天遍天系列教程之 灵动标签使用...
  10. 继续来研究JScript解析引擎的GC问题
  11. 如何修改一个类的私有成员?
  12. IOS+Android 车牌识别SDK开发包——可以各种角度瞬间OCR识别车牌牌号
  13. 为什么蚕宝宝很有钱?
  14. alios是安卓吗_鸿蒙OS系统被质疑,谷歌也有新布局!阿里云OS事件会再现吗?
  15. Mysql DBA 高级运维学习之路-mysql数据库乱码问题
  16. 一名程序员的内心独白:我很忙,但我的代码还是很糟糕
  17. 在阿里云OSS,如何更好搭建自己的云储存
  18. TeamViewer14检测为商业用途最优解
  19. CAD绘制区域覆盖对象
  20. mgo EnsureIndex注意事项

热门文章

  1. 数理统计-6.1 点估计的概念与无偏性
  2. 四平方和定理_简化循环
  3. SVPWM与SPWM的区别
  4. 怎么使用PS一键抠图?
  5. Python数据分析上机
  6. 1. SCARA机器人建模
  7. php表格单元格怎么实现排序,excel表格数据怎么自动排列-excel表格如何实现自动排序...
  8. 【竞赛|数学建模】Part 1:什么是数学建模和各模块介绍
  9. Eclipse 汉化教程完美版
  10. 使用gomail发送邮件