`

OSGI之服务层

    博客分类:
  • OSGI
阅读更多

前言

 

什么是服务,简单的说就是把一件事情交给别人去做(服务提供者),至于别人如何去 对应调用者(服务使用者)来说是不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));
 

 

总的来说使用服务追踪器比自己定义服务监听器 使用起来更简单,而且不容易出错(但一定要启动opneclose追踪器)。

 

关于OSGI服务层相关的API就总结到这里,至此已经对OSGI的模块层、生命周期层、服务层分别进行了总结。但示例比较少,下次准备做一个完整的demo,并使用idea开发工具进行讲解。

 

 

  • 大小: 22.1 KB
0
0
分享到:
评论

相关推荐

    OSGi入门篇:服务层

    OSGi(Open Service Gateway Initiative)是一个Java社区定义的模块化服务平台,它允许在同一个运行环境中部署多个版本的同一个...对于需要高度模块化、动态更新和维护的应用程序,OSGi服务层是一个非常有价值的工具。

    OSGi入门篇:生命周期层

    OSGi(Open Services Gateway Initiative)是一个基于Java语言的服务平台,提供了一种动态化、模块化的应用程序架构。在OSGi架构中,整个生命周期管理是十分重要的组成部分,它保证了应用能够动态地进行安装、启动、...

    OSGi入门教程(OSGi Introduce)

    1. **运行环境**:基础的执行环境,支持OSGi服务的运行。 2. **安全层**:基于Java的安全机制,确保模块间的操作安全。 3. **模块层**:定义了Bundle的结构和交互方式,包括Import Package、Export Package、...

    osgi-core技术文档与说明

    OSGi核心技术文档详细介绍了OSGi的三个模块层次:服务层、模块层和生命周期层。 服务层是OSGi最上层的抽象,负责定义和实现所有服务的生命周期。在OSGi中,服务是指一组具有明确目的的代码和数据的集合,例如日志...

    OSGI类资源动态加载介绍

    OSGi服务层是框架中负责服务注册和服务请求的层次。在OSGi中,服务是注册到框架中的Java对象,可以设置Service的属性,并在需要时通过属性查找和获取服务。 OSGi框架支持懒加载模式,即只有在实际有请求发生时,...

    OSGi基本原理pdf (by 静默虚空 )

    OSGi框架从概念上可以分为三个层次:模块层、生命周期层和服务层。 模块层是OSGi框架中最基础的部分。它的主要功能是处理包和共享代码。模块化在OSGi中是通过为Jar包添加元数据(metadata)来实现的。元数据定义了...

    osgi简介PPt(自制)

    4. **服务层**:服务注册是OSGI的一个关键特性,它允许动态的协作和资源共享。bundles可以通过服务注册来共享对象,而不是直接通过类共享,这样可以更好地适应动态的安装和卸载。相关的实现集中在serviceReference.h...

    OSGI原理和实现

    服务层是OSGi中最活跃的部分,它支持基于服务的编程模型。在这个模型中,组件通过发布和消费服务来进行交互。服务是一个接口,可以被任何需要它的bundle使用。服务注册和查找是在运行时动态进行的,这使得系统能够...

    基于OSGi和Spring开发Web应用教程

    这样,Spring-DM会自动管理这些服务的生命周期,并使得其他bundle可以通过OSGi服务发现机制找到并使用它们。 总的来说,基于OSGi和Spring的Web应用开发结合了两者的优点:OSGi提供了模块化和动态部署的能力,而...

    OSGi进阶源代码

    在OSGi中,Spring可以作为服务注册和查找的平台,通过OSGi服务注册表来发现和管理其他服务,同时Spring的Bean可以在各个bundle之间共享。 3. **Hibernate**:Hibernate是一个对象关系映射(ORM)框架,简化了数据库...

    OSGi Service Platform Core Specification

    ### OSGi服务平台核心规范知识点解析 #### 核心概念与定义 **OSGi (Open Service Gateway Initiative)** 是一个为 Java 定义模块化系统的框架。它支持将应用程序和服务分解为可重用的组件,并在运行时动态安装、...

    osgi hibernate

    - 依赖注入:利用OSGi的服务发现机制,动态获取并使用Hibernate服务。 - 数据源管理:在OSGi环境中配置数据源,确保其能在多个模块间共享。 5. **挑战与解决方案**:集成过程中可能遇到的问题,比如类加载冲突、...

    OSGi+核心规范+4.1-中文版

    OSGi服务平台规范4.1是其第四个版本,由OSGi成员公司共同开发,扩展了原有的API并修复了部分问题,以确保与早期版本的兼容性。 **OSGi框架概述** OSGi核心规范4.1提供了基础框架,支持动态部署和管理称为bundle的...

    java -osgi

    - **L3层(系统服务)**:OSGi提供了一些系统级别的服务,例如权限管理和启动级别控制等。 #### 三、OSGi全栈 OSGi不仅是一个简单的模块化框架,还提供了一个完整的生态系统。其中包括了: - **权限管理**:通过`...

    开发一个简单的 OSGi Web 应用实例

    - 使用Spring配置文件(`computeAdd-context.xml` 和 `computeMultiply-context.xml`)将这两个实现注册为OSGi服务。 **步骤2:实现Web层** 1. **Web层(bundle com.zxn.example.web):** - 使用Spring MVC框架来...

Global site tag (gtag.js) - Google Analytics