前言
什么是服务,简单的说就是把一件事情交给别人去做(服务提供者),至于别人如何去 对应调用者(服务使用者)来说是不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, Dictionaryvar3); //注册服务 ServiceRegistration> registerService(String var1, Object var2, Dictionary var3); //注册服务 ServiceRegistrationregisterService(Classvar1, S var2, Dictionaryvar3); //获取服务引用 ServiceReference>[] getServiceReferences(String var1, String var2) throws InvalidSyntaxException; //获取服务引用 ServiceReference>[] getAllServiceReferences(String var1, String var2) throws InvalidSyntaxException; //获取服务引用 ServiceReference> getServiceReference(String var1); //获取服务引用 ServiceReferencegetServiceReference(Classvar1); //获取服务引用Collection> getServiceReferences(Class var1, String var2) throws InvalidSyntaxException; //获取服务对象S getService(ServiceReferencevar1); //取消服务对象 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{ //添加服务 T addingService(ServiceReferencevar1); //修改服务 void modifiedService(ServiceReferencevar1, T var2); //移除服务 void removedService(ServiceReferencevar1, 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开发工具进行讲解。