采集终端架构分享

基于OSGI的终端采集架构

icon
在分享知识之前,先来简单来介绍一下公司的相关产品信息,以便于在知识分享的过程中,让大家更容易结合业务场景来了解其运作情况。

公司及产品简介

云智慧(北京)科技有限公司成立于2009年,是国内领先的应用性能管理服务商。基于大数据分析,为企业级用户提供全面、专业的端到端的应用性能管理(End To End Application Performance Management)解决方案。

现有两套独居规模的产品线:

  1. 监控宝
    端到端的立体监控体系,保障IT基础设施及交付能力最佳
  2. 透视宝
    面向业务的基于大数据分析的端到端应用性能管理解决方案

通俗来讲,整个产品线亦可理解为:

从服务监测,数据分析,预前告警及提供事前解决方案的一站式服务

那么今天所要分享的便是其中的服务监测这块内容,亦可理解为数据采集终端.其采用的是基于OSGI技术的架构设计,在接下来的介绍内容中,会分两块进行描述:

  1. OSGI初识
  2. OSGI在实际业务场景中的应用

1) OSGI初识

什么是OSGI

OSGI(Open Service Gateway Initiative)是OSGiAlliance组织制定的一个基于Java语言的服务(业务)规范,强调面向模块及服务的方式开发组件,通过组件的高可利用快速构建应用.

特性
  • 组件化设计
  • 面向服务
  • 严格的包引用规范机制
  • 统一生命周期管理
核心内容
  1. OSGI容器
    icon
    上图展示了整个osgi的整个服务框架. 注:图片来源^1

目前基于OSGI规范的实现如下:

- equinox[^4]
- felix[^2]
- knopflerfish[^3]
  1. 生命周期管理
    icon
    上图展示了整个bundle插件的从安装,运行,停止,卸载的整个状态转移过程. 注:图片来源^5
  1. Bundle,即OSGI容器所要装载的实例

    一个完整的bundle主要包括以下几部分:

    • MANIFEST.MF(示例)

      Manifest-Version: 1.0
      Bundle-ManifestVersion: 2
      Bundle-Name: SampleMain
      Bundle-SymbolicName: SampleMain
      Bundle-Version: 1.0.0.qualifier
      Bundle-Activator: com.sample.main.Activator
      Import-Package:org.apache.felix.service.command;version="0.12.1",
      org.osgi.framework;version="1.3.0"
      Bundle-RequiredExecutionEnvironment: JavaSE-1.7
      Export-Package: com.sample.main.api
      

      关键配置说明:

      • Bundle-Activator:用于配置自己的事件定义,用于在bundle的启动或停止方法中处理自己的业务
      • Import-Package:用于配置需要导入依赖的类引用,当且仅当被导入的包也export对应的包才能导入
      • Export-Package:用于配置需要导出的包,即可被其他bundle可见且可导入的包路径
    • Activator.java(示例)

      public class Activator  implements BundleActivator{
      
          //可以从bundlecontext中获取其它bundle注册的服务
          private static BundleContext context;
      
          static BundleContext getContext() {
              return context;
          }
      
          public void start(BundleContext bundleContext) throws Exception {
              Activator.context = bundleContext;
      
              //TODO register the service
          }
      
        public void stop(BundleContext bundleContext) throws Exception {
            Activator.context = null;

            //TODO unregister the service
        }

    }
系统类说明:
- `BundleActivator`:用于捕捉bundle的start和stop事件。 
- `BundleContext`:bundle在框架中的上下文,可以通过它与框架交互。
开源应用
  • Eclipse
  • IBM websphere
  • Apache
    • Caml: Apache Camel是Apache基金会下的一个开源项目,它是一个基于规则路由和中介引擎,提供企业集成模式的Java对象的实现,通过应用程序接口(或称为陈述式的Java领域特定语言(DSL))来配置路由和中介的规则。
    • Karaf: 是一个基于osgi运行环境的轻量级容器
    • ServiceMix:开源集成容器,它结合了Apache ActiveMQ、Camel、CXF和Karaf的特性和功能到一个强大的运行平台,你可以用它构建属于自己的集成解决方案.
    • Ace:是一个软件发布框架,用于管理和发布模块化软件的组件、配置数据、其他基于OSGi的产品以及相关目标系统

