标签:
1、类加载问题ClassNotFoundException
在OSGi环境中,ClassNotFoundException是最常见的,主要是因为在OSGi环境,每一个Bundle都有自己独立的ClassLoader,而Bundle之间的通信交互是通过依赖关系(import/export)来控制,随着系统越来越复杂,依赖关系也就越来越复杂(尤其是在一些没有严格控制依赖的系统中),因此,出现这种异常的频率非常高。类似的,也经常会出现NoClassDefDoundErr、ClassCastException异常。
遇到这类问题,直接从异常日志是很难精确定位根源的,必须打开OSGi提供的调试日志(详细调试选项可以参考OSGi项目的Debug.java类),这样从日志就可以看出是哪个Bundle加载哪个类遇到什么问题。
但是,如果我们在启动服务的时候就打开这些调试日志,系统启动会非常慢,因为输出了很多的日志,运行期也同样非常慢。那么,就需要一个可以动态开关调试日志的功能。我们可以通过扩展OSGi的CommandProvider,实现一个自己的命令,然后可以动态的修改Debug.java属性值来达到目的。
例如修改调试参数的命令:
public class DebugCommandProvider implements CommandProvider { public void _debug(CommandInterpreter ci) { String debug_field = ci.nextArgument(); String tip = ci.nextArgument(); if (null != debug_field && debug_field.trim().length() > 0) { if (null != tip && tip.trim().length() > 0) { try { debug_field = debug_field.trim().toUpperCase(); Field field = Debug.class.getDeclaredField(debug_field); boolean flag = tip.trim().toUpperCase().equals("ON"); field.set(null, flag); ci.print("设置Debug参数:[" + debug_field + "=" + flag + "]\r\n"); } catch (Exception e) { ci.print("设置Debug参数时出现异常:" + e.getMessage() + "\r\n"); } } else { ci.println("未指定参数,例如: debug debug_loader <on|off>"); } } else { ci.println("未指定参数,例如: debug debug_loader <on|off>"); } } ... }
2、OSGi调试环境扩展
为了让开发人员能知道OSGi容器里面的方方面面,框架本身提供了一个调试控制台,通过命令可以查看容器装载的Bundle、服务、依赖等所有需要知道的系统服务信息。
在开发环境下,通过JVM参数-console开启,直接在Eclipse控制台就可以输入命令;如果不希望命令输出和控制台日志混合的话,可以单独指定一个端口。
通常,生产环境就会指定一个端口来隔离应用日志,然后telnet进去操作命令。
例如生产环境开启控制台的配置:
<servlet-class>org.eclipse.equinox.servletbridge.BridgeServlet</servlet-class> <init-param> <param-name>enableFrameworkControls</param-name> <param-value>true</param-value> </init-param> <init-param> <param-name>commandline</param-name> <param-value>-console 9999</param-value> </init-param> </servlet>
但是,这个配置不能用在集群下,会端口冲突,动态web配置显得麻烦。
然后,一个Web形式的OSGi控制台就应该出来了,主要实现就是提供一个命令的重定向,将输入的命令重定向到OSGi控制台,然后将命令结果输出到web页面。核心就是围绕org.eclipse.osgi.framework.internal.core.FrameworkConsole类传参,并指定好PrintWriter。
3、服务异常自动分析报告
无论什么好东西,如果不好好的使用的话,时间久了都会把它玩烂。OSGi就是如此,如果模块、依赖起初没有严格规划好,越到后面,遇到的问题就越多,也越复杂,维护成本非常高。
但是,有些问题可以根据经验和具体实现来自动分析,然后得出一份异常分析报告并给出相应的解决方案,好处就是根据分析报告可以快速定位解决问题。
报告可以包括参数配置信息、Bundle依赖校验结果、Bundle启动异常、bean异常、上下文发布异常、资源注册信息等
值得一提的是Bundle依赖校验,通常都是采用eclipse自带的依赖校验功能,可以直接看出不满足的依赖信息。但是,生成环境在没有eclipse的情况就没那么简单了。虽然OSGi会将依赖校验不满足的信息写到一个日志文件里面,但是太冗余,对于问题定位很多时候都没什么帮助。
举个例子,如果有一个依赖链涉及很多的Bundle,而处于这个链中间的某个Bundle依赖有问题导致不能正常解析,结果就导致依赖它的Bundle(包括间接的)都不能解析。笨办法就是操作控制台,一个一个排查(通过diag命令可以看未满足约束信息),简单的情况可以整出结果来,稍微复杂的情况就绕不出来了。最后实在不行就只有将项目拷贝到eclipse中看校验结果。
该问题的终极解决办法就是在框架装载完Bundle后自动输出校验结果,而且只输出依赖链的叶子节点信息,这样就可以一目了然的发现是哪个Bundle导致的依赖问题。核心就是围绕org.eclipse.osgi.service.resolver.State类,它有一个方法getResolverErrors()可以根据BundleDescription来获取解析错误信息,然后对这些信息进行分析得出结果。
4、分层启动
如果一个系统比较庞大,并且模块依赖比较规范,比如系统分了平台层、业务层,只有业务层能依赖平台层,这样就可以优先启动平台层,然后再启动业务层。通过这样的启动可以减少服务依赖的等待时间,并且如果平台层启动发现问题时,可以停止后续的启动过程,对于问题的定位效率也是非常大的提升。
OSGi有一个StartLevel的服务,首先约定Bundle的启动级别,然后通过调整框架级别就可以实现分层启动了。
5、OSGi与Spring的整合问题
可以通过SpringDM进行整合。
但是也有一些优化,比如在对Bundle的Class进行扫描的时候,同时会对它依赖的Bundle进行扫描,其实这是多余的,只关心当前Bundle的类,其他的即使扫描出来也没有任何用处,但是该操作又比较耗时。
6、OSGi部署优化
在集群环境下,部署的web应用都会同步到各个节点,如果应用较大的话,同步操作也比较耗时。
如果节点都在同一个机器上的话,可以让所有节点都从同一个Bundle插件目录读取,然后将里面的Bundle安装到OSGi框架。
标签:
原文地址:http://www.cnblogs.com/forrestajun/p/osgiexperience.html