OSGI service layer

foreword

 

What is a service, in a nutshell, is to give someone else to do something (service provider), and it doesn't matter how others respond to the caller (service user) . Serve users (customers) in the real world, and only care about the result, not the process; for example, if a customer orders a cheese pizza, how does the restaurant make the pizza? The customer will not ask (process), and the customer only cares about the final pizza Tasty or not (results), also in the world of programming.

 

Service-oriented or micro-service-oriented, in essence, is to classify and encapsulate a set of services by business and deploy them independently, which is the basis for building a distributed system. Service-oriented can realize the decoupling between systems: the core business logic is completed within the respective systems, and when some work requires external assistance, it is only necessary to call external services according to a certain protocol. This loosely coupled relationship can also realize hot-plugging of services; in addition, when services are unavailable, it is also convenient to take some bottom-up measures.

 

The same is true for the design of a large-scale e-commerce website system. The system is generally composed of hundreds of subsystems, each of which handles its own internal core business. When the assistance of other subsystems is required, the service interface can be called. . In this way, each system can be independently developed and maintained in parallel, and all services can be reused in multiple subsystems only need to be developed once. This is SOA service governance related to the company level, which is generally completed by a mature service governance framework. Taking the popular RPC service governance framework as an example, the service framework is generally divided into: service provider, service registry, and service user. ,as follows:



 

 

Well, let's stop talking about services. What does this have to do with today's topic OSGI service layer? In fact , the role of the OSGI service layer is exactly the same as that of the SOA service governance framework, and the design ideas are almost the same, which are designed according to the three roles and relationships in the above figure. The only difference is that SOA service governance is oriented to company-level service governance, while the OSGI service level is oriented to the internal service governance of a single JVM instance, which is a reduced version of ordinary service.

 

OSGI service layer

 

Why do we need to implement service within the JVM ? In fact, the reason is similar to the purpose of company-level servitization:

It can realize the complete decoupling of multiple modules (high cohesion and low coupling module), and realize a single system built by multiple loosely coupled modules ( Bundle );

Realize hot-plugging of services. Like ordinary SOA service governance, the OSGI framework can implement hot-update and hot-replacement of services within it (for another implementation);

For parallel development, a single system can be divided into multiple modules ( Bundle ) in advance within the project team, and the boundary protocols (interfaces, or services) between each module can be defined and developed in parallel by different developers. They only need Just focus on the modules you are responsible for. It's an exciting thing to think about. Let's start to see how the OSGI specification defines the service layer APIs and how we use these APIs . The API of the service layer is defined in the context BundleContext :

public interface BundleContext extends BundleReference {
    //Add service listener
    void addServiceListener(ServiceListener var1, String var2) throws InvalidSyntaxException;
    //Add service listener
    void addServiceListener(ServiceListener var1);
    // cancel the service listener
    void removeServiceListener(ServiceListener var1);
    // register service
    ServiceRegistration<?> registerService(String[] var1, Object var2, Dictionary<String, ?> var3);
    // register service
    ServiceRegistration<?> registerService(String var1, Object var2, Dictionary<String, ?> var3);
    // register service
    <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开发工具进行讲解。

 

 

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=326342129&siteId=291194637