#Dubbo框架和Websphere部署若干问题及解决办法
前言:Rest协议(等同于dubbo协议的语义,而http、grizzly、mina、netty是技术范畴的协议)是支持http协议和netty协议的(通过RestServerFactory生成),而http协议dubbo提供了三种servlet容器实现,分别为jetty容器、tomcat容器、外部应用服务器servlet容器。其中,jetty容器和tomcat容器是dubbo内置,而外部应用服务器servlet容器则是依赖于web.xml加载启动DispatcherServlet(在实现的service方法调用传入的handler方法)并由(resteay框架的HttpServletDispatcher,实现service方法)转发到servlet容器。需要说明的是,内置的jetty容器和tomcat容器也是会转发到resteay框架的HttpServletDispatcher,最后转发到上述容器。Netty协议是通过resteay框架的NettyJaxrsServer实现容器。Resteasy框架做的是服务发布的注解解析和请求转发,是最靠近应用服务的那一层,是应用服务和web容器的中间层。
一、嵌入式servlet容器配置(tomcat)和外部应用服务器的servlet容器配置(外部servlet容器配置,如websphere)
即必须将dubbo的BootstrapListener和DispatherServlet添加到web.xml,以完成dubbo的REST功能与外部servlet容器的集成。
注意:如果你是用spring的ContextLoaderListener来加载spring,则必须保证BootstrapListener(dubbo用来维护serveltcontext和port的)配置在ContextLoaderListener(启动dubbo的)之前,否则dubbo初始化会出错。
其实,这种场景下你依然可以坚持用嵌入式server,但外部应用服务器的servlet容器往往比嵌入式server更加强大(特别是如果你是部署到更健壮更可伸缩的WebLogic,WebSphere等),另外有时也便于在应用服务器做统一管理、监控等等。
二、Dubbo中rest协议暴露端口的销毁解决办法(当dubbo没启用外部servlet容器的时候,一旦dubbo启用外部容器,这不存在该问题)
当dubbo服务在容器(如tomcat,wehsphere)启动时,暴露的端口号是随着容器的停止而销毁的。例如,当项目部署到Wabsphere,停止应用后,暴露的端口号始终还是处于监听状态,会导致重新启动应用后报端口已占用的错误,需将Wabsphere重启才能销毁端口。此处解决办法为,监听容器的销毁,调用dubbo服务的销毁方法,操作如下:
新建类DubboServiceListener :
import com.alibaba.dubbo.config.ProtocolConfig;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
public class DubboServiceListener implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent sce) {
System.out.println("初始化");
}
@Override
public void contextDestroyed(ServletContextEvent sce) {
System.out.println("销毁dubbo实例中....");
ProtocolConfig.destroyAll();
System.out.println ("销毁dubbo服务完成!");
}
}
web.xml 增加配置:
com. **.DubboServiceListener
三、Websphere环境下遇到的java.lang.NoSuchMethodError问题及解决方法
项目在tomcat部署运行没有问题,但到Websphere部署运行后,外部PC机访问后出现如下错误:
java.lang.NoSuchMethodError: javax.ws.rs.core.HttpHeaders.getHeaderString(Ljava/lang/String;)Ljava/lang/String;
at org.jboss.resteasy.core.interception.PreMatchContainerRequestContext.getHeaderString(PreMatchContainerRequestContext.java:201)
at com.alibaba.dubbo.rpc.protocol.rest.RpcContextFilter.filter(RpcContextFilter.java:54)
at org.jboss.resteasy.core.ResourceMethodInvoker.invokeOnTarget(ResourceMethodInvoker.java:256)
at org.jboss.resteasy.core.ResourceMethodInvoker.invoke(ResourceMethodInvoker.java:242)
……
NoSuchMethodError问题一般为jar包冲突问题。如上述错误所示,
PreMatchContainerRequestContext在调用javax.ws.rs.core.HttpHeaders的getHeaderString时,容器中存在一个jar包有javax.ws.rs.core.HttpHeaders包名类名都一样的类,但是这个类里面没有getHeaderString这个方法,且被优先加载了,实际需要加载的javax.ws.rs-api-2.0.jar里面的HttpHeaders类就没有被加载,所以报出上述错误。
出现这个错误有两种情况:
1.项目中jar包互相冲突,存在javax.ws.rs这个jar包的不同版本
2.项目中jar包和容器的jar包冲突,存在包名类名一样的类(本情况属于这一种)
首先确定是否为第一种情况。在工程依赖文件pom.xml的本地路径下,进入cmd,执行mvn dependency:tree >t.txt,打开t.txt可看到项目依赖jar包间接引用情况。查看是否存在javax.ws.rs-api.jar的不同版本。(或者自己猜测是某个包可能出现javax.ws.rs.core.HttpHeaders这个类,再检查有无getHeaderString这个方法)。若出现了冲突jar包,则通过maven的exclusions排除。本项目遇到的情况为:引入的resteasy-jaxrs.jar间接引入了jaxrs-api.jar,而jaxrs-api.jar中存在javax.ws.rs.core.HttpHeaders,也没有getHeaderString方法。于是通过maven exclusions掉,但是报错依旧,故不是这种情况引起。
由于项目为websphere(版本号8.1)部署,进lib下查看所有启动时加载jar包,反编译查看后发现j2ee.jar也存在javax.ws.rs.core.HttpHeaders,且没有getHeaderString方法。但是lib下的jar不可以随便做更改。这里可以用websphere引用共享库的方式,让websphere优先去加载javax.ws.rs-api-2.0.jar。具体操作如下:
点击“新建”按钮后,输入共享库名称,这个自己随便定义,但是类路径需要填写之前所建的共享库目录。注意需勾选“请对此共享库使用隔离的类装入器”。点击“应用”后保存。
注意:此处最好重启一下was,实际项目中没有重启was遇到若干错误,最后发现重启后得以解决。
3) 在应用中引入共享库:
进入Websphere企业应用,点击需要引用共享库的应用,选择共享库引用,勾选应用程序或者模块(此处我们勾选模块),点击引用共享库,将刚才新建的共享库引用后确定。
4)进入“类装入和更新检测”,将“类装入器顺序”改为“类已装入并且是先使用本地类装入器(父类最后)”,点击确定,保存,完成所有配置。启动应用重新访问,问题解决。