2) OSGI在实际业务场景中的应用

就采集终端的业务特性,在公司在的整个业务体系占据着重要地位,可以说是数据奠基者,后端分析之根本,所以在考虑它的设计要求时,主要关注以下几个方面:

  1. 稳定性 确保使用新的采集插件或补丁时不会影响系统的运行
  2. 扩展性 采集服务是在不断完善与扩张的,因此需要灵活扩展插件
  3. 可用性 要保证7*24的正常运行。在出现故障,可支持自动重启或主从机切换
  4. 安全性 要保障客户机的数据安全

OSGI的特性主要体现在对插件的扩展性方面,设计思路大致如下:

  1. 定义一个接口插件包,用于抽象定义发现服务或采集数据的接口,以及相关的实体,要保证此接口不轻易改动
  2. 定义一个或多个针对上述接口的扩展实现,比如主机,mysql,tomcat等服务,只需要在此插件中实现某一种服务类型的服务发现或采集业务实现,即完成了采集终端一种服务的扩展开发。
  3. 定义一个调用接口服务的插件包,在其内部只需通过framework提供的bundleContext上下文对象,直接利用接口定义即可获取到所有实现此接口的所有扩展实例,从而避免了与扩展实现之间的直接依赖.

    在此开发过程中,主要利用了osgi中的包隔离机制动态加载以及通过面向服务调用这三方面来实现服务的动态扩展。

具体代码示例如下:

  1. 定义接口插件,如discoverapi.jar

    public interface IDiscover {
    
        /**
         * discover the service
         * @return ServiceInfo the service's infomation
         */
        public ServiceInfo doDiscover();
    
    }
    
  2. 定义实现插件,如: 主机发现 hostdiscover.jar

    public class HostDiscover implements IDiscover {
    
        public ServiceInfo doDiscover(){
            //TODO do some stuff
            return xxxx;
        }
    
    }
    
  3. 定义实现插件,如: mysql服务发现 mysqldiscover.jar

    public class MysqlDiscover implements IDiscover {
    
        public ServiceInfo doDiscover(){
            //TODO do some stuff
            return xxxx;
        }
    
    }
    
  4. 定义调用插件,如:servicemain.jar
public class ServiceComponent {

    public void doDiscover() throws InvalidSyntaxException{
        List<IDiscover> serviceList = getBundles(Activator.getContext(), IDiscover.class);

        for(IDiscover ids : serviceList){
            System.out.println("find service:");
            System.out.println("    the plugin [id: "+ids.id()+"+, name:"+ids.name()+" ], discover the detail:"+ids.doDiscover()+"\n");
        }
    }


    public static <T> List<T> getBundles(BundleContext bundleContext,Class<T> clazz) throws InvalidSyntaxException {
        Collection<ServiceReference<T>> srfList = null;

        String filter = "(group=" + clazz.getName() + ")";
        try {
             srfList = bundleContext.getServiceReferences(clazz, filter);
        } catch (InvalidSyntaxException e) {
            throw new InvalidSyntaxException("the filter syntax invalid error.",filter,e);
        }

        List<T> bundleList = (List<T>)new ArrayList(srfList.size());
        for (ServiceReference<T> srf : srfList) {
            bundleList.add(bundleContext.getService(srf));
        }
        return bundleList;
    }
}

实例下载:参考附件

  1. SampleMain
  2. SamplePluginApi
  3. SamplePluginHost
  4. SamplePluginMysql
  5. felix framework
多谢各位捧场!~