前言
什么是服务,简单的说就是把一件事情交给别人去做(服务提供者),至于别人如何去 对应调用者(服务使用者)来说是不care的。在现实世界中 服务使用者(客户),只关心结果是什么,而不关心过程;比如客户点了一个芝士披萨,餐厅如何去做这个披萨客户不会去过问(过程),客户只管最后的披萨是否好吃(结果),在程序设计的世界中也是如此。
服务化或者微服务化,本质上就是对一组服务按业务进行分类封装,并进行独立部署,这是构建分布式系统的基础。服务化可以实现系统之间的解耦:核心业务逻辑都在各自系统内部完成,有些工作需要使用外部协助时,只需要根据一定协议调用外部服务即可。这种松耦合的关系,还可以实现服务的热插拔;另外服务不可用时,也可以方便的采取一些兜底措施。
对于一个大型电商网站系统的设计也是如此,该系统一般会由成百上千个子系统构成,这些子系统各自处理自己内部的核心业务,当需要其他子系统协助时,通过调用服务接口即可。如此,各个系统可以独自并行开发和维护,并且所有的服务只需要开发一次就可以在多个子系统中复用。这就是涉及到公司级的SOA服务治理,一般都会采用成熟的服务治理框架来完成,以现在流行的RPC服务治理框架为例,服务框架一般分为:服务提供者、服务注册中心、服务使用者,如下:
好了 打住 关于服务就不扯远了,这跟今天的主题OSGI服务层有什么关系呢?其实OSGI服务层的作用跟SOA服务治理框架的作用完全一样,而且设计思想也几乎是一样的,都是按照上图的三个角色以及关系进行设计的。唯一不同的时:SOA服务治理面向的公司级服务治理,而OSGI服务层面向的是单个JVM实例内部服务治理,也就是普通服务化的缩小版。
OSGI服务层
为什么要在JVM内部还要实现服务化呢?其实原因与公司级服务化的目的类似:
可以实现多个模块的完全解耦(高内聚低耦合模块),实现一个由多个松耦合模块(Bundle)构建起的单个系统;
实现服务的热插拔,与普通SOA服务治理一样,OSGI框架可以在其内部实现服务的热更新、热替换(换另外一种实现);
并行开发,在项目组内部可以提前把单个系统拆分成多个模块(Bundle),定义好各个模块之间的边界协议(接口,或者说服务),由不同的开发人员并行开发,他们只需要专注自己负责的模块即可。想想就是一件令人兴奋的事,下面就开始来看看OSGI规范是如何定义的服务层API,自己我们如何去使用这些API。服务层的API定义在上下文BundleContext中:
public interface BundleContext extends BundleReference { //添加服务监听器 void addServiceListener(ServiceListener var1, String var2) throws InvalidSyntaxException; //添加服务监听器 void addServiceListener(ServiceListener var1); //取消服务监听器 void removeServiceListener(ServiceListener var1); //注册服务 ServiceRegistration<?> registerService(String[] var1, Object var2, Dictionary<String, ?> var3); //注册服务 ServiceRegistration<?> registerService(String var1, Object var2, Dictionary<String, ?> var3); //注册服务 <S> ServiceRegistration<S> registerService(Class<S> var1, S var2, Dictionary<String, ?> var3); //获取服务引用 ServiceReference<?>[] getServiceReferences(String var1, String var2) throws InvalidSyntaxException; //获取服务引用 ServiceReference<?>[] getAllServiceReferences(String var1, String var2) throws InvalidSyntaxException; //获取服务引用 ServiceReference<?> getServiceReference(String var1); //获取服务引用 <S> ServiceReference<S> getServiceReference(Class<S> var1); //获取服务引用 <S> Collection<ServiceReference<S>> getServiceReferences(Class<S> var1, String var2) throws InvalidSyntaxException; //获取服务对象 <S> S getService(ServiceReference<S> var1); //取消服务对象 boolean ungetService(ServiceReference<?> var1); //省略其它生命周期层方法 }
纵观这些API,大致可以归为3类:注册服务方法、获取服务方法、监听器相关方法。
注册服务方法
OSGI框架提供了几个重载的注册服务方法,其作用就是发布服务到“注册中心”。发布一个服务很简单:
public class HelloWorldAtivictor implements BundleActivator{ @Override public void start(BundleContext context) throws Exception { Dictionary dictionary = new Properties(); dictionary.put("test","test"); ServiceRegistration registration = context.registerService(HelloService.class.getName(),new HelloServiceImpl("小明"),dictionary); dictionary.put("test","test2"); registration.setProperties(dictionary);//修改元数据 System.out.println("start server bundle"); registration.unregister();//取消服务注册 } @Override public void stop(BundleContext context) throws Exception { System.out.println("stop"); } }
本示例中使用的注册方法有三个参数:第一个是服务的名字(一般是接口名),第二个是服务的具体实现对象,第三个是元数据主要用来区分同一个接口发布成不同的实现服务,客户端可以根据条件筛选自己需要的服务。
服务注册成功后会返回一个ServiceRegistration对象,这个对象是Bundle内部私有的。可以通过调用其setProperties方法修改元数据;以及unregister方法取消服务注册,在Bundle停止时,框架会自动清理所有的该Bundle注册的服务,所以一般情况下不需要我们手动去取消服务注册。
获取服务方法
获取一个OSGI服务,需要两步 第一步通过上下文的getServiceReference方法 从注册中心获取注册引用:
public class ClientActivator implements BundleActivator { @Override public void start(BundleContext context) throws Exception { ServiceReference ref = context.getServiceReference(HelloService.class.getName()); if(ref!=null){ try{ HelloService helloService = ((HelloService)context.getService(ref)); if(helloService!=null){//调用服务方法 helloService.sayHello(); } }catch (Exception e){ System.out.println("服务调用异常"); } finally { context.ungetService(ref); } } System.out.println("start client bundle"); } @Override public void stop(BundleContext context) throws Exception { } }
第二步,调用上下文对象的getService方法获取到真实的服务引用,然后就可以调用服务接口中定义的方法了:
HelloService helloService = ((HelloService)context.getService(ref)); helloService.sayHello();
由于在OSGI框架下所有的服务都有可能随时消失,注意开发时需要进行适当的空值判断。另外由于getService方法获取的是提供服务Bundle中的一个真实引用,尽量不要长期持有,否则服务Bundle在需要停止时,无法正常的进行拉结回收(因为还在被其他对象引用)。在使用完服务后最好手动调用ungetService方法告诉注册中心释放引用。
监听器相关方法
由于OSGI框架中的服务有可能随时消失或者更新,前面提到了尽量不要长期持有一个对象,这势必会导致每次在使用时都需要重复取服务。如果有一种方式能在服务发生变化是通知服务使用方,同步进行更新,这样就可以解脱出来了。OSGI服务层的服务层中定义了监听器注册机制(ServiceListener),来解决这个问题。
服务的状态变化一般有三种:注册、更新、注销。也就是说监听器 需要监听这三种变化,并通知服务使用者做出响应。首先来看一个简单的监听器实现:
public class HelloListener implements ServiceListener { private BundleContext context; public HelloListener(BundleContext context) { this.context = context; } public void serviceChanged(ServiceEvent event) { switch (event.getType()) { case ServiceEvent.REGISTERED: Object service = context.getService(event.getServiceReference()); if(service instanceof HelloService){ ClientActivator.helloService = (HelloService) context.getService(event.getServiceReference()); } break; case ServiceEvent.MODIFIED: break; case ServiceEvent.UNREGISTERING: service = context.getService(event.getServiceReference()); if(service instanceof HelloService){ context.ungetService(event.getServiceReference()); ClientActivator.helloService = null; } break; default: break; } } }
主要就是实现serviceChanged方法,它有一个ServiceEvent类型参数,用于接收当前的变更类型。每当接收到变化时就可以更新helloService对象,当服务消失时把helloService置为null。这个对象就可以被放到一个对象的成员变量里(本示例是一个静态常量里),方便复用。如果有多个可用的服务,可以把这些服务对象放到一个集合中,根据业务需要使用不同的服务对象。
监听器定义完成后,还需要把它注册到一个服务下,注册服务监听器一般在启动器中完成:
public class ClientActivator implements BundleActivator { public static volatile HelloService helloService;//服务对象 private ExecutorService executorService; @Override public void start(BundleContext context) throws Exception { System.out.println("client开始启动"); //注册监听器 HelloListener listener = new HelloListener(context); context.addServiceListener(listener); //这里可以启动一个线程测试HelloService服务是否可用 executorService = Executors.newSingleThreadExecutor(); executorService.submit(new TestTask(context)); } @Override public void stop(BundleContext context) throws Exception { executorService.shutdown();//优雅的关闭线程池 } } //模拟测试helloService服务 public class TestTask implements Runnable{ private BundleContext context; public TestTask(BundleContext context) { this.context = context; } @Override public void run() { boolean flag = true; while (flag){ try { if (ClientActivator.helloService != null) { ClientActivator.helloService.sayHello(); } else { System.out.println("没有可用的服务"); } }catch (Exception e){ System.out.println("业务异常"); } //睡5秒重试 try { Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); System.out.println("线程池关闭"); flag = false; } } } }
注册一个服务监听器很简单,只需要调用Bundle上下文对象的addServiceListener方法即可。当服务提供方注册服务到注册中心时,就可以自动触发对静态的成员变量helloService进行赋值。当服务提供方取消服务注册时,会自动触发对静态的成员变量helloService置空。
可见自己实现服务监听器是件比较麻烦的工作,OSGI提供了服务追踪ServiceTracker对服务监听进行了封装,使用起来更简单,而且不容易出错。
服务追踪器ServiceTracker
使用ServiceTracker跟踪服务,无需自己实现服务监听器,使用方式很简单:ServiceTracker有两个构造函数,调用构造函数创建追踪器对象,并调用其open方法开启追踪即可:
public class TrackerActivator implements BundleActivator { private ServiceTracker serviceTracker; private ExecutorService executorService; @Override public void start(BundleContext context) throws Exception { serviceTracker = new ServiceTracker(context, HelloService.class.getName(), null); serviceTracker.open();//打开追踪器 //新开一个线程,模拟调用服务 executorService = Executors.newSingleThreadExecutor(); executorService.submit(new TestTask(serviceTracker)); System.out.println("start tracker bundle"); } @Override public void stop(BundleContext context) throws Exception { serviceTracker.close();//关闭追踪器 executorService.shutdown(); } } //模式测试服务 public class TestTask implements Runnable{ public ServiceTracker serviceTracker; public TestTask(ServiceTracker serviceTracker) { this.serviceTracker = serviceTracker; } @Override public void run() { boolean flag = true; while (flag){ try { HelloService helloService = (HelloService)serviceTracker.getService(); if (helloService!= null) { helloService.sayHello(); } else { System.out.println("没有可用的服务"); } }catch (Exception e){ System.out.println("业务异常"); } //睡5秒重试 try { Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); System.out.println("线程池关闭"); flag = false; } } } }
上面模拟代码比较长,但对于使用追踪器来说,只有三行代码:
ServiceTracker serviceTracker = new ServiceTracker(context, HelloService.class.getName(), null); serviceTracker.open();//打开追踪器 //获取服务 HelloService helloService = (HelloService)serviceTracker.getService();
创建追踪器对象-->开启追踪器-->调用最终器的getService()方法获取服务对象 即可。使用ServiceTracker追踪一个服务就这么简单。
另外ServiceTracker的构造方法第三个参数可以传入一个“定制器”对象,上述示例中传入的是null,即非定制模式。接下来看下定制模式,首先就要创建一个定制器 实现ServiceTrackerCustomizer接口即可,这个接口定义了三个方法:
public interface ServiceTrackerCustomizer<S, T> { //添加服务 T addingService(ServiceReference<S> var1); //修改服务 void modifiedService(ServiceReference<S> var1, T var2); //移除服务 void removedService(ServiceReference<S> var1, T var2); }
“定制器”的作用是在服务发生变化时(注册服务、修改服务、移除服务)可以打印一些日志,或者做一些保证等之定义操作,创建一个自己的“定制器”类,实现这个接口:
public class MyServiceTrackerCustomizer implements ServiceTrackerCustomizer { private BundleContext context; public MyServiceTrackerCustomizer(BundleContext context) { this.context = context; } @Override public Object addingService(ServiceReference serviceReference) { HelloService helloService = (HelloService)context.getService(serviceReference); //返回一个包装后的服务 return new MyHelloService(helloService); } @Override public void modifiedService(ServiceReference serviceReference, Object o) { System.out.println("服务被修改了"); } @Override public void removedService(ServiceReference serviceReference, Object o) { System.out.println("服务被移除了"); } } class MyHelloService implements HelloService{ private HelloService helloService; public MyHelloService(HelloService helloService) { this.helloService = helloService; } @Override public void sayHello() { System.out.println("包装服务开始"); helloService.sayHello(); System.out.println("包装服务结束"); } }
这个定制器,在发现有服务注册时,使用了一个装饰器对服务进行了包装;在服务修改或者移除时,打印一条日志信息。使用带定制器的 服务追踪器 很简单,把定制器传入ServiceTracker构造方法第三个参数即可:
ServiceTracker serviceTracker = new ServiceTracker(context, HelloService.class.getName(), new MyServiceTrackerCustomizer(context));
总的来说使用服务追踪器比自己定义服务监听器 使用起来更简单,而且不容易出错(但一定要启动opne和close追踪器)。
关于OSGI服务层相关的API就总结到这里,至此已经对OSGI的模块层、生命周期层、服务层分别进行了总结。但示例比较少,下次准备做一个完整的demo,并使用idea开发工具进行讲解。
相关推荐
基于OSGi的两层服务模型正是针对ROSGi的这些缺点,从一种新的视角来看待分布式环境中的OSGi平台,将每一个独立的OSGi平台视为一个能够提供远程服务并可能要获取其他远程服务的构件,使用面向服务的构件模型的方式来...
这是来自cnblogs博主 静默虚空 的文章 ,为方便离线查看,转存为了pdf
本文档主要针对OSGi.NET模块化框架使用进行了描述 OSGi.NET框架是一个参照了OSGi规范的模块化管理框架。框架为应用程序(组件(bundle))提供了一个标准环境。整个框架可以划分为一些层次: 1.运行环境 2.模块...
OSGI技术 讲述了osgi的三个模块服务层 模块层 生命周期层 主要介绍了如何进行模块化的开发等等,可以灵活的开发网络模块
JEMMA DAL Web API捆绑包 ... DAL WEB API提供对OSGi设备抽象层中可用的设备,功能和事件的REST和WebSocket访问。 REST API用于提供对设备和功能服务的访问。 函数操作调用和FunctionData编辑是使用Java Reflection实
该捆绑包为以下群集提供了对OSGi设备抽象层( 和 )的支持: 布尔控制服务器ColorControlServer 简单计量服务器门锁服务器PowerProfileServer 恒温服务器WindowCoveringServer ApplianceControlServer(仅用于...
JEMMA DAL Web API捆绑包通过DAL WEB API与智能家居进行交互此README文件提供了有关如何通过JEMMA及其REST API(更准确地称为DAL ... WebSocket API提供了基于OSGi EventAdmin构建的发布/订阅服务您可以使用一些伪造的
XF支持的特性非常广泛,但特性主要在以下一些方面: 支持的Web服务标准包括: SOAP WS-Addressing WS-Policy WS-ReliableMessaging ...JAX-WS API,用于Web服务开发 ...JAX-RS (JSR 311 1.0) API...引用OSGi远程服务实现
基于嵌入式inux的家庭服务网关的搭建 我们的嵌入式linux服务网关选用了ARM9...功能搭配使用更加灵活,我们在用户应用层引入了OSGI(开放式服务网关)架构,一切用户的应用操作都是通过这个OSGI架构与外界进行信息交换
HAP客户端它是一种添加可靠层以将数据从平台传输到配置服务器的服务。 它可以使用准时和批处理模式(尤其是在没有可用连接时使用)。 HAP 代理在家庭网络 xml apis 中公开以访问平台中的数据并通过 REST 接口向...
针对现有框架在集成、封装、表示、标准化等方面存在的不足,提出、设计并实现了一个符合OSGi规范、具有较强扩展性、灵活性...最后基于AAE框架构建了一个水利信息门户服务平台,并给出了应用、开发实例,运行效果良好。
JAX-RS (JSR 311) 是一个社区驱动的标准用于使用 Java 构建 RESTful Web 服务。JAX-RS 的参考实现是 Jersey,并提供 OSGI ...该项目用于连接服务层的 Jersey 和 OSGi。这意味着 OSGi 服务可发布为 RESTful Web 服务。
它提出了一种在OSGi层之上运行的模块化体系结构,使其可以通过插件高度扩展。 它支持多种协议绑定,例如HTTP和CoAP。 提供了各种互通代理,以实现与供应商特定技术(例如Zigbee和Phidgets设备)的无缝通信。 ...
它为基础平台层提供基础设施服务,⽐如命名服务、分布式⽂件系统、 MapReduce等。 (1)ZooKeeper集群⽤于命名映射,做为Hadoop集群的命名服务器,基础平台层的任务调度控制台可以通过命名服务器访问Hadoo
模块:依赖注入(OSGi声明式服务)。带有JWT支持的JAX-RS 2.1(RESTEasy v4.6.0)。用于创建和验证JWT的JWT模块。持久性(SQL / NO-SQL)层-JPA(EclipseLink v2.7.8)或MyBatis v3.5.6或MongoDB v4.4.3阿光(V4.0.2...
org.springframework.context-3.0.0.M4.jar: 提供在基础IoC功能上的扩展服务,此外还提供许多企业级服务的支持,如邮件服务、任务调度、JNDI定位、EJB集成、远程访问、缓存以及各种视图层框架的封装等 org.spring...
代理的通用本体抽象层(COALA)有助于在MAS和ABM之间重用代理代码 COALA通过与代理和组织相关的通用概念,支持跨流行平台和中间件(多代理系统,应用服务器等)的可重用和可重现的行为(如何协商,学习,协调……)...
1、微内核2、配置简单3、模块化4、开箱即用5、完全兼容Spring6、设计理念极其先进,很多思想来自OSGi,但是在现有技术的实现 缺点: 二次改造定制难缺少成熟的SOA或者RPC框架Dubbox: 1、完全兼容...
它非常动态(由于OSGi服务),因此可以添加新的自定义收集器(用户/自定义实现)。 调度程序:调度程序或事件驱动的收集器调用调度程序,以将收集的数据调度到附加程序。 追加者:追加者负责将收集的数据发送/存储...
服务应用 资源池 数据库 大数据与nosql zookeeper hadoop hbase mongodb strom spark java语言 语言语法基础 异常 泛型 内部类 反射 序列化 nIo 匿名类 包装类 优先级 引用 语言工具类库 ...